Experimental control-flow operator (#368)

* AST and syntax for delimited control

* stashing for later

* a little more progress

* progress on delimited continuations

* delimit, suspend, and resume implemented (draft)

* example that generates the natural numbers

* fixes

* tinkering

* changed demo to experimental

* comments and name changes

* describe delimited continuations in the README

* renamed Snapshot to Continuation, edits to comments

* Update executable_semantics/ast/statement.h

improve comment for MakeDelimitStmt

Co-authored-by: Dave Abrahams <dabrahams@google.com>

* Update executable_semantics/interpreter/interpreter.cpp

remove snake_case

Co-authored-by: Dave Abrahams <dabrahams@google.com>

* edits to comments, change name of variable

* updates to handle review edits

* trailing whitespace

* fixes to delimited continuations, added more tests, also fixed assignment to do a copy

* improvements from Geoffrey

* new test from Geoffrey, fix for empty blocks

* more suggestions from Geoffrey

* more tests for delimited continuations, renaming some of them

* renamed test files

* improve a comment

* sketch of creating continuation

* initial implementation of shift/reset style continuations

* more documentation

* fix some camel case

* implemented deep copy of continuations, added a test case for it

* fixed a bug and got the recursive test case working

* removed __delimit, polished up __continuation

* back to shallow copy for continuations

* suggestions from Geoffrey

* removed structured binding (for now)

* Update executable_semantics/ast/expression.cpp

Co-authored-by: Geoff Romer <gromer@google.com>

* responses to Geoffrey

Co-authored-by: Dave Abrahams <dabrahams@google.com>
Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
Jeremy G. Siek
2021-03-26 11:22:48 -04:00
committed by GitHub
co-authored by Dave Abrahams Geoff Romer
parent 6df0d51516
commit 3fa72d2984
35 changed files with 674 additions and 31 deletions
@@ -7,6 +7,7 @@
#include <cassert>
#include <iostream>
#include <iterator>
#include <list>
#include <map>
#include <optional>
#include <utility>
@@ -83,6 +84,9 @@ auto CopyVal(const Value* val, int line_num) -> const Value* {
return MakeFunVal(*val->u.fun.name, val->u.fun.param, val->u.fun.body);
case ValKind::PtrV:
return MakePtrVal(val->u.ptr);
case ValKind::ContinuationV:
// Copying a continuation is "shallow".
return val;
case ValKind::FunctionTV:
return MakeFunTypeVal(CopyVal(val->u.fun_type.param, line_num),
CopyVal(val->u.fun_type.ret, line_num));
@@ -99,6 +103,8 @@ auto CopyVal(const Value* val, int line_num) -> const Value* {
return MakeVarTypeVal(*val->u.var_type);
case ValKind::AutoTV:
return MakeAutoTypeVal();
case ValKind::ContinuationTV:
return MakeContinuationTypeVal();
case ValKind::TupleTV: {
auto new_fields = new VarValues();
for (auto& field : *val->u.tuple_type.fields) {
@@ -199,8 +205,10 @@ void PrintState(std::ostream& out) {
PrintStack(state->stack, out);
out << std::endl << "heap: ";
PrintHeap(state->heap, out);
out << std::endl << "env: ";
PrintEnv(CurrentEnv(state), out);
if (!state->stack.IsEmpty() && !state->stack.Top()->scopes.IsEmpty()) {
out << std::endl << "env: ";
PrintEnv(CurrentEnv(state), out);
}
out << std::endl << "}" << std::endl;
}
@@ -242,6 +250,20 @@ auto ValToPtr(const Value* v, int line_num) -> Address {
}
}
// Returns *continuation represented as a list of frames.
//
// - Precondition: continuation->tag == ValKind::ContinuationV.
auto ContinuationToVector(const Value* continuation, int sourceLocation)
-> std::vector<Frame*> {
if (continuation->tag == ValKind::ContinuationV) {
return *continuation->u.continuation.stack;
} else {
std::cerr << sourceLocation << ": runtime error: expected an integer"
<< std::endl;
exit(-1);
}
}
auto EvalPrim(Operator op, const std::vector<const Value*>& args, int line_num)
-> const Value* {
switch (op) {
@@ -278,7 +300,7 @@ void InitGlobals(std::list<Declaration>* fs) {
auto ChoiceDeclaration::InitGlobals(Env& globals) const -> void {
auto alts = new VarValues();
for (auto kv : alternatives) {
auto t = ToType(line_num, InterpExp(Env(), kv.second));
auto t = ToType(this->line_num, InterpExp(Env(), kv.second));
alts->push_back(make_pair(kv.first, t));
}
auto ct = MakeChoiceTypeVal(name, alts);
@@ -496,7 +518,7 @@ auto PatternMatch(const Value* p, const Value* v, Env env,
void PatternAssignment(const Value* pat, const Value* val, int line_num) {
switch (pat->tag) {
case ValKind::PtrV:
state->heap[ValToPtr(pat, line_num)] = val;
state->heap[ValToPtr(pat, line_num)] = CopyVal(val, line_num);
break;
case ValKind::TupleV: {
switch (val->tag) {
@@ -618,6 +640,7 @@ void StepLvalue() {
case ExpressionKind::TypeT:
case ExpressionKind::FunctionT:
case ExpressionKind::AutoT:
case ExpressionKind::ContinuationT:
case ExpressionKind::PatternVariable: {
frame->todo.Pop();
frame->todo.Push(MakeExpToLvalAct());
@@ -743,6 +766,12 @@ void StepExp() {
act->pos++;
break;
}
case ExpressionKind::ContinuationT: {
const Value* v = MakeContinuationTypeVal();
frame->todo.Pop(1);
frame->todo.Push(MakeValAct(v));
break;
}
} // switch (exp->tag)
}
@@ -826,10 +855,14 @@ void StepStmt() {
break;
case StatementKind::Block: {
if (act->pos == -1) {
auto* scope = new Scope(CurrentEnv(state), std::list<std::string>());
frame->scopes.Push(scope);
frame->todo.Push(MakeStmtAct(stmt->u.block.stmt));
act->pos++;
if (stmt->u.block.stmt) {
auto* scope = new Scope(CurrentEnv(state), {});
frame->scopes.Push(scope);
frame->todo.Push(MakeStmtAct(stmt->u.block.stmt));
act->pos++;
} else {
frame->todo.Pop();
}
} else {
Scope* scope = frame->scopes.Top();
KillScope(stmt->line_num, scope);
@@ -876,6 +909,43 @@ void StepStmt() {
}
frame->todo.Push(MakeStmtAct(stmt->u.sequence.stmt));
break;
case StatementKind::Continuation: {
// Create a continuation object by creating a frame similar the
// way one is created in a function call.
Scope* scope = new Scope(CurrentEnv(state), std::list<std::string>());
Stack<Scope*> scopes;
scopes.Push(scope);
Stack<Action*> todo;
todo.Push(
MakeStmtAct(MakeReturn(stmt->line_num, MakeUnit(stmt->line_num))));
todo.Push(MakeStmtAct(stmt->u.continuation.body));
Frame* continuation_frame = new Frame("__continuation", scopes, todo);
Address continuation_address =
AllocateValue(MakeContinuation({continuation_frame}));
// Store the continuation's address in the frame.
continuation_frame->continuation = continuation_address;
// Bind the continuation object to the continuation variable
frame->scopes.Top()->env.Set(*stmt->u.continuation.continuation_variable,
continuation_address);
// Pop the continuation statement.
frame->todo.Pop();
break;
}
case StatementKind::Run:
// Evaluate the argument of the run statement.
frame->todo.Push(MakeExpAct(stmt->u.run.argument));
act->pos++;
break;
case StatementKind::Await:
// Pause the current continuation
frame->todo.Pop();
std::vector<Frame*> paused;
do {
paused.push_back(state->stack.Pop());
} while (!paused.back()->IsContinuation());
// Update the continuation with the paused stack.
state->heap[paused.back()->continuation] = MakeContinuation(paused);
break;
}
}
@@ -1156,6 +1226,7 @@ void HandleValue() {
case ExpressionKind::BoolT:
case ExpressionKind::TypeT:
case ExpressionKind::AutoT:
case ExpressionKind::ContinuationT:
std::cerr << "internal error, bad expression context in handle_value"
<< std::endl;
exit(-1);
@@ -1311,6 +1382,25 @@ void HandleValue() {
frame->todo.Push(MakeValAct(ret_val));
break;
}
case StatementKind::Run: {
frame->todo.Pop(2);
// Push an expression statement action to ignore the result
// value from the continuation.
Action* ignore_result = MakeStmtAct(
MakeExpStmt(stmt->line_num, MakeUnit(stmt->line_num)));
ignore_result->pos = 0;
frame->todo.Push(ignore_result);
// Push the continuation onto the current stack.
std::vector<Frame*> continuation_vector =
ContinuationToVector(val_act->u.val, stmt->line_num);
for (auto frame_iter = continuation_vector.rbegin();
frame_iter != continuation_vector.rend(); ++frame_iter) {
state->stack.Push(*frame_iter);
}
break;
}
case StatementKind::Continuation:
case StatementKind::Await:
case StatementKind::Block:
case StatementKind::Sequence:
case StatementKind::Break: