Index: runtime/vm/parser.cc |
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc |
index f0fda88ecb9e24b517f8d808c4d8c08e75b57bbd..4eb855ea435b0f577f500c703b5683edc2b55dff 100644 |
--- a/runtime/vm/parser.cc |
+++ b/runtime/vm/parser.cc |
@@ -10028,6 +10028,56 @@ AstNode* Parser::ParseStatement() { |
ReportError(expr_pos, "generator functions may not return a value"); |
} |
AstNode* expr = ParseAwaitableExpr(kAllowConst, kConsumeCascades, NULL); |
+ if (I->type_checks() && |
+ current_function().IsAsyncClosure() && |
+ (current_block_->scope->function_level() == 0)) { |
+ // In checked mode, when the declared result type is Future<T>, verify |
+ // that the returned expression is of type T or Future<T> as follows: |
+ // return temp = expr, temp is Future ? temp as Future<T> : temp as T; |
+ // In case of a mismatch, we need a TypeError and not a CastError, so |
+ // we do not actually implement an "as" test, but an "assignable" test. |
+ const Function& async_func = |
+ Function::Handle(Z, current_function().parent_function()); |
+ const AbstractType& result_type = |
+ AbstractType::ZoneHandle(Z, async_func.result_type()); |
+ const Class& future_class = |
+ Class::ZoneHandle(Z, I->object_store()->future_class()); |
+ ASSERT(!future_class.IsNull()); |
+ if (result_type.type_class() == future_class.raw()) { |
+ const TypeArguments& result_type_args = |
+ TypeArguments::ZoneHandle(Z, result_type.arguments()); |
+ if (!result_type_args.IsNull() && (result_type_args.Length() == 1)) { |
+ const AbstractType& result_type_arg = |
+ AbstractType::ZoneHandle(Z, result_type_args.TypeAt(0)); |
+ LetNode* checked_expr = new(Z) LetNode(expr_pos); |
+ LocalVariable* temp = checked_expr->AddInitializer(expr); |
+ temp->set_is_final(); |
+ const AbstractType& future_type = |
+ AbstractType::ZoneHandle(Z, future_class.RareType()); |
+ AstNode* is_future = new(Z) LoadLocalNode(expr_pos, temp); |
+ is_future = new(Z) ComparisonNode(expr_pos, |
+ Token::kIS, |
+ is_future, |
+ new(Z) TypeNode(expr_pos, |
+ future_type)); |
+ AstNode* as_future_t = new(Z) LoadLocalNode(expr_pos, temp); |
+ as_future_t = new(Z) AssignableNode(expr_pos, |
+ as_future_t, |
+ result_type, |
+ Symbols::FunctionResult()); |
+ AstNode* as_t = new(Z) LoadLocalNode(expr_pos, temp); |
+ as_t = new(Z) AssignableNode(expr_pos, |
+ as_t, |
+ result_type_arg, |
+ Symbols::FunctionResult()); |
+ checked_expr->AddNode(new(Z) ConditionalExprNode(expr_pos, |
+ is_future, |
+ as_future_t, |
+ as_t)); |
+ expr = checked_expr; |
+ } |
+ } |
+ } |
statement = new(Z) ReturnNode(statement_pos, expr); |
} else { |
if (current_function().IsSyncGenClosure() && |