Additional improvements to inference-update-3 tests.

This CL makes the following improvements (suggested in the code review
of https://dart-review.googlesource.com/c/sdk/+/356303):

- Add parenthetical comment "(Testing this case here. Otherwise
  continued below.)", to reduce confusion for a reader reading the
  tests for the first time.

- In all test cases using a promotable variable `o`, declare `Object?
  o;` first, then assign `o = ... as Object?;`. This makes the test
  cases more symmetrical, since `o = ... as Object?;` now appears in
  every test case, rather than getting coalesced with the variable
  declaration in some test cases but not others.

- Consistently use `e` rather than `E` to refer to the whole
  expression being tested.

- Expand on the explanation for how each test case matches up to the
  type metavariables K, T1, T2, etc., and why the expected result
  occurs.

- Remove tickmarks around type metavariables.

- Clarify that the type S is chosen, rather than T, only when the
  feature is enabled.

Change-Id: I149b323daeac9fc44104681370cea33ee010faa4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/357204
Reviewed-by: Lasse Nielsen <lrn@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2024-04-04 21:24:34 +00:00 committed by Commit Queue
parent 479b3bdef6
commit 34561fac0a
52 changed files with 8915 additions and 3190 deletions

View file

@ -16,11 +16,11 @@ import '../static_type_helper.dart';
Object? contextIterable<T>(Iterable<T> x) => x;
test(bool b) {
// - A conditional expression `E` of the form `b ? e1 : e2` with context type
// `K` is analyzed as follows:
// - A conditional expression `e` of the form `b ? e1 : e2` with context type
// K is analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K`.
// - Let `T2` be the type of `e2` inferred with context type `K`.
// - Let T1 be the type of `e1` inferred with context type K.
// - Let T2 be the type of `e2` inferred with context type K.
{
// Check the context type of `e1` and `e2`:
// - Where the context is established using a function call argument.
@ -29,7 +29,8 @@ test(bool b) {
: (contextType('')..expectStaticType<Exactly<String>>()));
// - Where the context is established using local variable promotion.
var o = '' as Object?;
Object? o;
o = '' as Object?;
if (o is String) {
o = b
? (contextType('')..expectStaticType<Exactly<String>>())
@ -37,62 +38,123 @@ test(bool b) {
}
}
// - Let `T` be `UP(T1, T2)`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(T1, T2).
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int, and T2=double, therefore T=num and S=Object, so T <: S,
// and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int
// - T2 = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var i = 1;
var d = 2.0;
context<Object>((b ? i : d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>, and T2=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>
// - T2 = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableInt = <int>[] as Iterable<int>;
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((b ? iterableInt : iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, if `T1 <: S` and `T2 <: S`, then the type of `E` is `S` if
// `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if T1 <: S and T2 <: S, and `inference-update-3` is enabled,
// then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>, and T2=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but T1 <: S and T2 <: S, hence the
// type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>
// - T2 = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - T1 <: S
// - T2 <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var iterableInt = <int>[] as Iterable<int>;
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (b ? iterableInt : listNum)..expectStaticType<Exactly<Object>>();
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var i = 1;
var o = '' as Object?;
Object? o;
var d = 2.0;
o = '' as Object?;
if (o is String?) {
// K=String?, T1=Null, and T2=int, therefore T=int? and S=String?, so T is
// not <: S. T1 <: S, but T2 is not <: S. Hence the type of E is int?.
// This example has:
// - K = String?
// - T1 = Null
// - T2 = int
// Which implies:
// - T = int?
// - S = String?
// We have:
// - T <!: S
// - T1 <: S
// - T2 <!: S
// The fact that T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = int?.
// We avoid having a compile-time error because `o` can be demoted.
o = (b ? null : i)..expectStaticType<Exactly<int?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int, and T2=Null, therefore T=int? and S=String?, so T is
// not <: S. T2 <: S, but T1 is not <: S. Hence the type of E is int?.
// This example has:
// - K = String?
// - T1 = int
// - T2 = Null
// Which implies:
// - T = int?
// - S = String?
// We have:
// - T <!: S
// - T1 <!: S
// - T2 <: S
// The fact that T1 <!: S precludes using S as static type.
// Therefore the type of `e` is T = int?.
// We avoid having a compile-time error because `o` can be demoted.
o = (b ? i : null)..expectStaticType<Exactly<int?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int, and T2=double, therefore T=num and S=String?, so
// none of T, T1, nor T2 are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int
// - T2 = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - T1 <!: S
// - T2 <!: S
// The fact that T1 <!: S and T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (b ? i : d)..expectStaticType<Exactly<num>>();
}

View file

@ -29,11 +29,11 @@ class C2<T> implements B1<T>, B2<T> {}
Object? contextB1<T>(B1<T> x) => x;
test(bool b) {
// - A conditional expression `E` of the form `b ? e1 : e2` with context type
// `K` is analyzed as follows:
// - A conditional expression `e` of the form `b ? e1 : e2` with context type
// K is analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K`.
// - Let `T2` be the type of `e2` inferred with context type `K`.
// - Let T1 be the type of `e1` inferred with context type K.
// - Let T2 be the type of `e2` inferred with context type K.
{
// Check the context type of `e1` and `e2`:
// - Where the context is established using a function call argument.
@ -42,7 +42,8 @@ test(bool b) {
: (contextType('')..expectStaticType<Exactly<String>>()));
// - Where the context is established using local variable promotion.
var o = '' as Object?;
Object? o;
o = '' as Object?;
if (o is String) {
o = b
? (contextType('')..expectStaticType<Exactly<String>>())
@ -50,71 +51,148 @@ test(bool b) {
}
}
// - Let `T` be `UP(T1, T2)`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(T1, T2).
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int, and T2=double, therefore T=num and S=Object, so T <: S,
// and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int
// - T2 = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var i = 1;
var d = 2.0;
context<Object>((b ? i : d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>, and T2=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>
// - T2 = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableInt = <int>[] as Iterable<int>;
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((b ? iterableInt : iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, if `T1 <: S` and `T2 <: S`, then the type of `E` is `S`.
// - Otherwise, if T1 <: S and T2 <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>, and T2=C2<double>, therefore T=A and S=B1<Object?>,
// so T is not <: S, but T1 <: S and T2 <: S, hence the type of E is
// B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>
// - T2 = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - T1 <: S
// - T2 <: S
// Therefore the type of `e` is S = B1<Object?>.
var c1Int = C1<int>();
var c2Double = C2<double>();
contextB1((b ? c1Int : c2Double)..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>, and T2=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but T1 <: S and T2 <: S, hence the type
// of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>
// - T2 = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - T1 <: S
// - T2 <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>(
(b ? c1Int : c2Double)..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>, and T2=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but T1 <: S and T2 <: S, hence the
// type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>
// - T2 = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - T1 <: S
// - T2 <: S
// Therefore the type of `e` is S = Iterable<num>.
var iterableInt = <int>[] as Iterable<int>;
var listNum = <num>[];
context<Iterable<num>>((b ? iterableInt : listNum)
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var i = 1;
var o = '' as Object?;
Object? o;
var d = 2.0;
o = '' as Object?;
if (o is String?) {
// K=String?, T1=Null, and T2=int, therefore T=int? and S=String?, so T is
// not <: S. T1 <: S, but T2 is not <: S. Hence the type of E is int?.
// This example has:
// - K = String?
// - T1 = Null
// - T2 = int
// Which implies:
// - T = int?
// - S = String?
// We have:
// - T <!: S
// - T1 <: S
// - T2 <!: S
// The fact that T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = int?.
// We avoid having a compile-time error because `o` can be demoted.
o = (b ? null : i)..expectStaticType<Exactly<int?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int, and T2=Null, therefore T=int? and S=String?, so T is
// not <: S. T2 <: S, but T1 is not <: S. Hence the type of E is int?.
// This example has:
// - K = String?
// - T1 = int
// - T2 = Null
// Which implies:
// - T = int?
// - S = String?
// We have:
// - T <!: S
// - T1 <!: S
// - T2 <: S
// The fact that T1 <!: S precludes using S as static type.
// Therefore the type of `e` is T = int?.
// We avoid having a compile-time error because `o` can be demoted.
o = (b ? i : null)..expectStaticType<Exactly<int?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int, and T2=double, therefore T=num and S=String?, so
// none of T, T1, nor T2 are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int
// - T2 = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - T1 <!: S
// - T2 <!: S
// The fact that T1 <!: S and T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (b ? i : d)..expectStaticType<Exactly<num>>();
}

View file

@ -48,18 +48,18 @@ extension Extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -70,62 +70,106 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((Extension(Indexable<int?, Object?>(null))[0] ??= d)
..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Extension(Indexable<Iterable<int>?, Object?>(null))[0] ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Extension(Indexable<Function?, Function?>(null))[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<Iterable<int>?, Object?>(null))[0] ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -136,32 +180,65 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<int?, Object?>(null))[0] ??= d)
..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<double?, Object?>(null))[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<int?, Object?>(null))[0] ??= d)
..expectStaticType<Exactly<num>>();
@ -170,10 +247,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<C1<int> Function()?, Function?>(null))[0] ??=
callableClassC2Int)
@ -182,10 +269,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<C1<int> Function()?, Function?>(null))[0] ??=
callableClassC2Int)
@ -194,10 +291,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<C1<int> Function()?, Function?>(null))[0] ??=
callableClassC2Int)

View file

@ -51,18 +51,18 @@ extension Extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -73,73 +73,131 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((Extension(Indexable<int?, Object?>(null))[0] ??= d)
..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Extension(Indexable<Iterable<int>?, Object?>(null))[0] ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Extension(Indexable<Function?, Function?>(null))[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((Extension(Indexable<C1<int>?, Object?>(null))[0] ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((Extension(Indexable<C1<int>?, Object?>(null))[0] ??=
c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>(
(Extension(Indexable<Iterable<int>?, Object?>(null))[0] ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>(
(Extension(Indexable<C1<int> Function()?, Function?>(null))[0] ??=
@ -147,32 +205,65 @@ main() {
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<int?, Object?>(null))[0] ??= d)
..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<double?, Object?>(null))[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<int?, Object?>(null))[0] ??= d)
..expectStaticType<Exactly<num>>();
@ -181,10 +272,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<C1<int> Function()?, Function?>(null))[0] ??=
callableClassC2Int)
@ -193,10 +294,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<C1<int> Function()?, Function?>(null))[0] ??=
callableClassC2Int)
@ -205,10 +316,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(Indexable<C1<int> Function()?, Function?>(null))[0] ??=
callableClassC2Int)

View file

@ -52,18 +52,18 @@ Indexable<ReadType, WriteType>? maybeIndexable<ReadType, WriteType>(
Indexable<ReadType, WriteType>(value);
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -74,42 +74,65 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>((Extension(maybeIndexable<int?, Object?>(null))?[0] ??= d)
..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion(
(Extension(maybeIndexable<Iterable<int>?, Object?>(null))?[0] ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>(
(Extension(maybeIndexable<Function?, Function?>(null))?[0] ??=
@ -117,15 +140,26 @@ main() {
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Object (which becomes Object? after null
// shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
// (Which becomes Object? after null shorting completes.)
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>?) {
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<Iterable<int>?, Object?>(null))?[0] ??=
@ -133,11 +167,21 @@ main() {
..expectStaticType<Exactly<Object?>>();
}
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function() (which becomes A Function()? after
// null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()?) {
@ -148,24 +192,46 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<int?, Object?>(null))?[0] ??= d)
..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<double?, Object?>(null))?[0] ??=
intQuestion)
@ -173,9 +239,21 @@ main() {
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<int?, Object?>(null))?[0] ??= d)
..expectStaticType<Exactly<num?>>();
@ -184,11 +262,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<C1<int> Function()?, Function?>(null))?[
0] ??= callableClassC2Int)
@ -197,11 +285,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<C1<int> Function()?, Function?>(null))?[
0] ??= callableClassC2Int)
@ -210,11 +308,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<C1<int> Function()?, Function?>(null))?[
0] ??= callableClassC2Int)

View file

@ -55,18 +55,18 @@ Indexable<ReadType, WriteType>? maybeIndexable<ReadType, WriteType>(
Indexable<ReadType, WriteType>(value);
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -77,42 +77,65 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>((Extension(maybeIndexable<int?, Object?>(null))?[0] ??= d)
..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion(
(Extension(maybeIndexable<Iterable<int>?, Object?>(null))?[0] ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>(
(Extension(maybeIndexable<Function?, Function?>(null))?[0] ??=
@ -120,41 +143,76 @@ main() {
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?> (which becomes B1<Object?>? after null
// shorting completes).
// This example has:
// - K = B1<_>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
// (Which becomes B1<Object?>? after null shorting completes.)
var c2Double = C2<double>();
contextB1Question(
(Extension(maybeIndexable<C1<int>?, Object?>(null))?[0] ??= c2Double)
..expectStaticType<Exactly<B1<Object?>?>>());
// K=B1<Object>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object> (which becomes B1<Object>? after null
// shorting completes).
// This example has:
// - K = B1<Object>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
// (Which becomes B1<Object>? after null shorting completes.)
contextB1Question<Object>(
(Extension(maybeIndexable<C1<int>?, Object?>(null))?[0] ??= c2Double)
..expectStaticType<Exactly<B1<Object>?>>());
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Iterable<num> (which becomes
// Iterable<num>? after null shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var listNum = <num>[];
context<Iterable<num>?>(
(Extension(maybeIndexable<Iterable<int>?, Object?>(null))?[0] ??=
listNum)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function() (which becomes B1<int>
// Function()? after null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
// (Which becomes B1<int> Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()?>(
(Extension(maybeIndexable<C1<int> Function()?, Function?>(null))?[0] ??=
@ -162,24 +220,46 @@ main() {
..expectStaticType<Exactly<B1<int> Function()?>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<int?, Object?>(null))?[0] ??= d)
..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<double?, Object?>(null))?[0] ??=
intQuestion)
@ -187,9 +267,21 @@ main() {
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<int?, Object?>(null))?[0] ??= d)
..expectStaticType<Exactly<num?>>();
@ -198,11 +290,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<C1<int> Function()?, Function?>(null))?[
0] ??= callableClassC2Int)
@ -211,11 +313,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<C1<int> Function()?, Function?>(null))?[
0] ??= callableClassC2Int)
@ -224,11 +336,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(maybeIndexable<C1<int> Function()?, Function?>(null))?[
0] ??= callableClassC2Int)

View file

@ -56,18 +56,18 @@ extension Extension on String {
main() {
var s = '' as String?;
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -78,66 +78,110 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>(
(Extension(s)?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion((Extension(s)?.pIterableIntQuestion ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((Extension(s)?.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Object (which becomes Object? after null
// shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
// (Which becomes Object? after null shorting completes.)
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>?) {
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object?>>();
}
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function() (which becomes A Function()? after
// null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()?) {
@ -147,32 +191,66 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
@ -180,11 +258,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -192,11 +280,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -204,11 +302,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();

View file

@ -61,18 +61,18 @@ extension Extension on String {
main() {
var s = '' as String?;
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -83,109 +83,201 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>(
(Extension(s)?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion((Extension(s)?.pIterableIntQuestion ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((Extension(s)?.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?> (which becomes B1<Object?>? after null
// shorting completes).
// This example has:
// - K = B1<_>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
// (Which becomes B1<Object?>? after null shorting completes.)
var c2Double = C2<double>();
contextB1Question((Extension(s)?.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>?>>());
// K=B1<Object>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object> (which becomes B1<Object>? after null
// shorting completes).
// This example has:
// - K = B1<Object>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
// (Which becomes B1<Object>? after null shorting completes.)
contextB1Question<Object>((Extension(s)?.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>?>>());
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Iterable<num> (which becomes
// Iterable<num>? after null shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var listNum = <num>[];
context<Iterable<num>?>((Extension(s)?.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function() (which becomes B1<int>
// Function()? after null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
// (Which becomes B1<int> Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()?>((Extension(s)?.pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()?>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
@ -193,11 +285,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -205,11 +307,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -217,11 +329,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(s)?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();

View file

@ -54,18 +54,18 @@ extension Extension on String {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -76,60 +76,104 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(Extension('').pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Extension('').pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Extension('').pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -139,31 +183,64 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -171,10 +248,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -182,10 +269,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -193,10 +290,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -59,18 +59,18 @@ extension Extension on String {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -81,100 +81,191 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(Extension('').pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Extension('').pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Extension('').pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((Extension('').pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((Extension('').pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((Extension('').pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((Extension('').pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -182,10 +273,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -193,10 +294,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -204,10 +315,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension('').pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -53,15 +53,15 @@ extension Extension on String {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -75,61 +75,104 @@ extension Extension on String {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((Extension(this).pIntQuestion ??= d)
..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Extension(this).pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Extension(this).pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is
// `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -139,32 +182,65 @@ extension Extension on String {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pIntQuestion ??= d)
..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pIntQuestion ??= d)
..expectStaticType<Exactly<num>>();
@ -173,10 +249,20 @@ extension Extension on String {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -184,10 +270,20 @@ extension Extension on String {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -195,10 +291,20 @@ extension Extension on String {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -58,15 +58,15 @@ extension Extension on String {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -80,101 +80,193 @@ extension Extension on String {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((Extension(this).pIntQuestion ??= d)
..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Extension(this).pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Extension(this).pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is
// S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((Extension(this).pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((Extension(this).pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((Extension(this).pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((Extension(this).pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pIntQuestion ??= d)
..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pIntQuestion ??= d)
..expectStaticType<Exactly<num>>();
@ -183,10 +275,20 @@ extension Extension on String {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -194,10 +296,20 @@ extension Extension on String {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -205,10 +317,20 @@ extension Extension on String {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Extension(this).pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -53,15 +53,15 @@ class Test {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -74,61 +74,104 @@ class Test {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((this.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((this.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is
// `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -138,31 +181,64 @@ class Test {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -170,10 +246,20 @@ class Test {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -181,10 +267,20 @@ class Test {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -192,10 +288,20 @@ class Test {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -58,15 +58,15 @@ class Test {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -79,100 +79,192 @@ class Test {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((this.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((this.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is
// S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((this.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((this.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((this.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((this.pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -180,10 +272,20 @@ class Test {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -191,10 +293,20 @@ class Test {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -202,10 +314,20 @@ class Test {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -53,15 +53,15 @@ extension on String {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -74,61 +74,104 @@ extension on String {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((this.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((this.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is
// `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -138,31 +181,64 @@ extension on String {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -170,10 +246,20 @@ extension on String {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -181,10 +267,20 @@ extension on String {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -192,10 +288,20 @@ extension on String {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -58,15 +58,15 @@ extension on String {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -79,100 +79,192 @@ extension on String {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((this.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((this.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is
// S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((this.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((this.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((this.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((this.pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -180,10 +272,20 @@ extension on String {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -191,10 +293,20 @@ extension on String {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -202,10 +314,20 @@ extension on String {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -53,15 +53,15 @@ extension on String {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -73,60 +73,103 @@ extension on String {
pStringQuestion ??= contextType('')..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is
// `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -136,31 +179,64 @@ extension on String {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -168,10 +244,20 @@ extension on String {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -179,10 +265,20 @@ extension on String {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -190,10 +286,20 @@ extension on String {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -58,15 +58,15 @@ extension on String {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -78,99 +78,191 @@ extension on String {
pStringQuestion ??= contextType('')..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is
// S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -178,10 +270,20 @@ extension on String {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -189,10 +291,20 @@ extension on String {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -200,10 +312,20 @@ extension on String {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -48,18 +48,18 @@ extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -70,62 +70,106 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Indexable<Iterable<int>?, Object?>(null)[0] ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Indexable<Function?, Function?>(null)[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<Iterable<int>?, Object?>(null)[0] ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -136,32 +180,65 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<double?, Object?>(null)[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>();
@ -170,10 +247,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)
@ -182,10 +269,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)
@ -194,10 +291,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)

View file

@ -51,18 +51,18 @@ extension<ReadType, WriteType> on Indexable<ReadType, WriteType> {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -73,72 +73,130 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Indexable<Iterable<int>?, Object?>(null)[0] ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Indexable<Function?, Function?>(null)[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((Indexable<C1<int>?, Object?>(null)[0] ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((Indexable<C1<int>?, Object?>(null)[0] ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((Indexable<Iterable<int>?, Object?>(null)[0] ??=
listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>(
(Indexable<C1<int> Function()?, Function?>(null)[0] ??=
@ -146,32 +204,65 @@ main() {
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<double?, Object?>(null)[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>();
@ -180,10 +271,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)
@ -192,10 +293,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)
@ -204,10 +315,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)

View file

@ -52,18 +52,18 @@ Indexable<ReadType, WriteType>? maybeIndexable<ReadType, WriteType>(
Indexable<ReadType, WriteType>(value);
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -74,67 +74,111 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>((maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion(
(maybeIndexable<Iterable<int>?, Object?>(null)?[0] ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((maybeIndexable<Function?, Function?>(null)?[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Object (which becomes Object? after null
// shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
// (Which becomes Object? after null shorting completes.)
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>?) {
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<Iterable<int>?, Object?>(null)?[0] ??= listNum)
..expectStaticType<Exactly<Object?>>();
}
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function() (which becomes A Function()? after
// null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()?) {
@ -145,33 +189,67 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<double?, Object?>(null)?[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>();
@ -180,11 +258,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)
@ -193,11 +281,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)
@ -206,11 +304,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)

View file

@ -55,18 +55,18 @@ Indexable<ReadType, WriteType>? maybeIndexable<ReadType, WriteType>(
Indexable<ReadType, WriteType>(value);
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -77,80 +77,138 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>((maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion(
(maybeIndexable<Iterable<int>?, Object?>(null)?[0] ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((maybeIndexable<Function?, Function?>(null)?[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?> (which becomes B1<Object?>? after null
// shorting completes).
// This example has:
// - K = B1<_>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
// (Which becomes B1<Object?>? after null shorting completes.)
var c2Double = C2<double>();
contextB1Question((maybeIndexable<C1<int>?, Object?>(null)?[0] ??= c2Double)
..expectStaticType<Exactly<B1<Object?>?>>());
// K=B1<Object>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object> (which becomes B1<Object>? after null
// shorting completes).
// This example has:
// - K = B1<Object>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
// (Which becomes B1<Object>? after null shorting completes.)
contextB1Question<Object>((maybeIndexable<C1<int>?, Object?>(null)?[0] ??=
c2Double)
..expectStaticType<Exactly<B1<Object>?>>());
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Iterable<num> (which becomes
// Iterable<num>? after null shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var listNum = <num>[];
context<Iterable<num>?>(
(maybeIndexable<Iterable<int>?, Object?>(null)?[0] ??= listNum)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function() (which becomes B1<int>
// Function()? after null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
// (Which becomes B1<int> Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()?>(
(maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
@ -158,33 +216,67 @@ main() {
..expectStaticType<Exactly<B1<int> Function()?>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<double?, Object?>(null)?[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>();
@ -193,11 +285,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)
@ -206,11 +308,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)
@ -219,11 +331,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)

View file

@ -55,18 +55,18 @@ extension on String {
main() {
var s = '' as String?;
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -76,65 +76,109 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>(
(s?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion((s?.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((s?.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Object (which becomes Object? after null
// shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
// (Which becomes Object? after null shorting completes.)
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>?) {
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object?>>();
}
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function() (which becomes A Function()? after
// null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()?) {
@ -144,32 +188,66 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
@ -177,11 +255,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -189,11 +277,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -201,11 +299,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();

View file

@ -61,18 +61,18 @@ extension on String {
main() {
var s = '' as String?;
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -82,108 +82,200 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>(
(s?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion((s?.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((s?.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?> (which becomes B1<Object?>? after null
// shorting completes).
// This example has:
// - K = B1<_>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
// (Which becomes B1<Object?>? after null shorting completes.)
var c2Double = C2<double>();
contextB1Question((s?.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>?>>());
// K=B1<Object>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object> (which becomes B1<Object>? after null
// shorting completes).
// This example has:
// - K = B1<Object>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
// (Which becomes B1<Object>? after null shorting completes.)
contextB1Question<Object>((s?.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>?>>());
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Iterable<num> (which becomes
// Iterable<num>? after null shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var listNum = <num>[];
context<Iterable<num>?>((s?.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function() (which becomes B1<int>
// Function()? after null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
// (Which becomes B1<int> Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()?>((s?.pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()?>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
@ -191,11 +283,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -203,11 +305,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -215,11 +327,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (s?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();

View file

@ -53,18 +53,18 @@ extension on String {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -74,59 +74,103 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((''.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((''.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((''.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -136,31 +180,64 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -168,10 +245,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -179,10 +266,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -190,10 +287,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -58,18 +58,18 @@ extension on String {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -79,99 +79,190 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((''.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((''.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((''.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((''.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((''.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((''.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((''.pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -179,10 +270,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -190,10 +291,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -201,10 +312,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (''.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -53,15 +53,15 @@ class Test {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -73,60 +73,103 @@ class Test {
pStringQuestion ??= contextType('')..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is
// `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -136,31 +179,64 @@ class Test {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -168,10 +244,20 @@ class Test {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -179,10 +265,20 @@ class Test {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -190,10 +286,20 @@ class Test {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -58,15 +58,15 @@ class Test {
set pStringQuestion(String? value) {}
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -78,99 +78,191 @@ class Test {
pStringQuestion ??= contextType('')..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is
// S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -178,10 +270,20 @@ class Test {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -189,10 +291,20 @@ class Test {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -200,10 +312,20 @@ class Test {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -43,18 +43,18 @@ class Indexable<ReadType, WriteType> {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -65,62 +65,106 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Indexable<Iterable<int>?, Object?>(null)[0] ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Indexable<Function?, Function?>(null)[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<Iterable<int>?, Object?>(null)[0] ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -131,32 +175,65 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<double?, Object?>(null)[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>();
@ -165,10 +242,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)
@ -177,10 +264,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)
@ -189,10 +286,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)

View file

@ -46,18 +46,18 @@ class Indexable<ReadType, WriteType> {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -68,72 +68,130 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Indexable<Iterable<int>?, Object?>(null)[0] ??=
iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Indexable<Function?, Function?>(null)[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((Indexable<C1<int>?, Object?>(null)[0] ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((Indexable<C1<int>?, Object?>(null)[0] ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((Indexable<Iterable<int>?, Object?>(null)[0] ??=
listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>(
(Indexable<C1<int> Function()?, Function?>(null)[0] ??=
@ -141,32 +199,65 @@ main() {
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<double?, Object?>(null)[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<int?, Object?>(null)[0] ??= d)
..expectStaticType<Exactly<num>>();
@ -175,10 +266,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)
@ -187,10 +288,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)
@ -199,10 +310,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Indexable<C1<int> Function()?, Function?>(null)[0] ??=
callableClassC2Int)

View file

@ -30,18 +30,18 @@ class CallableClass<T> {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
var string = '';
@ -51,39 +51,54 @@ main() {
var numQuestion = null as num?;
numQuestion ??= contextType(0)..expectStaticType<Exactly<num?>>();
numQuestion = null as num?;
if (numQuestion is int?) {
numQuestion ??= contextType(0)..expectStaticType<Exactly<int?>>();
}
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
var local1 = null as Object?;
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
Object? local1;
var d = 2.0;
local1 = null as Object?;
if (local1 is int?) {
// We avoid having a compile-time error because `local1` can be demoted.
context<Object>((local1 ??= d)..expectStaticType<Exactly<num>>());
}
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
local1 = null as Object?;
if (local1 is Iterable<int>?) {
@ -92,17 +107,34 @@ main() {
..expectStaticType<Exactly<Iterable<num>>>());
}
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
var local2 = null as Function?;
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
Function? local2;
var callableClassInt = CallableClass<int>();
local2 = null as Function?;
context<Function>(
(local2 ??= callableClassInt)..expectStaticType<Exactly<Function>>());
// Verify that the RHS is not coerced to the promoted type.
// K=Object, T1=Function?, and T2'=CallableClass<int>, therefore T=Object
// and S=Object, so T <: S, and hence the type of E is Object.
// This example has:
// - K = Object
// - T1 = Function?
// - T2' = CallableClass<int>
// Which implies:
// - T = Object
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = Object.
local1 = null as Object?;
if (local1 is Function?) {
// We avoid having a compile-time error because `local1` can be demoted.
@ -111,27 +143,50 @@ main() {
}
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
var local1 = null as Object?;
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
Object? local1;
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
local1 = null as Object?;
o = [0] as Object?;
if (local1 is Iterable<int>? && o is Iterable<num>) {
// We avoid having a compile-time error because `local1` and `o` can be
// demoted.
o = (local1 ??= listNum)..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
var local2 = null as Function?;
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
Function? local2;
var callableClassC2Int = CallableClass<C2<int>>();
local2 = null as Function?;
o = (() => B1<int>()) as Object?;
if (local2 is C1<int> Function()? && o is B1<int> Function()) {
// We avoid having a compile-time error because `local2` and `o` can be
@ -141,16 +196,28 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var local1 = null as Object?;
Object? local1;
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
local1 = null as Object?;
o = 0 as Object?;
if (local1 is int? && o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `local1` and `o` can be
// demoted.
o = (local1 ??= d)..expectStaticType<Exactly<num>>();
@ -158,9 +225,19 @@ main() {
local1 = null as Object?;
o = 0 as Object?;
if (local1 is double? && o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `local1` and `o` can be
// demoted.
o = (local1 ??= intQuestion)..expectStaticType<Exactly<num?>>();
@ -168,21 +245,44 @@ main() {
local1 = null as Object?;
o = '' as Object?;
if (local1 is int? && o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `local1` and `o` can be
// demoted.
o = (local1 ??= d)..expectStaticType<Exactly<num>>();
}
var local2 = null as Function?;
Function? local2;
var callableClassC2Int = CallableClass<C2<int>>();
local2 = null as Function?;
o = (() => C1<int>()) as Object?;
if (local2 is C1<int> Function()? && o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `local2` and `o` can be
// demoted.
o = (local2 ??= callableClassC2Int)
@ -192,10 +292,20 @@ main() {
local2 = null as Function?;
o = (() => C2<int>()) as Object?;
if (local2 is C1<int> Function()? && o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `local2` and `o` can be
// demoted.
o = (local2 ??= callableClassC2Int)
@ -205,10 +315,20 @@ main() {
local2 = null as Function?;
o = 0 as Object?;
if (local2 is C1<int> Function()? && o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `local2` and `o` can be
// demoted.
o = (local2 ??= callableClassC2Int)

View file

@ -33,18 +33,18 @@ class CallableClass<T> {
Object? contextB1<T>(B1<T> x) => x;
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
var string = '';
@ -54,39 +54,54 @@ main() {
var numQuestion = null as num?;
numQuestion ??= contextType(0)..expectStaticType<Exactly<num?>>();
numQuestion = null as num?;
if (numQuestion is int?) {
numQuestion ??= contextType(0)..expectStaticType<Exactly<int?>>();
}
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
var local1 = null as Object?;
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
Object? local1;
var d = 2.0;
local1 = null as Object?;
if (local1 is int?) {
// We avoid having a compile-time error because `local1` can be demoted.
context<Object>((local1 ??= d)..expectStaticType<Exactly<num>>());
}
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
local1 = null as Object?;
if (local1 is Iterable<int>?) {
@ -95,17 +110,34 @@ main() {
..expectStaticType<Exactly<Iterable<num>>>());
}
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
var local2 = null as Function?;
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
Function? local2;
var callableClassInt = CallableClass<int>();
local2 = null as Function?;
context<Function>(
(local2 ??= callableClassInt)..expectStaticType<Exactly<Function>>());
// Verify that the RHS is not coerced to the promoted type.
// K=Object, T1=Function?, and T2'=CallableClass<int>, therefore T=Object
// and S=Object, so T <: S, and hence the type of E is Object.
// This example has:
// - K = Object
// - T1 = Function?
// - T2' = CallableClass<int>
// Which implies:
// - T = Object
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = Object.
local1 = null as Object?;
if (local1 is Function?) {
// We avoid having a compile-time error because `local1` can be demoted.
@ -114,23 +146,41 @@ main() {
}
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
var local1 = null as Object?;
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
Object? local1;
var c2Double = C2<double>();
local1 = null as Object?;
if (local1 is C1<int>?) {
// We avoid having a compile-time error because `local1` can be demoted.
contextB1(
(local1 ??= c2Double)..expectStaticType<Exactly<B1<Object?>>>());
}
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
local1 = null as Object?;
if (local1 is C1<int>?) {
// We avoid having a compile-time error because `local1` can be demoted.
@ -138,9 +188,18 @@ main() {
(local1 ??= c2Double)..expectStaticType<Exactly<B1<Object>>>());
}
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
local1 = null as Object?;
if (local1 is Iterable<int>?) {
@ -149,12 +208,22 @@ main() {
(local1 ??= listNum)..expectStaticType<Exactly<Iterable<num>>>());
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
var local2 = null as Function?;
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
Function? local2;
var callableClassC2Int = CallableClass<C2<int>>();
local2 = null as Function?;
if (local2 is C1<int> Function()?) {
// We avoid having a compile-time error because `local2` can be demoted.
context<B1<int> Function()>((local2 ??= callableClassC2Int)
@ -162,16 +231,28 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var local1 = null as Object?;
Object? local1;
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
local1 = null as Object?;
o = 0 as Object?;
if (local1 is int? && o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `local1` and `o` can be
// demoted.
o = (local1 ??= d)..expectStaticType<Exactly<num>>();
@ -179,9 +260,19 @@ main() {
local1 = null as Object?;
o = 0 as Object?;
if (local1 is double? && o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `local1` and `o` can be
// demoted.
o = (local1 ??= intQuestion)..expectStaticType<Exactly<num?>>();
@ -189,21 +280,44 @@ main() {
local1 = null as Object?;
o = '' as Object?;
if (local1 is int? && o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `local1` and `o` can be
// demoted.
o = (local1 ??= d)..expectStaticType<Exactly<num>>();
}
var local2 = null as Function?;
Function? local2;
var callableClassC2Int = CallableClass<C2<int>>();
local2 = null as Function?;
o = (() => C1<int>()) as Object?;
if (local2 is C1<int> Function()? && o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `local2` and `o` can be
// demoted.
o = (local2 ??= callableClassC2Int)
@ -213,10 +327,20 @@ main() {
local2 = null as Function?;
o = (() => C2<int>()) as Object?;
if (local2 is C1<int> Function()? && o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `local2` and `o` can be
// demoted.
o = (local2 ??= callableClassC2Int)
@ -226,10 +350,20 @@ main() {
local2 = null as Function?;
o = 0 as Object?;
if (local2 is C1<int> Function()? && o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `local2` and `o` can be
// demoted.
o = (local2 ??= callableClassC2Int)

View file

@ -47,18 +47,18 @@ Indexable<ReadType, WriteType>? maybeIndexable<ReadType, WriteType>(
Indexable<ReadType, WriteType>(value);
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -69,67 +69,111 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>((maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion(
(maybeIndexable<Iterable<int>?, Object?>(null)?[0] ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((maybeIndexable<Function?, Function?>(null)?[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Object (which becomes Object? after null
// shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
// (Which becomes Object? after null shorting completes.)
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>?) {
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<Iterable<int>?, Object?>(null)?[0] ??= listNum)
..expectStaticType<Exactly<Object?>>();
}
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function() (which becomes A Function()? after
// null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()?) {
@ -140,33 +184,67 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<double?, Object?>(null)?[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>();
@ -175,11 +253,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)
@ -188,11 +276,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)
@ -201,11 +299,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)

View file

@ -50,18 +50,18 @@ Indexable<ReadType, WriteType>? maybeIndexable<ReadType, WriteType>(
Indexable<ReadType, WriteType>(value);
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -72,80 +72,138 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>((maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion(
(maybeIndexable<Iterable<int>?, Object?>(null)?[0] ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((maybeIndexable<Function?, Function?>(null)?[0] ??=
callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?> (which becomes B1<Object?>? after null
// shorting completes).
// This example has:
// - K = B1<_>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
// (Which becomes B1<Object?>? after null shorting completes.)
var c2Double = C2<double>();
contextB1Question((maybeIndexable<C1<int>?, Object?>(null)?[0] ??= c2Double)
..expectStaticType<Exactly<B1<Object?>?>>());
// K=B1<Object>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object> (which becomes B1<Object>? after null
// shorting completes).
// This example has:
// - K = B1<Object>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
// (Which becomes B1<Object>? after null shorting completes.)
contextB1Question<Object>((maybeIndexable<C1<int>?, Object?>(null)?[0] ??=
c2Double)
..expectStaticType<Exactly<B1<Object>?>>());
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Iterable<num> (which becomes
// Iterable<num>? after null shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var listNum = <num>[];
context<Iterable<num>?>(
(maybeIndexable<Iterable<int>?, Object?>(null)?[0] ??= listNum)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function() (which becomes B1<int>
// Function()? after null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
// (Which becomes B1<int> Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()?>(
(maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
@ -153,33 +211,67 @@ main() {
..expectStaticType<Exactly<B1<int> Function()?>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<double?, Object?>(null)?[0] ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<int?, Object?>(null)?[0] ??= d)
..expectStaticType<Exactly<num?>>();
@ -188,11 +280,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)
@ -201,11 +303,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)
@ -214,11 +326,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (maybeIndexable<C1<int> Function()?, Function?>(null)?[0] ??=
callableClassC2Int)

View file

@ -55,18 +55,18 @@ class Test {
main() {
var test = Test() as Test?;
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -76,65 +76,109 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>(
(test?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion((test?.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((test?.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Object (which becomes Object? after null
// shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
// (Which becomes Object? after null shorting completes.)
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>?) {
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object?>>();
}
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function() (which becomes A Function()? after
// null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()?) {
@ -144,32 +188,66 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
@ -177,11 +255,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -189,11 +277,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -201,11 +299,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();

View file

@ -60,18 +60,18 @@ class Test {
main() {
var test = Test() as Test?;
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -81,108 +81,200 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object?, T1=int?, and T2'=double, therefore T=num and S=Object?, so T
// <: S, and hence the type of E is num (which becomes num? after null
// shorting completes).
// This example has:
// - K = Object?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object?
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
var d = 2.0;
context<Object?>(
(test?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>());
// K=Iterable<_>?, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>?, so T <: S, and hence the type
// of E is Iterable<num> (which becomes Iterable<num>? after null shorting
// completes).
// This example has:
// - K = Iterable<_>?
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>?
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var iterableDouble = <double>[] as Iterable<double>;
contextIterableQuestion((test?.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=Function?, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function?, so T <: S,
// and hence the type of E is Function (which becomes Function? after null
// shorting completes).
// This example has:
// - K = Function?
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function?
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
// (Which becomes Function? after null shorting completes.)
var callableClassInt = CallableClass<int>();
context<Function?>((test?.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function?>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?> (which becomes B1<Object?>? after null
// shorting completes).
// This example has:
// - K = B1<_>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
// (Which becomes B1<Object?>? after null shorting completes.)
var c2Double = C2<double>();
contextB1Question((test?.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>?>>());
// K=B1<Object>?, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>?, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object> (which becomes B1<Object>? after null
// shorting completes).
// This example has:
// - K = B1<Object>?
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
// (Which becomes B1<Object>? after null shorting completes.)
contextB1Question<Object>((test?.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>?>>());
// K=Iterable<num>?, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>?, so T is not <: S, but NonNull(T1) <: S and
// T2' <: S, hence the type of E is Iterable<num> (which becomes
// Iterable<num>? after null shorting completes).
// This example has:
// - K = Iterable<num>?
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
// (Which becomes Iterable<num>? after null shorting completes.)
var listNum = <num>[];
context<Iterable<num>?>((test?.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>?>>());
// K=B1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function()?, so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function() (which becomes B1<int>
// Function()? after null shorting completes).
// This example has:
// - K = B1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
// (Which becomes B1<int> Function()? after null shorting completes.)
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()?>((test?.pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()?>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num (which becomes num? after null shorting completes).
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num
// (which becomes num? after null shorting completes).
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// (Which becomes num? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pIntQuestion ??= d)..expectStaticType<Exactly<num?>>();
}
@ -190,11 +282,21 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()?) {
// K=C1<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function()?, so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C1<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -202,11 +304,21 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()?) {
// K=C2<int> Function()?, T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function()?, so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function() (which
// becomes A Function()? after null shorting completes).
// This example has:
// - K = C2<int> Function()?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();
@ -214,11 +326,21 @@ main() {
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int?,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type
// of E is A Function() (which becomes A Function()? after null shorting
// completes).
// This example has:
// - K = int?
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// (Which becomes A Function()? after null shorting completes.)
// We avoid having a compile-time error because `o` can be demoted.
o = (test?.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()?>>();

View file

@ -55,18 +55,18 @@ class Test {
main() {
var test = Test();
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -76,60 +76,104 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(test.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((test.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((test.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -139,31 +183,64 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -171,10 +248,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -182,10 +269,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -193,10 +290,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -60,18 +60,18 @@ class Test {
main() {
var test = Test();
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -81,100 +81,191 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(test.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((test.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((test.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((test.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((test.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((test.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((test.pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -182,10 +273,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -193,10 +294,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -204,10 +315,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (test.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -53,18 +53,18 @@ class Test {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -74,60 +74,104 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(Test.staticIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Test.staticIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Test.staticFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -137,31 +181,64 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -169,10 +246,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -180,10 +267,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -191,10 +288,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -58,18 +58,18 @@ class Test {
}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -79,100 +79,191 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(Test.staticIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((Test.staticIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((Test.staticFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((Test.staticC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((Test.staticC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((Test.staticIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((Test.staticC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -180,10 +271,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -191,10 +292,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -202,10 +313,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (Test.staticC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -42,15 +42,15 @@ class Indexable<ReadType, WriteType> {
operator []=(int index, WriteType value) {}
}
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K` is
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that the
// - Let T1 be the read type of `e1`. This is the static type that `e1` would
// have as an expression with a context type schema of `_`.
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly promoted)
// type of the variable.
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for the
// case of indexed assignment, the type schema that `operator[]=` imposes
// on its second argument).
@ -71,24 +71,33 @@ class Test2 extends Indexable<String?, String?> {
}
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type of
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
class Test3 extends Indexable<int?, Object?> {
Test3() : super(null);
test() {
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((super[0] ??= d)..expectStaticType<Exactly<num>>());
}
@ -97,9 +106,16 @@ class Test3 extends Indexable<int?, Object?> {
class Test4 extends Indexable<Iterable<int>?, Object?> {
Test4() : super(null);
test() {
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((super[0] ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
@ -109,25 +125,44 @@ class Test4 extends Indexable<Iterable<int>?, Object?> {
class Test5 extends Indexable<Function?, Function?> {
Test5() : super(null);
test() {
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>(
(super[0] ??= callableClassInt)..expectStaticType<Exactly<Function>>());
}
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E` is
// `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3` is
// enabled, then the type of `e` is S.
class Test6 extends Indexable<Iterable<int>?, Object?> {
Test6() : super(null);
test() {
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= listNum)..expectStaticType<Exactly<Object>>();
@ -138,12 +173,23 @@ class Test6 extends Indexable<Iterable<int>?, Object?> {
class Test7 extends Indexable<C1<int> Function()?, Function?> {
Test7() : super(null);
test() {
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => B1<int>()) as Object?;
Object? o;
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= callableClassC2Int)
@ -152,16 +198,27 @@ class Test7 extends Indexable<C1<int> Function()?, Function?> {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
class Test8 extends Indexable<int?, Object?> {
Test8() : super(null);
test() {
var d = 2.0;
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= d)..expectStaticType<Exactly<num>>();
}
@ -172,11 +229,22 @@ class Test9 extends Indexable<double?, Object?> {
Test9() : super(null);
test() {
var intQuestion = null as int?;
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= intQuestion)..expectStaticType<Exactly<num?>>();
}
@ -187,10 +255,23 @@ class Test10 extends Indexable<int?, Object?> {
Test10() : super(null);
test() {
var d = 2.0;
var o = '' as Object?;
Object? o;
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= d)..expectStaticType<Exactly<num>>();
}
@ -201,12 +282,23 @@ class Test11 extends Indexable<C1<int> Function()?, Function?> {
Test11() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => C1<int>()) as Object?;
Object? o;
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -218,12 +310,23 @@ class Test12 extends Indexable<C1<int> Function()?, Function?> {
Test12() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => C2<int>()) as Object?;
Object? o;
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -235,12 +338,23 @@ class Test13 extends Indexable<C1<int> Function()?, Function?> {
Test13() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -46,15 +46,15 @@ class Indexable<ReadType, WriteType> {
operator []=(int index, WriteType value) {}
}
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K` is
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that the
// - Let T1 be the read type of `e1`. This is the static type that `e1` would
// have as an expression with a context type schema of `_`.
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly promoted)
// type of the variable.
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for the
// case of indexed assignment, the type schema that `operator[]=` imposes
// on its second argument).
@ -75,24 +75,33 @@ class Test2 extends Indexable<String?, String?> {
}
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type of
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
class Test3 extends Indexable<int?, Object?> {
Test3() : super(null);
test() {
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((super[0] ??= d)..expectStaticType<Exactly<num>>());
}
@ -101,9 +110,16 @@ class Test3 extends Indexable<int?, Object?> {
class Test4 extends Indexable<Iterable<int>?, Object?> {
Test4() : super(null);
test() {
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((super[0] ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
@ -113,23 +129,39 @@ class Test4 extends Indexable<Iterable<int>?, Object?> {
class Test5 extends Indexable<Function?, Function?> {
Test5() : super(null);
test() {
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>(
(super[0] ??= callableClassInt)..expectStaticType<Exactly<Function>>());
}
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E` is
// `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
class Test6 extends Indexable<C1<int>?, Object?> {
Test6() : super(null);
test() {
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1(
(super[0] ??= c2Double)..expectStaticType<Exactly<B1<Object?>>>());
@ -139,9 +171,18 @@ class Test6 extends Indexable<C1<int>?, Object?> {
class Test7 extends Indexable<C1<int>?, Object?> {
Test7() : super(null);
test() {
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
var c2Double = C2<double>();
contextB1<Object>(
(super[0] ??= c2Double)..expectStaticType<Exactly<B1<Object>>>());
@ -151,9 +192,18 @@ class Test7 extends Indexable<C1<int>?, Object?> {
class Test8 extends Indexable<Iterable<int>?, Object?> {
Test8() : super(null);
test() {
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>(
(super[0] ??= listNum)..expectStaticType<Exactly<Iterable<num>>>());
@ -163,26 +213,46 @@ class Test8 extends Indexable<Iterable<int>?, Object?> {
class Test9 extends Indexable<C1<int> Function()?, Function?> {
Test9() : super(null);
test() {
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((super[0] ??= callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
class Test10 extends Indexable<int?, Object?> {
Test10() : super(null);
test() {
var d = 2.0;
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= d)..expectStaticType<Exactly<num>>();
}
@ -193,11 +263,22 @@ class Test11 extends Indexable<double?, Object?> {
Test11() : super(null);
test() {
var intQuestion = null as int?;
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= intQuestion)..expectStaticType<Exactly<num?>>();
}
@ -208,10 +289,23 @@ class Test12 extends Indexable<int?, Object?> {
Test12() : super(null);
test() {
var d = 2.0;
var o = '' as Object?;
Object? o;
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= d)..expectStaticType<Exactly<num>>();
}
@ -222,12 +316,23 @@ class Test13 extends Indexable<C1<int> Function()?, Function?> {
Test13() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => C1<int>()) as Object?;
Object? o;
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -239,12 +344,23 @@ class Test14 extends Indexable<C1<int> Function()?, Function?> {
Test14() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => C2<int>()) as Object?;
Object? o;
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -256,12 +372,23 @@ class Test15 extends Indexable<C1<int> Function()?, Function?> {
Test15() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -55,15 +55,15 @@ class Base {
class Test extends Base {
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -76,61 +76,104 @@ class Test extends Base {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(super.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((super.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((super.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is
// `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -140,31 +183,64 @@ class Test extends Base {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -172,10 +248,20 @@ class Test extends Base {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -183,10 +269,20 @@ class Test extends Base {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -194,10 +290,20 @@ class Test extends Base {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -60,15 +60,15 @@ class Base {
class Test extends Base {
test() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K
// is analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// - Otherwise, J is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
@ -81,100 +81,192 @@ class Test extends Base {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T
// <: S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(super.pIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type
// of E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((super.pIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((super.pFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is
// S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((super.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((super.pC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore
// T=Object and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S
// and T2' <: S, hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((super.pIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=B1<int> Function(), so T is not <: S, but NonNull(T1)
// <: S and T2' <: S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((super.pC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E
// is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T
// is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of
// E is num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -182,10 +274,20 @@ class Test extends Base {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -193,10 +295,20 @@ class Test extends Base {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -204,10 +316,20 @@ class Test extends Base {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced
// from T2=CallableClass<C2<int>>), therefore T=A Function() and S=int,
// so T is not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the
// type of E is A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (super.pC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -42,15 +42,15 @@ class Indexable<ReadType, WriteType> {
operator []=(int index, WriteType value) {}
}
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K` is
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that the
// - Let T1 be the read type of `e1`. This is the static type that `e1` would
// have as an expression with a context type schema of `_`.
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly promoted)
// type of the variable.
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for the
// case of indexed assignment, the type schema that `operator[]=` imposes
// on its second argument).
@ -71,24 +71,33 @@ class Test2 extends Indexable<String?, String?> {
}
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type of
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
class Test3 extends Indexable<int?, Object?> {
Test3() : super(null);
test() {
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((this[0] ??= d)..expectStaticType<Exactly<num>>());
}
@ -97,9 +106,16 @@ class Test3 extends Indexable<int?, Object?> {
class Test4 extends Indexable<Iterable<int>?, Object?> {
Test4() : super(null);
test() {
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((this[0] ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
@ -109,25 +125,44 @@ class Test4 extends Indexable<Iterable<int>?, Object?> {
class Test5 extends Indexable<Function?, Function?> {
Test5() : super(null);
test() {
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>(
(this[0] ??= callableClassInt)..expectStaticType<Exactly<Function>>());
}
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E` is
// `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3` is
// enabled, then the type of `e` is S.
class Test6 extends Indexable<Iterable<int>?, Object?> {
Test6() : super(null);
test() {
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= listNum)..expectStaticType<Exactly<Object>>();
@ -138,12 +173,23 @@ class Test6 extends Indexable<Iterable<int>?, Object?> {
class Test7 extends Indexable<C1<int> Function()?, Function?> {
Test7() : super(null);
test() {
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => B1<int>()) as Object?;
Object? o;
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= callableClassC2Int)
@ -152,16 +198,27 @@ class Test7 extends Indexable<C1<int> Function()?, Function?> {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
class Test8 extends Indexable<int?, Object?> {
Test8() : super(null);
test() {
var d = 2.0;
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= d)..expectStaticType<Exactly<num>>();
}
@ -172,11 +229,22 @@ class Test9 extends Indexable<double?, Object?> {
Test9() : super(null);
test() {
var intQuestion = null as int?;
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= intQuestion)..expectStaticType<Exactly<num?>>();
}
@ -187,10 +255,23 @@ class Test10 extends Indexable<int?, Object?> {
Test10() : super(null);
test() {
var d = 2.0;
var o = '' as Object?;
Object? o;
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= d)..expectStaticType<Exactly<num>>();
}
@ -201,12 +282,23 @@ class Test11 extends Indexable<C1<int> Function()?, Function?> {
Test11() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => C1<int>()) as Object?;
Object? o;
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -218,12 +310,23 @@ class Test12 extends Indexable<C1<int> Function()?, Function?> {
Test12() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => C2<int>()) as Object?;
Object? o;
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -235,12 +338,23 @@ class Test13 extends Indexable<C1<int> Function()?, Function?> {
Test13() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -46,15 +46,15 @@ class Indexable<ReadType, WriteType> {
operator []=(int index, WriteType value) {}
}
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K` is
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that the
// - Let T1 be the read type of `e1`. This is the static type that `e1` would
// have as an expression with a context type schema of `_`.
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly promoted)
// type of the variable.
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for the
// case of indexed assignment, the type schema that `operator[]=` imposes
// on its second argument).
@ -75,24 +75,33 @@ class Test2 extends Indexable<String?, String?> {
}
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type of
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
class Test3 extends Indexable<int?, Object?> {
Test3() : super(null);
test() {
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>((this[0] ??= d)..expectStaticType<Exactly<num>>());
}
@ -101,9 +110,16 @@ class Test3 extends Indexable<int?, Object?> {
class Test4 extends Indexable<Iterable<int>?, Object?> {
Test4() : super(null);
test() {
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((this[0] ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
@ -113,23 +129,39 @@ class Test4 extends Indexable<Iterable<int>?, Object?> {
class Test5 extends Indexable<Function?, Function?> {
Test5() : super(null);
test() {
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>(
(this[0] ??= callableClassInt)..expectStaticType<Exactly<Function>>());
}
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E` is
// `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
class Test6 extends Indexable<C1<int>?, Object?> {
Test6() : super(null);
test() {
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((this[0] ??= c2Double)..expectStaticType<Exactly<B1<Object?>>>());
}
@ -138,9 +170,18 @@ class Test6 extends Indexable<C1<int>?, Object?> {
class Test7 extends Indexable<C1<int>?, Object?> {
Test7() : super(null);
test() {
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
var c2Double = C2<double>();
contextB1<Object>(
(this[0] ??= c2Double)..expectStaticType<Exactly<B1<Object>>>());
@ -150,9 +191,18 @@ class Test7 extends Indexable<C1<int>?, Object?> {
class Test8 extends Indexable<Iterable<int>?, Object?> {
Test8() : super(null);
test() {
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>(
(this[0] ??= listNum)..expectStaticType<Exactly<Iterable<num>>>());
@ -162,26 +212,46 @@ class Test8 extends Indexable<Iterable<int>?, Object?> {
class Test9 extends Indexable<C1<int> Function()?, Function?> {
Test9() : super(null);
test() {
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((this[0] ??= callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
class Test10 extends Indexable<int?, Object?> {
Test10() : super(null);
test() {
var d = 2.0;
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= d)..expectStaticType<Exactly<num>>();
}
@ -192,11 +262,22 @@ class Test11 extends Indexable<double?, Object?> {
Test11() : super(null);
test() {
var intQuestion = null as int?;
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= intQuestion)..expectStaticType<Exactly<num?>>();
}
@ -207,10 +288,23 @@ class Test12 extends Indexable<int?, Object?> {
Test12() : super(null);
test() {
var d = 2.0;
var o = '' as Object?;
Object? o;
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= d)..expectStaticType<Exactly<num>>();
}
@ -221,12 +315,23 @@ class Test13 extends Indexable<C1<int> Function()?, Function?> {
Test13() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => C1<int>()) as Object?;
Object? o;
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -238,12 +343,23 @@ class Test14 extends Indexable<C1<int> Function()?, Function?> {
Test14() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = (() => C2<int>()) as Object?;
Object? o;
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -255,12 +371,23 @@ class Test15 extends Indexable<C1<int> Function()?, Function?> {
Test15() : super(null);
test() {
var callableClassC2Int = CallableClass<C2<int>>();
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (this[0] ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -50,18 +50,18 @@ String? get topLevelStringQuestion => null;
set topLevelStringQuestion(String? value) {}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -71,60 +71,104 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(topLevelIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((topLevelIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((topLevelFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, and `inference-update-3`
// is enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Object>>();
}
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is A Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = A Function().
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => B1<int>()) as Object?;
if (o is B1<int> Function()) {
@ -134,31 +178,64 @@ main() {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -166,10 +243,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -177,10 +264,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -188,10 +285,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -54,18 +54,18 @@ String? get topLevelStringQuestion => null;
set topLevelStringQuestion(String? value) {}
main() {
// - An if-null assignment `E` of the form `e1 ??= e2` with context type `K`
// is analyzed as follows:
// - An if-null assignment `e` of the form `e1 ??= e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the read type of `e1`. This is the static type that `e1`
// - Let T1 be the read type of `e1`. This is the static type that `e1`
// would have as an expression with a context type schema of `_`.
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If the lvalue is a local variable, `J` is the current (possibly
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If the lvalue is a local variable, J is the current (possibly
// promoted) type of the variable.
// - Otherwise, `J` is the write type `e1`. This is the type schema that
// the setter associated with `e1` imposes on its single argument (or,
// for the case of indexed assignment, the type schema that
// `operator[]=` imposes on its second argument).
// - Otherwise, J is the write type `e1`. This is the type schema that the
// setter associated with `e1` imposes on its single argument (or, for
// the case of indexed assignment, the type schema that `operator[]=`
// imposes on its second argument).
{
// Check the context type of `e`.
// ignore: dead_null_aware_expression
@ -75,100 +75,191 @@ main() {
..expectStaticType<Exactly<String?>>();
}
// - Let `J'` be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, `J'` is the declared (unpromoted) type
// of `e1`.
// - Otherwise `J' = J`.
// - Let `T2'` be the coerced type of `e2`, defined as follows:
// - If `T2` is a subtype of `J'`, then `T2' = T2` (no coercion is
// needed).
// - Otherwise, if `T2` can be coerced to a some other type which *is* a
// subtype of `J'`, then apply that coercion and let `T2'` be the type
// - Let J' be the unpromoted write type of `e1`, defined as follows:
// - If `e1` is a local variable, J' is the declared (unpromoted) type of
// `e1`.
// - Otherwise J' = J.
// - Let T2' be the coerced type of `e2`, defined as follows:
// - If T2 is a subtype of J', then T2' = T2 (no coercion is needed).
// - Otherwise, if T2 can be coerced to a some other type which *is* a
// subtype of J', then apply that coercion and let T2' be the type
// resulting from the coercion.
// - Otherwise, it is a compile-time error.
// - Let `T` be `UP(NonNull(T1), T2')`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2').
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2'=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(topLevelIntQuestion ??= d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2'=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2' = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((topLevelIterableIntQuestion ??= iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
// K=Function, T1=Function?, and T2'=int Function() (coerced from
// T2=CallableClass<int>), therefore T=Function and S=Function, so T <: S,
// and hence the type of E is Function.
// This example has:
// - K = Function
// - T1 = Function?
// - T2' = int Function()
// (coerced from T2=CallableClass<int>)
// Which implies:
// - T = Function
// - S = Function
// We have:
// - T <: S
// Therefore the type of `e` is T = Function.
var callableClassInt = CallableClass<int>();
context<Function>((topLevelFunctionQuestion ??= callableClassInt)
..expectStaticType<Exactly<Function>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2' <: S`, then the type of `E`
// is `S`.
// - Otherwise, if NonNull(T1) <: S and T2' <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object?>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object?>.
var c2Double = C2<double>();
contextB1((topLevelC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2'=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2' <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2' = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((topLevelC1IntQuestion ??= c2Double)
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2'=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2' <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2' = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = Iterable<num>.
var listNum = <num>[];
context<Iterable<num>>((topLevelIterableIntQuestion ??= listNum)
..expectStaticType<Exactly<Iterable<num>>>());
// K=B1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>), therefore T=A Function() and
// S=B1<int> Function(), so T is not <: S, but NonNull(T1) <: S and T2' <:
// S, hence the type of E is B1<int> Function().
// This example has:
// - K = B1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = B1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <: S
// Therefore the type of `e` is S = B1<int> Function().
var callableClassC2Int = CallableClass<C2<int>>();
context<B1<int> Function()>((topLevelC1IntFunctionQuestion ??=
callableClassC2Int)
..expectStaticType<Exactly<B1<int> Function()>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var d = 2.0;
var o = 0 as Object?;
Object? o;
var intQuestion = null as int?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2'=double, therefore T=num and S=int?, so T is
// not <: S. NonNull(T1) <: S, but T2' is not <: S. Hence the type of E is
// num.
// This example has:
// - K = int?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2'=int?, therefore T=num? and S=int?, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2' = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelDoubleQuestion ??= intQuestion)
..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2'=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2' are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2' = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <!: S
// The fact that NonNull(T1) <!: S and T2' <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelIntQuestion ??= d)..expectStaticType<Exactly<num>>();
}
@ -176,10 +267,20 @@ main() {
var callableClassC2Int = CallableClass<C2<int>>();
o = (() => C1<int>()) as Object?;
if (o is C1<int> Function()) {
// K=C1<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C1<int> Function(), so T is not <: S. NonNull(T1) <:
// S, but T2' is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C1<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C1<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2' <!: S
// The fact that T2' <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -187,10 +288,20 @@ main() {
o = (() => C2<int>()) as Object?;
if (o is C2<int> Function()) {
// K=C2<int> Function(), T1=C1<int> Function()?, and T2'=C2<int>
// Function() (coerced from T2=CallableClass<C2<int>>), therefore T=A
// Function() and S=C2<int> Function(), so T is not <: S. T2' <: S, but
// NonNull(T1) is not <: S. Hence the type of E is A Function().
// This example has:
// - K = C2<int> Function()
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = C2<int> Function()
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();
@ -198,10 +309,20 @@ main() {
o = 0 as Object?;
if (o is int) {
// K=int, T1=C1<int> Function()?, and T2'=C2<int> Function() (coerced from
// T2=CallableClass<C2<int>>), therefore T=A Function() and S=int, so T is
// not <: S. T2' <: S, but NonNull(T1) is not <: S. Hence the type of E is
// A Function().
// This example has:
// - K = int
// - T1 = C1<int> Function()?
// - T2' = C2<int> Function()
// (coerced from T2=CallableClass<C2<int>>)
// Which implies:
// - T = A Function()
// - S = int
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2' <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = A Function().
// We avoid having a compile-time error because `o` can be demoted.
o = (topLevelC1IntFunctionQuestion ??= callableClassC2Int)
..expectStaticType<Exactly<A Function()>>();

View file

@ -20,10 +20,10 @@ Object? contextUnknown<T>(T x) => x;
Object? contextIterable<T>(Iterable<T> x) => x;
main() {
// - An if-null expression `E` of the form `e1 ?? e2` with context type `K` is
// - An if-null expression `e` of the form `e1 ?? e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K?`.
// - Let T1 be the type of `e1` inferred with context type K?.
{
// Check the context type of `e1`:
// - Where the context is established using a function call argument.
@ -31,7 +31,8 @@ main() {
context<num>((contextType(1)..expectStaticType<Exactly<num?>>()) ?? 2);
// - Where the context is established using local variable promotion.
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is num?) {
o = (contextType(1)..expectStaticType<Exactly<num?>>()) ?? 2;
}
@ -41,8 +42,8 @@ main() {
}
}
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If `K` is `_`, `J = T1`.
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If K is `_`, J = T1.
{
// Check the context type of `e2`.
var string = '';
@ -54,7 +55,7 @@ main() {
(contextType('')..expectStaticType<Exactly<String?>>()));
}
// - Otherwise, `J = K`.
// - Otherwise, J = K.
{
var intQuestion = null as int?;
context<num?>(
@ -63,64 +64,125 @@ main() {
intQuestion ?? (contextType(2)..expectStaticType<Exactly<num>>()));
}
// - Let `T` be `UP(NonNull(T1), T2)`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2).
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2 = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var intQuestion = null as int?;
var d = 2.0;
context<Object>((intQuestion ?? d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2 = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableIntQuestion = null as Iterable<int>?;
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((iterableIntQuestion ?? iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2 <: S`, then the type of `E` is
// `S` if `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if NonNull(T1) <: S and T2 <: S, and `inference-update-3` is
// enabled, then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>?, and T2=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2 <: S,
// hence the type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2 = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var iterableIntQuestion = null as Iterable<int>?;
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (iterableIntQuestion ?? listNum)..expectStaticType<Exactly<Object>>();
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var intQuestion = null as int?;
var d = 2.0;
var o = 0 as Object?;
Object? o;
var doubleQuestion = null as double?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2=double, therefore T=num and S=int?, so T is not
// <: S. NonNull(T1) <: S, but T2 is not <: S. Hence the type of E is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2 = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <!: S
// The fact that T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (intQuestion ?? d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2=int?, therefore T=num? and S=int?, so T is
// not <: S. T2 <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2 = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2 <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (doubleQuestion ?? intQuestion)..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2 are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2 = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2 <!: S
// The fact that NonNull(T1) <!: S and T2 <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (intQuestion ?? d)..expectStaticType<Exactly<num>>();
}

View file

@ -33,10 +33,10 @@ class C2<T> implements B1<T>, B2<T> {}
Object? contextB1<T>(B1<T> x) => x;
main() {
// - An if-null expression `E` of the form `e1 ?? e2` with context type `K` is
// - An if-null expression `e` of the form `e1 ?? e2` with context type K is
// analyzed as follows:
//
// - Let `T1` be the type of `e1` inferred with context type `K?`.
// - Let T1 be the type of `e1` inferred with context type K?.
{
// Check the context type of `e1`:
// - Where the context is established using a function call argument.
@ -44,7 +44,8 @@ main() {
context<num>((contextType(1)..expectStaticType<Exactly<num?>>()) ?? 2);
// - Where the context is established using local variable promotion.
var o = 0 as Object?;
Object? o;
o = 0 as Object?;
if (o is num?) {
o = (contextType(1)..expectStaticType<Exactly<num?>>()) ?? 2;
}
@ -54,8 +55,8 @@ main() {
}
}
// - Let `T2` be the type of `e2` inferred with context type `J`, where:
// - If `K` is `_`, `J = T1`.
// - Let T2 be the type of `e2` inferred with context type J, where:
// - If K is `_`, J = T1.
{
// Check the context type of `e2`.
var string = '';
@ -67,7 +68,7 @@ main() {
(contextType('')..expectStaticType<Exactly<String?>>()));
}
// - Otherwise, `J = K`.
// - Otherwise, J = K.
{
var intQuestion = null as int?;
context<num?>(
@ -76,75 +77,151 @@ main() {
intQuestion ?? (contextType(2)..expectStaticType<Exactly<num>>()));
}
// - Let `T` be `UP(NonNull(T1), T2)`.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let T be UP(NonNull(T1), T2).
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
// (Testing this case here. Otherwise continued below.)
{
// K=Object, T1=int?, and T2=double, therefore T=num and S=Object, so T <:
// S, and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int?
// - T2 = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var intQuestion = null as int?;
var d = 2.0;
context<Object>((intQuestion ?? d)..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>?, and T2=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>?
// - T2 = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableIntQuestion = null as Iterable<int>?;
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((iterableIntQuestion ?? iterableDouble)
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, if `NonNull(T1) <: S` and `T2 <: S`, then the type of `E` is
// `S`.
// - Otherwise, if NonNull(T1) <: S and T2 <: S, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>?, and T2=C2<double>, therefore T=A and S=B1<Object?>,
// so T is not <: S, but NonNull(T1) <: S and T2 <: S, hence the type of E
// is B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>?
// - T2 = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <: S
// Therefore the type of `e` is S = B1<Object?>.
var c1IntQuestion = null as C1<int>?;
var c2Double = C2<double>();
contextB1(
(c1IntQuestion ?? c2Double)..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>?, and T2=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but NonNull(T1) <: S and T2 <: S, hence
// the type of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>?
// - T2 = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>(
(c1IntQuestion ?? c2Double)..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>?, and T2=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but NonNull(T1) <: S and T2 <: S,
// hence the type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>?
// - T2 = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <: S
// Therefore the type of `e` is S = Iterable<num>.
var iterableIntQuestion = null as Iterable<int>?;
var listNum = <num>[];
context<Iterable<num>>((iterableIntQuestion ?? listNum)
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var intQuestion = null as int?;
var d = 2.0;
var o = 0 as Object?;
Object? o;
var doubleQuestion = null as double?;
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=int?, and T2=double, therefore T=num and S=int?, so T is not
// <: S. NonNull(T1) <: S, but T2 is not <: S. Hence the type of E is num.
// This example has:
// - K = int?
// - T1 = int?
// - T2 = double
// Which implies:
// - T = num
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <: S
// - T2 <!: S
// The fact that T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (intQuestion ?? d)..expectStaticType<Exactly<num>>();
}
o = 0 as Object?;
if (o is int?) {
// K=int?, T1=double?, and T2=int?, therefore T=num? and S=int?, so T is
// not <: S. T2 <: S, but NonNull(T1) is not <: S. Hence the type of E is
// num?.
// This example has:
// - K = int?
// - T1 = double?
// - T2 = int?
// Which implies:
// - T = num?
// - S = int?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2 <: S
// The fact that NonNull(T1) <!: S precludes using S as static type.
// Therefore the type of `e` is T = num?.
// We avoid having a compile-time error because `o` can be demoted.
o = (doubleQuestion ?? intQuestion)..expectStaticType<Exactly<num?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int?, and T2=double, therefore T=num and S=String?, so
// none of T, NonNull(T1), nor T2 are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int?
// - T2 = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - NonNull(T1) <!: S
// - T2 <!: S
// The fact that NonNull(T1) <!: S and T2 <!: S precludes using S as
// static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (intQuestion ?? d)..expectStaticType<Exactly<num>>();
}

View file

@ -16,13 +16,13 @@ import '../static_type_helper.dart';
Object? contextIterable<T>(Iterable<T> x) => x;
test(int i) {
// The static type of a switch expression `E` of the form `switch (e0) { p1 =>
// e1, p2 => e2, ... pn => en }` with context type `K` is computed as follows:
// The static type of a switch expression `e` of the form `switch (e0) { p1 =>
// e1, p2 => e2, ... pn => en }` with context type K is computed as follows:
//
// - The scrutinee (`e0`) is first analyzed with context type `_`.
// - If the switch expression has no cases, its static type is `Never`.
// - Otherwise, for each case `pi => ei`, let `Ti` be the type of `ei`
// inferred with context type `K`.
// - Otherwise, for each case `pi => ei`, let Ti be the type of `ei` inferred
// with context type K.
{
// Check the context type of each `ei`:
// - Where the context is established using a function call argument.
@ -33,7 +33,8 @@ test(int i) {
});
// - Where the context is established using local variable promotion.
var o = '' as Object?;
Object? o;
o = '' as Object?;
if (o is String) {
o = switch (i) {
0 => (contextType('')..expectStaticType<Exactly<String>>()),
@ -43,35 +44,61 @@ test(int i) {
}
}
// - Let `T` be the least upper bound of the static types of all the case
// - Let T be the least upper bound of the static types of all the case
// expressions.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
{
// K=Object, T1=int, and T2=double, therefore T=num and S=Object, so T <: S,
// and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int
// - T2 = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(switch (i) { 0 => i, _ => d })..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>, and T2=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>
// - T2 = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableInt = <int>[] as Iterable<int>;
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((switch (i) { 0 => iterableInt, _ => iterableDouble })
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, if `Ti <: S` for all `i`, then the type of `E` is `S` if
// `inference-update-3` is enabled, else the type of `E` is `T`.
// - Otherwise, if Ti <: S for all i, and `inference-update-3` is enabled,
// then the type of `e` is S.
{
// K=Iterable<num>, T1=Iterable<int>, and T2=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but T1 <: S and T2 <: S, hence the
// type of E is Object.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>
// - T2 = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - T1 <: S
// - T2 <: S
// However, inference-update-3 is not enabled.
// Therefore the type of `e` is T = Object.
var iterableInt = <int>[] as Iterable<int>;
var listNum = <num>[];
var o = [0] as Object?;
Object? o;
o = [0] as Object?;
if (o is Iterable<num>) {
// We avoid having a compile-time error because `o` can be demoted.
o = (switch (i) { 0 => iterableInt, _ => listNum })
@ -79,27 +106,61 @@ test(int i) {
}
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var o = '' as Object?;
Object? o;
var d = 2.0;
o = '' as Object?;
if (o is String?) {
// K=String?, T1=Null, and T2=int, therefore T=int? and S=String?, so T is
// not <: S. T1 <: S, but T2 is not <: S. Hence the type of E is int?.
// This example has:
// - K = String?
// - T1 = Null
// - T2 = int
// Which implies:
// - T = int?
// - S = String?
// We have:
// - T <!: S
// - T1 <: S
// - T2 <!: S
// The fact that T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = int?.
// We avoid having a compile-time error because `o` can be demoted.
o = (switch (i) { 0 => null, _ => i })..expectStaticType<Exactly<int?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int, and T2=Null, therefore T=int? and S=String?, so T is
// not <: S. T2 <: S, but T1 is not <: S. Hence the type of E is int?.
// This example has:
// - K = String?
// - T1 = int
// - T2 = Null
// Which implies:
// - T = int?
// - S = String?
// We have:
// - T <!: S
// - T1 <!: S
// - T2 <: S
// The fact that T1 <!: S precludes using S as static type.
// Therefore the type of `e` is T = int?.
// We avoid having a compile-time error because `o` can be demoted.
o = (switch (i) { 0 => i, _ => null })..expectStaticType<Exactly<int?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int, and T2=double, therefore T=num and S=String?, so
// none of T, T1, nor T2 are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int
// - T2 = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - T1 <!: S
// - T2 <!: S
// The fact that T1 <!: S and T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (switch (i) { 0 => i, _ => d })..expectStaticType<Exactly<num>>();
}

View file

@ -29,13 +29,13 @@ class C2<T> implements B1<T>, B2<T> {}
Object? contextB1<T>(B1<T> x) => x;
test(int i) {
// The static type of a switch expression `E` of the form `switch (e0) { p1 =>
// e1, p2 => e2, ... pn => en }` with context type `K` is computed as follows:
// The static type of a switch expression `e` of the form `switch (e0) { p1 =>
// e1, p2 => e2, ... pn => en }` with context type K is computed as follows:
//
// - The scrutinee (`e0`) is first analyzed with context type `_`.
// - If the switch expression has no cases, its static type is `Never`.
// - Otherwise, for each case `pi => ei`, let `Ti` be the type of `ei`
// inferred with context type `K`.
// - Otherwise, for each case `pi => ei`, let Ti be the type of `ei` inferred
// with context type K.
{
// Check the context type of each `ei`:
// - Where the context is established using a function call argument.
@ -46,7 +46,8 @@ test(int i) {
});
// - Where the context is established using local variable promotion.
var o = '' as Object?;
Object? o;
o = '' as Object?;
if (o is String) {
o = switch (i) {
0 => (contextType('')..expectStaticType<Exactly<String>>()),
@ -56,72 +57,148 @@ test(int i) {
}
}
// - Let `T` be the least upper bound of the static types of all the case
// - Let T be the least upper bound of the static types of all the case
// expressions.
// - Let `S` be the greatest closure of `K`.
// - If `T <: S`, then the type of `E` is `T`.
// - Let S be the greatest closure of K.
// - If T <: S, then the type of `e` is T.
{
// K=Object, T1=int, and T2=double, therefore T=num and S=Object, so T <: S,
// and hence the type of E is num.
// This example has:
// - K = Object
// - T1 = int
// - T2 = double
// Which implies:
// - T = num
// - S = Object
// We have:
// - T <: S
// Therefore the type of `e` is T = num.
var d = 2.0;
context<Object>(
(switch (i) { 0 => i, _ => d })..expectStaticType<Exactly<num>>());
// K=Iterable<_>, T1=Iterable<int>, and T2=Iterable<double>, therefore
// T=Iterable<num> and S=Iterable<Object?>, so T <: S, and hence the type of
// E is Iterable<num>.
// This example has:
// - K = Iterable<_>
// - T1 = Iterable<int>
// - T2 = Iterable<double>
// Which implies:
// - T = Iterable<num>
// - S = Iterable<Object?>
// We have:
// - T <: S
// Therefore the type of `e` is T = Iterable<num>.
var iterableInt = <int>[] as Iterable<int>;
var iterableDouble = <double>[] as Iterable<double>;
contextIterable((switch (i) { 0 => iterableInt, _ => iterableDouble })
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, if `Ti <: S` for all `i`, then the type of `E` is `S`.
// - Otherwise, if Ti <: S for all i, then the type of `e` is S.
{
// K=B1<_>, T1=C1<int>, and T2=C2<double>, therefore T=A and S=B1<Object?>,
// so T is not <: S, but T1 <: S and T2 <: S, hence the type of E is
// B1<Object?>.
// This example has:
// - K = B1<_>
// - T1 = C1<int>
// - T2 = C2<double>
// Which implies:
// - T = A
// - S = B1<Object?>
// We have:
// - T <!: S
// - T1 <: S
// - T2 <: S
// Therefore the type of `e` is S = B1<Object?>.
var c1Int = C1<int>();
var c2Double = C2<double>();
contextB1((switch (i) { 0 => c1Int, _ => c2Double })
..expectStaticType<Exactly<B1<Object?>>>());
// K=B1<Object>, T1=C1<int>, and T2=C2<double>, therefore T=A and
// S=B1<Object>, so T is not <: S, but T1 <: S and T2 <: S, hence the type
// of E is B1<Object>.
// This example has:
// - K = B1<Object>
// - T1 = C1<int>
// - T2 = C2<double>
// Which implies:
// - T = A
// - S = B1<Object>
// We have:
// - T <!: S
// - T1 <: S
// - T2 <: S
// Therefore the type of `e` is S = B1<Object>.
contextB1<Object>((switch (i) { 0 => c1Int, _ => c2Double })
..expectStaticType<Exactly<B1<Object>>>());
// K=Iterable<num>, T1=Iterable<int>, and T2=List<num>, therefore T=Object
// and S=Iterable<num>, so T is not <: S, but T1 <: S and T2 <: S, hence the
// type of E is Iterable<num>.
// This example has:
// - K = Iterable<num>
// - T1 = Iterable<int>
// - T2 = List<num>
// Which implies:
// - T = Object
// - S = Iterable<num>
// We have:
// - T <!: S
// - T1 <: S
// - T2 <: S
// Therefore the type of `e` is S = Iterable<num>.
var iterableInt = <int>[] as Iterable<int>;
var listNum = <num>[];
context<Iterable<num>>((switch (i) { 0 => iterableInt, _ => listNum })
..expectStaticType<Exactly<Iterable<num>>>());
}
// - Otherwise, the type of `E` is `T`.
// - Otherwise, the type of `e` is T.
{
var o = '' as Object?;
Object? o;
var d = 2.0;
o = '' as Object?;
if (o is String?) {
// K=String?, T1=Null, and T2=int, therefore T=int? and S=String?, so T is
// not <: S. T1 <: S, but T2 is not <: S. Hence the type of E is int?.
// This example has:
// - K = String?
// - T1 = Null
// - T2 = int
// Which implies:
// - T = int?
// - S = String?
// We have:
// - T <!: S
// - T1 <: S
// - T2 <!: S
// The fact that T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = int?.
// We avoid having a compile-time error because `o` can be demoted.
o = (switch (i) { 0 => null, _ => i })..expectStaticType<Exactly<int?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int, and T2=Null, therefore T=int? and S=String?, so T is
// not <: S. T2 <: S, but T1 is not <: S. Hence the type of E is int?.
// This example has:
// - K = String?
// - T1 = int
// - T2 = Null
// Which implies:
// - T = int?
// - S = String?
// We have:
// - T <!: S
// - T1 <!: S
// - T2 <: S
// The fact that T1 <!: S precludes using S as static type.
// Therefore the type of `e` is T = int?.
// We avoid having a compile-time error because `o` can be demoted.
o = (switch (i) { 0 => i, _ => null })..expectStaticType<Exactly<int?>>();
}
o = '' as Object?;
if (o is String?) {
// K=String?, T1=int, and T2=double, therefore T=num and S=String?, so
// none of T, T1, nor T2 are <: S. Hence the type of E is num.
// This example has:
// - K = String?
// - T1 = int
// - T2 = double
// Which implies:
// - T = num
// - S = String?
// We have:
// - T <!: S
// - T1 <!: S
// - T2 <!: S
// The fact that T1 <!: S and T2 <!: S precludes using S as static type.
// Therefore the type of `e` is T = num.
// We avoid having a compile-time error because `o` can be demoted.
o = (switch (i) { 0 => i, _ => d })..expectStaticType<Exactly<num>>();
}