mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
Make continuations consistently "shallow" (#874)
Prior to this change, `__await` would make a deep copy of the continuation stack, but shallow-copy the individual stack frames within it. As a result, continuations appeared to have shallow semantics so long as the continuation stack had only a single frame. This change also removes an obsolete test from the brief period when we intended continuations to have deep-copy semantics, which has been passing basically by accident.
This commit is contained in:
@@ -1002,11 +1002,12 @@ auto Interpreter::StepStmt() -> Transition {
|
||||
todo.Push(arena->New<StatementAction>(
|
||||
arena->New<Return>(arena, stmt->source_loc())));
|
||||
todo.Push(arena->New<StatementAction>(cast<Continuation>(*stmt).Body()));
|
||||
auto continuation_stack = arena->New<std::vector<Nonnull<Frame*>>>();
|
||||
auto continuation_frame =
|
||||
arena->New<Frame>("__continuation", scopes, todo);
|
||||
continuation_stack->push_back(continuation_frame);
|
||||
Address continuation_address =
|
||||
heap.AllocateValue(arena->New<ContinuationValue>(
|
||||
std::vector<Nonnull<Frame*>>({continuation_frame})));
|
||||
heap.AllocateValue(arena->New<ContinuationValue>(continuation_stack));
|
||||
// Store the continuation's address in the frame.
|
||||
continuation_frame->continuation = continuation_address;
|
||||
// Bind the continuation object to the continuation variable
|
||||
@@ -1031,11 +1032,11 @@ auto Interpreter::StepStmt() -> Transition {
|
||||
arena->New<TupleLiteral>(stmt->source_loc())));
|
||||
frame->todo.Push(ignore_result);
|
||||
// Push the continuation onto the current stack.
|
||||
const std::vector<Nonnull<Frame*>>& continuation_vector =
|
||||
cast<ContinuationValue>(*act->results()[0]).Stack();
|
||||
for (auto frame_iter = continuation_vector.rbegin();
|
||||
frame_iter != continuation_vector.rend(); ++frame_iter) {
|
||||
stack.Push(*frame_iter);
|
||||
std::vector<Nonnull<Frame*>>& continuation_vector =
|
||||
*cast<ContinuationValue>(*act->results()[0]).Stack();
|
||||
while (!continuation_vector.empty()) {
|
||||
stack.Push(continuation_vector.back());
|
||||
continuation_vector.pop_back();
|
||||
}
|
||||
return ManualTransition{};
|
||||
}
|
||||
@@ -1048,8 +1049,10 @@ auto Interpreter::StepStmt() -> Transition {
|
||||
paused.push_back(stack.Pop());
|
||||
} while (paused.back()->continuation == std::nullopt);
|
||||
// Update the continuation with the paused stack.
|
||||
heap.Write(*paused.back()->continuation,
|
||||
arena->New<ContinuationValue>(paused), stmt->source_loc());
|
||||
const auto& continuation = cast<ContinuationValue>(
|
||||
*heap.Read(*paused.back()->continuation, stmt->source_loc()));
|
||||
CHECK(continuation.Stack()->empty());
|
||||
*continuation.Stack() = std::move(paused);
|
||||
return ManualTransition{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,7 +292,7 @@ void Value::Print(llvm::raw_ostream& out) const {
|
||||
case Value::Kind::ContinuationValue: {
|
||||
out << "{";
|
||||
llvm::ListSeparator sep(" :: ");
|
||||
for (Nonnull<Frame*> frame : cast<ContinuationValue>(*this).Stack()) {
|
||||
for (Nonnull<Frame*> frame : *cast<ContinuationValue>(*this).Stack()) {
|
||||
out << sep << *frame;
|
||||
}
|
||||
out << "}";
|
||||
@@ -352,8 +352,8 @@ auto CopyVal(Nonnull<Arena*> arena, Nonnull<const Value*> val,
|
||||
case Value::Kind::PointerValue:
|
||||
return arena->New<PointerValue>(cast<PointerValue>(*val).Val());
|
||||
case Value::Kind::ContinuationValue:
|
||||
// Copying a continuation is "shallow".
|
||||
return val;
|
||||
return arena->New<ContinuationValue>(
|
||||
cast<ContinuationValue>(*val).Stack());
|
||||
case Value::Kind::FunctionType: {
|
||||
const auto& fn_type = cast<FunctionType>(*val);
|
||||
return arena->New<FunctionType>(
|
||||
|
||||
@@ -493,19 +493,25 @@ class VariableType : public Value {
|
||||
};
|
||||
|
||||
// A first-class continuation representation of a fragment of the stack.
|
||||
// A continuation value behaves like a pointer to the underlying stack
|
||||
// fragment, which is exposed by `Stack()`.
|
||||
class ContinuationValue : public Value {
|
||||
public:
|
||||
explicit ContinuationValue(std::vector<Nonnull<Frame*>> stack)
|
||||
: Value(Kind::ContinuationValue), stack(std::move(stack)) {}
|
||||
explicit ContinuationValue(Nonnull<std::vector<Nonnull<Frame*>>*> stack)
|
||||
: Value(Kind::ContinuationValue), stack(stack) {}
|
||||
|
||||
static auto classof(const Value* value) -> bool {
|
||||
return value->kind() == Kind::ContinuationValue;
|
||||
}
|
||||
|
||||
auto Stack() const -> const std::vector<Nonnull<Frame*>>& { return stack; }
|
||||
// The call stack of the suspended continuation, starting with the top
|
||||
// frame (the reverse of the usual order). Note that this provides mutable
|
||||
// access, even when *this is const, because of the reference-like semantics
|
||||
// of ContinuationValue.
|
||||
auto Stack() const -> Nonnull<std::vector<Nonnull<Frame*>>*> { return stack; }
|
||||
|
||||
private:
|
||||
std::vector<Nonnull<Frame*>> stack;
|
||||
Nonnull<std::vector<Nonnull<Frame*>>*> stack;
|
||||
};
|
||||
|
||||
// The String type.
|
||||
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
||||
// Exceptions. See /LICENSE for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
// RUN: executable_semantics %s 2>&1 | \
|
||||
// RUN: FileCheck --match-full-lines --allow-unused-prefixes=false %s
|
||||
// RUN: executable_semantics --trace %s 2>&1 | \
|
||||
// RUN: FileCheck --match-full-lines --allow-unused-prefixes %s
|
||||
// AUTOUPDATE: executable_semantics %s
|
||||
// CHECK: result: 3
|
||||
|
||||
package ExecutableSemanticsTest api;
|
||||
|
||||
// Test the way in which copying of continuations interacts with data
|
||||
// on the stack such as the variable `x`. In this example the copy
|
||||
// happens after the variable `x` is created.
|
||||
|
||||
var y: i32 = 0;
|
||||
|
||||
fn main() -> i32 {
|
||||
__continuation k1 {
|
||||
var x: i32 = 0;
|
||||
x = x + 1;
|
||||
__await;
|
||||
x = x + 2;
|
||||
y = x;
|
||||
}
|
||||
__run k1;
|
||||
var k2: __Continuation = k1;
|
||||
__run k2;
|
||||
return y;
|
||||
}
|
||||
+9
-4
@@ -14,12 +14,17 @@ package ExecutableSemanticsTest api;
|
||||
// Assignment for continuations is shallow, so `k2` refers to the same
|
||||
// continuation as `k1`.
|
||||
|
||||
var x: i32 = 0;
|
||||
|
||||
fn Foo() {
|
||||
x = x + 1;
|
||||
__await;
|
||||
x = x + 2;
|
||||
}
|
||||
|
||||
fn main() -> i32 {
|
||||
var x: i32 = 0;
|
||||
__continuation k1 {
|
||||
x = x + 1;
|
||||
__await;
|
||||
x = x + 2;
|
||||
Foo();
|
||||
}
|
||||
var k2: __Continuation = k1;
|
||||
__run k1;
|
||||
|
||||
Reference in New Issue
Block a user