[cfe] Refactor return type checking in preparation for nnbd spec change

Change-Id: Ie73726b59b23be4c5e35b207ec72732c8be7dada
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/150982
Reviewed-by: Dmitry Stefantsov <dmitryas@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Johnni Winther 2020-06-22 11:33:35 +00:00 committed by commit-bot@chromium.org
parent 1d3fbbed7c
commit cc5df58bd2
36 changed files with 1911 additions and 415 deletions

View file

@ -4887,9 +4887,7 @@ class InferenceVisitor
StatementInferenceResult visitReturnStatement(
covariant ReturnStatementImpl node) {
ClosureContext closureContext = inferrer.closureContext;
DartType typeContext = !closureContext.isGenerator
? closureContext.returnOrYieldContext
: const UnknownType();
DartType typeContext = closureContext.returnContext;
DartType inferredType;
if (node.expression != null) {
ExpressionInferenceResult expressionResult = inferrer.inferExpression(
@ -5682,24 +5680,18 @@ class InferenceVisitor
StatementInferenceResult visitYieldStatement(YieldStatement node) {
ClosureContext closureContext = inferrer.closureContext;
ExpressionInferenceResult expressionResult;
if (closureContext.isGenerator) {
DartType typeContext = closureContext.returnOrYieldContext;
if (node.isYieldStar && typeContext != null) {
typeContext = inferrer.wrapType(
typeContext,
closureContext.isAsync
? inferrer.coreTypes.streamClass
: inferrer.coreTypes.iterableClass,
inferrer.library.nonNullable);
}
expressionResult = inferrer.inferExpression(
node.expression, typeContext, true,
isVoidAllowed: true);
} else {
expressionResult = inferrer.inferExpression(
node.expression, const UnknownType(), true,
isVoidAllowed: true);
DartType typeContext = closureContext.yieldContext;
if (node.isYieldStar && typeContext is! UnknownType) {
typeContext = inferrer.wrapType(
typeContext,
closureContext.isAsync
? inferrer.coreTypes.streamClass
: inferrer.coreTypes.iterableClass,
inferrer.library.nonNullable);
}
expressionResult = inferrer.inferExpression(
node.expression, typeContext, true,
isVoidAllowed: true);
closureContext.handleYield(inferrer, node, expressionResult);
return const StatementInferenceResult();
}

View file

@ -0,0 +1,856 @@
// 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.md file.
part of 'type_inferrer.dart';
/// Keeps track of information about the innermost function or closure being
/// inferred.
abstract class ClosureContext {
/// Returns `true` if this is an `async` or an `async*` function.
bool get isAsync;
/// The typing expectation for the subexpression of a `return` statement
/// inside the function.
///
/// For an `async` function, this is a "FutureOr" type (since it is
/// permissible for such a function to return either a direct value or a
/// future).
///
/// For generator functions (which do not allow return statements) this is the
/// unknown type.
DartType get returnContext;
/// The typing expectation for the subexpression of a `yield` statement inside
/// the function.
///
/// For `sync*` and `async*` functions, the expected type is the element type
/// of the generated `Iterable` or `Stream`, respectively.
///
/// For non-generator functions (which do not allow yield statements) this is
/// the unknown type.
DartType get yieldContext;
factory ClosureContext(TypeInferrerImpl inferrer, AsyncMarker asyncMarker,
DartType returnContext, bool needToInferReturnType) {
assert(returnContext != null);
DartType declaredReturnType =
inferrer.computeGreatestClosure(returnContext);
bool isAsync = asyncMarker == AsyncMarker.Async ||
asyncMarker == AsyncMarker.AsyncStar;
bool isGenerator = asyncMarker == AsyncMarker.SyncStar ||
asyncMarker == AsyncMarker.AsyncStar;
if (isGenerator) {
if (isAsync) {
DartType yieldContext = inferrer.getTypeArgumentOf(
returnContext, inferrer.coreTypes.streamClass);
return new _AsyncStarClosureContext(
yieldContext, declaredReturnType, needToInferReturnType);
} else {
DartType yieldContext = inferrer.getTypeArgumentOf(
returnContext, inferrer.coreTypes.iterableClass);
return new _SyncStarClosureContext(
yieldContext, declaredReturnType, needToInferReturnType);
}
} else if (isAsync) {
returnContext = inferrer.wrapFutureOrType(
inferrer.typeSchemaEnvironment.unfutureType(returnContext));
return new _AsyncClosureContext(
returnContext, declaredReturnType, needToInferReturnType);
} else {
return new _SyncClosureContext(
returnContext, declaredReturnType, needToInferReturnType);
}
}
/// Handles an explicit return statement.
///
/// If the return type is declared, the expression type is checked. If the
/// return type is inferred the expression type registered for inference
/// in [inferReturnType].
void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
DartType type, bool isArrow);
/// Handles an explicit yield statement.
///
/// If the return type is declared, the expression type is checked. If the
/// return type is inferred the expression type registered for inference
/// in [inferReturnType].
void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
ExpressionInferenceResult expressionResult);
/// Handles an implicit return statement.
///
/// If the return type is declared, the expression type is checked. If the
/// return type is inferred the expression type registered for inference
/// in [inferReturnType].
StatementInferenceResult handleImplicitReturn(TypeInferrerImpl inferrer,
Statement body, StatementInferenceResult inferenceResult, int fileOffset);
/// Infers the return type for the function.
///
/// If the function is a non-generator function this is based on the explicit
/// and implicit return statements registered in [handleReturn] and
/// [handleImplicitReturn].
///
/// If the function is a generator function this is based on the explicit
/// yield statements registered in [handleYield].
DartType inferReturnType(TypeInferrerImpl inferrer, {bool hasImplicitReturn});
}
class _SyncClosureContext implements ClosureContext {
bool get isAsync => false;
/// The typing expectation for the subexpression of a `return` or `yield`
/// statement inside the function.
///
/// For non-generator async functions, this will be a "FutureOr" type (since
/// it is permissible for such a function to return either a direct value or
/// a future).
///
/// For generator functions containing a `yield*` statement, the expected type
/// for the subexpression of the `yield*` statement is the result of wrapping
/// this typing expectation in `Stream` or `Iterator`, as appropriate.
final DartType _returnContext;
@override
DartType get returnContext => _returnContext;
@override
DartType get yieldContext => const UnknownType();
final DartType _declaredReturnType;
final bool _needToInferReturnType;
DartType _inferredReturnType;
/// Whether the function is an arrow function.
bool _isArrow;
/// A list of return statements in functions whose return type is being
/// inferred.
///
/// The returns are checked for validity after the return type is inferred.
List<ReturnStatement> _returnStatements;
/// A list of return expression types in functions whose return type is
/// being inferred.
List<DartType> _returnExpressionTypes;
_SyncClosureContext(this._returnContext, this._declaredReturnType,
this._needToInferReturnType) {
if (_needToInferReturnType) {
_returnStatements = [];
_returnExpressionTypes = [];
}
}
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;
} else {
statement.expression = inferrer.helper.wrapInProblem(
new NullLiteral()..fileOffset = statement.fileOffset,
messageReturnWithoutExpression,
statement.fileOffset,
noLength)
..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;
} else {
ensureAssignability();
}
}
}
/// Updates the inferred return type based on the presence of a return
/// statement returning the given [type].
@override
void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
DartType type, bool isArrow) {
// The first return we see tells us if we have an arrow function.
if (this._isArrow == null) {
this._isArrow = isArrow;
} else {
assert(this._isArrow == isArrow);
}
if (_needToInferReturnType) {
// Add the return to a list to be checked for validity after we've
// inferred the return type.
_returnStatements.add(statement);
_returnExpressionTypes.add(type);
} else {
_checkValidReturn(inferrer, _declaredReturnType, statement, type);
}
}
@override
void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
ExpressionInferenceResult expressionResult) {
node.expression = expressionResult.expression..parent = node;
}
@override
DartType inferReturnType(TypeInferrerImpl inferrer,
{bool hasImplicitReturn}) {
assert(_needToInferReturnType);
assert(hasImplicitReturn != null);
DartType inferredType;
if (_returnStatements.isNotEmpty) {
// Use the types seen from the explicit return statements.
for (int i = 0; i < _returnStatements.length; i++) {
ReturnStatement statement = _returnStatements[i];
DartType type = _returnExpressionTypes[i];
// The return expression has to be assignable to the return type
// expectation from the downwards inference context.
if (statement.expression != null) {
if (!inferrer.isAssignable(_returnContext, type)) {
type = inferrer.computeGreatestClosure(_returnContext);
}
}
if (inferredType == null) {
inferredType = type;
} else {
inferredType = inferrer.typeSchemaEnvironment.getStandardUpperBound(
inferredType, type, inferrer.library.library);
}
}
} else if (hasImplicitReturn) {
// No explicit returns we have an implicit `return null`.
inferredType = inferrer.typeSchemaEnvironment.nullType;
} else {
// No explicit return and the function doesn't complete normally; that is,
// it throws.
if (inferrer.isNonNullableByDefault) {
inferredType = new NeverType(inferrer.library.nonNullable);
} else {
inferredType = inferrer.typeSchemaEnvironment.nullType;
}
}
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) {
_checkValidReturn(inferrer, inferredType, _returnStatements[i],
_returnExpressionTypes[i]);
}
return _inferredReturnType =
demoteTypeInLibrary(inferredType, inferrer.library.library);
}
@override
StatementInferenceResult handleImplicitReturn(
TypeInferrerImpl inferrer,
Statement body,
StatementInferenceResult inferenceResult,
int fileOffset) {
DartType returnType;
if (_needToInferReturnType) {
assert(_inferredReturnType != null,
"Return type has not yet been inferred.");
returnType = _inferredReturnType;
} else {
returnType = _declaredReturnType;
}
if (inferrer.library.isNonNullableByDefault &&
(containsInvalidType(returnType) ||
returnType.isPotentiallyNonNullable) &&
inferrer.flowAnalysis.isReachable) {
Statement resultStatement =
inferenceResult.hasChanged ? inferenceResult.statement : body;
// Create a synthetic return statement with the error.
Statement returnStatement = new ReturnStatement(inferrer.helper
.wrapInProblem(
new NullLiteral()..fileOffset = fileOffset,
templateImplicitReturnNull.withArguments(
returnType, inferrer.library.isNonNullableByDefault),
fileOffset,
noLength))
..fileOffset = fileOffset;
if (resultStatement is Block) {
resultStatement.statements.add(returnStatement);
} else {
resultStatement =
new Block(<Statement>[resultStatement, returnStatement])
..fileOffset = fileOffset;
}
return new StatementInferenceResult.single(resultStatement);
}
return inferenceResult;
}
}
/// Keeps track of information about the innermost function or closure being
/// inferred.
class _AsyncClosureContext implements ClosureContext {
bool get isAsync => true;
/// The typing expectation for the subexpression of a `return` or `yield`
/// statement inside the function.
///
/// For non-generator async functions, this will be a "FutureOr" type (since
/// it is permissible for such a function to return either a direct value or
/// a future).
///
/// For generator functions containing a `yield*` statement, the expected type
/// for the subexpression of the `yield*` statement is the result of wrapping
/// this typing expectation in `Stream` or `Iterator`, as appropriate.
final DartType _returnContext;
@override
DartType get returnContext => _returnContext;
@override
DartType get yieldContext => const UnknownType();
final DartType _declaredReturnType;
final bool _needToInferReturnType;
DartType _inferredReturnType;
/// Whether the function is an arrow function.
bool _isArrow;
/// A list of return statements in functions whose return type is being
/// inferred.
///
/// The returns are checked for validity after the return type is inferred.
List<ReturnStatement> _returnStatements;
/// A list of return expression types in functions whose return type is
/// being inferred.
List<DartType> _returnExpressionTypes;
_AsyncClosureContext(this._returnContext, this._declaredReturnType,
this._needToInferReturnType) {
if (_needToInferReturnType) {
_returnStatements = [];
_returnExpressionTypes = [];
}
}
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;
} else {
statement.expression = inferrer.helper.wrapInProblem(
new NullLiteral()..fileOffset = statement.fileOffset,
messageReturnWithoutExpression,
statement.fileOffset,
noLength)
..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;
} else {
// The caller will check that the return expression is assignable to the
// return type.
ensureAssignability();
}
}
}
/// Updates the inferred return type based on the presence of a return
/// statement returning the given [type].
@override
void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
DartType type, bool isArrow) {
// The first return we see tells us if we have an arrow function.
if (this._isArrow == null) {
this._isArrow = isArrow;
} else {
assert(this._isArrow == isArrow);
}
if (_needToInferReturnType) {
// Add the return to a list to be checked for validity after we've
// inferred the return type.
_returnStatements.add(statement);
_returnExpressionTypes.add(type);
} else {
_checkValidReturn(inferrer, _declaredReturnType, statement, type);
}
}
@override
void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
ExpressionInferenceResult expressionResult) {
node.expression = expressionResult.expression..parent = node;
}
DartType computeAssignableType(TypeInferrerImpl inferrer,
DartType contextType, DartType expressionType) {
contextType = inferrer.computeGreatestClosure(contextType);
DartType initialContextType = contextType;
if (!inferrer.isAssignable(initialContextType, expressionType)) {
// If the body of the function is async, the expected return type has the
// shape FutureOr<T>. We check both branches for FutureOr here: both T
// and Future<T>.
DartType unfuturedExpectedType =
inferrer.typeSchemaEnvironment.unfutureType(contextType);
DartType futuredExpectedType = inferrer.wrapFutureType(
unfuturedExpectedType, inferrer.library.nonNullable);
if (inferrer.isAssignable(unfuturedExpectedType, expressionType)) {
contextType = unfuturedExpectedType;
} else if (inferrer.isAssignable(futuredExpectedType, expressionType)) {
contextType = futuredExpectedType;
}
}
return contextType;
}
@override
DartType inferReturnType(TypeInferrerImpl inferrer,
{bool hasImplicitReturn}) {
assert(_needToInferReturnType);
assert(hasImplicitReturn != null);
DartType inferredType;
if (_returnStatements.isNotEmpty) {
// Use the types seen from the explicit return statements.
for (int i = 0; i < _returnStatements.length; i++) {
ReturnStatement statement = _returnStatements[i];
DartType type = _returnExpressionTypes[i];
// 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);
}
}
DartType unwrappedType =
inferrer.typeSchemaEnvironment.unfutureType(type);
if (inferredType == null) {
inferredType = unwrappedType;
} else {
inferredType = inferrer.typeSchemaEnvironment.getStandardUpperBound(
inferredType, unwrappedType, inferrer.library.library);
}
}
} else if (hasImplicitReturn) {
// No explicit returns we have an implicit `return null`.
inferredType = inferrer.typeSchemaEnvironment.nullType;
} else {
// No explicit return and the function doesn't complete normally; that is,
// it throws.
if (inferrer.isNonNullableByDefault) {
inferredType = new NeverType(inferrer.library.nonNullable);
} else {
inferredType = inferrer.typeSchemaEnvironment.nullType;
}
}
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);
}
for (int i = 0; i < _returnStatements.length; ++i) {
_checkValidReturn(inferrer, inferredType, _returnStatements[i],
_returnExpressionTypes[i]);
}
return _inferredReturnType =
demoteTypeInLibrary(inferredType, inferrer.library.library);
}
@override
StatementInferenceResult handleImplicitReturn(
TypeInferrerImpl inferrer,
Statement body,
StatementInferenceResult inferenceResult,
int fileOffset) {
DartType returnType;
if (_needToInferReturnType) {
assert(_inferredReturnType != null,
"Return type has not yet been inferred.");
returnType = _inferredReturnType;
} else {
returnType = _declaredReturnType;
}
returnType = inferrer.typeSchemaEnvironment.unfutureType(returnType);
if (inferrer.library.isNonNullableByDefault &&
(containsInvalidType(returnType) ||
returnType.isPotentiallyNonNullable) &&
inferrer.flowAnalysis.isReachable) {
Statement resultStatement =
inferenceResult.hasChanged ? inferenceResult.statement : body;
// Create a synthetic return statement with the error.
Statement returnStatement = new ReturnStatement(inferrer.helper
.wrapInProblem(
new NullLiteral()..fileOffset = fileOffset,
templateImplicitReturnNull.withArguments(
returnType, inferrer.library.isNonNullableByDefault),
fileOffset,
noLength))
..fileOffset = fileOffset;
if (resultStatement is Block) {
resultStatement.statements.add(returnStatement);
} else {
resultStatement =
new Block(<Statement>[resultStatement, returnStatement])
..fileOffset = fileOffset;
}
return new StatementInferenceResult.single(resultStatement);
}
return inferenceResult;
}
}
/// Keeps track of information about the innermost function or closure being
/// inferred.
class _SyncStarClosureContext implements ClosureContext {
bool get isAsync => false;
/// The typing expectation for the subexpression of a `return` or `yield`
/// statement inside the function.
///
/// For non-generator async functions, this will be a "FutureOr" type (since
/// it is permissible for such a function to return either a direct value or
/// a future).
///
/// For generator functions containing a `yield*` statement, the expected type
/// for the subexpression of the `yield*` statement is the result of wrapping
/// this typing expectation in `Stream` or `Iterator`, as appropriate.
final DartType _yieldElementContext;
@override
DartType get returnContext => const UnknownType();
@override
DartType get yieldContext => _yieldElementContext;
final DartType _declaredReturnType;
final bool _needToInferReturnType;
/// A list of return expression types in functions whose return type is
/// being inferred.
List<DartType> _yieldElementTypes;
_SyncStarClosureContext(this._yieldElementContext, this._declaredReturnType,
this._needToInferReturnType) {
if (_needToInferReturnType) {
_yieldElementTypes = [];
}
}
/// Updates the inferred return type based on the presence of a return
/// statement returning the given [type].
@override
void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
DartType type, bool isArrow) {}
@override
void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
ExpressionInferenceResult expressionResult) {
DartType expectedType = node.isYieldStar
? inferrer.wrapType(_yieldElementContext,
inferrer.coreTypes.iterableClass, inferrer.library.nonNullable)
: _yieldElementContext;
Expression expression = inferrer.ensureAssignableResult(
expectedType, expressionResult,
fileOffset: node.fileOffset);
node.expression = expression..parent = node;
DartType type = expressionResult.inferredType;
if (!identical(expressionResult.expression, expression)) {
type = inferrer.computeGreatestClosure(expectedType);
}
if (_needToInferReturnType) {
DartType elementType = type;
if (node.isYieldStar) {
elementType = inferrer.getDerivedTypeArgumentOf(
type, inferrer.coreTypes.iterableClass) ??
elementType;
}
_yieldElementTypes.add(elementType);
}
}
@override
DartType inferReturnType(TypeInferrerImpl inferrer,
{bool hasImplicitReturn}) {
assert(_needToInferReturnType);
assert(hasImplicitReturn != null);
DartType inferredElementType;
if (_yieldElementTypes.isNotEmpty) {
// Use the types seen from the explicit return statements.
for (int i = 0; i < _yieldElementTypes.length; i++) {
DartType type = _yieldElementTypes[i];
if (inferredElementType == null) {
inferredElementType = type;
} else {
inferredElementType = inferrer.typeSchemaEnvironment
.getStandardUpperBound(
inferredElementType, type, inferrer.library.library);
}
}
} else if (hasImplicitReturn) {
// No explicit returns we have an implicit `return null`.
inferredElementType = inferrer.typeSchemaEnvironment.nullType;
} else {
// No explicit return and the function doesn't complete normally; that is,
// it throws.
if (inferrer.isNonNullableByDefault) {
inferredElementType = new NeverType(inferrer.library.nonNullable);
} else {
inferredElementType = inferrer.typeSchemaEnvironment.nullType;
}
}
DartType inferredType = inferrer.wrapType(inferredElementType,
inferrer.coreTypes.iterableClass, inferrer.library.nonNullable);
if (!inferrer.typeSchemaEnvironment.isSubtypeOf(inferredType,
_yieldElementContext, SubtypeCheckMode.withNullabilities)) {
// If the inferred return type isn't a subtype of the context, we use the
// context.
inferredType = inferrer.computeGreatestClosure2(_declaredReturnType);
}
return demoteTypeInLibrary(inferredType, inferrer.library.library);
}
@override
StatementInferenceResult handleImplicitReturn(
TypeInferrerImpl inferrer,
Statement body,
StatementInferenceResult inferenceResult,
int fileOffset) {
// There is no implicit return.
return inferenceResult;
}
}
/// Keeps track of information about the innermost function or closure being
/// inferred.
class _AsyncStarClosureContext implements ClosureContext {
bool get isAsync => true;
/// The typing expectation for the subexpression of a `return` or `yield`
/// statement inside the function.
///
/// For non-generator async functions, this will be a "FutureOr" type (since
/// it is permissible for such a function to return either a direct value or
/// a future).
///
/// For generator functions containing a `yield*` statement, the expected type
/// for the subexpression of the `yield*` statement is the result of wrapping
/// this typing expectation in `Stream` or `Iterator`, as appropriate.
final DartType _yieldElementContext;
@override
DartType get returnContext => const UnknownType();
@override
DartType get yieldContext => _yieldElementContext;
final DartType _declaredReturnType;
final bool _needToInferReturnType;
/// A list of return expression types in functions whose return type is
/// being inferred.
List<DartType> _yieldElementTypes;
_AsyncStarClosureContext(this._yieldElementContext, this._declaredReturnType,
this._needToInferReturnType) {
if (_needToInferReturnType) {
_yieldElementTypes = [];
}
}
/// Updates the inferred return type based on the presence of a return
/// statement returning the given [type].
@override
void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
DartType type, bool isArrow) {}
@override
void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
ExpressionInferenceResult expressionResult) {
DartType expectedType = node.isYieldStar
? inferrer.wrapType(_yieldElementContext,
inferrer.coreTypes.streamClass, inferrer.library.nonNullable)
: _yieldElementContext;
Expression expression = inferrer.ensureAssignableResult(
expectedType, expressionResult,
fileOffset: node.fileOffset);
node.expression = expression..parent = node;
DartType type = expressionResult.inferredType;
if (!identical(expressionResult.expression, expression)) {
type = inferrer.computeGreatestClosure(expectedType);
}
if (_needToInferReturnType) {
DartType elementType = type;
if (node.isYieldStar) {
elementType = inferrer.getDerivedTypeArgumentOf(
type, inferrer.coreTypes.streamClass) ??
type;
}
_yieldElementTypes.add(elementType);
}
}
@override
DartType inferReturnType(TypeInferrerImpl inferrer,
{bool hasImplicitReturn}) {
assert(_needToInferReturnType);
assert(hasImplicitReturn != null);
DartType inferredElementType;
if (_yieldElementTypes.isNotEmpty) {
// Use the types seen from the explicit return statements.
for (DartType elementType in _yieldElementTypes) {
if (inferredElementType == null) {
inferredElementType = elementType;
} else {
inferredElementType = inferrer.typeSchemaEnvironment
.getStandardUpperBound(
inferredElementType, elementType, inferrer.library.library);
}
}
} else if (hasImplicitReturn) {
// No explicit returns we have an implicit `return null`.
inferredElementType = inferrer.typeSchemaEnvironment.nullType;
} else {
// No explicit return and the function doesn't complete normally; that is,
// it throws.
if (inferrer.isNonNullableByDefault) {
inferredElementType = new NeverType(inferrer.library.nonNullable);
} else {
inferredElementType = inferrer.typeSchemaEnvironment.nullType;
}
}
DartType inferredType = inferrer.wrapType(inferredElementType,
inferrer.coreTypes.streamClass, inferrer.library.nonNullable);
if (!inferrer.typeSchemaEnvironment.isSubtypeOf(inferredType,
_yieldElementContext, SubtypeCheckMode.withNullabilities)) {
// If the inferred return type isn't a subtype of the context, we use the
// context.
inferredType = inferrer.computeGreatestClosure2(_declaredReturnType);
}
return demoteTypeInLibrary(inferredType, inferrer.library.library);
}
@override
StatementInferenceResult handleImplicitReturn(
TypeInferrerImpl inferrer,
Statement body,
StatementInferenceResult inferenceResult,
int fileOffset) {
// There is no implicit return.
return inferenceResult;
}
}

View file

@ -78,6 +78,8 @@ import 'type_schema_environment.dart'
TypeVariableEliminator,
TypeSchemaEnvironment;
part 'closure_context.dart';
/// Given a [FunctionNode], gets the named parameter identified by [name], or
/// `null` if there is no parameter with the given name.
VariableDeclaration getNamedFormal(FunctionNode function, String name) {
@ -104,361 +106,6 @@ bool isOverloadableArithmeticOperator(String name) {
identical(name, '%');
}
/// Keeps track of information about the innermost function or closure being
/// inferred.
class ClosureContext {
final bool isAsync;
final bool isGenerator;
/// The typing expectation for the subexpression of a `return` or `yield`
/// statement inside the function.
///
/// For non-generator async functions, this will be a "FutureOr" type (since
/// it is permissible for such a function to return either a direct value or
/// a future).
///
/// For generator functions containing a `yield*` statement, the expected type
/// for the subexpression of the `yield*` statement is the result of wrapping
/// this typing expectation in `Stream` or `Iterator`, as appropriate.
final DartType returnOrYieldContext;
final DartType declaredReturnType;
final bool _needToInferReturnType;
DartType _inferredReturnType;
/// The type that actually appeared as the subexpression of `return` or
/// `yield` statements inside the function.
///
/// For non-generator async functions, this is the "unwrapped" type (e.g. if
/// the function is expected to return `Future<int>`, this is `int`).
///
/// For generator functions containing a `yield*` statement, the type that
/// appeared as the subexpression of the `yield*` statement was the result of
/// wrapping this type in `Stream` or `Iterator`, as appropriate.
DartType _inferredUnwrappedReturnOrYieldType;
/// Whether the function is an arrow function.
bool isArrow;
/// A list of return statements in functions whose return type is being
/// inferred.
///
/// The returns are checked for validity after the return type is inferred.
List<ReturnStatement> returnStatements;
/// A list of return expression types in functions whose return type is
/// being inferred.
List<DartType> returnExpressionTypes;
factory ClosureContext(TypeInferrerImpl inferrer, AsyncMarker asyncMarker,
DartType returnContext, bool needToInferReturnType) {
assert(returnContext != null);
DartType declaredReturnType =
inferrer.computeGreatestClosure(returnContext);
bool isAsync = asyncMarker == AsyncMarker.Async ||
asyncMarker == AsyncMarker.AsyncStar;
bool isGenerator = asyncMarker == AsyncMarker.SyncStar ||
asyncMarker == AsyncMarker.AsyncStar;
if (isGenerator) {
if (isAsync) {
returnContext = inferrer.getTypeArgumentOf(
returnContext, inferrer.coreTypes.streamClass);
} else {
returnContext = inferrer.getTypeArgumentOf(
returnContext, inferrer.coreTypes.iterableClass);
}
} else if (isAsync) {
returnContext = inferrer.wrapFutureOrType(
inferrer.typeSchemaEnvironment.unfutureType(returnContext));
}
return new ClosureContext._(isAsync, isGenerator, returnContext,
declaredReturnType, needToInferReturnType);
}
ClosureContext._(this.isAsync, this.isGenerator, this.returnOrYieldContext,
this.declaredReturnType, this._needToInferReturnType) {
if (_needToInferReturnType) {
returnStatements = [];
returnExpressionTypes = [];
}
}
bool checkValidReturn(TypeInferrerImpl inferrer, DartType returnType,
ReturnStatement statement, DartType expressionType) {
// The rules for valid returns for functions with return type T and possibly
// a return expression with static type S.
DartType flattenedReturnType = isAsync
? inferrer.typeSchemaEnvironment.unfutureType(returnType)
: returnType;
if (statement.expression == null) {
// Sync: return; is a valid return if T is void, dynamic, or Null.
// Async: return; is a valid return if flatten(T) is void, dynamic, or
// Null.
if (flattenedReturnType is VoidType ||
flattenedReturnType is DynamicType ||
flattenedReturnType == inferrer.coreTypes.nullType) {
return true;
}
statement.expression = inferrer.helper.wrapInProblem(
new NullLiteral()..fileOffset = statement.fileOffset,
messageReturnWithoutExpression,
statement.fileOffset,
noLength)
..parent = statement;
return false;
}
// Arrow functions are valid if:
// Sync: T is void or return exp; is a valid for a block-bodied function.
// Async: flatten(T) is void or return exp; is valid for a block-bodied
// function.
if (isArrow && flattenedReturnType is VoidType) return true;
// Sync: invalid if T is void and S is not void, dynamic, or Null
// Async: invalid if T is void and flatten(S) is not void, dynamic, or Null.
DartType flattenedExpressionType = isAsync
? inferrer.typeSchemaEnvironment.unfutureType(expressionType)
: expressionType;
if (returnType is VoidType &&
flattenedExpressionType is! VoidType &&
flattenedExpressionType is! DynamicType &&
flattenedExpressionType != inferrer.coreTypes.nullType) {
statement.expression = inferrer.helper.wrapInProblem(
statement.expression,
messageReturnFromVoidFunction,
statement.expression.fileOffset,
noLength)
..parent = statement;
return false;
}
// Sync: invalid if S is void and T is not void, dynamic, or Null.
// Async: invalid if flatten(S) is void and flatten(T) is not void, dynamic,
// or Null.
if (flattenedExpressionType is VoidType &&
flattenedReturnType is! VoidType &&
flattenedReturnType is! DynamicType &&
flattenedReturnType != inferrer.coreTypes.nullType) {
statement.expression = inferrer.helper.wrapInProblem(statement.expression,
messageVoidExpression, statement.expression.fileOffset, noLength)
..parent = statement;
return false;
}
// The caller will check that the return expression is assignable to the
// return type.
return true;
}
void _updateInferredUnwrappedReturnOrYieldType(
TypeInferrerImpl inferrer, DartType unwrappedType) {
if (_inferredUnwrappedReturnOrYieldType == null) {
_inferredUnwrappedReturnOrYieldType = unwrappedType;
} else {
_inferredUnwrappedReturnOrYieldType = inferrer.typeSchemaEnvironment
.getStandardUpperBound(_inferredUnwrappedReturnOrYieldType,
unwrappedType, inferrer.library.library);
}
}
/// Updates the inferred return type based on the presence of a return
/// statement returning the given [type].
void handleReturn(TypeInferrerImpl inferrer, ReturnStatement statement,
DartType type, bool isArrow) {
if (isGenerator) return;
// The first return we see tells us if we have an arrow function.
if (this.isArrow == null) {
this.isArrow = isArrow;
} else {
assert(this.isArrow == isArrow);
}
if (_needToInferReturnType) {
// Add the return to a list to be checked for validity after we've
// inferred the return type.
returnStatements.add(statement);
returnExpressionTypes.add(type);
// The return expression has to be assignable to the return type
// expectation from the downwards inference context.
if (statement.expression != null) {
Expression expression = inferrer.ensureAssignable(
returnOrYieldContext, type, statement.expression,
fileOffset: statement.fileOffset,
isReturnFromAsync: isAsync,
isVoidAllowed: true);
if (!identical(statement.expression, expression)) {
statement.expression = expression..parent = statement;
// Not assignable, use the expectation.
type = inferrer.computeGreatestClosure(returnOrYieldContext);
}
}
DartType unwrappedType = type;
if (isAsync) {
unwrappedType = inferrer.typeSchemaEnvironment.unfutureType(type);
}
_updateInferredUnwrappedReturnOrYieldType(inferrer, unwrappedType);
} else if (checkValidReturn(
inferrer, declaredReturnType, statement, type) &&
statement.expression != null) {
// If we are not inferring a type we can immediately check that the return
// is valid.
DartType wrappedType = type;
if (isAsync) {
wrappedType = inferrer.typeSchemaEnvironment.futureType(
inferrer.typeSchemaEnvironment.unfutureType(type),
Nullability.nonNullable);
}
Expression expression = inferrer.ensureAssignable(
returnOrYieldContext, wrappedType, statement.expression,
fileOffset: statement.fileOffset,
isReturnFromAsync: isAsync,
isVoidAllowed: true);
statement.expression = expression..parent = statement;
}
}
void handleYield(TypeInferrerImpl inferrer, YieldStatement node,
ExpressionInferenceResult expressionResult) {
if (!isGenerator) {
node.expression = expressionResult.expression..parent = node;
return;
}
DartType expectedType = node.isYieldStar
? _wrapAsyncOrGenerator(
inferrer, returnOrYieldContext, inferrer.library.nonNullable)
: returnOrYieldContext;
Expression expression = inferrer.ensureAssignableResult(
expectedType, expressionResult,
fileOffset: node.fileOffset, isReturnFromAsync: isAsync);
node.expression = expression..parent = node;
DartType type = expressionResult.inferredType;
if (!identical(expressionResult.expression, expression)) {
type = inferrer.computeGreatestClosure(expectedType);
}
if (_needToInferReturnType) {
DartType unwrappedType = type;
if (node.isYieldStar) {
unwrappedType = inferrer.getDerivedTypeArgumentOf(
type,
isAsync
? inferrer.coreTypes.streamClass
: inferrer.coreTypes.iterableClass) ??
type;
}
_updateInferredUnwrappedReturnOrYieldType(inferrer, unwrappedType);
}
}
DartType inferReturnType(TypeInferrerImpl inferrer,
{bool hasImplicitReturn}) {
assert(_needToInferReturnType);
assert(hasImplicitReturn != null);
DartType inferredType;
if (_inferredUnwrappedReturnOrYieldType != null) {
// Use the types seen from the explicit return statements.
inferredType = _inferredUnwrappedReturnOrYieldType;
} else if (hasImplicitReturn) {
// No explicit returns we have an implicit `return null`.
inferredType = inferrer.typeSchemaEnvironment.nullType;
} else {
// No explicit return and the function doesn't complete normally; that is,
// it throws.
if (inferrer.isNonNullableByDefault) {
inferredType = new NeverType(inferrer.library.nonNullable);
} else {
inferredType = inferrer.typeSchemaEnvironment.nullType;
}
}
inferredType = _wrapAsyncOrGenerator(
inferrer, inferredType, inferrer.library.nonNullable);
if (!inferrer.typeSchemaEnvironment.isSubtypeOf(inferredType,
returnOrYieldContext, 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) {
checkValidReturn(inferrer, inferredType, returnStatements[i],
returnExpressionTypes[i]);
}
return _inferredReturnType =
demoteTypeInLibrary(inferredType, inferrer.library.library);
}
StatementInferenceResult handleImplicitReturn(
TypeInferrerImpl inferrer,
Statement body,
StatementInferenceResult inferenceResult,
int fileOffset) {
if (isGenerator) {
// There is no implicit return.
return inferenceResult;
}
DartType returnType;
if (_needToInferReturnType) {
assert(_inferredReturnType != null,
"Return type has not yet been inferred.");
returnType = _inferredReturnType;
} else {
returnType = declaredReturnType;
}
if (isAsync) {
returnType = inferrer.typeSchemaEnvironment.unfutureType(returnType);
}
if (inferrer.library.isNonNullableByDefault &&
(containsInvalidType(returnType) ||
returnType.isPotentiallyNonNullable) &&
inferrer.flowAnalysis.isReachable) {
Statement resultStatement =
inferenceResult.hasChanged ? inferenceResult.statement : body;
// Create a synthetic return statement with the error.
Statement returnStatement = new ReturnStatement(inferrer.helper
.wrapInProblem(
new NullLiteral()..fileOffset = fileOffset,
templateImplicitReturnNull.withArguments(
returnType, inferrer.library.isNonNullableByDefault),
fileOffset,
noLength))
..fileOffset = fileOffset;
if (resultStatement is Block) {
resultStatement.statements.add(returnStatement);
} else {
resultStatement =
new Block(<Statement>[resultStatement, returnStatement])
..fileOffset = fileOffset;
}
return new StatementInferenceResult.single(resultStatement);
}
return inferenceResult;
}
DartType _wrapAsyncOrGenerator(
TypeInferrerImpl inferrer, DartType type, Nullability nullability) {
if (isGenerator) {
if (isAsync) {
return inferrer.wrapType(
type, inferrer.coreTypes.streamClass, nullability);
} else {
return inferrer.wrapType(
type, inferrer.coreTypes.iterableClass, nullability);
}
} else if (isAsync) {
return inferrer.wrapFutureType(type, nullability);
} else {
return type;
}
}
}
/// Enum denoting the kinds of contravariance check that might need to be
/// inserted for a method call.
enum MethodContravarianceCheckKind {
@ -731,13 +378,11 @@ class TypeInferrerImpl implements TypeInferrer {
DartType expectedType, ExpressionInferenceResult result,
{int fileOffset,
bool isVoidAllowed: false,
bool isReturnFromAsync: false,
Template<Message Function(DartType, DartType, bool)> errorTemplate}) {
return ensureAssignable(
expectedType, result.inferredType, result.expression,
fileOffset: fileOffset,
isVoidAllowed: isVoidAllowed,
isReturnFromAsync: isReturnFromAsync,
errorTemplate: errorTemplate);
}
@ -746,10 +391,16 @@ class TypeInferrerImpl implements TypeInferrer {
/// Checks whether [expressionType] can be assigned to the greatest closure of
/// [contextType], and inserts an implicit downcast, inserts a tear-off, or
/// reports an error if appropriate.
///
/// If [runtimeCheckedType] is provided, this is used for the implicit cast,
/// otherwise [contextType] is used. This is used for return from async
/// where the returned expression is wrapped in a `Future`, if necessary,
/// before returned and therefore shouldn't be checked to be a `Future`
/// directly.
Expression ensureAssignable(
DartType contextType, DartType expressionType, Expression expression,
{int fileOffset,
bool isReturnFromAsync: false,
DartType runtimeCheckedType,
bool isVoidAllowed: false,
Template<Message Function(DartType, DartType, bool)> errorTemplate}) {
assert(contextType != null);
@ -762,22 +413,7 @@ class TypeInferrerImpl implements TypeInferrer {
fileOffset ??= expression.fileOffset;
contextType = computeGreatestClosure(contextType);
DartType initialContextType = contextType;
if (isReturnFromAsync &&
!isAssignable(initialContextType, expressionType)) {
// If the body of the function is async, the expected return type has the
// shape FutureOr<T>. We check both branches for FutureOr here: both T
// and Future<T>.
DartType unfuturedExpectedType =
typeSchemaEnvironment.unfutureType(contextType);
DartType futuredExpectedType =
wrapFutureType(unfuturedExpectedType, library.nonNullable);
if (isAssignable(unfuturedExpectedType, expressionType)) {
contextType = unfuturedExpectedType;
} else if (isAssignable(futuredExpectedType, expressionType)) {
contextType = futuredExpectedType;
}
}
DartType initialContextType = runtimeCheckedType ?? contextType;
Template<Message Function(DartType, DartType, bool)>
preciseTypeErrorTemplate = _getPreciseTypeErrorTemplate(expression);

View file

@ -0,0 +1,51 @@
// 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 'dart:async';
class Class {}
dynamic returnDynamic() => new Class();
Class returnClass() async => new Class();
Future<Class> returnFutureClass() async => new Class();
FutureOr<Class> returnFutureOrClass() async => new Class();
Class returnClassFromDynamic() async => returnDynamic();
Future<Class> returnFutureClassDynamic() async => returnDynamic();
FutureOr<Class> returnFutureOrClassDynamic() async => returnDynamic();
Class returnClassFromFutureClass() async => returnFutureClass();
Future<Class> returnFutureClassFromFutureClass() async => returnFutureClass();
FutureOr<Class> returnFutureOrClassFromFutureClass() async =>
returnFutureClass();
Class returnClassFromFutureOrClass() async => returnFutureOrClass();
Future<Class> returnFutureClassFromFutureOrClass() async =>
returnFutureOrClass();
FutureOr<Class> returnFutureOrClassFromFutureOrClass() async =>
returnFutureOrClass();
main() async {
await returnClass();
await returnFutureClass();
await returnFutureOrClass();
await returnClassFromDynamic();
await returnFutureClassDynamic();
await returnFutureOrClassDynamic();
await returnClassFromFutureClass();
await returnFutureClassFromFutureClass();
await returnFutureOrClassFromFutureClass();
await returnClassFromFutureOrClass();
await returnFutureClassFromFutureOrClass();
await returnFutureOrClassFromFutureOrClass();
}

View file

@ -0,0 +1,49 @@
library;
import self as self;
import "dart:core" as core;
import "dart:async" as asy;
import "dart:async";
class Class extends core::Object {
synthetic constructor •() → self::Class*
;
abstract member-signature get _identityHashCode() → core::int*;
abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
abstract member-signature operator ==(dynamic other) → core::bool*;
abstract member-signature get hashCode() → core::int*;
abstract member-signature method toString() → core::String*;
abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
abstract member-signature get runtimeType() → core::Type*;
}
static method returnDynamic() → dynamic
;
static method returnClass() → self::Class*
;
static method returnFutureClass() → asy::Future<self::Class*>*
;
static method returnFutureOrClass() → FutureOr<self::Class*>*
;
static method returnClassFromDynamic() → self::Class*
;
static method returnFutureClassDynamic() → asy::Future<self::Class*>*
;
static method returnFutureOrClassDynamic() → FutureOr<self::Class*>*
;
static method returnClassFromFutureClass() → self::Class*
;
static method returnFutureClassFromFutureClass() → asy::Future<self::Class*>*
;
static method returnFutureOrClassFromFutureClass() → FutureOr<self::Class*>*
;
static method returnClassFromFutureOrClass() → self::Class*
;
static method returnFutureClassFromFutureOrClass() → asy::Future<self::Class*>*
;
static method returnFutureOrClassFromFutureOrClass() → FutureOr<self::Class*>*
;
static method main() → dynamic
;

View file

@ -0,0 +1,81 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/general/future_return.dart:11:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Class returnClass() async => new Class();
// ^^^^^^^^^^^
//
// pkg/front_end/testcases/general/future_return.dart:17:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Class returnClassFromDynamic() async => returnDynamic();
// ^^^^^^^^^^^^^^^^^^^^^^
//
// pkg/front_end/testcases/general/future_return.dart:23:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Class returnClassFromFutureClass() async => returnFutureClass();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// pkg/front_end/testcases/general/future_return.dart:30:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Class returnClassFromFutureOrClass() async => returnFutureOrClass();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
import self as self;
import "dart:core" as core;
import "dart:async" as asy;
import "dart:async";
class Class extends core::Object {
synthetic constructor •() → self::Class*
: super core::Object::•()
;
abstract member-signature get _identityHashCode() → core::int*;
abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
abstract member-signature operator ==(dynamic other) → core::bool*;
abstract member-signature get hashCode() → core::int*;
abstract member-signature method toString() → core::String*;
abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
abstract member-signature get runtimeType() → core::Type*;
}
static method returnDynamic() → dynamic
return new self::Class::•();
static method returnClass() → self::Class* async
return new self::Class::•();
static method returnFutureClass() → asy::Future<self::Class*>* async
return new self::Class::•();
static method returnFutureOrClass() → FutureOr<self::Class*>* async
return new self::Class::•();
static method returnClassFromDynamic() → self::Class* async
return self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
static method returnFutureClassDynamic() → asy::Future<self::Class*>* async
return self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
static method returnFutureOrClassDynamic() → FutureOr<self::Class*>* async
return self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
static method returnClassFromFutureClass() → self::Class* async
return self::returnFutureClass();
static method returnFutureClassFromFutureClass() → asy::Future<self::Class*>* async
return self::returnFutureClass();
static method returnFutureOrClassFromFutureClass() → FutureOr<self::Class*>* async
return self::returnFutureClass();
static method returnClassFromFutureOrClass() → self::Class* async
return self::returnFutureOrClass();
static method returnFutureClassFromFutureOrClass() → asy::Future<self::Class*>* async
return self::returnFutureOrClass();
static method returnFutureOrClassFromFutureOrClass() → FutureOr<self::Class*>* async
return self::returnFutureOrClass();
static method main() → dynamic async {
await self::returnClass();
await self::returnFutureClass();
await self::returnFutureOrClass();
await self::returnClassFromDynamic();
await self::returnFutureClassDynamic();
await self::returnFutureOrClassDynamic();
await self::returnClassFromFutureClass();
await self::returnFutureClassFromFutureClass();
await self::returnFutureOrClassFromFutureClass();
await self::returnClassFromFutureOrClass();
await self::returnFutureClassFromFutureOrClass();
await self::returnFutureOrClassFromFutureOrClass();
}

View file

@ -0,0 +1,418 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/general/future_return.dart:11:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Class returnClass() async => new Class();
// ^^^^^^^^^^^
//
// pkg/front_end/testcases/general/future_return.dart:17:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Class returnClassFromDynamic() async => returnDynamic();
// ^^^^^^^^^^^^^^^^^^^^^^
//
// pkg/front_end/testcases/general/future_return.dart:23:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Class returnClassFromFutureClass() async => returnFutureClass();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
//
// pkg/front_end/testcases/general/future_return.dart:30:7: Error: Functions marked 'async' must have a return type assignable to 'Future'.
// Class returnClassFromFutureOrClass() async => returnFutureOrClass();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//
import self as self;
import "dart:core" as core;
import "dart:async" as asy;
import "dart:_internal" as _in;
import "dart:async";
class Class extends core::Object {
synthetic constructor •() → self::Class*
: super core::Object::•()
;
abstract member-signature get _identityHashCode() → core::int*;
abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
abstract member-signature operator ==(dynamic other) → core::bool*;
abstract member-signature get hashCode() → core::int*;
abstract member-signature method toString() → core::String*;
abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
abstract member-signature get runtimeType() → core::Type*;
}
static method returnDynamic() → dynamic
return new self::Class::•();
static method returnClass() → self::Class* /* originally async */ {
final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
FutureOr<dynamic>* :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 {
#L1:
{
:return_value = new self::Class::•();
break #L1;
}
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 returnFutureClass() → asy::Future<self::Class*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
FutureOr<self::Class*>* :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 {
#L2:
{
:return_value = new self::Class::•();
break #L2;
}
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 returnFutureOrClass() → FutureOr<self::Class*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
FutureOr<self::Class*>* :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 {
#L3:
{
:return_value = new self::Class::•();
break #L3;
}
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 returnClassFromDynamic() → self::Class* /* originally async */ {
final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
FutureOr<dynamic>* :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:
{
:return_value = self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
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 returnFutureClassDynamic() → asy::Future<self::Class*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
FutureOr<self::Class*>* :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 {
#L5:
{
:return_value = self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
break #L5;
}
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 returnFutureOrClassDynamic() → FutureOr<self::Class*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
FutureOr<self::Class*>* :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 {
#L6:
{
:return_value = self::returnDynamic() as{TypeError} FutureOr<self::Class*>*;
break #L6;
}
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 returnClassFromFutureClass() → self::Class* /* originally async */ {
final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
FutureOr<dynamic>* :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 {
#L7:
{
:return_value = self::returnFutureClass();
break #L7;
}
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 returnFutureClassFromFutureClass() → asy::Future<self::Class*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
FutureOr<self::Class*>* :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 {
#L8:
{
:return_value = self::returnFutureClass();
break #L8;
}
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 returnFutureOrClassFromFutureClass() → FutureOr<self::Class*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
FutureOr<self::Class*>* :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 {
#L9:
{
:return_value = self::returnFutureClass();
break #L9;
}
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 returnClassFromFutureOrClass() → self::Class* /* originally async */ {
final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
FutureOr<dynamic>* :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 {
#L10:
{
:return_value = self::returnFutureOrClass();
break #L10;
}
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 returnFutureClassFromFutureOrClass() → asy::Future<self::Class*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
FutureOr<self::Class*>* :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 {
#L11:
{
:return_value = self::returnFutureOrClass();
break #L11;
}
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 returnFutureOrClassFromFutureOrClass() → FutureOr<self::Class*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
FutureOr<self::Class*>* :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 {
#L12:
{
:return_value = self::returnFutureOrClass();
break #L12;
}
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 /* originally async */ {
final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
FutureOr<dynamic>* :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 {
#L13:
{
[yield] let dynamic #t1 = asy::_awaitHelper(self::returnClass(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t2 = asy::_awaitHelper(self::returnFutureClass(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t3 = asy::_awaitHelper(self::returnFutureOrClass(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t4 = asy::_awaitHelper(self::returnClassFromDynamic(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t5 = asy::_awaitHelper(self::returnFutureClassDynamic(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t6 = asy::_awaitHelper(self::returnFutureOrClassDynamic(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t7 = asy::_awaitHelper(self::returnClassFromFutureClass(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t8 = asy::_awaitHelper(self::returnFutureClassFromFutureClass(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t9 = asy::_awaitHelper(self::returnFutureOrClassFromFutureClass(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t10 = asy::_awaitHelper(self::returnClassFromFutureOrClass(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t11 = asy::_awaitHelper(self::returnFutureClassFromFutureOrClass(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(:result);
[yield] let dynamic #t12 = asy::_awaitHelper(self::returnFutureOrClassFromFutureOrClass(), :async_op_then, :async_op_error, :async_op) in null;
_in::unsafeCast<self::Class*>(: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};
}

View file

@ -0,0 +1,21 @@
import 'dart:async';
class Class {}
dynamic returnDynamic() => new Class();
Class returnClass() async => new Class();
Future<Class> returnFutureClass() async => new Class();
FutureOr<Class> returnFutureOrClass() async => new Class();
Class returnClassFromDynamic() async => returnDynamic();
Future<Class> returnFutureClassDynamic() async => returnDynamic();
FutureOr<Class> returnFutureOrClassDynamic() async => returnDynamic();
Class returnClassFromFutureClass() async => returnFutureClass();
Future<Class> returnFutureClassFromFutureClass() async => returnFutureClass();
FutureOr<Class> returnFutureOrClassFromFutureClass() async =>
returnFutureClass();
Class returnClassFromFutureOrClass() async => returnFutureOrClass();
Future<Class> returnFutureClassFromFutureOrClass() async =>
returnFutureOrClass();
FutureOr<Class> returnFutureOrClassFromFutureOrClass() async =>
returnFutureOrClass();
main() async {}

View file

@ -0,0 +1,22 @@
import 'dart:async';
Class returnClass() async => new Class();
Class returnClassFromDynamic() async => returnDynamic();
Class returnClassFromFutureClass() async => returnFutureClass();
Class returnClassFromFutureOrClass() async => returnFutureOrClass();
Future<Class> returnFutureClass() async => new Class();
Future<Class> returnFutureClassDynamic() async => returnDynamic();
Future<Class> returnFutureClassFromFutureClass() async => returnFutureClass();
Future<Class> returnFutureClassFromFutureOrClass() async =>
returnFutureOrClass();
FutureOr<Class> returnFutureOrClass() async => new Class();
FutureOr<Class> returnFutureOrClassDynamic() async => returnDynamic();
FutureOr<Class> returnFutureOrClassFromFutureClass() async =>
returnFutureClass();
FutureOr<Class> returnFutureOrClassFromFutureOrClass() async =>
returnFutureOrClass();
class Class {}
dynamic returnDynamic() => new Class();
main() async {}

View file

@ -0,0 +1,31 @@
// 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 'dart:async';
class Class {}
dynamic returnDynamic() => new Class();
Class returnClass() => new Class();
Future<dynamic> returnFutureDynamic() async => new Class();
Future<Class> returnFutureClass() async => new Class();
Stream<FutureOr<Class>> error() async* {
yield returnFutureDynamic();
}
Stream<FutureOr<Class>> stream() async* {
yield returnDynamic();
yield returnClass();
yield returnFutureClass();
}
main() async {
await for (FutureOr<Class> cls in stream()) {
print(cls);
}
}

View file

@ -0,0 +1,35 @@
library;
import self as self;
import "dart:core" as core;
import "dart:async" as asy;
import "dart:async";
class Class extends core::Object {
synthetic constructor •() → self::Class*
;
abstract member-signature get _identityHashCode() → core::int*;
abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
abstract member-signature operator ==(dynamic other) → core::bool*;
abstract member-signature get hashCode() → core::int*;
abstract member-signature method toString() → core::String*;
abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
abstract member-signature get runtimeType() → core::Type*;
}
static method returnDynamic() → dynamic
;
static method returnClass() → self::Class*
;
static method returnFutureDynamic() → asy::Future<dynamic>*
;
static method returnFutureClass() → asy::Future<self::Class*>*
;
static method error() → asy::Stream<FutureOr<self::Class*>*>*
;
static method stream() → asy::Stream<FutureOr<self::Class*>*>*
;
static method main() → dynamic
;

View file

@ -0,0 +1,56 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/general/stream_future.dart:18:9: Error: A value of type 'Future<dynamic>' can't be assigned to a variable of type 'FutureOr<Class>'.
// - 'Future' is from 'dart:async'.
// - 'Class' is from 'pkg/front_end/testcases/general/stream_future.dart'.
// yield returnFutureDynamic();
// ^
//
import self as self;
import "dart:core" as core;
import "dart:async" as asy;
import "dart:async";
class Class extends core::Object {
synthetic constructor •() → self::Class*
: super core::Object::•()
;
abstract member-signature get _identityHashCode() → core::int*;
abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
abstract member-signature operator ==(dynamic other) → core::bool*;
abstract member-signature get hashCode() → core::int*;
abstract member-signature method toString() → core::String*;
abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
abstract member-signature get runtimeType() → core::Type*;
}
static method returnDynamic() → dynamic
return new self::Class::•();
static method returnClass() → self::Class*
return new self::Class::•();
static method returnFutureDynamic() → asy::Future<dynamic>* async
return new self::Class::•();
static method returnFutureClass() → asy::Future<self::Class*>* async
return new self::Class::•();
static method error() → asy::Stream<FutureOr<self::Class*>*>* async* {
yield let final<BottomType> #t1 = invalid-expression "pkg/front_end/testcases/general/stream_future.dart:18:9: Error: A value of type 'Future<dynamic>' can't be assigned to a variable of type 'FutureOr<Class>'.
- 'Future' is from 'dart:async'.
- 'Class' is from 'pkg/front_end/testcases/general/stream_future.dart'.
yield returnFutureDynamic();
^" in self::returnFutureDynamic() as{TypeError} FutureOr<self::Class*>*;
}
static method stream() → asy::Stream<FutureOr<self::Class*>*>* async* {
yield self::returnDynamic() as{TypeError,ForDynamic} FutureOr<self::Class*>*;
yield self::returnClass();
yield self::returnFutureClass();
}
static method main() → dynamic async {
await for (FutureOr<self::Class*>* cls in self::stream()) {
core::print(cls);
}
}

View file

@ -0,0 +1,225 @@
library;
//
// Problems in library:
//
// pkg/front_end/testcases/general/stream_future.dart:18:9: Error: A value of type 'Future<dynamic>' can't be assigned to a variable of type 'FutureOr<Class>'.
// - 'Future' is from 'dart:async'.
// - 'Class' is from 'pkg/front_end/testcases/general/stream_future.dart'.
// yield returnFutureDynamic();
// ^
//
import self as self;
import "dart:core" as core;
import "dart:async" as asy;
import "dart:_internal" as _in;
import "dart:async";
class Class extends core::Object {
synthetic constructor •() → self::Class*
: super core::Object::•()
;
abstract member-signature get _identityHashCode() → core::int*;
abstract member-signature method _instanceOf(dynamic instantiatorTypeArguments, dynamic functionTypeArguments, dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOf(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfTrue(dynamic type) → core::bool*;
abstract member-signature method _simpleInstanceOfFalse(dynamic type) → core::bool*;
abstract member-signature operator ==(dynamic other) → core::bool*;
abstract member-signature get hashCode() → core::int*;
abstract member-signature method toString() → core::String*;
abstract member-signature method noSuchMethod(core::Invocation* invocation) → dynamic;
abstract member-signature get runtimeType() → core::Type*;
}
static method returnDynamic() → dynamic
return new self::Class::•();
static method returnClass() → self::Class*
return new self::Class::•();
static method returnFutureDynamic() → asy::Future<dynamic>* /* originally async */ {
final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
FutureOr<dynamic>* :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 {
#L1:
{
:return_value = new self::Class::•();
break #L1;
}
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 returnFutureClass() → asy::Future<self::Class*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<self::Class*>* :async_completer = new asy::_AsyncAwaitCompleter::•<self::Class*>();
FutureOr<self::Class*>* :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 {
#L2:
{
:return_value = new self::Class::•();
break #L2;
}
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 error() → asy::Stream<FutureOr<self::Class*>*>* /* originally async* */ {
asy::_AsyncStarStreamController<FutureOr<self::Class*>*>* :controller;
dynamic :controller_stream;
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;
dynamic :saved_try_context_var1;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try
try {
#L3:
{
if(:controller.{asy::_AsyncStarStreamController::add}(let final<BottomType> #t1 = invalid-expression "pkg/front_end/testcases/general/stream_future.dart:18:9: Error: A value of type 'Future<dynamic>' can't be assigned to a variable of type 'FutureOr<Class>'.
- 'Future' is from 'dart:async'.
- 'Class' is from 'pkg/front_end/testcases/general/stream_future.dart'.
yield returnFutureDynamic();
^" in self::returnFutureDynamic() as{TypeError} FutureOr<self::Class*>*))
return null;
else
[yield] null;
}
return;
}
on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
:controller.{asy::_AsyncStarStreamController::addError}(exception, stack_trace);
}
finally {
:controller.{asy::_AsyncStarStreamController::close}();
}
:async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
:async_op_then = asy::_asyncThenWrapperHelper(:async_op);
:async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
:controller = new asy::_AsyncStarStreamController::•<FutureOr<self::Class*>*>(:async_op);
:controller_stream = :controller.{asy::_AsyncStarStreamController::stream};
return :controller_stream;
}
static method stream() → asy::Stream<FutureOr<self::Class*>*>* /* originally async* */ {
asy::_AsyncStarStreamController<FutureOr<self::Class*>*>* :controller;
dynamic :controller_stream;
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;
dynamic :saved_try_context_var1;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try
try {
#L4:
{
if(:controller.{asy::_AsyncStarStreamController::add}(self::returnDynamic() as{TypeError,ForDynamic} FutureOr<self::Class*>*))
return null;
else
[yield] null;
if(:controller.{asy::_AsyncStarStreamController::add}(self::returnClass()))
return null;
else
[yield] null;
if(:controller.{asy::_AsyncStarStreamController::add}(self::returnFutureClass()))
return null;
else
[yield] null;
}
return;
}
on dynamic catch(dynamic exception, core::StackTrace* stack_trace) {
:controller.{asy::_AsyncStarStreamController::addError}(exception, stack_trace);
}
finally {
:controller.{asy::_AsyncStarStreamController::close}();
}
:async_stack_trace = asy::_asyncStackTraceHelper(:async_op);
:async_op_then = asy::_asyncThenWrapperHelper(:async_op);
:async_op_error = asy::_asyncErrorWrapperHelper(:async_op);
:controller = new asy::_AsyncStarStreamController::•<FutureOr<self::Class*>*>(:async_op);
:controller_stream = :controller.{asy::_AsyncStarStreamController::stream};
return :controller_stream;
}
static method main() → dynamic /* originally async */ {
final asy::_AsyncAwaitCompleter<dynamic>* :async_completer = new asy::_AsyncAwaitCompleter::•<dynamic>();
FutureOr<dynamic>* :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;
dynamic :saved_try_context_var1;
dynamic :exception0;
dynamic :stack_trace0;
function :async_op([dynamic :result, dynamic :exception, dynamic :stack_trace]) → dynamic yielding
try {
#L5:
{
{
asy::Stream<FutureOr<self::Class*>*>* :stream = self::stream();
asy::_asyncStarListenHelper(:stream, :async_op);
asy::_StreamIterator<FutureOr<self::Class*>*>* :for-iterator = new asy::_StreamIterator::•<FutureOr<self::Class*>*>(:stream);
try
#L6:
while (true) {
dynamic #t2 = asy::_asyncStarMoveNextHelper(:stream);
[yield] let dynamic #t3 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::moveNext}(), :async_op_then, :async_op_error, :async_op) in null;
if(_in::unsafeCast<core::bool>(:result)) {
FutureOr<self::Class*>* cls = :for-iterator.{asy::_StreamIterator::current};
{
core::print(cls);
}
}
else
break #L6;
}
finally
if(!:for-iterator.{asy::_StreamIterator::_subscription}.{core::Object::==}(null)) {
[yield] let dynamic #t4 = asy::_awaitHelper(:for-iterator.{asy::_StreamIterator::cancel}(), :async_op_then, :async_op_error, :async_op) in null;
: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};
}

View file

@ -0,0 +1,11 @@
import 'dart:async';
class Class {}
dynamic returnDynamic() => new Class();
Class returnClass() => new Class();
Future<dynamic> returnFutureDynamic() async => new Class();
Future<Class> returnFutureClass() async => new Class();
Stream<FutureOr<Class>> error() async* {}
Stream<FutureOr<Class>> stream() async* {}
main() async {}

View file

@ -0,0 +1,12 @@
import 'dart:async';
Class returnClass() => new Class();
Future<Class> returnFutureClass() async => new Class();
Future<dynamic> returnFutureDynamic() async => new Class();
Stream<FutureOr<Class>> error() async* {}
Stream<FutureOr<Class>> stream() async* {}
class Class {}
dynamic returnDynamic() => new Class();
main() async {}

View file

@ -20,7 +20,7 @@ void test() {
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new Future<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
/*@returnType=FutureOr<int*>**/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x ? 2 : new Future<int>.value(3);
});
Future<int> t5 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(

View file

@ -35,7 +35,7 @@ class MyFuture<T extends core::Object* = dynamic> extends core::Object implement
static method test() → void {
self::MyFuture<core::bool*>* f;
asy::Future<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await asy::Future::value<core::int*>(3));
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* async {
return (await x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* => (x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*);

View file

@ -71,7 +71,7 @@ static method test() → void {
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;

View file

@ -20,7 +20,7 @@ void test() {
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new MyFuture<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
/*@returnType=FutureOr<int*>**/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x
? 2
: new MyFuture<int>.value(3);

View file

@ -35,7 +35,7 @@ class MyFuture<T extends core::Object* = dynamic> extends core::Object implement
static method test() → void {
self::MyFuture<core::bool*>* f;
asy::Future<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await new self::MyFuture::value<core::int*>(3));
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* async {
return (await x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* => (x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*);

View file

@ -71,7 +71,7 @@ static method test() → void {
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;

View file

@ -20,7 +20,7 @@ void test() {
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new Future<int>.value(3));
MyFuture<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
/*@returnType=FutureOr<int*>**/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x ? 2 : new Future<int>.value(3);
});
MyFuture<int> t5 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(

View file

@ -35,7 +35,7 @@ class MyFuture<T extends core::Object* = dynamic> extends core::Object implement
static method test() → void {
self::MyFuture<core::bool*>* f;
self::MyFuture<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await asy::Future::value<core::int*>(3));
self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* async {
return (await x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
self::MyFuture<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* => (x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*);

View file

@ -71,7 +71,7 @@ static method test() → void {
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;

View file

@ -20,7 +20,7 @@ void test() {
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new MyFuture<int>.value(3));
MyFuture<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
/*@returnType=FutureOr<int*>**/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x
? 2
: new MyFuture<int>.value(3);

View file

@ -35,7 +35,7 @@ class MyFuture<T extends core::Object* = dynamic> extends core::Object implement
static method test() → void {
self::MyFuture<core::bool*>* f;
self::MyFuture<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await new self::MyFuture::value<core::int*>(3));
self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* async {
return (await x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
self::MyFuture<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* => (x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*);

View file

@ -71,7 +71,7 @@ static method test() → void {
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
self::MyFuture<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::bool* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;

View file

@ -20,7 +20,7 @@ void test() {
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new MyFuture<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=Future.then*/ then(
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
/*@returnType=FutureOr<int*>*/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x
? 2
: new MyFuture<int>.value(3);

View file

@ -35,7 +35,7 @@ class MyFuture<T extends core::Object* = dynamic> extends core::Object implement
static method test() → void {
asy::Future<core::bool*>* f;
asy::Future<core::int*>* t1 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await new self::MyFuture::value<core::int*>(3));
asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> async {
return (await x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> => (x ?{core::Object*} 2 : new self::MyFuture::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>);

View file

@ -71,7 +71,7 @@ static method test() → void {
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;

View file

@ -20,7 +20,7 @@ void test() {
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async =>
x ? 2 : await new Future<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=Future.then*/ then(
/*@ returnType=Future<int*>* */ (/*@ type=bool* */ x) async {
/*@returnType=FutureOr<int*>*/ (/*@ type=bool* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x ? 2 : new Future<int>.value(3);
});
Future<int> t5 = f. /*@ typeArgs=int* */ /*@target=Future.then*/ then(

View file

@ -35,7 +35,7 @@ class MyFuture<T extends core::Object* = dynamic> extends core::Object implement
static method test() → void {
asy::Future<core::bool*>* f;
asy::Future<core::int*>* t1 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async => x ?{core::int*} 2 : await asy::Future::value<core::int*>(3));
asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* async {
asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> async {
return (await x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> => (x ?{core::Object*} 2 : asy::Future::value<core::int*>(3)) as{TypeError} FutureOr<core::int*>);

View file

@ -71,7 +71,7 @@ static method test() → void {
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → asy::Future<core::int*>* /* originally async */ {
asy::Future<core::int*>* t2 = f.{asy::Future::then}<core::int*>((core::bool* x) → FutureOr<core::int*> /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;

View file

@ -20,7 +20,7 @@ void test() {
/*@ returnType=Future<int*>* */ (/*@ type=int* */ x) async =>
x /*@target=num.==*/ ?? await new Future<int>.value(3));
Future<int> t2 = f. /*@ typeArgs=int* */ /*@target=MyFuture.then*/ then(
/*@ returnType=Future<int*>* */ (/*@ type=int* */ x) async {
/*@returnType=FutureOr<int*>**/ (/*@ type=int* */ x) async {
return /*info:DOWN_CAST_COMPOSITE*/ await x /*@target=num.==*/ ??
new Future<int>.value(3);
});

View file

@ -35,7 +35,7 @@ class MyFuture<T extends core::Object* = dynamic> extends core::Object implement
static method test() → void {
self::MyFuture<core::int*>* f;
asy::Future<core::int*>* t1 = f.{self::MyFuture::then}<core::int*>((core::int* x) → asy::Future<core::int*>* async => let final core::int* #t1 = x in #t1.{core::num::==}(null) ?{core::int*} await asy::Future::value<core::int*>(3) : #t1);
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::int* x) → asy::Future<core::int*>* async {
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::int* x) → FutureOr<core::int*>* async {
return (let final core::int* #t2 = await x in #t2.{core::num::==}(null) ?{core::Object*} asy::Future::value<core::int*>(3) : #t2) as{TypeError} FutureOr<core::int*>*;
});
asy::Future<core::int*>* t5 = f.{self::MyFuture::then}<core::int*>((core::int* x) → FutureOr<core::int*>* => (let final core::int* #t3 = x in #t3.{core::num::==}(null) ?{core::Object*} asy::Future::value<core::int*>(3) : #t3) as{TypeError} FutureOr<core::int*>*);

View file

@ -72,7 +72,7 @@ static method test() → void {
:async_completer.{asy::_AsyncAwaitCompleter::start}(:async_op);
return :async_completer.{asy::Completer::future};
});
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::int* x) → asy::Future<core::int*>* /* originally async */ {
asy::Future<core::int*>* t2 = f.{self::MyFuture::then}<core::int*>((core::int* x) → FutureOr<core::int*>* /* originally async */ {
final asy::_AsyncAwaitCompleter<core::int*>* :async_completer = new asy::_AsyncAwaitCompleter::•<core::int*>();
FutureOr<core::int*>* :return_value;
dynamic :async_stack_trace;