Skip to content

Commit

Permalink
[vm] Adjust return value of async/async*/sync* after the language spe…
Browse files Browse the repository at this point in the history
…c change

Calculation of element type for the value returned from
async/async*/sync* functions was recently adjusted
in the spec to address soundness issue
(dart-lang/language#3218).

This change adjusts Dart VM to conform to the new spec.

TEST=language/async/regression_54311_test
TEST=co19/Language/Functions/element_type_*

Fixes #54316
Issue #54311
Issue #54159

Change-Id: I4c51e7cba704d034350519375210bdb2086c5432
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/342082
Reviewed-by: Alexander Aprelev <[email protected]>
Reviewed-by: Erik Ernst <[email protected]>
Commit-Queue: Alexander Markov <[email protected]>
  • Loading branch information
alexmarkov authored and Commit Queue committed Dec 19, 2023
1 parent e58efcb commit 6b12473
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 35 deletions.
71 changes: 37 additions & 34 deletions runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -559,33 +559,25 @@ Fragment StreamingFlowGraphBuilder::SetupCapturedParameters(
}

Fragment StreamingFlowGraphBuilder::InitSuspendableFunction(
const Function& dart_function) {
const Function& dart_function,
const AbstractType* emitted_value_type) {
Fragment body;
if (dart_function.IsAsyncFunction()) {
const auto& result_type =
AbstractType::Handle(Z, dart_function.result_type());
auto& type_args = TypeArguments::ZoneHandle(Z);
if (result_type.IsType() &&
(Class::Handle(Z, result_type.type_class()).IsFutureClass() ||
result_type.IsFutureOrType())) {
ASSERT(result_type.IsFinalized());
type_args = Type::Cast(result_type).GetInstanceTypeArguments(H.thread());
}

ASSERT(emitted_value_type != nullptr);
auto& type_args = TypeArguments::ZoneHandle(Z, TypeArguments::New(1));
type_args.SetTypeAt(0, *emitted_value_type);
type_args = Class::Handle(Z, IG->object_store()->future_class())
.GetInstanceTypeArguments(H.thread(), type_args);
body += TranslateInstantiatedTypeArguments(type_args);
body += B->Call1ArgStub(TokenPosition::kNoSource,
Call1ArgStubInstr::StubId::kInitAsync);
body += Drop();
} else if (dart_function.IsAsyncGenerator()) {
const auto& result_type =
AbstractType::Handle(Z, dart_function.result_type());
auto& type_args = TypeArguments::ZoneHandle(Z);
if (result_type.IsType() &&
(result_type.type_class() == IG->object_store()->stream_class())) {
ASSERT(result_type.IsFinalized());
type_args = Type::Cast(result_type).GetInstanceTypeArguments(H.thread());
}

ASSERT(emitted_value_type != nullptr);
auto& type_args = TypeArguments::ZoneHandle(Z, TypeArguments::New(1));
type_args.SetTypeAt(0, *emitted_value_type);
type_args = Class::Handle(Z, IG->object_store()->stream_class())
.GetInstanceTypeArguments(H.thread(), type_args);
body += TranslateInstantiatedTypeArguments(type_args);
body += B->Call1ArgStub(TokenPosition::kNoSource,
Call1ArgStubInstr::StubId::kInitAsyncStar);
Expand All @@ -595,15 +587,11 @@ Fragment StreamingFlowGraphBuilder::InitSuspendableFunction(
SuspendInstr::StubId::kYieldAsyncStar);
body += Drop();
} else if (dart_function.IsSyncGenerator()) {
const auto& result_type =
AbstractType::Handle(Z, dart_function.result_type());
auto& type_args = TypeArguments::ZoneHandle(Z);
if (result_type.IsType() &&
(result_type.type_class() == IG->object_store()->iterable_class())) {
ASSERT(result_type.IsFinalized());
type_args = Type::Cast(result_type).GetInstanceTypeArguments(H.thread());
}

ASSERT(emitted_value_type != nullptr);
auto& type_args = TypeArguments::ZoneHandle(Z, TypeArguments::New(1));
type_args.SetTypeAt(0, *emitted_value_type);
type_args = Class::Handle(Z, IG->object_store()->iterable_class())
.GetInstanceTypeArguments(H.thread(), type_args);
body += TranslateInstantiatedTypeArguments(type_args);
body += B->Call1ArgStub(TokenPosition::kNoSource,
Call1ArgStubInstr::StubId::kInitSyncStar);
Expand All @@ -618,6 +606,8 @@ Fragment StreamingFlowGraphBuilder::InitSuspendableFunction(
if (scope->num_context_variables() > 0) {
body += CloneContext(scope->context_slots());
}
} else {
ASSERT(emitted_value_type == nullptr);
}
return body;
}
Expand Down Expand Up @@ -784,17 +774,30 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(

LocalVariable* first_parameter = nullptr;
TokenPosition token_position = TokenPosition::kNoSource;
const AbstractType* emitted_value_type = nullptr;
{
AlternativeReadingScope alt(&reader_);
FunctionNodeHelper function_node_helper(this);
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kPositionalParameters);
intptr_t list_length = ReadListLength(); // read number of positionals.
if (list_length > 0) {
intptr_t first_parameter_offset = ReaderOffset() + data_program_offset_;
first_parameter = LookupVariable(first_parameter_offset);
{
AlternativeReadingScope alt2(&reader_);
intptr_t list_length = ReadListLength(); // read number of positionals.
if (list_length > 0) {
intptr_t first_parameter_offset = ReaderOffset() + data_program_offset_;
first_parameter = LookupVariable(first_parameter_offset);
}
}
token_position = function_node_helper.position_;
if (dart_function.IsSuspendableFunction()) {
function_node_helper.ReadUntilExcluding(
FunctionNodeHelper::kEmittedValueType);
if (ReadTag() == kSomething) {
emitted_value_type = &T.BuildType(); // read emitted value type.
} else {
UNREACHABLE();
}
}
}

auto graph_entry = flow_graph_builder_->graph_entry_ =
Expand Down Expand Up @@ -833,7 +836,7 @@ FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(
// objects than necessary during GC.
const Fragment body =
ClearRawParameters(dart_function) + B->BuildNullAssertions() +
InitSuspendableFunction(dart_function) +
InitSuspendableFunction(dart_function, emitted_value_type) +
BuildFunctionBody(dart_function, first_parameter, is_constructor);

auto extra_entry_point_style =
Expand Down
3 changes: 2 additions & 1 deletion runtime/vm/compiler/frontend/kernel_binary_flowgraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ class StreamingFlowGraphBuilder : public KernelReaderHelper {
TokenPosition position);
Fragment CheckStackOverflowInPrologue(const Function& dart_function);
Fragment SetupCapturedParameters(const Function& dart_function);
Fragment InitSuspendableFunction(const Function& dart_function);
Fragment InitSuspendableFunction(const Function& dart_function,
const AbstractType* emitted_value_type);
Fragment ShortcutForUserDefinedEquals(const Function& dart_function,
LocalVariable* first_parameter);
Fragment TypeArgumentsHandling(const Function& dart_function);
Expand Down
5 changes: 5 additions & 0 deletions runtime/vm/compiler/frontend/scope_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,11 @@ ScopeBuildingResult* ScopeBuilder::BuildScopes() {
// async/async*/sync* function. It may reference receiver or type
// arguments of the enclosing function which need to be captured.
VisitDartType();

// Visit optional future value type.
if (helper_.ReadTag() == kSomething) {
VisitDartType();
}
}

// We generate a synthetic body for implicit closure functions - which
Expand Down
29 changes: 29 additions & 0 deletions tests/language/async/regression_54311_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2023, 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.

// Verifies that futureValueType(FutureOr<Object>) = Object.
//
// This is a special case because NORM(FutureOr<Object>) = Object,
// and futureValueType(Object) = Object?, so futureValueType cannot be
// applied to a normalized type.
//
// Regression test for https://github.com/dart-lang/sdk/issues/54311.

import 'dart:async';

import "package:expect/expect.dart";

FutureOr<Object> fn1() async {
return Future<Object>.value(42);
}

FutureOr<Object> fn2() async => 42;

void main() async {
final value1 = await fn1();
Expect.equals(42, value1);

final value2 = await fn2();
Expect.equals(42, value2);
}

0 comments on commit 6b12473

Please sign in to comment.