[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 {}