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