From 34f5c9d0b4bafdb0ce3dd748b36cd46db1fd322c Mon Sep 17 00:00:00 2001 From: Johnni Winther Date: Mon, 6 Jul 2020 07:04:40 +0000 Subject: [PATCH] [cfe] Implement new async return rules Closes #41800 Closes #41900 Closes #42134 Closes #42282 Closes #42236 Closes #42169 Change-Id: Ia994bc07fba4e2342fcb59d44fc77608198a328b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/152150 Commit-Queue: Johnni Winther Reviewed-by: Dmitry Stefantsov --- .../lib/src/messages/codes_generated.dart | 20 + pkg/compiler/lib/src/ir/static_type_base.dart | 2 +- pkg/dev_compiler/lib/src/kernel/compiler.dart | 2 +- .../src/fasta/kernel/inference_visitor.dart | 4 +- .../fasta/type_inference/closure_context.dart | 418 ++++++++++++------ .../fasta/type_inference/type_inferrer.dart | 23 +- pkg/front_end/messages.status | 3 + pkg/front_end/messages.yaml | 12 + .../test/spell_checking_list_code.txt | 1 + .../test/spell_checking_list_tests.txt | 2 + pkg/front_end/testcases/nnbd/issue41437a.dart | 8 +- .../nnbd/issue41437a.dart.strong.expect | 50 +-- ...issue41437a.dart.strong.transformed.expect | 66 +-- .../issue41437a.dart.textual_outline.expect | 12 + ...1437a.dart.textual_outline_modelled.expect | 12 + .../nnbd/issue41437a.dart.weak.expect | 50 +-- .../issue41437a.dart.weak.transformed.expect | 66 +-- .../testcases/nnbd/return_async.dart | 39 ++ .../nnbd/return_async.dart.outline.expect | 18 + .../nnbd/return_async.dart.strong.expect | 33 ++ ...eturn_async.dart.strong.transformed.expect | 160 +++++++ .../return_async.dart.textual_outline.expect | 8 + ...async.dart.textual_outline_modelled.expect | 8 + .../nnbd/return_async.dart.weak.expect | 33 ++ .../return_async.dart.weak.transformed.expect | 160 +++++++ pkg/kernel/lib/ast.dart | 23 +- pkg/kernel/lib/src/future_value_type.dart | 101 +++++ .../lib/testing/type_parser_environment.dart | 7 + pkg/kernel/lib/type_checker.dart | 4 +- pkg/kernel/lib/type_environment.dart | 45 +- pkg/kernel/test/flatten_test.dart | 92 ++++ pkg/kernel/test/future_value_type_test.dart | 89 ++++ .../_internal/vm/bin/vmservice_server.dart | 2 +- .../return_async_test.dart | 4 +- 34 files changed, 1216 insertions(+), 361 deletions(-) create mode 100644 pkg/front_end/testcases/nnbd/issue41437a.dart.textual_outline.expect create mode 100644 pkg/front_end/testcases/nnbd/issue41437a.dart.textual_outline_modelled.expect create mode 100644 pkg/front_end/testcases/nnbd/return_async.dart create mode 100644 pkg/front_end/testcases/nnbd/return_async.dart.outline.expect create mode 100644 pkg/front_end/testcases/nnbd/return_async.dart.strong.expect create mode 100644 pkg/front_end/testcases/nnbd/return_async.dart.strong.transformed.expect create mode 100644 pkg/front_end/testcases/nnbd/return_async.dart.textual_outline.expect create mode 100644 pkg/front_end/testcases/nnbd/return_async.dart.textual_outline_modelled.expect create mode 100644 pkg/front_end/testcases/nnbd/return_async.dart.weak.expect create mode 100644 pkg/front_end/testcases/nnbd/return_async.dart.weak.transformed.expect create mode 100644 pkg/kernel/lib/src/future_value_type.dart create mode 100644 pkg/kernel/test/flatten_test.dart create mode 100644 pkg/kernel/test/future_value_type_test.dart diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart index a386e5d5947..309004e2532 100644 --- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart +++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart @@ -7541,6 +7541,26 @@ const MessageCode messageReturnWithoutExpression = const MessageCode( severity: Severity.warning, message: r"""Must explicitly return a value from a non-void function."""); +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code codeReturnWithoutExpressionAsync = + messageReturnWithoutExpressionAsync; + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const MessageCode messageReturnWithoutExpressionAsync = const MessageCode( + "ReturnWithoutExpressionAsync", + message: + r"""A value must be explicitly returned from a non-void async function."""); + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const Code codeReturnWithoutExpressionSync = + messageReturnWithoutExpressionSync; + +// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. +const MessageCode messageReturnWithoutExpressionSync = const MessageCode( + "ReturnWithoutExpressionSync", + message: + r"""A value must be explicitly returned from a non-void function."""); + // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE. const Template templateSdkRootNotFound = const Template( diff --git a/pkg/compiler/lib/src/ir/static_type_base.dart b/pkg/compiler/lib/src/ir/static_type_base.dart index e3750dacf91..b62c4c906da 100644 --- a/pkg/compiler/lib/src/ir/static_type_base.dart +++ b/pkg/compiler/lib/src/ir/static_type_base.dart @@ -100,7 +100,7 @@ abstract class StaticTypeBase extends ir.Visitor { @override ir.DartType visitAwaitExpression(ir.AwaitExpression node) { - return typeEnvironment.unfutureType(visitNode(node.operand)); + return typeEnvironment.flatten(visitNode(node.operand)); } @override diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart index 0e7f31e7f8b..1585a7aa5d1 100644 --- a/pkg/dev_compiler/lib/src/kernel/compiler.dart +++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart @@ -3125,7 +3125,7 @@ class ProgramCompiler extends ComputeOnceConstantVisitor var gen = emitGeneratorFn((_) => []); // Return type of an async body is `Future`, where T is the // declared return type. - var returnType = _types.unfutureType(function + var returnType = _types.flatten(function .computeThisFunctionType(_currentLibrary.nonNullable) .returnType); return js.call('#.async(#, #)', diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart index 950adb63f13..1e8e21ef0df 100644 --- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart +++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart @@ -271,7 +271,7 @@ class InferenceVisitor node.operand, typeContext, true, isVoidAllowed: !inferrer.isNonNullableByDefault); DartType inferredType = - inferrer.typeSchemaEnvironment.unfutureType(operandResult.inferredType); + inferrer.typeSchemaEnvironment.flatten(operandResult.inferredType); node.operand = operandResult.expression..parent = node; return new ExpressionInferenceResult(inferredType, node); } @@ -2161,7 +2161,7 @@ class InferenceVisitor bool typeContextIsMap = node.keyType is! ImplicitTypeArgument; bool typeContextIsIterable = false; DartType unfuturedTypeContext = - inferrer.typeSchemaEnvironment.unfutureType(typeContext); + inferrer.typeSchemaEnvironment.flatten(typeContext); if (!inferrer.isTopLevel && inferenceNeeded) { // Ambiguous set/map literal if (unfuturedTypeContext is InterfaceType) { diff --git a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart index 1ff344fc587..0251f0c7430 100644 --- a/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart +++ b/pkg/front_end/lib/src/fasta/type_inference/closure_context.dart @@ -53,8 +53,13 @@ abstract class ClosureContext { yieldContext, declaredReturnType, needToInferReturnType); } } else if (isAsync) { - returnContext = inferrer.wrapFutureOrType( - inferrer.typeSchemaEnvironment.unfutureType(returnContext)); + if (inferrer.isNonNullableByDefault) { + returnContext = inferrer.wrapFutureOrType( + inferrer.computeFutureValueTypeSchema(returnContext)); + } else { + returnContext = inferrer.wrapFutureOrType( + inferrer.typeSchemaEnvironment.flatten(returnContext)); + } return new _AsyncClosureContext( returnContext, declaredReturnType, needToInferReturnType); } else { @@ -148,58 +153,115 @@ class _SyncClosureContext implements ClosureContext { void _checkValidReturn(TypeInferrerImpl inferrer, DartType returnType, ReturnStatement statement, DartType expressionType) { - // The rules for valid returns for functions with [returnType] `T` and - // a return expression with static [expressionType] `S`. - if (statement.expression == null) { - // `return;` is a valid return if T is void, dynamic, or Null. - if (returnType is VoidType || - returnType is DynamicType || - returnType == inferrer.coreTypes.nullType) { - // Valid return; + if (inferrer.isNonNullableByDefault) { + if (statement.expression == null) { + // It is a compile-time error if s is `return;`, unless T is void, + // dynamic, or Null. + if (returnType is VoidType || + returnType is DynamicType || + returnType == inferrer.coreTypes.nullType) { + // Valid return; + } else { + statement.expression = inferrer.helper.wrapInProblem( + new NullLiteral()..fileOffset = statement.fileOffset, + messageReturnWithoutExpressionSync, + statement.fileOffset, + noLength) + ..parent = statement; + } } else { - statement.expression = inferrer.helper.wrapInProblem( - new NullLiteral()..fileOffset = statement.fileOffset, - messageReturnWithoutExpression, - statement.fileOffset, - noLength) - ..parent = statement; + if (_isArrow && returnType is VoidType) { + // For `=> e` it is a compile-time error if T is not void, and it + // would have been a compile-time error to declare the function with + // the body `{ return e; }` rather than `=> e`. + return; + } + + if (returnType is VoidType && + !(expressionType is VoidType || + expressionType is DynamicType || + expressionType == inferrer.coreTypes.nullType)) { + // It is a compile-time error if s is `return e;`, T is void, and S is + // neither void, dynamic, nor Null. + statement.expression = inferrer.helper.wrapInProblem( + statement.expression, + messageReturnFromVoidFunction, + statement.expression.fileOffset, + noLength) + ..parent = statement; + } else if (!(returnType is VoidType || returnType is DynamicType) && + expressionType is VoidType) { + // It is a compile-time error if s is `return e;`, T is neither void + // nor dynamic, and S is void. + statement.expression = inferrer.helper.wrapInProblem( + statement.expression, + messageVoidExpression, + statement.expression.fileOffset, + noLength) + ..parent = statement; + } else if (expressionType is! VoidType) { + // It is a compile-time error if s is `return e;`, S is not void, and + // S is not assignable to T. + Expression expression = inferrer.ensureAssignable( + _returnContext, expressionType, statement.expression, + fileOffset: statement.expression.fileOffset, isVoidAllowed: true); + statement.expression = expression..parent = statement; + } } } else { - void ensureAssignability() { - Expression expression = inferrer.ensureAssignable( - _returnContext, expressionType, statement.expression, - fileOffset: statement.fileOffset, isVoidAllowed: true); - statement.expression = expression..parent = statement; - } - - if (_isArrow && returnType is VoidType) { - // Arrow functions are valid if: T is void or return exp; is a valid for - // a block-bodied function. - ensureAssignability(); - } else if (returnType is VoidType && - expressionType is! VoidType && - expressionType is! DynamicType && - expressionType != inferrer.coreTypes.nullType) { - // Invalid if T is void and S is not void, dynamic, or Null - statement.expression = inferrer.helper.wrapInProblem( - statement.expression, - messageReturnFromVoidFunction, - statement.expression.fileOffset, - noLength) - ..parent = statement; - } else if (expressionType is VoidType && - returnType is! VoidType && - returnType is! DynamicType && - returnType != inferrer.coreTypes.nullType) { - // Invalid if S is void and T is not void, dynamic, or Null. - statement.expression = inferrer.helper.wrapInProblem( - statement.expression, - messageVoidExpression, - statement.expression.fileOffset, - noLength) - ..parent = statement; + // The rules for valid returns for functions with [returnType] `T` and + // a return expression with static [expressionType] `S`. + if (statement.expression == null) { + // `return;` is a valid return if T is void, dynamic, or Null. + if (returnType is VoidType || + returnType is DynamicType || + returnType == inferrer.coreTypes.nullType) { + // Valid return; + } else { + statement.expression = inferrer.helper.wrapInProblem( + new NullLiteral()..fileOffset = statement.fileOffset, + messageReturnWithoutExpression, + statement.fileOffset, + noLength) + ..parent = statement; + } } else { - ensureAssignability(); + void ensureAssignability() { + Expression expression = inferrer.ensureAssignable( + _returnContext, expressionType, statement.expression, + fileOffset: statement.fileOffset, isVoidAllowed: true); + statement.expression = expression..parent = statement; + } + + if (_isArrow && returnType is VoidType) { + // Arrow functions are valid if: T is void or return exp; is a valid + // for a block-bodied function. + ensureAssignability(); + } else if (returnType is VoidType && + expressionType is! VoidType && + expressionType is! DynamicType && + expressionType != inferrer.coreTypes.nullType) { + // Invalid if T is void and S is not void, dynamic, or Null + statement.expression = inferrer.helper.wrapInProblem( + statement.expression, + messageReturnFromVoidFunction, + statement.expression.fileOffset, + noLength) + ..parent = statement; + } else if (expressionType is VoidType && + returnType is! VoidType && + returnType is! DynamicType && + returnType != inferrer.coreTypes.nullType) { + // Invalid if S is void and T is not void, dynamic, or Null. + statement.expression = inferrer.helper.wrapInProblem( + statement.expression, + messageVoidExpression, + statement.expression.fileOffset, + noLength) + ..parent = statement; + } else { + ensureAssignability(); + } } } } @@ -380,72 +442,159 @@ class _AsyncClosureContext implements ClosureContext { void _checkValidReturn(TypeInferrerImpl inferrer, DartType returnType, ReturnStatement statement, DartType expressionType) { - // The rules for valid returns for async functions with [returnType] `T` and - // a return expression with static [expressionType] `S`. - DartType flattenedReturnType = - inferrer.typeSchemaEnvironment.unfutureType(returnType); - if (statement.expression == null) { - // `return;` is a valid return if flatten(T) is void, dynamic, or Null. - if (flattenedReturnType is VoidType || - flattenedReturnType is DynamicType || - flattenedReturnType == inferrer.coreTypes.nullType) { - // Valid return; + if (inferrer.isNonNullableByDefault) { + DartType futureValueType = + computeFutureValueType(inferrer.coreTypes, returnType); + + if (statement.expression == null) { + // It is a compile-time error if s is `return;`, unless T_v is void, + // dynamic, or Null. + if (futureValueType is VoidType || + futureValueType is DynamicType || + futureValueType == inferrer.coreTypes.nullType) { + // Valid return; + } else { + statement.expression = inferrer.helper.wrapInProblem( + new NullLiteral()..fileOffset = statement.fileOffset, + messageReturnWithoutExpressionAsync, + statement.fileOffset, + noLength) + ..parent = statement; + } } else { - statement.expression = inferrer.helper.wrapInProblem( - new NullLiteral()..fileOffset = statement.fileOffset, - messageReturnWithoutExpression, - statement.fileOffset, - noLength) - ..parent = statement; + if (_isArrow && + inferrer.typeSchemaEnvironment.flatten(returnType) is VoidType) { + // For `async => e` it is a compile-time error if flatten(T) is not + // void, and it would have been a compile-time error to declare the + // function with the body `async { return e; }` rather than + // `async => e`. + return; + } + + DartType flattenedExpressionType = + inferrer.typeSchemaEnvironment.flatten(expressionType); + if (futureValueType is VoidType && + !(flattenedExpressionType is VoidType || + flattenedExpressionType is DynamicType || + flattenedExpressionType == inferrer.coreTypes.nullType)) { + // It is a compile-time error if s is `return e;`, T_v is void, and + // flatten(S) is neither void, dynamic, Null. + statement.expression = inferrer.helper.wrapInProblem( + new NullLiteral()..fileOffset = statement.fileOffset, + messageReturnFromVoidFunction, + statement.expression.fileOffset, + noLength) + ..parent = statement; + } else if (!(futureValueType is VoidType || + futureValueType is DynamicType) && + flattenedExpressionType is VoidType) { + // It is a compile-time error if s is `return e;`, T_v is neither void + // nor dynamic, and flatten(S) is void. + statement.expression = inferrer.helper.wrapInProblem( + new NullLiteral()..fileOffset = statement.fileOffset, + messageVoidExpression, + statement.expression.fileOffset, + noLength) + ..parent = statement; +/* } else if (flattenedExpressionType is! VoidType && + !inferrer.isAssignable(futureValueType, expressionType) && + !inferrer.typeSchemaEnvironment + .performNullabilityAwareSubtypeCheck( + flattenedExpressionType, futureValueType) + .isSubtypeWhenUsingNullabilities()) { + // It is a compile-time error if s is `return e;`, flatten(S) is not + // void, S is not assignable to T_v, and flatten(S) is not a subtype + // of T_v. + statement.expression = inferrer.helper.wrapInProblem( + new NullLiteral()..fileOffset = statement.fileOffset, + messageReturnWithoutExpressionAsync, + statement.expression.fileOffset, + noLength) + ..parent = statement;*/ + } else if (flattenedExpressionType is! VoidType && + !inferrer.typeSchemaEnvironment + .performNullabilityAwareSubtypeCheck( + flattenedExpressionType, futureValueType) + .isSubtypeWhenUsingNullabilities()) { + // It is a compile-time error if s is `return e;`, flatten(S) is not + // void, S is not assignable to T_v, and flatten(S) is not a subtype + // of T_v. + statement.expression = inferrer.ensureAssignable( + futureValueType, expressionType, statement.expression, + fileOffset: statement.expression.fileOffset, + runtimeCheckedType: _returnContext, + isVoidAllowed: false) + ..parent = statement; + } } } else { - DartType flattenedExpressionType = - inferrer.typeSchemaEnvironment.unfutureType(expressionType); - - void ensureAssignability() { - DartType wrappedType = inferrer.typeSchemaEnvironment - .futureType(flattenedExpressionType, Nullability.nonNullable); - Expression expression = inferrer.ensureAssignable( - computeAssignableType(inferrer, _returnContext, wrappedType), - wrappedType, - statement.expression, - fileOffset: statement.fileOffset, - isVoidAllowed: true, - runtimeCheckedType: _returnContext); - statement.expression = expression..parent = statement; - } - - if (_isArrow && flattenedReturnType is VoidType) { - // Arrow functions are valid if: flatten(T) is void or return exp; is - // valid for a block-bodied function. - ensureAssignability(); - } else if (returnType is VoidType && - flattenedExpressionType is! VoidType && - flattenedExpressionType is! DynamicType && - flattenedExpressionType != inferrer.coreTypes.nullType) { - // Invalid if T is void and flatten(S) is not void, dynamic, or Null. - statement.expression = inferrer.helper.wrapInProblem( - statement.expression, - messageReturnFromVoidFunction, - statement.expression.fileOffset, - noLength) - ..parent = statement; - } else if (flattenedExpressionType is VoidType && - flattenedReturnType is! VoidType && - flattenedReturnType is! DynamicType && - flattenedReturnType != inferrer.coreTypes.nullType) { - // Invalid if flatten(S) is void and flatten(T) is not void, dynamic, - // or Null. - statement.expression = inferrer.helper.wrapInProblem( - statement.expression, - messageVoidExpression, - statement.expression.fileOffset, - noLength) - ..parent = statement; + // The rules for valid returns for async functions with [returnType] `T` + // and a return expression with static [expressionType] `S`. + DartType flattenedReturnType = + inferrer.typeSchemaEnvironment.flatten(returnType); + if (statement.expression == null) { + // `return;` is a valid return if flatten(T) is void, dynamic, or Null. + if (flattenedReturnType is VoidType || + flattenedReturnType is DynamicType || + flattenedReturnType == inferrer.coreTypes.nullType) { + // Valid return; + } else { + statement.expression = inferrer.helper.wrapInProblem( + new NullLiteral()..fileOffset = statement.fileOffset, + messageReturnWithoutExpression, + statement.fileOffset, + noLength) + ..parent = statement; + } } else { - // The caller will check that the return expression is assignable to the - // return type. - ensureAssignability(); + DartType flattenedExpressionType = + inferrer.typeSchemaEnvironment.flatten(expressionType); + + void ensureAssignability() { + DartType wrappedType = inferrer.typeSchemaEnvironment + .futureType(flattenedExpressionType, Nullability.nonNullable); + Expression expression = inferrer.ensureAssignable( + computeAssignableType(inferrer, _returnContext, wrappedType), + wrappedType, + statement.expression, + fileOffset: statement.fileOffset, + isVoidAllowed: true, + runtimeCheckedType: _returnContext); + statement.expression = expression..parent = statement; + } + + if (_isArrow && flattenedReturnType is VoidType) { + // Arrow functions are valid if: flatten(T) is void or return exp; is + // valid for a block-bodied function. + ensureAssignability(); + } else if (returnType is VoidType && + flattenedExpressionType is! VoidType && + flattenedExpressionType is! DynamicType && + flattenedExpressionType != inferrer.coreTypes.nullType) { + // Invalid if T is void and flatten(S) is not void, dynamic, or Null. + statement.expression = inferrer.helper.wrapInProblem( + statement.expression, + messageReturnFromVoidFunction, + statement.expression.fileOffset, + noLength) + ..parent = statement; + } else if (flattenedExpressionType is VoidType && + flattenedReturnType is! VoidType && + flattenedReturnType is! DynamicType && + flattenedReturnType != inferrer.coreTypes.nullType) { + // Invalid if flatten(S) is void and flatten(T) is not void, dynamic, + // or Null. + statement.expression = inferrer.helper.wrapInProblem( + statement.expression, + messageVoidExpression, + statement.expression.fileOffset, + noLength) + ..parent = statement; + } else { + // The caller will check that the return expression is assignable to + // the return type. + ensureAssignability(); + } } } } @@ -488,7 +637,7 @@ class _AsyncClosureContext implements ClosureContext { // shape FutureOr. We check both branches for FutureOr here: both T // and Future. DartType unfuturedExpectedType = - inferrer.typeSchemaEnvironment.unfutureType(contextType); + inferrer.typeSchemaEnvironment.flatten(contextType); DartType futuredExpectedType = inferrer.wrapFutureType( unfuturedExpectedType, inferrer.library.nonNullable); if (inferrer.isAssignable(unfuturedExpectedType, expressionType)) { @@ -514,15 +663,16 @@ class _AsyncClosureContext implements ClosureContext { // The return expression has to be assignable to the return type // expectation from the downwards inference context. - if (statement.expression != null) { - if (!inferrer.isAssignable( - computeAssignableType(inferrer, _returnContext, type), type)) { - // Not assignable, use the expectation. - type = inferrer.computeGreatestClosure(_returnContext); + if (!inferrer.isNonNullableByDefault) { + if (statement.expression != null) { + if (!inferrer.isAssignable( + computeAssignableType(inferrer, _returnContext, type), type)) { + // Not assignable, use the expectation. + type = inferrer.computeGreatestClosure(_returnContext); + } } } - DartType unwrappedType = - inferrer.typeSchemaEnvironment.unfutureType(type); + DartType unwrappedType = inferrer.typeSchemaEnvironment.flatten(type); if (inferredType == null) { inferredType = unwrappedType; } else { @@ -543,14 +693,26 @@ class _AsyncClosureContext implements ClosureContext { } } - inferredType = - inferrer.wrapFutureType(inferredType, inferrer.library.nonNullable); + if (inferrer.isNonNullableByDefault) { + if (!inferrer.typeSchemaEnvironment.isSubtypeOf( + inferredType, _returnContext, SubtypeCheckMode.withNullabilities)) { + // If the inferred return type isn't a subtype of the context, we use + // the context. + inferredType = inferrer.computeGreatestClosure2(_declaredReturnType); + } + inferredType = inferrer.wrapFutureType( + inferrer.typeSchemaEnvironment.flatten(inferredType), + inferrer.library.nonNullable); + } else { + inferredType = + inferrer.wrapFutureType(inferredType, inferrer.library.nonNullable); - if (!inferrer.typeSchemaEnvironment.isSubtypeOf( - inferredType, _returnContext, SubtypeCheckMode.withNullabilities)) { - // If the inferred return type isn't a subtype of the context, we use the - // context. - inferredType = inferrer.computeGreatestClosure2(_declaredReturnType); + if (!inferrer.typeSchemaEnvironment.isSubtypeOf( + inferredType, _returnContext, SubtypeCheckMode.withNullabilities)) { + // If the inferred return type isn't a subtype of the context, we use + // the context. + inferredType = inferrer.computeGreatestClosure2(_declaredReturnType); + } } for (int i = 0; i < _returnStatements.length; ++i) { @@ -576,7 +738,7 @@ class _AsyncClosureContext implements ClosureContext { } else { returnType = _declaredReturnType; } - returnType = inferrer.typeSchemaEnvironment.unfutureType(returnType); + returnType = inferrer.typeSchemaEnvironment.flatten(returnType); if (inferrer.library.isNonNullableByDefault && (containsInvalidType(returnType) || returnType.isPotentiallyNonNullable) && 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 ab66bc1589f..6e433798ea6 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 @@ -12,17 +12,12 @@ import 'package:front_end/src/fasta/kernel/internal_ast.dart'; import 'package:front_end/src/fasta/type_inference/type_demotion.dart'; import 'package:kernel/ast.dart'; - import 'package:kernel/class_hierarchy.dart' show ClassHierarchy; - import 'package:kernel/core_types.dart' show CoreTypes; - import 'package:kernel/type_algebra.dart'; - import 'package:kernel/type_environment.dart'; - import 'package:kernel/src/bounds_checks.dart' show calculateBounds; - +import 'package:kernel/src/future_value_type.dart'; import 'package:kernel/src/legacy_erasure.dart'; import '../../base/instrumentation.dart' @@ -3192,6 +3187,22 @@ class TypeInferrerImpl implements TypeInferrer { class_, nullability, [type ?? const DynamicType()]); } + /// Computes the `futureValueTypeSchema` for the type schema [type]. + /// + /// This is the same as the [futureValueType] except that this handles + /// the unknown type. + DartType computeFutureValueTypeSchema(DartType type) { + return type.accept1(new FutureValueTypeVisitor(unhandledTypeHandler: + (DartType node, CoreTypes coreTypes, + DartType Function(DartType node, CoreTypes coreTypes) recursor) { + if (node is UnknownType) { + // futureValueTypeSchema(_) = _. + return node; + } + throw new UnsupportedError("Unsupported type '${node.runtimeType}'."); + }), coreTypes); + } + Member _getInterfaceMember( Class class_, Name name, bool setter, int charOffset) { Member member = engine.hierarchyBuilder.getCombinedMemberSignatureKernel( diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status index 0fd1a31ec5a..f5a8184a63d 100644 --- a/pkg/front_end/messages.status +++ b/pkg/front_end/messages.status @@ -613,6 +613,9 @@ RequiredNamedParameterHasDefaultValueWarning/example: Fail RethrowNotCatch/example: Fail ReturnTypeFunctionExpression/analyzerCode: Fail ReturnTypeFunctionExpression/example: Fail +ReturnWithoutExpressionAsync/analyzerCode: Fail +ReturnWithoutExpressionSync/analyzerCode: Fail +ReturnWithoutExpressionSync/part_wrapped_script: Fail SdkRootNotFound/analyzerCode: Fail SdkRootNotFound/example: Fail SdkSpecificationNotFound/analyzerCode: Fail diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml index 65142f9d993..7b44c7447ce 100644 --- a/pkg/front_end/messages.yaml +++ b/pkg/front_end/messages.yaml @@ -3401,6 +3401,18 @@ ReturnWithoutExpression: analyzerCode: RETURN_WITHOUT_VALUE declaration: "int foo() { return; }" +ReturnWithoutExpressionSync: + template: "A value must be explicitly returned from a non-void function." + configuration: nnbd-strong + script: | + import "dart:async"; + FutureOr foo() { return; } + +ReturnWithoutExpressionAsync: + template: "A value must be explicitly returned from a non-void async function." + configuration: nnbd-strong + declaration: "Future foo() async { return; }" + ImplicitReturnNull: template: "A non-null value must be returned since the return type '#type' doesn't allow null." configuration: nnbd-strong diff --git a/pkg/front_end/test/spell_checking_list_code.txt b/pkg/front_end/test/spell_checking_list_code.txt index 190fa6af9b9..6c58a0a6878 100644 --- a/pkg/front_end/test/spell_checking_list_code.txt +++ b/pkg/front_end/test/spell_checking_list_code.txt @@ -348,6 +348,7 @@ emits emitted emitting en +encapsulation enforce enforced enforces diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt index dddddc7f06f..e62b6c1f2b2 100644 --- a/pkg/front_end/test/spell_checking_list_tests.txt +++ b/pkg/front_end/test/spell_checking_list_tests.txt @@ -78,6 +78,7 @@ callable camel capitalized casing +causal cc ccc charcode @@ -507,6 +508,7 @@ spurious sqrt squared sssp +stacks stashed stat stats diff --git a/pkg/front_end/testcases/nnbd/issue41437a.dart b/pkg/front_end/testcases/nnbd/issue41437a.dart index 778cd69c9d8..7092b26b042 100644 --- a/pkg/front_end/testcases/nnbd/issue41437a.dart +++ b/pkg/front_end/testcases/nnbd/issue41437a.dart @@ -11,19 +11,19 @@ Future getFutureBool() async { return true; } -Future test1() async => await getNull(); // error +Future test1() async => await getNull(); // ok Future test2() => getNull(); // ok bool test3() => getNull(); // ok -Future test4() async => await getFutureNull(); // error +Future test4() async => await getFutureNull(); // ok Future test5() => getFutureNull(); // error Future test6() => getFutureBool(); // ok Future test7() async => getFutureBool(); // ok test() async { - Future test1() async => await getNull(); // error + Future test1() async => await getNull(); // ok Future test2() => getNull(); // ok bool test3() => getNull(); // ok - Future test4() async => await getFutureNull(); // error + Future test4() async => await getFutureNull(); // ok Future test5() => getFutureNull(); // error Future test6() => getFutureBool(); // ok Future test7() async => getFutureBool(); // ok diff --git a/pkg/front_end/testcases/nnbd/issue41437a.dart.strong.expect b/pkg/front_end/testcases/nnbd/issue41437a.dart.strong.expect index 2864cb565d2..7b350029669 100644 --- a/pkg/front_end/testcases/nnbd/issue41437a.dart.strong.expect +++ b/pkg/front_end/testcases/nnbd/issue41437a.dart.strong.expect @@ -2,31 +2,11 @@ library /*isNonNullableByDefault*/; // // Problems in library: // -// pkg/front_end/testcases/nnbd/issue41437a.dart:14:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test1() async => await getNull(); // error -// ^ -// -// pkg/front_end/testcases/nnbd/issue41437a.dart:17:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test4() async => await getFutureNull(); // error -// ^ -// // pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. // - 'Future' is from 'dart:async'. // Future test5() => getFutureNull(); // error // ^ // -// pkg/front_end/testcases/nnbd/issue41437a.dart:23:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test1() async => await getNull(); // error -// ^ -// -// pkg/front_end/testcases/nnbd/issue41437a.dart:26:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test4() async => await getFutureNull(); // error -// ^ -// // pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. // - 'Future' is from 'dart:async'. // Future test5() => getFutureNull(); // error @@ -60,21 +40,15 @@ static method getFutureBool() → asy::Future async { return true; } static method test1() → asy::Future async - return let final #t1 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:14:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. -Future test1() async => await getNull(); // error - ^" in await self::getNull() as{TypeError,ForNonNullableByDefault} FutureOr; + return await self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; static method test2() → asy::Future return self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Future; static method test3() → core::bool return self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} core::bool; static method test4() → asy::Future async - return let final #t2 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:17:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. -Future test4() async => await getFutureNull(); // error - ^" in await self::getFutureNull() as{TypeError,ForNonNullableByDefault} FutureOr; + return await self::getFutureNull() as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; static method test5() → asy::Future - return let final #t3 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + return let final #t1 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future test5() => getFutureNull(); // error ^" in self::getFutureNull() as{TypeError,ForNonNullableByDefault} asy::Future; @@ -84,21 +58,15 @@ static method test7() → asy::Future async return self::getFutureBool(); static method test() → dynamic async { function test1() → asy::Future async - return let final #t4 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:23:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. - Future test1() async => await getNull(); // error - ^" in await self::getNull() as{TypeError,ForNonNullableByDefault} FutureOr; + return await self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; function test2() → asy::Future return self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Future; function test3() → core::bool return self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} core::bool; function test4() → asy::Future async - return let final #t5 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:26:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. - Future test4() async => await getFutureNull(); // error - ^" in await self::getFutureNull() as{TypeError,ForNonNullableByDefault} FutureOr; + return await self::getFutureNull() as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; function test5() → asy::Future - return let final #t6 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + return let final #t2 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future test5() => getFutureNull(); // error ^" in self::getFutureNull() as{TypeError,ForNonNullableByDefault} asy::Future; @@ -106,17 +74,17 @@ static method test() → dynamic async { return self::getFutureBool(); function test7() → asy::Future async return self::getFutureBool(); - asy::Future var1 = let final #t7 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:31:52: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var1 = let final #t3 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:31:52: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var1 = (() async => await getNull())(); // error ^" in (() → asy::Future async => await self::getNull()).call() as{TypeError,ForNonNullableByDefault} asy::Future; asy::Future var2 = (() → dynamic => self::getNull()).call() as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Future; core::bool var3 = (() → dynamic => self::getNull()).call() as{TypeError,ForDynamic,ForNonNullableByDefault} core::bool; - asy::Future var4 = let final #t8 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:34:58: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var4 = let final #t4 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:34:58: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var4 = (() async => await getFutureNull())(); // error ^" in (() → asy::Future async => await self::getFutureNull()).call() as{TypeError,ForNonNullableByDefault} asy::Future; - asy::Future var5 = let final #t9 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:35:46: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var5 = let final #t5 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:35:46: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var5 = (() => getFutureNull())(); // error ^" in (() → asy::Future => self::getFutureNull()).call() as{TypeError,ForNonNullableByDefault} asy::Future; diff --git a/pkg/front_end/testcases/nnbd/issue41437a.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/issue41437a.dart.strong.transformed.expect index 58e27371988..4efaadff820 100644 --- a/pkg/front_end/testcases/nnbd/issue41437a.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/nnbd/issue41437a.dart.strong.transformed.expect @@ -2,31 +2,11 @@ library /*isNonNullableByDefault*/; // // Problems in library: // -// pkg/front_end/testcases/nnbd/issue41437a.dart:14:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test1() async => await getNull(); // error -// ^ -// -// pkg/front_end/testcases/nnbd/issue41437a.dart:17:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test4() async => await getFutureNull(); // error -// ^ -// // pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. // - 'Future' is from 'dart:async'. // Future test5() => getFutureNull(); // error // ^ // -// pkg/front_end/testcases/nnbd/issue41437a.dart:23:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test1() async => await getNull(); // error -// ^ -// -// pkg/front_end/testcases/nnbd/issue41437a.dart:26:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test4() async => await getFutureNull(); // error -// ^ -// // pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. // - 'Future' is from 'dart:async'. // Future test5() => getFutureNull(); // error @@ -120,12 +100,8 @@ static method test1() → asy::Future /* originally async */ { try { #L3: { - final #t1 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:14:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. -Future test1() async => await getNull(); // error - ^"; - [yield] let dynamic #t2 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; - :return_value = :result as{TypeError,ForNonNullableByDefault} FutureOr; + [yield] let dynamic #t1 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; + :return_value = :result as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; break #L3; } asy::_completeOnAsyncReturn(:async_completer, :return_value); @@ -157,12 +133,8 @@ static method test4() → asy::Future /* originally async */ { try { #L4: { - final #t3 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:17:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. -Future test4() async => await getFutureNull(); // error - ^"; - [yield] let dynamic #t4 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; - :return_value = :result as{TypeError,ForNonNullableByDefault} FutureOr; + [yield] let dynamic #t2 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; + :return_value = :result as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; break #L4; } asy::_completeOnAsyncReturn(:async_completer, :return_value); @@ -178,7 +150,7 @@ Future test4() async => await getFutureNull(); // error return :async_completer.{asy::Completer::future}; } static method test5() → asy::Future - return let final #t5 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + return let final #t3 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future test5() => getFutureNull(); // error ^" in self::getFutureNull() as{TypeError,ForNonNullableByDefault} asy::Future; @@ -236,12 +208,8 @@ static method test() → dynamic /* originally async */ { try { #L7: { - final #t6 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:23:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. - Future test1() async => await getNull(); // error - ^"; - [yield] let dynamic #t7 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; - :return_value = :result as{TypeError,ForNonNullableByDefault} FutureOr; + [yield] let dynamic #t4 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; + :return_value = :result as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; break #L7; } asy::_completeOnAsyncReturn(:async_completer, :return_value); @@ -273,12 +241,8 @@ static method test() → dynamic /* originally async */ { try { #L8: { - final #t8 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:26:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. - Future test4() async => await getFutureNull(); // error - ^"; - [yield] let dynamic #t9 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; - :return_value = :result as{TypeError,ForNonNullableByDefault} FutureOr; + [yield] let dynamic #t5 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; + :return_value = :result as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; break #L8; } asy::_completeOnAsyncReturn(:async_completer, :return_value); @@ -294,7 +258,7 @@ static method test() → dynamic /* originally async */ { return :async_completer.{asy::Completer::future}; } function test5() → asy::Future - return let final #t10 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + return let final #t6 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future test5() => getFutureNull(); // error ^" in self::getFutureNull() as{TypeError,ForNonNullableByDefault} asy::Future; @@ -327,7 +291,7 @@ static method test() → dynamic /* originally async */ { :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); return :async_completer.{asy::Completer::future}; } - asy::Future var1 = let final #t11 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:31:52: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var1 = let final #t7 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:31:52: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var1 = (() async => await getNull())(); // error ^" in (() → asy::Future /* originally async */ { @@ -343,7 +307,7 @@ static method test() → dynamic /* originally async */ { try { #L10: { - [yield] let dynamic #t12 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; + [yield] let dynamic #t8 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; :return_value = :result; break #L10; } @@ -361,7 +325,7 @@ static method test() → dynamic /* originally async */ { }).call() as{TypeError,ForNonNullableByDefault} asy::Future; asy::Future var2 = (() → dynamic => self::getNull()).call() as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Future; core::bool var3 = (() → dynamic => self::getNull()).call() as{TypeError,ForDynamic,ForNonNullableByDefault} core::bool; - asy::Future var4 = let final #t13 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:34:58: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var4 = let final #t9 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:34:58: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var4 = (() async => await getFutureNull())(); // error ^" in (() → asy::Future /* originally async */ { @@ -377,7 +341,7 @@ static method test() → dynamic /* originally async */ { try { #L11: { - [yield] let dynamic #t14 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; + [yield] let dynamic #t10 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; :return_value = :result; break #L11; } @@ -393,7 +357,7 @@ static method test() → dynamic /* originally async */ { :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); return :async_completer.{asy::Completer::future}; }).call() as{TypeError,ForNonNullableByDefault} asy::Future; - asy::Future var5 = let final #t15 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:35:46: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var5 = let final #t11 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:35:46: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var5 = (() => getFutureNull())(); // error ^" in (() → asy::Future => self::getFutureNull()).call() as{TypeError,ForNonNullableByDefault} asy::Future; diff --git a/pkg/front_end/testcases/nnbd/issue41437a.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/issue41437a.dart.textual_outline.expect new file mode 100644 index 00000000000..064638f6222 --- /dev/null +++ b/pkg/front_end/testcases/nnbd/issue41437a.dart.textual_outline.expect @@ -0,0 +1,12 @@ +dynamic getNull() => null; +Future getFutureNull() async {} +Future getFutureBool() async {} +Future test1() async => await getNull(); +Future test2() => getNull(); +bool test3() => getNull(); +Future test4() async => await getFutureNull(); +Future test5() => getFutureNull(); +Future test6() => getFutureBool(); +Future test7() async => getFutureBool(); +test() async {} +main() {} diff --git a/pkg/front_end/testcases/nnbd/issue41437a.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/issue41437a.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..e910427d200 --- /dev/null +++ b/pkg/front_end/testcases/nnbd/issue41437a.dart.textual_outline_modelled.expect @@ -0,0 +1,12 @@ +Future getFutureBool() async {} +Future test1() async => await getNull(); +Future test2() => getNull(); +Future test4() async => await getFutureNull(); +Future test5() => getFutureNull(); +Future test6() => getFutureBool(); +Future test7() async => getFutureBool(); +Future getFutureNull() async {} +bool test3() => getNull(); +dynamic getNull() => null; +main() {} +test() async {} diff --git a/pkg/front_end/testcases/nnbd/issue41437a.dart.weak.expect b/pkg/front_end/testcases/nnbd/issue41437a.dart.weak.expect index 2864cb565d2..7b350029669 100644 --- a/pkg/front_end/testcases/nnbd/issue41437a.dart.weak.expect +++ b/pkg/front_end/testcases/nnbd/issue41437a.dart.weak.expect @@ -2,31 +2,11 @@ library /*isNonNullableByDefault*/; // // Problems in library: // -// pkg/front_end/testcases/nnbd/issue41437a.dart:14:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test1() async => await getNull(); // error -// ^ -// -// pkg/front_end/testcases/nnbd/issue41437a.dart:17:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test4() async => await getFutureNull(); // error -// ^ -// // pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. // - 'Future' is from 'dart:async'. // Future test5() => getFutureNull(); // error // ^ // -// pkg/front_end/testcases/nnbd/issue41437a.dart:23:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test1() async => await getNull(); // error -// ^ -// -// pkg/front_end/testcases/nnbd/issue41437a.dart:26:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test4() async => await getFutureNull(); // error -// ^ -// // pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. // - 'Future' is from 'dart:async'. // Future test5() => getFutureNull(); // error @@ -60,21 +40,15 @@ static method getFutureBool() → asy::Future async { return true; } static method test1() → asy::Future async - return let final #t1 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:14:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. -Future test1() async => await getNull(); // error - ^" in await self::getNull() as{TypeError,ForNonNullableByDefault} FutureOr; + return await self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; static method test2() → asy::Future return self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Future; static method test3() → core::bool return self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} core::bool; static method test4() → asy::Future async - return let final #t2 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:17:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. -Future test4() async => await getFutureNull(); // error - ^" in await self::getFutureNull() as{TypeError,ForNonNullableByDefault} FutureOr; + return await self::getFutureNull() as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; static method test5() → asy::Future - return let final #t3 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + return let final #t1 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future test5() => getFutureNull(); // error ^" in self::getFutureNull() as{TypeError,ForNonNullableByDefault} asy::Future; @@ -84,21 +58,15 @@ static method test7() → asy::Future async return self::getFutureBool(); static method test() → dynamic async { function test1() → asy::Future async - return let final #t4 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:23:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. - Future test1() async => await getNull(); // error - ^" in await self::getNull() as{TypeError,ForNonNullableByDefault} FutureOr; + return await self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; function test2() → asy::Future return self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Future; function test3() → core::bool return self::getNull() as{TypeError,ForDynamic,ForNonNullableByDefault} core::bool; function test4() → asy::Future async - return let final #t5 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:26:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. - Future test4() async => await getFutureNull(); // error - ^" in await self::getFutureNull() as{TypeError,ForNonNullableByDefault} FutureOr; + return await self::getFutureNull() as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; function test5() → asy::Future - return let final #t6 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + return let final #t2 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future test5() => getFutureNull(); // error ^" in self::getFutureNull() as{TypeError,ForNonNullableByDefault} asy::Future; @@ -106,17 +74,17 @@ static method test() → dynamic async { return self::getFutureBool(); function test7() → asy::Future async return self::getFutureBool(); - asy::Future var1 = let final #t7 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:31:52: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var1 = let final #t3 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:31:52: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var1 = (() async => await getNull())(); // error ^" in (() → asy::Future async => await self::getNull()).call() as{TypeError,ForNonNullableByDefault} asy::Future; asy::Future var2 = (() → dynamic => self::getNull()).call() as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Future; core::bool var3 = (() → dynamic => self::getNull()).call() as{TypeError,ForDynamic,ForNonNullableByDefault} core::bool; - asy::Future var4 = let final #t8 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:34:58: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var4 = let final #t4 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:34:58: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var4 = (() async => await getFutureNull())(); // error ^" in (() → asy::Future async => await self::getFutureNull()).call() as{TypeError,ForNonNullableByDefault} asy::Future; - asy::Future var5 = let final #t9 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:35:46: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var5 = let final #t5 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:35:46: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var5 = (() => getFutureNull())(); // error ^" in (() → asy::Future => self::getFutureNull()).call() as{TypeError,ForNonNullableByDefault} asy::Future; diff --git a/pkg/front_end/testcases/nnbd/issue41437a.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/issue41437a.dart.weak.transformed.expect index 58e27371988..4efaadff820 100644 --- a/pkg/front_end/testcases/nnbd/issue41437a.dart.weak.transformed.expect +++ b/pkg/front_end/testcases/nnbd/issue41437a.dart.weak.transformed.expect @@ -2,31 +2,11 @@ library /*isNonNullableByDefault*/; // // Problems in library: // -// pkg/front_end/testcases/nnbd/issue41437a.dart:14:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test1() async => await getNull(); // error -// ^ -// -// pkg/front_end/testcases/nnbd/issue41437a.dart:17:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test4() async => await getFutureNull(); // error -// ^ -// // pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. // - 'Future' is from 'dart:async'. // Future test5() => getFutureNull(); // error // ^ // -// pkg/front_end/testcases/nnbd/issue41437a.dart:23:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test1() async => await getNull(); // error -// ^ -// -// pkg/front_end/testcases/nnbd/issue41437a.dart:26:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. -// - 'Future' is from 'dart:async'. -// Future test4() async => await getFutureNull(); // error -// ^ -// // pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. // - 'Future' is from 'dart:async'. // Future test5() => getFutureNull(); // error @@ -120,12 +100,8 @@ static method test1() → asy::Future /* originally async */ { try { #L3: { - final #t1 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:14:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. -Future test1() async => await getNull(); // error - ^"; - [yield] let dynamic #t2 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; - :return_value = :result as{TypeError,ForNonNullableByDefault} FutureOr; + [yield] let dynamic #t1 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; + :return_value = :result as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; break #L3; } asy::_completeOnAsyncReturn(:async_completer, :return_value); @@ -157,12 +133,8 @@ static method test4() → asy::Future /* originally async */ { try { #L4: { - final #t3 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:17:31: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. -Future test4() async => await getFutureNull(); // error - ^"; - [yield] let dynamic #t4 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; - :return_value = :result as{TypeError,ForNonNullableByDefault} FutureOr; + [yield] let dynamic #t2 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; + :return_value = :result as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; break #L4; } asy::_completeOnAsyncReturn(:async_completer, :return_value); @@ -178,7 +150,7 @@ Future test4() async => await getFutureNull(); // error return :async_completer.{asy::Completer::future}; } static method test5() → asy::Future - return let final #t5 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + return let final #t3 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:18:25: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future test5() => getFutureNull(); // error ^" in self::getFutureNull() as{TypeError,ForNonNullableByDefault} asy::Future; @@ -236,12 +208,8 @@ static method test() → dynamic /* originally async */ { try { #L7: { - final #t6 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:23:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. - Future test1() async => await getNull(); // error - ^"; - [yield] let dynamic #t7 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; - :return_value = :result as{TypeError,ForNonNullableByDefault} FutureOr; + [yield] let dynamic #t4 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; + :return_value = :result as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; break #L7; } asy::_completeOnAsyncReturn(:async_completer, :return_value); @@ -273,12 +241,8 @@ static method test() → dynamic /* originally async */ { try { #L8: { - final #t8 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:26:33: Error: A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. - - 'Future' is from 'dart:async'. - Future test4() async => await getFutureNull(); // error - ^"; - [yield] let dynamic #t9 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; - :return_value = :result as{TypeError,ForNonNullableByDefault} FutureOr; + [yield] let dynamic #t5 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; + :return_value = :result as{TypeError,ForDynamic,ForNonNullableByDefault} FutureOr; break #L8; } asy::_completeOnAsyncReturn(:async_completer, :return_value); @@ -294,7 +258,7 @@ static method test() → dynamic /* originally async */ { return :async_completer.{asy::Completer::future}; } function test5() → asy::Future - return let final #t10 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + return let final #t6 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:27:27: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future test5() => getFutureNull(); // error ^" in self::getFutureNull() as{TypeError,ForNonNullableByDefault} asy::Future; @@ -327,7 +291,7 @@ static method test() → dynamic /* originally async */ { :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); return :async_completer.{asy::Completer::future}; } - asy::Future var1 = let final #t11 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:31:52: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var1 = let final #t7 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:31:52: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var1 = (() async => await getNull())(); // error ^" in (() → asy::Future /* originally async */ { @@ -343,7 +307,7 @@ static method test() → dynamic /* originally async */ { try { #L10: { - [yield] let dynamic #t12 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; + [yield] let dynamic #t8 = asy::_awaitHelper(self::getNull(), :async_op_then, :async_op_error, :async_op) in null; :return_value = :result; break #L10; } @@ -361,7 +325,7 @@ static method test() → dynamic /* originally async */ { }).call() as{TypeError,ForNonNullableByDefault} asy::Future; asy::Future var2 = (() → dynamic => self::getNull()).call() as{TypeError,ForDynamic,ForNonNullableByDefault} asy::Future; core::bool var3 = (() → dynamic => self::getNull()).call() as{TypeError,ForDynamic,ForNonNullableByDefault} core::bool; - asy::Future var4 = let final #t13 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:34:58: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var4 = let final #t9 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:34:58: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var4 = (() async => await getFutureNull())(); // error ^" in (() → asy::Future /* originally async */ { @@ -377,7 +341,7 @@ static method test() → dynamic /* originally async */ { try { #L11: { - [yield] let dynamic #t14 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; + [yield] let dynamic #t10 = asy::_awaitHelper(self::getFutureNull(), :async_op_then, :async_op_error, :async_op) in null; :return_value = :result; break #L11; } @@ -393,7 +357,7 @@ static method test() → dynamic /* originally async */ { :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); return :async_completer.{asy::Completer::future}; }).call() as{TypeError,ForNonNullableByDefault} asy::Future; - asy::Future var5 = let final #t15 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:35:46: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. + asy::Future var5 = let final #t11 = invalid-expression "pkg/front_end/testcases/nnbd/issue41437a.dart:35:46: Error: A value of type 'Future' can't be assigned to a variable of type 'Future'. - 'Future' is from 'dart:async'. Future var5 = (() => getFutureNull())(); // error ^" in (() → asy::Future => self::getFutureNull()).call() as{TypeError,ForNonNullableByDefault} asy::Future; diff --git a/pkg/front_end/testcases/nnbd/return_async.dart b/pkg/front_end/testcases/nnbd/return_async.dart new file mode 100644 index 00000000000..f4c53756757 --- /dev/null +++ b/pkg/front_end/testcases/nnbd/return_async.dart @@ -0,0 +1,39 @@ +// Copyright (c) 2020, 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. + +// This test is derived from `runtime/tests/vm/dart/causal_stacks/utils.dart`. + +import 'dart:async'; + +Future throwSync() { + throw ''; +} + +Future allYield() async { + await 0; + await allYield2(); +} + +Future allYield2() async { + await 0; + await allYield3(); +} + +Future allYield3() async { + await 0; + throwSync(); +} + +Future customErrorZone() async { + final completer = Completer(); + runZonedGuarded(() async { + await allYield(); + completer.complete(null); + }, (e, s) { + completer.completeError(e, s); + }); + return completer.future; +} + +main() {} diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.outline.expect b/pkg/front_end/testcases/nnbd/return_async.dart.outline.expect new file mode 100644 index 00000000000..09085fb1a56 --- /dev/null +++ b/pkg/front_end/testcases/nnbd/return_async.dart.outline.expect @@ -0,0 +1,18 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:async" as asy; + +import "dart:async"; + +static method throwSync() → asy::Future + ; +static method allYield() → asy::Future + ; +static method allYield2() → asy::Future + ; +static method allYield3() → asy::Future + ; +static method customErrorZone() → asy::Future + ; +static method main() → dynamic + ; diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.strong.expect b/pkg/front_end/testcases/nnbd/return_async.dart.strong.expect new file mode 100644 index 00000000000..2c9322b6b56 --- /dev/null +++ b/pkg/front_end/testcases/nnbd/return_async.dart.strong.expect @@ -0,0 +1,33 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:async" as asy; +import "dart:core" as core; + +import "dart:async"; + +static method throwSync() → asy::Future { + throw ""; +} +static method allYield() → asy::Future async { + await 0; + await self::allYield2(); +} +static method allYield2() → asy::Future async { + await 0; + await self::allYield3(); +} +static method allYield3() → asy::Future async { + await 0; + self::throwSync(); +} +static method customErrorZone() → asy::Future async { + final asy::Completer completer = asy::Completer::•(); + asy::runZonedGuarded>(() → asy::Future async { + await self::allYield(); + completer.{asy::Completer::complete}(null); + }, (core::Object e, core::StackTrace s) → core::Null? { + completer.{asy::Completer::completeError}(e, s); + }); + return completer.{asy::Completer::future}; +} +static method main() → dynamic {} diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.strong.transformed.expect b/pkg/front_end/testcases/nnbd/return_async.dart.strong.transformed.expect new file mode 100644 index 00000000000..ae00b6798db --- /dev/null +++ b/pkg/front_end/testcases/nnbd/return_async.dart.strong.transformed.expect @@ -0,0 +1,160 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:async" as asy; +import "dart:core" as core; +import "dart:_internal" as _in; + +import "dart:async"; + +static method throwSync() → asy::Future { + throw ""; +} +static method allYield() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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(0, :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + [yield] let dynamic #t2 = asy::_awaitHelper(self::allYield2(), :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; +} +static method allYield2() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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 { + #L2: + { + [yield] let dynamic #t3 = asy::_awaitHelper(0, :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + [yield] let dynamic #t4 = asy::_awaitHelper(self::allYield3(), :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; +} +static method allYield3() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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 { + #L3: + { + [yield] let dynamic #t5 = asy::_awaitHelper(0, :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + self::throwSync(); + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; +} +static method customErrorZone() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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 { + #L4: + { + final asy::Completer completer = asy::Completer::•(); + asy::runZonedGuarded>(() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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 { + #L5: + { + [yield] let dynamic #t6 = asy::_awaitHelper(self::allYield(), :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + completer.{asy::Completer::complete}(null); + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; + }, (core::Object e, core::StackTrace s) → core::Null? { + completer.{asy::Completer::completeError}(e, s); + }); + :return_value = completer.{asy::Completer::future}; + break #L4; + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; +} +static method main() → dynamic {} diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.textual_outline.expect b/pkg/front_end/testcases/nnbd/return_async.dart.textual_outline.expect new file mode 100644 index 00000000000..cb9684ff4a4 --- /dev/null +++ b/pkg/front_end/testcases/nnbd/return_async.dart.textual_outline.expect @@ -0,0 +1,8 @@ +import 'dart:async'; + +Future throwSync() {} +Future allYield() async {} +Future allYield2() async {} +Future allYield3() async {} +Future customErrorZone() async {} +main() {} diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/nnbd/return_async.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..0f39b9a7213 --- /dev/null +++ b/pkg/front_end/testcases/nnbd/return_async.dart.textual_outline_modelled.expect @@ -0,0 +1,8 @@ +import 'dart:async'; + +Future allYield() async {} +Future allYield2() async {} +Future allYield3() async {} +Future customErrorZone() async {} +Future throwSync() {} +main() {} diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.weak.expect b/pkg/front_end/testcases/nnbd/return_async.dart.weak.expect new file mode 100644 index 00000000000..2c9322b6b56 --- /dev/null +++ b/pkg/front_end/testcases/nnbd/return_async.dart.weak.expect @@ -0,0 +1,33 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:async" as asy; +import "dart:core" as core; + +import "dart:async"; + +static method throwSync() → asy::Future { + throw ""; +} +static method allYield() → asy::Future async { + await 0; + await self::allYield2(); +} +static method allYield2() → asy::Future async { + await 0; + await self::allYield3(); +} +static method allYield3() → asy::Future async { + await 0; + self::throwSync(); +} +static method customErrorZone() → asy::Future async { + final asy::Completer completer = asy::Completer::•(); + asy::runZonedGuarded>(() → asy::Future async { + await self::allYield(); + completer.{asy::Completer::complete}(null); + }, (core::Object e, core::StackTrace s) → core::Null? { + completer.{asy::Completer::completeError}(e, s); + }); + return completer.{asy::Completer::future}; +} +static method main() → dynamic {} diff --git a/pkg/front_end/testcases/nnbd/return_async.dart.weak.transformed.expect b/pkg/front_end/testcases/nnbd/return_async.dart.weak.transformed.expect new file mode 100644 index 00000000000..ae00b6798db --- /dev/null +++ b/pkg/front_end/testcases/nnbd/return_async.dart.weak.transformed.expect @@ -0,0 +1,160 @@ +library /*isNonNullableByDefault*/; +import self as self; +import "dart:async" as asy; +import "dart:core" as core; +import "dart:_internal" as _in; + +import "dart:async"; + +static method throwSync() → asy::Future { + throw ""; +} +static method allYield() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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(0, :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + [yield] let dynamic #t2 = asy::_awaitHelper(self::allYield2(), :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; +} +static method allYield2() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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 { + #L2: + { + [yield] let dynamic #t3 = asy::_awaitHelper(0, :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + [yield] let dynamic #t4 = asy::_awaitHelper(self::allYield3(), :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; +} +static method allYield3() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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 { + #L3: + { + [yield] let dynamic #t5 = asy::_awaitHelper(0, :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + self::throwSync(); + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; +} +static method customErrorZone() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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 { + #L4: + { + final asy::Completer completer = asy::Completer::•(); + asy::runZonedGuarded>(() → asy::Future /* originally async */ { + final asy::_AsyncAwaitCompleter :async_completer = new asy::_AsyncAwaitCompleter::•(); + FutureOr? :return_value; + dynamic :async_stack_trace; + (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 { + #L5: + { + [yield] let dynamic #t6 = asy::_awaitHelper(self::allYield(), :async_op_then, :async_op_error, :async_op) in null; + _in::unsafeCast(:result); + completer.{asy::Completer::complete}(null); + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; + }, (core::Object e, core::StackTrace s) → core::Null? { + completer.{asy::Completer::completeError}(e, s); + }); + :return_value = completer.{asy::Completer::future}; + break #L4; + } + asy::_completeOnAsyncReturn(:async_completer, :return_value); + return; + } + on dynamic catch(dynamic exception, core::StackTrace stack_trace) { + :async_completer.{asy::Completer::completeError}(exception, stack_trace); + } + :async_stack_trace = asy::_asyncStackTraceHelper(:async_op); + :async_op_then = asy::_asyncThenWrapperHelper(:async_op); + :async_op_error = asy::_asyncErrorWrapperHelper(:async_op); + :async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op); + return :async_completer.{asy::Completer::future}; +} +static method main() → dynamic {} diff --git a/pkg/kernel/lib/ast.dart b/pkg/kernel/lib/ast.dart index f96070575ee..ddceed3a3cf 100644 --- a/pkg/kernel/lib/ast.dart +++ b/pkg/kernel/lib/ast.dart @@ -5483,7 +5483,7 @@ class AwaitExpression extends Expression { } DartType getStaticType(StaticTypeContext context) { - return context.typeEnvironment.unfutureType(operand.getStaticType(context)); + return context.typeEnvironment.flatten(operand.getStaticType(context)); } R accept(ExpressionVisitor v) => v.visitAwaitExpression(this); @@ -8310,22 +8310,17 @@ class TypeParameterType extends DartType { @override void toTextInternal(AstPrinter printer) { - printer.writeTypeParameterName(parameter); - printer.write(nullabilityToString(declaredNullability)); if (promotedBound != null) { + printer.write('('); + printer.writeTypeParameterName(parameter); + printer.write(nullabilityToString(declaredNullability)); printer.write(" & "); - printer.write(promotedBound.toStringInternal()); - printer.write(" /* '"); + printer.writeType(promotedBound); + printer.write(')'); + printer.write(nullabilityToString(nullability)); + } else { + printer.writeTypeParameterName(parameter); printer.write(nullabilityToString(declaredNullability)); - printer.write("' & '"); - if (promotedBound is InvalidType) { - printer.write(nullabilityToString(Nullability.undetermined)); - } else { - printer.write(nullabilityToString(promotedBound.nullability)); - } - printer.write("' = '"); - printer.write(nullabilityToString(declaredNullability)); - printer.write("' */"); } } } diff --git a/pkg/kernel/lib/src/future_value_type.dart b/pkg/kernel/lib/src/future_value_type.dart new file mode 100644 index 00000000000..939bb69c417 --- /dev/null +++ b/pkg/kernel/lib/src/future_value_type.dart @@ -0,0 +1,101 @@ +// Copyright (c) 2020, 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 '../ast.dart'; +import '../core_types.dart'; + +DartType computeFutureValueType(CoreTypes coreTypes, DartType type) { + return type.accept1(const FutureValueTypeVisitor(), coreTypes); +} + +class FutureValueTypeVisitor implements DartTypeVisitor1 { + /// Helper function invoked on unknown implementers of [DartType]. + /// + /// Its arguments are the unhandled type and the function that can be invoked + /// from within the handler on parts of the unknown type to recursively call + /// the visitor. If not set, an exception is thrown then an unhandled + /// implementer of [DartType] is encountered. + final DartType Function(DartType node, CoreTypes coreTypes, + DartType Function(DartType node, CoreTypes coreTypes) recursor) + unhandledTypeHandler; + + const FutureValueTypeVisitor({this.unhandledTypeHandler}); + + DartType visit(DartType node, CoreTypes coreTypes) => + node.accept1(this, coreTypes); + + @override + DartType defaultDartType(DartType node, CoreTypes coreTypes) { + if (unhandledTypeHandler == null) { + throw new UnsupportedError("Unsupported type '${node.runtimeType}'."); + } else { + return unhandledTypeHandler(node, coreTypes, visit); + } + } + + @override + DartType visitBottomType(BottomType node, CoreTypes coreTypes) { + // Otherwise, for all S, futureValueType(S) = Object?. + return coreTypes.objectNullableRawType; + } + + @override + DartType visitDynamicType(DynamicType node, CoreTypes coreTypes) { + // futureValueType(dynamic) = dynamic. + return node; + } + + @override + DartType visitFunctionType(FunctionType node, CoreTypes coreTypes) { + // Otherwise, for all S, futureValueType(S) = Object?. + return coreTypes.objectNullableRawType; + } + + @override + DartType visitInterfaceType(InterfaceType node, CoreTypes coreTypes) { + if (node.classNode == coreTypes.futureClass) { + // futureValueType(Future) = S, for all S. + return node.typeArguments.single; + } + // Otherwise, for all S, futureValueType(S) = Object?. + return coreTypes.objectNullableRawType; + } + + @override + DartType visitFutureOrType(FutureOrType node, CoreTypes coreTypes) { + // futureValueType(FutureOr) = S, for all S. + return node.typeArgument; + } + + @override + DartType visitInvalidType(InvalidType node, CoreTypes coreTypes) { + // Return the invalid type itself to continue the encapsulation of the + // error state. + return node; + } + + @override + DartType visitNeverType(DartType node, CoreTypes coreTypes) { + // Otherwise, for all S, futureValueType(S) = Object?. + return coreTypes.objectNullableRawType; + } + + @override + DartType visitTypeParameterType(DartType node, CoreTypes coreTypes) { + // Otherwise, for all S, futureValueType(S) = Object?. + return coreTypes.objectNullableRawType; + } + + @override + DartType visitTypedefType(DartType node, CoreTypes coreTypes) { + // Otherwise, for all S, futureValueType(S) = Object?. + return coreTypes.objectNullableRawType; + } + + @override + DartType visitVoidType(DartType node, CoreTypes coreTypes) { + // futureValueType(void) = void. + return node; + } +} diff --git a/pkg/kernel/lib/testing/type_parser_environment.dart b/pkg/kernel/lib/testing/type_parser_environment.dart index 2e07a3edf7e..10ffc59fe29 100644 --- a/pkg/kernel/lib/testing/type_parser_environment.dart +++ b/pkg/kernel/lib/testing/type_parser_environment.dart @@ -121,6 +121,13 @@ class Env { _libraryEnvironment = _libraryEnvironment.extendWithTypeParameters(typeParameters); } + + void withTypeParameters(String typeParameters, void Function() f) { + TypeParserEnvironment oldLibraryEnvironment = _libraryEnvironment; + extendWithTypeParameters(typeParameters); + f(); + _libraryEnvironment = oldLibraryEnvironment; + } } class TypeParserEnvironment { diff --git a/pkg/kernel/lib/type_checker.dart b/pkg/kernel/lib/type_checker.dart index 9631b2c2be9..d5a0a2e1713 100644 --- a/pkg/kernel/lib/type_checker.dart +++ b/pkg/kernel/lib/type_checker.dart @@ -423,7 +423,7 @@ class TypeCheckingVisitor @override DartType visitAwaitExpression(AwaitExpression node) { - return environment.unfutureType(visitExpression(node.operand)); + return environment.flatten(visitExpression(node.operand)); } @override @@ -993,7 +993,7 @@ class TypeCheckingVisitor } else { var type = visitExpression(node.expression); if (currentAsyncMarker == AsyncMarker.Async) { - type = environment.unfutureType(type); + type = environment.flatten(type); } checkAssignable(node.expression, type, currentReturnType); } diff --git a/pkg/kernel/lib/type_environment.dart b/pkg/kernel/lib/type_environment.dart index 2426f3178ff..14bb59d8cb5 100644 --- a/pkg/kernel/lib/type_environment.dart +++ b/pkg/kernel/lib/type_environment.dart @@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. library kernel.type_environment; +import 'package:kernel/type_algebra.dart'; + import 'ast.dart'; import 'class_hierarchy.dart'; import 'core_types.dart'; @@ -79,27 +81,38 @@ abstract class TypeEnvironment extends Types { coreTypes.futureClass, nullability, [type]); } - /// Removes a level of `Future<>` types wrapping a type. - /// - /// This implements the function `flatten` from the spec, which unwraps a + DartType _withDeclaredNullability(DartType type, Nullability nullability) { + if (type == nullType) return type; + return type.withDeclaredNullability( + uniteNullabilities(type.declaredNullability, nullability)); + } + + /// Returns the `flatten` of [type] as defined in the spec, which unwraps a /// layer of Future or FutureOr from a type. - DartType unfutureType(DartType type) { - if (type is FutureOrType) return type.typeArgument; - if (type is InterfaceType) { - if (type.classNode == coreTypes.futureClass) { - return type.typeArguments[0]; - } - // It is a compile-time error to implement, extend, or mixin FutureOr so - // we aren't concerned with it. If a class implements multiple - // instantiations of Future, getTypeAsInstanceOf is responsible for - // picking the least one in the sense required by the spec. + DartType flatten(DartType t) { + // if T is S? then flatten(T) = flatten(S)? + // otherwise if T is S* then flatten(T) = flatten(S)* + // -- this is preserve with the calls to [_withDeclaredNullability] below. + + // otherwise if T is FutureOr then flatten(T) = S + if (t is FutureOrType) { + return _withDeclaredNullability(t.typeArgument, t.declaredNullability); + } + + // otherwise if T <: Future then let S be a type such that T <: Future + // and for all R, if T <: Future then S <: R; then flatten(T) = S + DartType resolved = _resolveTypeParameterType(t); + if (resolved is InterfaceType) { List futureArguments = - getTypeArgumentsAsInstanceOf(type, coreTypes.futureClass); + getTypeArgumentsAsInstanceOf(resolved, coreTypes.futureClass); if (futureArguments != null) { - return futureArguments[0]; + return _withDeclaredNullability( + futureArguments.single, t.declaredNullability); } } - return type; + + // otherwise flatten(T) = T + return t; } /// Returns the non-type parameter type bound of [type]. diff --git a/pkg/kernel/test/flatten_test.dart b/pkg/kernel/test/flatten_test.dart new file mode 100644 index 00000000000..ab6c1ee5acf --- /dev/null +++ b/pkg/kernel/test/flatten_test.dart @@ -0,0 +1,92 @@ +// Copyright (c) 2019, 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 "package:expect/expect.dart" show Expect; + +import 'package:kernel/ast.dart' hide MapEntry; +import 'package:kernel/class_hierarchy.dart'; +import 'package:kernel/testing/type_parser_environment.dart'; +import 'package:kernel/type_environment.dart'; + +const Set data = { + const Test('Null', 'Null'), + const Test('Never?', 'Never?'), + const Test('void', 'void'), + const Test('dynamic', 'dynamic'), + const Test('bool', 'bool'), + const Test('bool?', 'bool?'), + const Test('bool*', 'bool*'), + const Test('List', 'List'), + const Test('List?', 'List?'), + const Test('List*', 'List*'), + const Test('FutureOr', 'bool'), + const Test('FutureOr?', 'bool?'), + const Test('FutureOr*', 'bool*'), + const Test('FutureOr*', 'bool?'), + const Test('FutureOr?', 'bool?'), + const Test('FutureOr?', 'bool?'), + const Test('FutureOr', 'Null'), + const Test('FutureOr?', 'Null'), + const Test('FutureOr*', 'Null'), + const Test('Future', 'bool'), + const Test('Future?', 'bool?'), + const Test('Future*', 'bool*'), + const Test('Future', 'bool?'), + const Test('Future', 'bool*'), + const Test('() ->* bool*', '() ->* bool*'), + const Test('() -> bool*', '() -> bool*'), + const Test('() ->? bool*', '() ->? bool*'), + const Test('() ->* bool', '() ->* bool'), + const Test('() ->? bool', '() ->? bool'), + const Test('() -> bool', '() -> bool'), + const Test('T', 'T', 'T'), + const Test('T?', 'T?', 'T'), + const Test('T*', 'T*', 'T'), + const Test('T', 'T', 'T extends bool'), + const Test('T?', 'T?', 'T extends bool'), + const Test('T*', 'T*', 'T extends bool'), + const Test('T', 'T', 'T extends FutureOr'), + const Test('T?', 'T?', 'T extends FutureOr'), + const Test('T*', 'T*', 'T extends FutureOr'), + const Test('T', 'bool', 'T extends Future'), + const Test('T?', 'bool?', 'T extends Future'), + const Test('T*', 'bool*', 'T extends Future'), + const Test('T & bool', 'T & bool', 'T'), + const Test('T & bool?', 'T & bool?', 'T'), + const Test('T & bool*', 'T & bool*', 'T'), +}; + +class Test { + final String input; + final String output; + final String typeParameters; + + const Test(this.input, this.output, [this.typeParameters]); +} + +main() { + Env env = new Env(''); + ClassHierarchy classHierarchy = + new ClassHierarchy(env.component, env.coreTypes); + TypeEnvironment typeEnvironment = + new TypeEnvironment(env.coreTypes, classHierarchy); + data.forEach((Test test) { + env.withTypeParameters(test.typeParameters, () { + String input = test.input; + String output = test.output; + DartType inputType = env.parseType(input); + DartType expectedOutputType = env.parseType(output); + DartType actualOutputType = typeEnvironment.flatten(inputType); + print('flatten($inputType) ' + '${test.typeParameters != null ? 'with ${test.typeParameters} ' : ''}' + '= $actualOutputType, expected $expectedOutputType'); + Expect.equals( + expectedOutputType, + actualOutputType, + "Unexpected flatten of $inputType ('$input'):\n" + "Expected: ${expectedOutputType} ('$output')\n" + "Actual: ${actualOutputType}"); + }); + }); +} diff --git a/pkg/kernel/test/future_value_type_test.dart b/pkg/kernel/test/future_value_type_test.dart new file mode 100644 index 00000000000..5c0f032f3b1 --- /dev/null +++ b/pkg/kernel/test/future_value_type_test.dart @@ -0,0 +1,89 @@ +// Copyright (c) 2019, 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 "package:expect/expect.dart" show Expect; + +import 'package:kernel/ast.dart' hide MapEntry; +import 'package:kernel/src/future_value_type.dart'; +import 'package:kernel/testing/type_parser_environment.dart'; + +const Map data = { + 'Null': 'Object?', + 'Never': 'Object?', + 'Never?': 'Object?', + 'void': 'void', + 'dynamic': 'dynamic', + 'bool': 'Object?', + 'bool?': 'Object?', + 'bool*': 'Object?', + 'List': 'Object?', + '() -> void': 'Object?', + '(T) -> void': 'Object?', + 'X': 'Object?', + 'X_extends_FutureInt': 'Object?', + 'X_extends_FutureOrInt': 'Object?', + 'Future': 'dynamic', + 'Future?': 'dynamic', + 'Future*': 'dynamic', + 'Future': 'Object', + 'Future?': 'Object', + 'Future*': 'Object', + 'Future': 'int?', + 'Future?': 'int?', + 'Future*': 'int?', + 'Future?>': 'Future?', + 'Future?>?': 'Future?', + 'Future?>*': 'Future?', + 'Future?>': 'FutureOr?', + 'Future?>?': 'FutureOr?', + 'Future?>*': 'FutureOr?', + 'Future': 'Null', + 'Future?': 'Null', + 'Future*': 'Null', + 'Future': 'void', + 'Future?': 'void', + 'Future*': 'void', + 'FutureOr': 'dynamic', + 'FutureOr?': 'dynamic', + 'FutureOr*': 'dynamic', + 'FutureOr': 'Object', + 'FutureOr?': 'Object', + 'FutureOr*': 'Object', + 'FutureOr': 'int?', + 'FutureOr?': 'int?', + 'FutureOr*': 'int?', + 'FutureOr?>': 'Future?', + 'FutureOr?>?': 'Future?', + 'FutureOr?>*': 'Future?', + 'FutureOr?>': 'FutureOr?', + 'FutureOr?>?': 'FutureOr?', + 'FutureOr?>*': 'FutureOr?', + 'FutureOr': 'Null', + 'FutureOr?': 'Null', + 'FutureOr*': 'Null', + 'FutureOr': 'void', + 'FutureOr?': 'void', + 'FutureOr*': 'void', +}; + +main() { + Env env = new Env('') + ..extendWithTypeParameters('X,' + 'X_extends_FutureInt extends Future,' + 'X_extends_FutureOrInt extends FutureOr'); + data.forEach((String input, String output) { + DartType inputType = env.parseType(input); + DartType expectedOutputType = env.parseType(output); + DartType actualOutputType = + computeFutureValueType(env.coreTypes, inputType); + print( + 'futureValueType($inputType) = $actualOutputType: $expectedOutputType'); + Expect.equals( + expectedOutputType, + actualOutputType, + "Unexpected future value type of $inputType ('$input'):\n" + "Expected: ${expectedOutputType} ('$output')\n" + "Actual: ${actualOutputType}"); + }); +} diff --git a/sdk/lib/_internal/vm/bin/vmservice_server.dart b/sdk/lib/_internal/vm/bin/vmservice_server.dart index 26f1bfa86aa..9857cd653fb 100644 --- a/sdk/lib/_internal/vm/bin/vmservice_server.dart +++ b/sdk/lib/_internal/vm/bin/vmservice_server.dart @@ -393,7 +393,7 @@ class Server { 'uri': serverAddress.toString(), }; final file = File.fromUri(Uri.parse(serviceInfoFilenameLocal)); - return file.writeAsString(json.encode(serviceInfo)); + return file.writeAsString(json.encode(serviceInfo)) as Future; } Future startup() async { diff --git a/tests/language/implicit_downcast_during/return_async_test.dart b/tests/language/implicit_downcast_during/return_async_test.dart index aea8c61d28d..a976d1ed3cd 100644 --- a/tests/language/implicit_downcast_during/return_async_test.dart +++ b/tests/language/implicit_downcast_during/return_async_test.dart @@ -13,14 +13,14 @@ Future f1(A a) async { // ^^^^^^^^^^^^^^^^ // [analyzer] STATIC_TYPE_WARNING.RETURN_OF_INVALID_TYPE // ^ - // [cfe] A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. + // [cfe] A value of type 'FutureOr' can't be assigned to a variable of type 'B'. } Future f2(A a) async => a as FutureOr; // ^^^^^^^^^^^^^^^^ // [analyzer] STATIC_TYPE_WARNING.RETURN_OF_INVALID_TYPE // ^ -// [cfe] A value of type 'Future' can't be assigned to a variable of type 'FutureOr'. +// [cfe] A value of type 'FutureOr' can't be assigned to a variable of type 'B'. main() async { Object b;