tree: 1ae4df80e30f4a86703f30cfa0e52701499c6466
  1. conditional_expression_A01_t01.dart
  2. conditional_expression_A01_t02.dart
  3. conditional_expression_A01_t03.dart
  4. down_A01_t01.dart
  5. down_A02_t01.dart
  6. down_A02_t02.dart
  7. down_A02_t03.dart
  8. down_A02_t04.dart
  9. down_A02_t05.dart
  10. down_A02_t06.dart
  11. down_A03_t01.dart
  12. down_A03_t02.dart
  13. down_A03_t03.dart
  14. down_A03_t04.dart
  15. down_A04_t01.dart
  16. down_A04_t02.dart
  17. down_A04_t03.dart
  18. down_A04_t04.dart
  19. README.md
  20. up_A01_t01.dart
  21. up_A01_t02.dart
  22. up_A02_t01.dart
  23. up_A02_t02.dart
  24. up_A02_t03.dart
  25. up_A02_t04.dart
  26. up_A02_t05.dart
  27. up_A02_t06.dart
  28. up_A03_t01.dart
  29. up_A03_t02.dart
  30. up_A03_t03.dart
  31. up_A03_t04.dart
  32. up_A04_t01.dart
  33. up_A04_t02.dart
  34. up_A04_t03.dart
  35. up_A04_t04.dart
  36. up_A05_t01.dart
  37. up_A05_t02.dart
  38. up_A06_t01.dart
  39. up_A07_t01.dart
  40. up_A08_t01.dart
  41. up_A08_t02.dart
  42. up_A08_t03.dart
  43. up_A08_t04.dart
  44. up_A09_t01.dart
  45. up_A09_t02.dart
  46. up_A09_t03.dart
  47. up_A09_t04.dart
  48. up_A10_t01.dart
  49. up_A10_t02.dart
  50. up_A11_t01.dart
  51. up_A11_t02.dart
  52. up_A12_t01.dart
  53. up_A12_t02.dart
  54. up_A13_t01.dart
  55. up_A13_t02.dart
  56. up_A14_t01.dart
  57. up_A14_t02.dart
  58. up_A15_t01.dart
  59. up_A15_t02.dart
  60. up_A16_t01.dart
  61. up_A16_t02.dart
  62. up_A17_t01.dart
  63. up_A17_t02.dart
  64. up_A18_t01.dart
  65. up_A18_t02.dart
  66. up_A19_t01.dart
  67. up_A19_t02.dart
  68. up_A19_t03.dart
  69. up_A20_t01.dart
  70. up_A20_t02.dart
  71. up_A20_t03.dart
  72. up_A21_t01.dart
  73. up_A22_t01.dart
  74. up_A23_t01.dart
  75. up_A24_t01.dart
  76. up_A25_t01.dart
  77. up_A26_t01.dart
  78. up_A27_t01.dart
  79. up_A28_t01.dart
  80. up_A29_t01.dart
  81. up_A30_t01.dart
  82. up_A30_t02.dart
  83. up_A31_t01.dart
  84. up_A32_t01.dart
  85. up_A33_t01.dart
  86. up_A34_t01.dart
  87. up_A35_t01.dart
  88. up_A35_t02.dart
  89. up_A35_t03.dart
  90. up_A36_t01.dart
  91. up_A36_t02.dart
  92. up_A36_t03.dart
  93. up_A37_t01.dart
  94. up_A38_t01.dart
  95. up_A39_t01.dart
  96. up_A40_t01.dart
  97. up_A41_t01.dart
  98. up_A42_t01.dart
  99. up_lib.dart
TypeSystem/upper-lower-bounds/README.md

Some tests in this directory need to tell apart types that are mutual subtypes, such as Object and FutureOr<Object>, or dynamic, Object?, FutureOr<dynamic>, FutureOr<Object?> and FutureOr<Object>?. No subtype-based check can do this: expectStaticType<Exactly<T>>(), assignability and type parameter bounds are all defined in terms of subtyping, and NORM treats FutureOr<Object> and Object as the same type. The following probes make the difference visible at compile time.

Future<X> probeFuture<X>() => Future<X>.value(0 as dynamic);
FutureOr<X> probeFutureOr<X>() => 0 as dynamic;
Future<Future<X>> probeFuture2<X>() =>
    Future<Future<X>>.value(Future<X>.value(0 as dynamic));
Future<FutureOr<X>> probeFutureOr2<X>() => Future<FutureOr<X>>.value(0 as dynamic);

Type inference matches the type of a probe against the context type of the invocation. Because subtype constraint generation decomposes types syntactically rather than by subtyping, equivalent context types produce solutions for X that are not equivalent, and those solutions can be inspected with expectStaticType. A cascade is used so that the assertion is applied to the probe itself while the enclosing assignment (or argument position) still provides the context type:

v = probeFuture()..expectStaticType<Exactly<Future<Object>>>();

A dynamic receiver, on the other hand, accepts any member access, so v.checkDynamic (a getter which is deliberately not declared anywhere) compiles if and only if the static type of v is dynamic. Such an access throws NoSuchMethodError at run time, so it has to be guarded:

if (1 > 2) {
  v.checkDynamic;
}

A single probe only looks at the outermost FutureOr of the context type, so a probe with one level of nesting cannot tell FutureOr<Object> from FutureOr<FutureOr<Object>>: it infers X as Object in the first case and as FutureOr<Object> in the second, and those two solutions are again mutual subtypes. Probes with two levels of nesting shift the inference variable one FutureOr deeper, and each of the eleven types below then gets a distinct signature of static checks:

Static type of vv.checkDynamicv.expectStaticTypeprobeFuture()probeFuture2()probeFutureOr()probeFutureOr2()
ObjecterrorExactly<Object>Future<dynamic>Future<Future<dynamic>>FutureOr<Object>Future<FutureOr<dynamic>>
FutureOr<Object>errorExactly<Object>Future<Object>Future<Future<dynamic>>FutureOr<Object>Future<FutureOr<Object>>
FutureOr<FutureOr<Object>>errorExactly<Object>Future<FutureOr<Object>>Future<Future<Object>>FutureOr<FutureOr<Object>>Future<FutureOr<Object>>
Object?errorExactly<Object?>Future<dynamic>Future<Future<dynamic>>FutureOr<Object>Future<FutureOr<dynamic>>
FutureOr<Object?>errorExactly<Object?>Future<Object?>Future<Future<dynamic>>FutureOr<Object?>Future<FutureOr<Object>>
FutureOr<Object>?errorExactly<Object?>Future<Object>Future<Future<dynamic>>FutureOr<Object>Future<FutureOr<Object>>
FutureOr<FutureOr<Object?>>errorExactly<Object?>Future<FutureOr<Object?>>Future<Future<Object?>>FutureOr<FutureOr<Object?>>Future<FutureOr<Object?>>
FutureOr<FutureOr<Object>?>errorExactly<Object?>Future<FutureOr<Object>?>Future<Future<Object>>FutureOr<FutureOr<Object>?>Future<FutureOr<Object>>
FutureOr<FutureOr<Object>>?errorExactly<Object?>Future<FutureOr<Object>>Future<Future<Object>>FutureOr<FutureOr<Object>>Future<FutureOr<Object>>
dynamiccompilesnot applicableFuture<dynamic>Future<Future<dynamic>>FutureOr<dynamic>Future<FutureOr<dynamic>>
FutureOr<dynamic>errorExactly<Object?>Future<dynamic>Future<Future<dynamic>>FutureOr<dynamic>Future<FutureOr<dynamic>>

The four probe columns list the types that are actually inferred; any equivalent spelling passes the corresponding assertion just as well. This is what makes some of the cells coincide even though they are spelled differently: Future<Object?> and Future<dynamic> are mutual subtypes, and so are FutureOr<Object?> and FutureOr<dynamic>. In particular, FutureOr<FutureOr<Object?>> and dynamic have the same signature in all four probe columns, and checkDynamic is the only check that separates them.

The pattern generalizes to deeper nesting: a probe with k nested Futures infers X as Object if and only if the context type has at least k nested FutureOrs, and replacing the innermost Future with FutureOr makes the probe report whether the type at that level is nullable.

Note that extension members cannot be invoked on a dynamic receiver: for a dynamic v the call v.expectStaticType<Exactly<Object?>>() is a dynamic invocation which checks nothing statically (and fails at run time), which is why the second column is not applicable to the last row.

Required checks for every TOP and OBJECT type

The snippets below are the checks that a test should include once it has concluded that the static type of v is a particular TOP or OBJECT type. Each step rules out some of the remaining mutual subtypes; comments list what is still possible after that step.

v is void

print(v); // Type `void` cannot be used.
//    ^
// [analyzer] unspecified
// [cfe] unspecified

v is FutureOr<void>

print(v); // Rejects `void`
print(await v); // Type `void` cannot be used.
//    ^^^^^^^
// [analyzer] unspecified
// [cfe] unspecified

v is dynamic

  v.checkDynamic;
  v = 1; // Rejects `Never`
//    ^
// [analyzer] unspecified
// [cfe] unspecified

v is FutureOr<dynamic>

  v.checkNotDynamic; // Rejects `dynamic` and `Never`
//  ^^^^^^^^^^^^^^^
// [analyzer] unspecified
// [cfe] unspecified  
  (await v).checkDynamic;
  v = 1; // Rejects `FutureOr<Never>`

v is Object?

v.expectStaticType<Exactly<Object?>>();
// Remaining: `dynamic`, `Object?`, `FutureOr<dynamic>`, `FutureOr<Object?>`,
// `FutureOr<Object>?`, `FutureOr<FutureOr<Object?>>`, ...
v = probeFuture()..expectStaticType<Exactly<Future<dynamic>>>(); // Compile-time error if `v` is `FutureOr<Object>?`.
v = probeFutureOr()..expectStaticType<Exactly<FutureOr<Object>>>(); // Remaining: `Object?`.

v is FutureOr<Object?>

v.expectStaticType<Exactly<Object?>>();
// Remaining: `dynamic`, `Object?`, `FutureOr<dynamic>`, `FutureOr<Object>?`,
// `FutureOr<Object?>`, `FutureOr<FutureOr<Object?>>`,
// `FutureOr<FutureOr<Object>?>?`, ...
v = probeFutureOr()..expectStaticType<Exactly<FutureOr<Object?>>>();
// Remaining: `dynamic`, `FutureOr<dynamic>`, `FutureOr<Object?>`,
// `FutureOr<FutureOr<Object?>>`, `FutureOr<FutureOr<Object>?>?`, ...
v = probeFuture2()..expectStaticType<Exactly<Future<Future<dynamic>>>>(); // Compile-time error if `v` is `FutureOr<FutureOr<Object>?>?`.
v = probeFutureOr2()..expectStaticType<Exactly<Future<FutureOr<Object>>>>(); // Remaining: `FutureOr<Object?>`.

v is Object

v.expectStaticType<Exactly<Object>>();
// Remaining: `Object`, `FutureOr<Object>`, `FutureOr<FutureOr<Object>>`, ...
v = probeFuture()..expectStaticType<Exactly<Future<dynamic>>>(); // Remaining: `Object`.

If v is dynamic, v.expectStaticType<Exactly<Object>>() is a dynamic invocation: it checks nothing statically and fails at run time. To reject dynamic at compile time, add:

if (1 > 2) {
  v.checkNotDynamic;
//  ^^^^^^^^^^^^^^^
// [analyzer] unspecified
// [cfe] unspecified
}

v is FutureOr<Object>

v.expectStaticType<Exactly<Object>>();
// Remaining: `dynamic`, `Object`, `FutureOr<Object>`,
// `FutureOr<FutureOr<Object>>`, ...
v = probeFuture()..expectStaticType<Exactly<Future<Object>>>();
// Remaining: `FutureOr<Object>`, `FutureOr<FutureOr<Object>>`, ...
v = probeFuture2()..expectStaticType<Exactly<Future<Future<dynamic>>>>();
// Remaining: `FutureOr<Object>`.

v is FutureOr<Object>?

v.expectStaticType<Exactly<Object?>>();
// Remaining: `dynamic`, `Object?`, `FutureOr<dynamic>`, `FutureOr<Object>?`,
// `FutureOr<Object?>`, `FutureOr<FutureOr<Object?>>`, ...
v = probeFuture()..expectStaticType<Exactly<Future<Object>>>();
// Remaining: `FutureOr<Object>?`, `FutureOr<FutureOr<Object>>?`, ...
v = probeFuture2()..expectStaticType<Exactly<Future<Future<dynamic>>>>();
// Remaining: `FutureOr<Object>?`.