[cfe] Make undeclared return context undefined for async functions
Closes #46956.
Bug: https://github.com/dart-lang/sdk/issues/46956
Change-Id: I90b8e88864201fb522c5e626843e1dff262f591a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211023
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Dmitry Stefantsov <dmitryas@google.com>
diff --git a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
index 6d39d71..5a8daa1 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -953,7 +953,11 @@
DartType _computeReturnTypeContext(MemberBuilder member) {
if (member is ProcedureBuilder) {
- return member.function.returnType;
+ final bool isReturnTypeUndeclared = member.returnType == null &&
+ member.function.returnType is DynamicType;
+ return isReturnTypeUndeclared && libraryBuilder.isNonNullableByDefault
+ ? const UnknownType()
+ : member.function.returnType;
} else if (member is SourceFactoryBuilder) {
return member.function.returnType;
} else {
@@ -6450,12 +6454,8 @@
allowPotentiallyConstantType: allowPotentiallyConstantType);
if (message == null) return unresolved;
return new UnresolvedType(
- new NamedTypeBuilder(
- typeParameter.name!,
- builder.nullabilityBuilder,
- /* arguments = */ null,
- unresolved.fileUri,
- unresolved.charOffset)
+ new NamedTypeBuilder(typeParameter.name!, builder.nullabilityBuilder,
+ /* arguments = */ null, unresolved.fileUri, unresolved.charOffset)
..bind(new InvalidTypeDeclarationBuilder(
typeParameter.name!, message)),
unresolved.charOffset,
diff --git a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
index 1332a26..ff7e4aa 100644
--- a/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
+++ b/pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart
@@ -2502,7 +2502,8 @@
bool hasImplicitReturnType = false;
if (returnContext == null) {
hasImplicitReturnType = true;
- returnContext = const DynamicType();
+ returnContext =
+ isNonNullableByDefault ? const UnknownType() : const DynamicType();
}
if (!isTopLevel) {
List<VariableDeclaration> positionalParameters =
diff --git a/pkg/front_end/testcases/general/issue46956.dart b/pkg/front_end/testcases/general/issue46956.dart
new file mode 100644
index 0000000..4211b23
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46956.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2021, 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.
+
+import 'dart:async';
+
+class A<X> {}
+
+Future<X?> foo<X extends Object?>(A<X> x) => throw 42;
+
+bar(String? y) {}
+
+test(A<String> a) async {
+ final x = await () async {
+ return foo(a);
+ }();
+ bar(x); // Ok.
+}
+
+test2(A<String> a) async {
+ return /*@typeArgs=String*/ foo(a);
+}
+
+test3(A<String> a) {
+ return /*@typeArgs=String*/ foo(a);
+}
+
+main() {}
diff --git a/pkg/front_end/testcases/general/issue46956.dart.textual_outline.expect b/pkg/front_end/testcases/general/issue46956.dart.textual_outline.expect
new file mode 100644
index 0000000..c1dd88b
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46956.dart.textual_outline.expect
@@ -0,0 +1,10 @@
+import 'dart:async';
+
+class A<X> {}
+
+Future<X?> foo<X extends Object?>(A<X> x) => throw 42;
+bar(String? y) {}
+test(A<String> a) async {}
+test2(A<String> a) async {}
+test3(A<String> a) {}
+main() {}
diff --git a/pkg/front_end/testcases/general/issue46956.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/general/issue46956.dart.textual_outline_modelled.expect
new file mode 100644
index 0000000..1c9542b
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46956.dart.textual_outline_modelled.expect
@@ -0,0 +1,11 @@
+import 'dart:async';
+
+Future<X?> foo<X extends Object?>(A<X> x) => throw 42;
+bar(String? y) {}
+
+class A<X> {}
+
+main() {}
+test(A<String> a) async {}
+test2(A<String> a) async {}
+test3(A<String> a) {}
diff --git a/pkg/front_end/testcases/general/issue46956.dart.weak.expect b/pkg/front_end/testcases/general/issue46956.dart.weak.expect
new file mode 100644
index 0000000..eb4a9a3
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46956.dart.weak.expect
@@ -0,0 +1,28 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+class A<X extends core::Object? = dynamic> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X%>
+ : super core::Object::•()
+ ;
+}
+static method foo<X extends core::Object?>(self::A<self::foo::X%> x) → asy::Future<self::foo::X?>
+ return throw 42;
+static method bar(core::String? y) → dynamic {}
+static method test(self::A<core::String> a) → dynamic async {
+ final core::String? x = await(() → asy::Future<core::String?> async {
+ return self::foo<core::String>(a);
+ })(){() → asy::Future<core::String?>};
+ self::bar(x);
+}
+static method test2(self::A<core::String> a) → dynamic async {
+ return self::foo<core::String>(a);
+}
+static method test3(self::A<core::String> a) → dynamic {
+ return self::foo<core::String>(a);
+}
+static method main() → dynamic {}
diff --git a/pkg/front_end/testcases/general/issue46956.dart.weak.outline.expect b/pkg/front_end/testcases/general/issue46956.dart.weak.outline.expect
new file mode 100644
index 0000000..8c2c744
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46956.dart.weak.outline.expect
@@ -0,0 +1,23 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+
+import "dart:async";
+
+class A<X extends core::Object? = dynamic> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X%>
+ ;
+}
+static method foo<X extends core::Object?>(self::A<self::foo::X%> x) → asy::Future<self::foo::X?>
+ ;
+static method bar(core::String? y) → dynamic
+ ;
+static method test(self::A<core::String> a) → dynamic async
+ ;
+static method test2(self::A<core::String> a) → dynamic async
+ ;
+static method test3(self::A<core::String> a) → dynamic
+ ;
+static method main() → dynamic
+ ;
diff --git a/pkg/front_end/testcases/general/issue46956.dart.weak.transformed.expect b/pkg/front_end/testcases/general/issue46956.dart.weak.transformed.expect
new file mode 100644
index 0000000..ba98fca
--- /dev/null
+++ b/pkg/front_end/testcases/general/issue46956.dart.weak.transformed.expect
@@ -0,0 +1,102 @@
+library /*isNonNullableByDefault*/;
+import self as self;
+import "dart:core" as core;
+import "dart:async" as asy;
+import "dart:_internal" as _in;
+
+import "dart:async";
+
+class A<X extends core::Object? = dynamic> extends core::Object {
+ synthetic constructor •() → self::A<self::A::X%>
+ : super core::Object::•()
+ ;
+}
+static method foo<X extends core::Object?>(self::A<self::foo::X%> x) → asy::Future<self::foo::X?>
+ return throw 42;
+static method bar(core::String? y) → dynamic {}
+static method test(self::A<core::String> a) → dynamic /* originally async */ {
+ final asy::_Future<dynamic> :async_future = new asy::_Future::•<dynamic>();
+ core::bool* :is_sync = false;
+ FutureOr<dynamic>? :return_value;
+ (dynamic) → dynamic :async_op_then;
+ (core::Object, core::StackTrace) → dynamic :async_op_error;
+ core::int :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ dynamic :saved_try_context_var0;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L1:
+ {
+ [yield] let dynamic #t1 = asy::_awaitHelper((() → asy::Future<core::String?> /* originally async */ {
+ final asy::_Future<core::String?> :async_future = new asy::_Future::•<core::String?>();
+ core::bool* :is_sync = false;
+ FutureOr<core::String?>? :return_value;
+ (dynamic) → dynamic :async_op_then;
+ (core::Object, core::StackTrace) → dynamic :async_op_error;
+ core::int :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L2:
+ {
+ :return_value = self::foo<core::String>(a);
+ break #L2;
+ }
+ asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace stack_trace) {
+ asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
+ }
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_op(){() → dynamic};
+ :is_sync = true;
+ return :async_future;
+ })(){() → asy::Future<core::String?>}, :async_op_then, :async_op_error, :async_op) in null;
+ final core::String? x = _in::unsafeCast<core::String?>(:result);
+ self::bar(x);
+ }
+ asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace stack_trace) {
+ asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
+ }
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_op(){() → dynamic};
+ :is_sync = true;
+ return :async_future;
+}
+static method test2(self::A<core::String> a) → dynamic /* originally async */ {
+ final asy::_Future<dynamic> :async_future = new asy::_Future::•<dynamic>();
+ core::bool* :is_sync = false;
+ FutureOr<dynamic>? :return_value;
+ (dynamic) → dynamic :async_op_then;
+ (core::Object, core::StackTrace) → dynamic :async_op_error;
+ core::int :await_jump_var = 0;
+ dynamic :await_ctx_var;
+ function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
+ try {
+ #L3:
+ {
+ :return_value = self::foo<core::String>(a);
+ break #L3;
+ }
+ asy::_completeOnAsyncReturn(:async_future, :return_value, :is_sync);
+ return;
+ }
+ on dynamic catch(dynamic exception, core::StackTrace stack_trace) {
+ asy::_completeOnAsyncError(:async_future, exception, stack_trace, :is_sync);
+ }
+ :async_op_then = asy::_asyncThenWrapperHelper(:async_op);
+ :async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
+ :async_op(){() → dynamic};
+ :is_sync = true;
+ return :async_future;
+}
+static method test3(self::A<core::String> a) → dynamic {
+ return self::foo<core::String>(a);
+}
+static method main() → dynamic {}