From 497d6105df73987e7cb3b0a276b3dfa49a847cbe Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Wed, 8 May 2024 17:10:52 +0000 Subject: [PATCH] [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 Commit-Queue: Paul Berry Reviewed-by: Chloe Stefantsova --- CHANGELOG.md | 9 ++++ .../type_inference/inference_visitor.dart | 5 +- .../await_with_dynamic_context_test.dart | 47 +++++++++++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tests/language/await/await_with_dynamic_context_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index be25665af6e..4780dc6e371 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 463dcb0650c..653d7623431 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 @@ 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); 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 00000000000..484c646dd47 --- /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?) +// ?? (Future.value(C())..expectStaticType>>())))`, where `C` +// is a class that extends `B`, and `id` is a generic identity function having +// type `T Function(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`. +// +// 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?`. +// - Therefore downwards inference constraints the type parameter of +// `Future.value(C())` to `B`. +// - So the static type of `Future.value(C())` is `Future`. + +import '../static_type_helper.dart'; + +class B {} + +class C extends B {} + +T id(T t) => t; + +main() async { + dynamic x = await id(((null as Future?) ?? + (Future.value(C())..expectStaticType>>()))); +}