Illegal return type check for functions marked async* or sync*.

Extends the illegal return type check for async functions to cover
illegal return types for functions marked async* or sync* as well.

Adds new error messages for illegal return types of functions marked
async, async*, and sync*. These error messages conform with those
produced by the analyzer.

Closes https://github.com/dart-lang/sdk/issues/33068

Bug: www.dartbug.com/33068
Change-Id: I6d8690537139d3d094a4e295fcac9de4f4a7f0af
Reviewed-on: https://dart-review.googlesource.com/69581
Commit-Queue: Daniel Hillerström <hillerstrom@google.com>
Reviewed-by: Aske Simon Christensen <askesc@google.com>
diff --git a/pkg/analyzer/test/generated/static_type_warning_code_kernel_test.dart b/pkg/analyzer/test/generated/static_type_warning_code_kernel_test.dart
index 6114af6..1434544 100644
--- a/pkg/analyzer/test/generated/static_type_warning_code_kernel_test.dart
+++ b/pkg/analyzer/test/generated/static_type_warning_code_kernel_test.dart
@@ -38,55 +38,6 @@
 
   @override
   @failingTest
-  test_illegalAsyncGeneratorReturnType_function_nonStream() async {
-    await super.test_illegalAsyncGeneratorReturnType_function_nonStream();
-  }
-
-  @override
-  @failingTest
-  test_illegalAsyncGeneratorReturnType_function_subtypeOfStream() async {
-    await super.test_illegalAsyncGeneratorReturnType_function_subtypeOfStream();
-  }
-
-  @override
-  @failingTest
-  test_illegalAsyncGeneratorReturnType_method_nonStream() async {
-    await super.test_illegalAsyncGeneratorReturnType_method_nonStream();
-  }
-
-  @override
-  @failingTest
-  test_illegalAsyncGeneratorReturnType_method_subtypeOfStream() async {
-    await super.test_illegalAsyncGeneratorReturnType_method_subtypeOfStream();
-  }
-
-  @override
-  @failingTest
-  test_illegalSyncGeneratorReturnType_function_nonIterator() async {
-    await super.test_illegalSyncGeneratorReturnType_function_nonIterator();
-  }
-
-  @override
-  @failingTest
-  test_illegalSyncGeneratorReturnType_function_subclassOfIterator() async {
-    await super
-        .test_illegalSyncGeneratorReturnType_function_subclassOfIterator();
-  }
-
-  @override
-  @failingTest
-  test_illegalSyncGeneratorReturnType_method_nonIterator() async {
-    await super.test_illegalSyncGeneratorReturnType_method_nonIterator();
-  }
-
-  @override
-  @failingTest
-  test_illegalSyncGeneratorReturnType_method_subclassOfIterator() async {
-    await super.test_illegalSyncGeneratorReturnType_method_subclassOfIterator();
-  }
-
-  @override
-  @failingTest
   test_instanceAccessToStaticMember_method_invocation() async {
     await super.test_instanceAccessToStaticMember_method_invocation();
   }
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index 2f94387..88baf99 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -3278,6 +3278,31 @@
     message: r"""Illegal assignment to non-assignable expression.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeIllegalAsyncGeneratorReturnType =
+    messageIllegalAsyncGeneratorReturnType;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageIllegalAsyncGeneratorReturnType = const MessageCode(
+    "IllegalAsyncGeneratorReturnType",
+    analyzerCode: "ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE",
+    dart2jsCode: "*fatal*",
+    severity: Severity.error,
+    message:
+        r"""Functions marked 'async*' must have a return type assignable to 'Stream'.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeIllegalAsyncGeneratorVoidReturnType =
+    messageIllegalAsyncGeneratorVoidReturnType;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageIllegalAsyncGeneratorVoidReturnType =
+    const MessageCode("IllegalAsyncGeneratorVoidReturnType",
+        dart2jsCode: "*fatal*",
+        severity: Severity.error,
+        message:
+            r"""Functions marked 'async*' can't have return type 'void'.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeIllegalAsyncReturnType = messageIllegalAsyncReturnType;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
@@ -3287,7 +3312,7 @@
     dart2jsCode: "*fatal*",
     severity: Severity.error,
     message:
-        r"""Functions marked 'async' must have a return type that is a super type of 'Future'.""");
+        r"""Functions marked 'async' must have a return type assignable to 'Future'.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String name)> templateIllegalMixin =
@@ -3354,6 +3379,30 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeIllegalSyncGeneratorReturnType =
+    messageIllegalSyncGeneratorReturnType;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageIllegalSyncGeneratorReturnType = const MessageCode(
+    "IllegalSyncGeneratorReturnType",
+    analyzerCode: "ILLEGAL_SYNC_GENERATOR_RETURN_TYPE",
+    dart2jsCode: "*fatal*",
+    severity: Severity.error,
+    message:
+        r"""Functions marked 'sync*' must have a return type assignable to 'Iterable'.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Null> codeIllegalSyncGeneratorVoidReturnType =
+    messageIllegalSyncGeneratorVoidReturnType;
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const MessageCode messageIllegalSyncGeneratorVoidReturnType = const MessageCode(
+    "IllegalSyncGeneratorVoidReturnType",
+    dart2jsCode: "*fatal*",
+    severity: Severity.error,
+    message: r"""Functions marked 'sync*' can't have return type 'void'.""");
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeImplementsBeforeExtends = messageImplementsBeforeExtends;
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
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 8fb5c36..7c85c12 100644
--- a/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/body_builder.dart
@@ -706,9 +706,8 @@
     _typeInferrer.inferFunctionBody(
         this, _computeReturnTypeContext(member), asyncModifier, body);
 
-    // For async functions with declared return types, we need to determine
-    // whether those types are valid.
-
+    // For async, async*, and sync* functions with declared return types, we need
+    // to determine whether those types are valid.
     // TODO(hillerstrom): currently, we need to check whether [strongMode] is
     // enabled for two reasons:
     // 1) the [isSubtypeOf] predicate produces false-negatives when [strongMode]
@@ -717,22 +716,58 @@
     // [strongMode] is false. This particular behaviour can be observed when
     // running the fasta perf benchmarks.
     bool strongMode = library.loader.target.strongMode;
-    if (strongMode &&
-        builder.returnType != null &&
-        asyncModifier == AsyncMarker.Async) {
+    if (strongMode && builder.returnType != null) {
       DartType returnType = builder.function.returnType;
-      // In order to decide whether Future<T> <: [returnType] for every T, we
-      // rely on Future<Bot> and transitivity of the subtyping relation
-      // because Future<Bot> <: Future<T> for every T.
-      DartType futureBottomType = library.loader.futureOfBottom;
+      // We use the same trick in each case below. For example to decide whether
+      // Future<T> <: [returnType] for every T, we rely on Future<Bot> and
+      // transitivity of the subtyping relation because Future<Bot> <: Future<T>
+      // for every T.
+      bool Function(DartType, DartType) isSubtypeOf = (DartType subtype,
+              DartType supertype) =>
+          _typeInferrer.typeSchemaEnvironment.isSubtypeOf(subtype, supertype);
 
-      if (!_typeInferrer.typeSchemaEnvironment
-          .isSubtypeOf(futureBottomType, returnType)) {
+      // Determine whether there is a problem. We use [problem == null] to
+      // signal success.
+      Message problem;
+      switch (asyncModifier) {
+        case AsyncMarker.Async:
+          DartType futureBottomType = library.loader.futureOfBottom;
+          if (!isSubtypeOf(futureBottomType, returnType)) {
+            problem = fasta.messageIllegalAsyncReturnType;
+          }
+          break;
+
+        case AsyncMarker.AsyncStar:
+          DartType streamBottomType = library.loader.streamOfBottom;
+          if (returnType is VoidType) {
+            problem = fasta.messageIllegalAsyncGeneratorVoidReturnType;
+          } else if (!isSubtypeOf(streamBottomType, returnType)) {
+            problem = fasta.messageIllegalAsyncGeneratorReturnType;
+          }
+          break;
+
+        case AsyncMarker.SyncStar:
+          DartType iterableBottomType = library.loader.iterableOfBottom;
+          if (returnType is VoidType) {
+            problem = fasta.messageIllegalSyncGeneratorVoidReturnType;
+          } else if (!isSubtypeOf(iterableBottomType, returnType)) {
+            problem = fasta.messageIllegalSyncGeneratorReturnType;
+          }
+          break;
+
+        case AsyncMarker.Sync:
+          break; // skip
+        case AsyncMarker.SyncYielding:
+          unexpected("async, async*, sync, or sync*", "$asyncModifier",
+              member.charOffset, uri);
+          break;
+      }
+
+      if (problem != null) {
         // TODO(hillerstrom): once types get annotated with location
         // information, we can improve the quality of the error message by
-        // using the offset of [returnType].
-        addProblem(fasta.messageIllegalAsyncReturnType, member.charOffset,
-            member.name.length);
+        // using the offset of [returnType] (and the length of its name).
+        addProblem(problem, member.charOffset, member.name.length);
       }
     }
 
diff --git a/pkg/front_end/lib/src/fasta/source/source_loader.dart b/pkg/front_end/lib/src/fasta/source/source_loader.dart
index ebdbc42..4e6ed0c 100644
--- a/pkg/front_end/lib/src/fasta/source/source_loader.dart
+++ b/pkg/front_end/lib/src/fasta/source/source_loader.dart
@@ -120,6 +120,8 @@
   CoreTypes coreTypes;
   // Used when checking whether a return type of an async function is valid.
   DartType futureOfBottom;
+  DartType iterableOfBottom;
+  DartType streamOfBottom;
 
   @override
   TypeInferenceEngine typeInferenceEngine;
@@ -701,9 +703,14 @@
   void ignoreAmbiguousSupertypes(Class cls, Supertype a, Supertype b) {}
 
   void computeCoreTypes(Component component) {
+    DartType Function(Class) instantiateWithBottom =
+        (Class cls) => new InterfaceType(cls, <DartType>[const BottomType()]);
+
     coreTypes = new CoreTypes(component);
-    futureOfBottom = new InterfaceType(
-        coreTypes.futureClass, <DartType>[const BottomType()]);
+    futureOfBottom = instantiateWithBottom(coreTypes.futureClass);
+    iterableOfBottom = instantiateWithBottom(coreTypes.iterableClass);
+    streamOfBottom = instantiateWithBottom(coreTypes.streamClass);
+
     ticker.logMs("Computed core types");
   }
 
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 0a39dc7..ac71bab 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -189,8 +189,10 @@
 GetterNotFound/example: Fail
 GetterWithFormals/example: Fail
 IllegalAssignmentToNonAssignable/script1: Fail
+IllegalAsyncGeneratorVoidReturnType/analyzerCode: Fail # The analyzer doesn't report this error.
 IllegalMixin/example: Fail
 IllegalMixinDueToConstructors/example: Fail
+IllegalSyncGeneratorVoidReturnType/analyzerCode: Fail # The analyzer doesn't report this error.
 ImplementsBeforeExtends/script: Fail
 ImplementsBeforeWith/script: Fail
 ImplicitCallOfNonMethod/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 73058f2..29f8aa0 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -1668,8 +1668,28 @@
 NoUnnamedConstructorInObject:
   template: "'Object' has no unnamed constructor."
 
+IllegalAsyncGeneratorReturnType:
+  template: "Functions marked 'async*' must have a return type assignable to 'Stream'."
+  severity: ERROR
+  analyzerCode: ILLEGAL_ASYNC_GENERATOR_RETURN_TYPE
+  dart2jsCode: "*fatal*"
+  script:
+    - >-
+      int g() async* {
+        yield 0;
+      }
+
+IllegalAsyncGeneratorVoidReturnType:
+  template: "Functions marked 'async*' can't have return type 'void'."
+  severity: ERROR
+  dart2jsCode: "*fatal*"
+  script:
+    - >-
+      void g() async* {
+      }
+
 IllegalAsyncReturnType:
-  template: "Functions marked 'async' must have a return type that is a super type of 'Future'."
+  template: "Functions marked 'async' must have a return type assignable to 'Future'."
   severity: ERROR
   analyzerCode: ILLEGAL_ASYNC_RETURN_TYPE
   dart2jsCode: "*fatal*"
@@ -1679,6 +1699,26 @@
         return 0;
       }
 
+IllegalSyncGeneratorReturnType:
+  template: "Functions marked 'sync*' must have a return type assignable to 'Iterable'."
+  severity: ERROR
+  analyzerCode: ILLEGAL_SYNC_GENERATOR_RETURN_TYPE
+  dart2jsCode: "*fatal*"
+  script:
+    - >-
+      int g() sync* {
+        yield 0;
+      }
+
+IllegalSyncGeneratorVoidReturnType:
+  template: "Functions marked 'sync*' can't have return type 'void'."
+  severity: ERROR
+  dart2jsCode: "*fatal*"
+  script:
+    - >-
+      void g() sync* {
+      }
+
 IllegalMixinDueToConstructors:
   template: "Can't use '#name' as a mixin because it has constructors."
   analyzerCode: MIXIN_DECLARES_CONSTRUCTOR
diff --git a/tests/co19_2/co19_2-kernel.status b/tests/co19_2/co19_2-kernel.status
index 8dd69e1..a34a0b8 100644
--- a/tests/co19_2/co19_2-kernel.status
+++ b/tests/co19_2/co19_2-kernel.status
@@ -135,10 +135,6 @@
 Language/Expressions/Unary_Expressions/syntax_t27: CompileTimeError
 Language/Functions/Formal_Parameters/Optional_Formals/default_value_t01: MissingCompileTimeError
 Language/Functions/Formal_Parameters/Optional_Formals/default_value_t02: MissingCompileTimeError
-Language/Functions/generator_return_type_t01: MissingCompileTimeError
-Language/Functions/generator_return_type_t02: MissingCompileTimeError
-Language/Functions/generator_return_type_t05: MissingCompileTimeError
-Language/Functions/generator_return_type_t06: MissingCompileTimeError
 Language/Generics/scope_t06: MissingCompileTimeError
 Language/Generics/syntax_t02: CompileTimeError
 Language/Generics/syntax_t03: CompileTimeError
diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status
index 64d5ac7..32f3b70 100644
--- a/tests/language_2/language_2_dart2js.status
+++ b/tests/language_2/language_2_dart2js.status
@@ -470,8 +470,6 @@
 abstract_override_adds_optional_args_concrete_test: MissingCompileTimeError
 additional_interface_adds_optional_args_concrete_subclass_test: MissingCompileTimeError
 additional_interface_adds_optional_args_concrete_test: MissingCompileTimeError
-async_or_generator_return_type_stacktrace_test/02: MissingCompileTimeError
-async_or_generator_return_type_stacktrace_test/03: MissingCompileTimeError
 async_return_types_test/nestedFuture: Crash # 'file:*/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart': Failed assertion: line 208 pos 18: '!(_useKernel && _strongMode && !_disableRtiOptimization) ||
 async_star_cancel_while_paused_test: Crash # 'file:*/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart': Failed assertion: line 208 pos 18: '!(_useKernel && _strongMode && !_disableRtiOptimization) ||
 await_not_started_immediately_test: Crash # Assertion failure: Runtime type information not available for type_variable_local(bindCallback.R) in (local(_RootZone.bindCallback#)) for j:closure_call(_RootZone_bindCallback_closure.call).
@@ -601,8 +599,6 @@
 abstract_override_adds_optional_args_concrete_test: MissingCompileTimeError
 additional_interface_adds_optional_args_concrete_subclass_test: MissingCompileTimeError
 additional_interface_adds_optional_args_concrete_test: MissingCompileTimeError
-async_or_generator_return_type_stacktrace_test/02: MissingCompileTimeError
-async_or_generator_return_type_stacktrace_test/03: MissingCompileTimeError
 async_return_types_test/nestedFuture: Crash # Interpolated value #1 is not an Expression or List of Expressions: [VariableUse(f), Instance of 'LiteralNull', null]
 async_star_cancel_while_paused_test: Crash # Interpolated value #1 is not an Expression or List of Expressions: [VariableUse(f), Instance of 'LiteralNull', null]
 await_not_started_immediately_test: Crash # Assertion failure: Runtime type information not available for type_variable_local(bindCallback.R) in (local(_RootZone.bindCallback#)) for j:closure_call(_RootZone_bindCallback_closure.call).
diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status
index a9e587e..5ff7f66 100644
--- a/tests/language_2/language_2_dartdevc.status
+++ b/tests/language_2/language_2_dartdevc.status
@@ -226,8 +226,6 @@
 [ $compiler == dartdevk ]
 additional_interface_adds_optional_args_concrete_subclass_test: MissingCompileTimeError
 additional_interface_adds_optional_args_concrete_test: MissingCompileTimeError
-async_or_generator_return_type_stacktrace_test/02: MissingCompileTimeError
-async_or_generator_return_type_stacktrace_test/03: MissingCompileTimeError
 async_return_types_test/nestedFuture: MissingCompileTimeError
 built_in_identifier_type_annotation_test/dynamic-funarg: RuntimeError # Issue 30450, test name contains hyphen
 built_in_identifier_type_annotation_test/dynamic-funret: RuntimeError # Issue 30450, test name contains hyphen
diff --git a/tests/language_2/language_2_kernel.status b/tests/language_2/language_2_kernel.status
index f2da294..9f006ad 100644
--- a/tests/language_2/language_2_kernel.status
+++ b/tests/language_2/language_2_kernel.status
@@ -212,8 +212,6 @@
 abstract_override_adds_optional_args_concrete_test: MissingCompileTimeError # Issue 32014.
 additional_interface_adds_optional_args_concrete_subclass_test: MissingCompileTimeError # Issue 32014.
 additional_interface_adds_optional_args_concrete_test: MissingCompileTimeError # Issue 32014.
-async_or_generator_return_type_stacktrace_test/02: MissingCompileTimeError # Issue 33068
-async_or_generator_return_type_stacktrace_test/03: MissingCompileTimeError # Issue 33068
 async_return_types_test/nestedFuture: MissingCompileTimeError # Issue 33068
 call_non_method_field_test/01: MissingCompileTimeError # Issue 32975
 call_non_method_field_test/02: MissingCompileTimeError # Issue 32975