dart-sdk/tests/language/await/await_with_no_context_test.dart
Paul Berry f7d5d0cfbe Remove special analyzer behavior for await expressions with "null context".
In the front end, type inference of an expression always takes place
with respect to a type schema (the "context"). In the analyzer, type
inference of an expression sometimes takes place with respect to a
context, but sometimes takes place with respect to no context at all;
the latter circumstance arises when the analyzer uses its standard
AstVisitor mechanism to call one of the visit methods in the
ResolverVisitor class, and so the visit method's contextType argument
takes on the value null. Because of this I am calling this situation a
"null context".

In all the circumstances where the analyzer infers an expression using
a null context, the front end infers the same expression using a
context of _. Furthermore, prior to this change, all but one of the
analyzer's visit methods treated a null context the same as they
treated a context of _. The one exception was visitAwaitExpression: in
this method, if the context was the null context, then the analyzer
analyzed the await expression's subexpression using a context of _;
otherwise, it analyzed it using a context of FutureOr<_>. Whereas the
front end, lacking any notion of a "null context", analyzes the await
expression's subexpression using a context of FutureOr<_> in the same
circumstances.

This change brings the analyzer behavior into line with the front end.

Fixes https://github.com/dart-lang/language/issues/3648.

Bug: https://github.com/dart-lang/language/issues/3648
Change-Id: Ifd77988010d4387ce48eaa20dff4356beec03753
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/357521
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2024-03-20 17:33:28 +00:00

89 lines
3.1 KiB
Dart

// 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 postion 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 '../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();
// Target of a throw expression
try {
throw await ((null as Future<B>?) ??
(Future.value(C())..expectStaticType<Exactly<Future<C>>>()));
} on C {}
}