[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>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be25665..4780dc6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
index 463dcb0..653d762 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/inference_visitor.dart
@@ -857,9 +857,10 @@
@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);
diff --git a/tests/language/await/await_with_dynamic_context_test.dart b/tests/language/await/await_with_dynamic_context_test.dart
new file mode 100644
index 0000000..484c646
--- /dev/null
+++ b/tests/language/await/await_with_dynamic_context_test.dart
@@ -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>>>())));
+}