blob: caa2b0074bc3fc0025c02bb88f1e35d3b2acc6db [file] [log] [blame] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
// Verify that if an `await` expression appears in a syntactic position that
// doesn't impose a context on it, it supplies a context of `FutureOr<_>` to its
// operand, rather than `_` (which was the behavior prior to fixing
// https://github.com/dart-lang/language/issues/3648).
// Contexts of `FutureOr<_>` and `_` are difficult to distinguish. This test
// distinguishes them by awaiting the expression `((null as Future<B>?) ??
// (Future.value(C())..expectStaticType<Exactly<Future<C>>>()))`, where `C` is a
// class that extends `B`.
//
// This works because, if it is analyzed in context `Future<_>`, then:
// - The RHS of `??` is analyzed in context `Future<_>`.
// - Therefore downwards inference doesn't impose any constraint on the type
// parameter of `Future.value(C())`.
// - Therefore `Future.value(C())` gets its type parameter from upwards
// inference.
// - So it receives a type parameter of `C`.
// - So the static type of `Future.value(C())` is `Future<C>`.
//
// Whereas if it is analyzed in context `_`, then:
// - The static type of the LHS of `??` is used as the context for the RHS.
// That is, the RHS is analyzed in context `Future<B>?`.
// - Therefore downwards inference constraints the type parameter of
// `Future.value(C())` to `B`.
// - So the static type of `Future.value(C())` is `Future<B>`.
import 'package:expect/static_type_helper.dart';
class B {
var prop;
void method() {}
}
class C extends B {}
main() async {
// Initializer part of a C-style for loop
for (
await ((null as Future<B>?) ??
(Future.value(C())..expectStaticType<Exactly<Future<C>>>()));
false;
) {}
// Updater part of a C-style for loop
for (
var i = 0;
i < 1;
i++,
await ((null as Future<B>?) ??
(Future.value(C())..expectStaticType<Exactly<Future<C>>>()))
) {}
// Target of a property set
(await ((null as Future<B>?) ??
(Future.value(C())..expectStaticType<Exactly<Future<C>>>())))
.prop =
0;
// Target of a property get
(await ((null as Future<B>?) ??
(Future.value(C())..expectStaticType<Exactly<Future<C>>>())))
.prop;
// Assert statement message
try {
assert(
false,
await ((null as Future<B>?) ??
(Future.value(C())..expectStaticType<Exactly<Future<C>>>())),
);
} on AssertionError {}
// Expression statement
await ((null as Future<B>?) ??
(Future.value(C())..expectStaticType<Exactly<Future<C>>>()));
// Interpolation expression
"${await ((null as Future<B>?) ?? (Future.value(C())..expectStaticType<Exactly<Future<C>>>()))}";
// Target of a method invocation
(await ((null as Future<B>?) ??
(Future.value(C())..expectStaticType<Exactly<Future<C>>>())))
.method();
}