1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-03 00:08:46 +00:00

[cfe] Make the context for await expressions consistent with analyzer.

Fixes https://github.com/dart-lang/language/issues/3649.
Fixes https://github.com/dart-lang/sdk/issues/55418.

Bug: https://github.com/dart-lang/language/issues/3649
Change-Id: Ifb2fe47bb343a357e2338843775f140c01bd8a88
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/361302
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Chloe Stefantsova <cstefantsova@google.com>
This commit is contained in:
Paul Berry 2024-05-08 17:10:52 +00:00 committed by Commit Queue
parent 8c1de038e0
commit 497d6105df
3 changed files with 59 additions and 2 deletions

View File

@ -1,5 +1,14 @@
## 3.5.0
### Language
- **Breaking Change** [#55418][]: The context used by the compiler to perform
type inference on the operand of an `await` expression has been changed to
match the behavior of the analyzer. This change is not expected to make any
difference in practice.
[#55418]: https://github.com/dart-lang/sdk/issues/55418
### Dart Runtime
- The Dart VM only executes sound null safe code, running of unsound null

View File

@ -857,9 +857,10 @@ class InferenceVisitorImpl extends InferenceVisitorBase
@override
ExpressionInferenceResult visitAwaitExpression(
AwaitExpression node, DartType typeContext) {
if (!typeSchemaEnvironment.isEmptyContext(typeContext)) {
typeContext = wrapFutureOrType(typeContext);
if (typeContext is DynamicType) {
typeContext = const UnknownType();
}
typeContext = wrapFutureOrType(typeContext);
ExpressionInferenceResult operandResult = inferExpression(
node.operand, typeContext,
isVoidAllowed: !isNonNullableByDefault);

View File

@ -0,0 +1,47 @@
// 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 is analyzed in a `dynamic` context, it
// supplies a context of `FutureOr<_>` to its operand, rather than `dynamic`
// (which was the front end behavior prior to fixing
// https://github.com/dart-lang/language/issues/3649).
// Contexts of `FutureOr<_>` and `dynamic` are difficult to distinguish. This
// test distinguishes them by awaiting the expression `id(((null as Future<B>?)
// ?? (Future.value(C())..expectStaticType<Exactly<Future<C>>>())))`, where `C`
// is a class that extends `B`, and `id` is a generic identity function having
// type `T Function<T>(T)`.
//
// This works because, if it is analyzed in context `Future<_>`, then:
// - The call to `id` is analyzed in context `Future<_>`.
// - Therefore the `??` expression is analyzed in context `Future<_>`.
// - Therefore 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 `dynamic`, then:
// - The call to `id` is analyzed in context `dynamic`.
// - Therefore the `??` expression is analyzed in context `_`.
// - Therefore 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 {}
class C extends B {}
T id<T>(T t) => t;
main() async {
dynamic x = await id(((null as Future<B>?) ??
(Future.value(C())..expectStaticType<Exactly<Future<C>>>())));
}