From d71f5b1784da9fb4ad403fde3a8d1d25a0e9d624 Mon Sep 17 00:00:00 2001 From: Geoff Romer Date: Thu, 19 Aug 2021 13:35:47 -0700 Subject: [PATCH] Express stack updates using return values. (#747) This enables the interpreter logic to express its intent more directly, especially in the common cases, and enables us to get rid of ValAction. It's also a step toward simplifying and encapsulating `state->stack`. --- executable_semantics/interpreter/action.cpp | 3 - executable_semantics/interpreter/action.h | 15 - .../interpreter/interpreter.cpp | 729 +++++++++--------- .../interpreter/interpreter.h | 2 + 4 files changed, 360 insertions(+), 389 deletions(-) diff --git a/executable_semantics/interpreter/action.cpp b/executable_semantics/interpreter/action.cpp index 7f6a30b39075..16e10d743102 100644 --- a/executable_semantics/interpreter/action.cpp +++ b/executable_semantics/interpreter/action.cpp @@ -35,9 +35,6 @@ void Action::Print(llvm::raw_ostream& out) const { case Action::Kind::StatementAction: cast(*this).Stmt()->PrintDepth(1, out); break; - case Action::Kind::ValAction: - out << *cast(*this).Val(); - break; } out << "<" << pos << ">"; if (results.size() > 0) { diff --git a/executable_semantics/interpreter/action.h b/executable_semantics/interpreter/action.h index 8c539e7a273e..f8d56d0ef174 100644 --- a/executable_semantics/interpreter/action.h +++ b/executable_semantics/interpreter/action.h @@ -24,7 +24,6 @@ class Action { ExpressionAction, PatternAction, StatementAction, - ValAction, }; Action(const Value&) = delete; @@ -131,20 +130,6 @@ class StatementAction : public Action { const Statement* stmt; }; -class ValAction : public Action { - public: - explicit ValAction(const Value* val) : Action(Kind::ValAction), val(val) {} - - static auto classof(const Action* action) -> bool { - return action->Tag() == Kind::ValAction; - } - - auto Val() const -> const Value* { return val; } - - private: - const Value* val; -}; - } // namespace Carbon #endif // EXECUTABLE_SEMANTICS_INTERPRETER_ACTION_H_ diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index caa15748137f..93bac533c287 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "common/check.h" @@ -20,10 +21,12 @@ #include "executable_semantics/interpreter/action.h" #include "executable_semantics/interpreter/frame.h" #include "executable_semantics/interpreter/stack.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Casting.h" using llvm::cast; +using llvm::dyn_cast; namespace Carbon { @@ -188,56 +191,7 @@ static void InitGlobals(const std::list>& fs) { } } -// { S, H} -> { { C, E, F} :: S, H} -// where C is the body of the function, -// E is the environment (functions + parameters + locals) -// F is the function -void CallFunction(int line_num, std::vector operas, - State* state) { - switch (operas[0]->Tag()) { - case Value::Kind::FunctionValue: { - const auto& fn = cast(*operas[0]); - // Bind arguments to parameters - std::optional matches = - PatternMatch(fn.Param(), operas[1], line_num); - CHECK(matches) << "internal error in call_function, pattern match failed"; - // Create the new frame and push it on the stack - Env values = globals; - std::list params; - for (const auto& [name, value] : *matches) { - values.Set(name, value); - params.push_back(name); - } - auto scopes = Stack>(global_arena->New(values, params)); - auto todo = - Stack>(global_arena->New(fn.Body())); - auto frame = global_arena->New(fn.Name(), scopes, todo); - state->stack.Push(frame); - break; - } - case Value::Kind::ClassType: { - const Value* arg = CopyVal(operas[1], line_num); - const Value* sv = global_arena->RawNew(operas[0], arg); - Ptr frame = state->stack.Top(); - frame->todo.Push(global_arena->New(sv)); - break; - } - case Value::Kind::AlternativeConstructorValue: { - const auto& alt = cast(*operas[0]); - const Value* arg = CopyVal(operas[1], line_num); - const Value* av = global_arena->RawNew( - alt.AltName(), alt.ChoiceName(), arg); - Ptr frame = state->stack.Top(); - frame->todo.Push(global_arena->New(av)); - break; - } - default: - FATAL_RUNTIME_ERROR(line_num) - << "in call, expected a function, not " << *operas[0]; - } -} - -void DeallocateScope(int line_num, Ptr scope) { +void DeallocateScope(Ptr scope) { for (const auto& l : scope->locals) { std::optional
a = scope->values.Get(l); CHECK(a); @@ -245,14 +199,14 @@ void DeallocateScope(int line_num, Ptr scope) { } } -void DeallocateLocals(int line_num, Ptr frame) { +void DeallocateLocals(Ptr frame) { while (!frame->scopes.IsEmpty()) { - DeallocateScope(line_num, frame->scopes.Top()); + DeallocateScope(frame->scopes.Top()); frame->scopes.Pop(); } } -void CreateTuple(Ptr frame, Ptr act, const Expression* exp) { +const Value* CreateTuple(Ptr act, const Expression* exp) { // { { (v1,...,vn) :: C, E, F} :: S, H} // -> { { `(v1,...,vn) :: C, E, F} :: S, H} const auto& tup_lit = cast(*exp); @@ -263,9 +217,7 @@ void CreateTuple(Ptr frame, Ptr act, const Expression* exp) { {.name = tup_lit.Fields()[i].name, .value = act->Results()[i]}); } - const Value* tv = global_arena->RawNew(std::move(elements)); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(tv)); + return global_arena->RawNew(std::move(elements)); } auto PatternMatch(const Value* p, const Value* v, int line_num) @@ -416,11 +368,71 @@ void PatternAssignment(const Value* pat, const Value* val, int line_num) { } } -// State transitions for lvalues. +// State transition functions +// +// The `Step*` family of functions implement state transitions in the +// interpreter by executing a step of the Action at the top of the todo stack, +// and then returning a Transition that specifies how `state.stack` should be +// updated. `Transition` is a variant of several "transition types" representing +// the different kinds of state transition. -void StepLvalue() { - Ptr frame = state->stack.Top(); - Ptr act = frame->todo.Top(); +// Transition type which indicates that the current Action is now done. +struct Done { + // The value computed by the Action. Should always be null for Statement + // Actions, and never null for any other kind of Action. + const Value* result = nullptr; +}; + +// Transition type which spawns a new Action on the todo stack above the current +// Action, and increments the current Action's position counter. +struct Spawn { + Ptr child; +}; + +// Transition type which spawns a new Action that replaces the current action +// on the todo stack. +struct Delegate { + Ptr delegate; +}; + +// Transition type which keeps the current Action at the top of the stack, +// and increments its position counter. +struct RunAgain {}; + +// Transition type which unwinds the `todo` and `scopes` stacks until it +// reaches a specified Action lower in the stack. +struct UnwindTo { + const Ptr new_top; +}; + +// Transition type which unwinds the entire current stack frame, and returns +// a specified value to the caller. +struct UnwindFunctionCall { + const Value* return_val; +}; + +// Transition type which removes the current action from the top of the todo +// stack, then creates a new stack frame which calls the specified function +// with the specified arguments. +struct CallFunction { + const FunctionValue* function; + const Value* args; + int line_num; +}; + +// Transition type which does nothing. +// +// TODO(geoffromer): This is a temporary placeholder during refactoring. All +// uses of this type should be replaced with meaningful transitions. +struct ManualTransition {}; + +using Transition = + std::variant; + +// State transitions for lvalues. +Transition StepLvalue() { + Ptr act = state->stack.Top()->todo.Top(); const Expression* exp = cast(*act).Exp(); if (tracing_output) { llvm::outs() << "--- step lvalue " << *exp << " --->\n"; @@ -432,60 +444,49 @@ void StepLvalue() { Address pointer = GetFromEnv(exp->LineNumber(), cast(*exp).Name()); const Value* v = global_arena->RawNew(pointer); - frame->todo.Pop(); - frame->todo.Push(global_arena->New(v)); - break; + return Done{v}; } case Expression::Kind::FieldAccessExpression: { if (act->Pos() == 0) { // { {e.f :: C, E, F} :: S, H} // -> { e :: [].f :: C, E, F} :: S, H} - frame->todo.Push(global_arena->New( - cast(*exp).Aggregate())); - act->IncrementPos(); + return Spawn{global_arena->New( + cast(*exp).Aggregate())}; } else { // { v :: [].f :: C, E, F} :: S, H} // -> { { &v.f :: C, E, F} :: S, H } Address aggregate = cast(*act->Results()[0]).Val(); Address field = aggregate.SubobjectAddress( cast(*exp).Field()); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New( - global_arena->RawNew(field))); + return Done{global_arena->RawNew(field)}; } - break; } case Expression::Kind::IndexExpression: { if (act->Pos() == 0) { // { {e[i] :: C, E, F} :: S, H} // -> { e :: [][i] :: C, E, F} :: S, H} - frame->todo.Push(global_arena->New( - cast(*exp).Aggregate())); - act->IncrementPos(); + return Spawn{global_arena->New( + cast(*exp).Aggregate())}; + } else if (act->Pos() == 1) { - frame->todo.Push(global_arena->New( - cast(*exp).Offset())); - act->IncrementPos(); - } else if (act->Pos() == 2) { + return Spawn{global_arena->New( + cast(*exp).Offset())}; + } else { // { v :: [][i] :: C, E, F} :: S, H} // -> { { &v[i] :: C, E, F} :: S, H } Address aggregate = cast(*act->Results()[0]).Val(); std::string f = std::to_string(cast(*act->Results()[1]).Val()); Address field = aggregate.SubobjectAddress(f); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New( - global_arena->RawNew(field))); + return Done{global_arena->RawNew(field)}; } - break; } case Expression::Kind::TupleLiteral: { if (act->Pos() == 0) { // { {(f1=e1,...) :: C, E, F} :: S, H} // -> { {e1 :: (f1=[],...) :: C, E, F} :: S, H} const Expression* e1 = cast(*exp).Fields()[0].expression; - frame->todo.Push(global_arena->New(e1)); - act->IncrementPos(); + return Spawn{global_arena->New(e1)}; } else if (act->Pos() != static_cast(cast(*exp).Fields().size())) { // { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S, @@ -494,12 +495,10 @@ void StepLvalue() { // H} const Expression* elt = cast(*exp).Fields()[act->Pos()].expression; - frame->todo.Push(global_arena->New(elt)); - act->IncrementPos(); + return Spawn{global_arena->New(elt)}; } else { - CreateTuple(frame, act, exp); + return Done{CreateTuple(act, exp)}; } - break; } case Expression::Kind::IntLiteral: case Expression::Kind::BoolLiteral: @@ -519,10 +518,8 @@ void StepLvalue() { } // State transitions for expressions. - -void StepExp() { - Ptr frame = state->stack.Top(); - Ptr act = frame->todo.Top(); +Transition StepExp() { + Ptr act = state->stack.Top()->todo.Top(); const Expression* exp = cast(*act).Exp(); if (tracing_output) { llvm::outs() << "--- step exp " << *exp << " --->\n"; @@ -532,36 +529,28 @@ void StepExp() { if (act->Pos() == 0) { // { { e[i] :: C, E, F} :: S, H} // -> { { e :: [][i] :: C, E, F} :: S, H} - frame->todo.Push(global_arena->New( - cast(*exp).Aggregate())); - act->IncrementPos(); + return Spawn{global_arena->New( + cast(*exp).Aggregate())}; } else if (act->Pos() == 1) { - frame->todo.Push(global_arena->New( - cast(*exp).Offset())); - act->IncrementPos(); - } else if (act->Pos() == 2) { - auto tuple = act->Results()[0]; - switch (tuple->Tag()) { - case Value::Kind::TupleValue: { - // { { v :: [][i] :: C, E, F} :: S, H} - // -> { { v_i :: C, E, F} : S, H} - std::string f = - std::to_string(cast(*act->Results()[1]).Val()); - const Value* field = cast(*tuple).FindField(f); - if (field == nullptr) { - FATAL_RUNTIME_ERROR_NO_LINE() - << "field " << f << " not in " << *tuple; - } - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(field)); - break; - } - default: - FATAL_RUNTIME_ERROR_NO_LINE() - << "expected a tuple in field access, not " << *tuple; + return Spawn{global_arena->New( + cast(*exp).Offset())}; + } else { + // { { v :: [][i] :: C, E, F} :: S, H} + // -> { { v_i :: C, E, F} : S, H} + auto* tuple = dyn_cast(act->Results()[0]); + if (tuple == nullptr) { + FATAL_RUNTIME_ERROR_NO_LINE() + << "expected a tuple in field access, not " << *tuple; } + std::string f = + std::to_string(cast(*act->Results()[1]).Val()); + const Value* field = tuple->FindField(f); + if (field == nullptr) { + FATAL_RUNTIME_ERROR_NO_LINE() + << "field " << f << " not in " << *tuple; + } + return Done{field}; } - break; } case Expression::Kind::TupleLiteral: { if (act->Pos() == 0) { @@ -570,10 +559,9 @@ void StepExp() { // -> { {e1 :: (f1=[],...) :: C, E, F} :: S, H} const Expression* e1 = cast(*exp).Fields()[0].expression; - frame->todo.Push(global_arena->New(e1)); - act->IncrementPos(); + return Spawn{global_arena->New(e1)}; } else { - CreateTuple(frame, act, exp); + return Done{CreateTuple(act, exp)}; } } else if (act->Pos() != static_cast(cast(*exp).Fields().size())) { @@ -583,98 +571,95 @@ void StepExp() { // H} const Expression* elt = cast(*exp).Fields()[act->Pos()].expression; - frame->todo.Push(global_arena->New(elt)); - act->IncrementPos(); + return Spawn{global_arena->New(elt)}; } else { - CreateTuple(frame, act, exp); + return Done{CreateTuple(act, exp)}; } - break; } case Expression::Kind::FieldAccessExpression: { const auto& access = cast(*exp); if (act->Pos() == 0) { // { { e.f :: C, E, F} :: S, H} // -> { { e :: [].f :: C, E, F} :: S, H} - frame->todo.Push( - global_arena->New(access.Aggregate())); - act->IncrementPos(); + return Spawn{global_arena->New(access.Aggregate())}; } else { // { { v :: [].f :: C, E, F} :: S, H} // -> { { v_f :: C, E, F} : S, H} - const Value* element = act->Results()[0]->GetField( - FieldPath(access.Field()), exp->LineNumber()); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(element)); + return Done{act->Results()[0]->GetField(FieldPath(access.Field()), + exp->LineNumber())}; } - break; } case Expression::Kind::IdentifierExpression: { CHECK(act->Pos() == 0); const auto& ident = cast(*exp); // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H} Address pointer = GetFromEnv(exp->LineNumber(), ident.Name()); - const Value* pointee = state->heap.Read(pointer, exp->LineNumber()); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(pointee)); - break; + return Done{state->heap.Read(pointer, exp->LineNumber())}; } case Expression::Kind::IntLiteral: CHECK(act->Pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} - frame->todo.Pop(1); - frame->todo.Push(global_arena->New( - global_arena->RawNew(cast(*exp).Val()))); - break; + return Done{global_arena->RawNew(cast(*exp).Val())}; case Expression::Kind::BoolLiteral: CHECK(act->Pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} - frame->todo.Pop(1); - frame->todo.Push(global_arena->New( - global_arena->RawNew(cast(*exp).Val()))); - break; + return Done{ + global_arena->RawNew(cast(*exp).Val())}; case Expression::Kind::PrimitiveOperatorExpression: { const auto& op = cast(*exp); if (act->Pos() != static_cast(op.Arguments().size())) { // { {v :: op(vs,[],e,es) :: C, E, F} :: S, H} // -> { {e :: op(vs,v,[],es) :: C, E, F} :: S, H} const Expression* arg = op.Arguments()[act->Pos()]; - frame->todo.Push(global_arena->New(arg)); - act->IncrementPos(); + return Spawn{global_arena->New(arg)}; } else { // { {v :: op(vs,[]) :: C, E, F} :: S, H} // -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H} - const Value* v = EvalPrim(op.Op(), act->Results(), exp->LineNumber()); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(v)); + return Done{EvalPrim(op.Op(), act->Results(), exp->LineNumber())}; } - break; } case Expression::Kind::CallExpression: if (act->Pos() == 0) { // { {e1(e2) :: C, E, F} :: S, H} // -> { {e1 :: [](e2) :: C, E, F} :: S, H} - frame->todo.Push(global_arena->New( - cast(*exp).Function())); - act->IncrementPos(); + return Spawn{global_arena->New( + cast(*exp).Function())}; } else if (act->Pos() == 1) { // { { v :: [](e) :: C, E, F} :: S, H} // -> { { e :: v([]) :: C, E, F} :: S, H} - frame->todo.Push(global_arena->New( - cast(*exp).Argument())); - act->IncrementPos(); + return Spawn{global_arena->New( + cast(*exp).Argument())}; } else if (act->Pos() == 2) { // { { v2 :: v1([]) :: C, E, F} :: S, H} // -> { {C',E',F'} :: {C, E, F} :: S, H} - frame->todo.Pop(1); - CallFunction(exp->LineNumber(), act->Results(), state); + switch (act->Results()[0]->Tag()) { + case Value::Kind::ClassType: { + const Value* arg = CopyVal(act->Results()[1], exp->LineNumber()); + return Done{ + global_arena->RawNew(act->Results()[0], arg)}; + } + case Value::Kind::AlternativeConstructorValue: { + const auto& alt = + cast(*act->Results()[0]); + const Value* arg = CopyVal(act->Results()[1], exp->LineNumber()); + return Done{global_arena->RawNew( + alt.AltName(), alt.ChoiceName(), arg)}; + } + case Value::Kind::FunctionValue: + return CallFunction{ + .function = cast(act->Results()[0]), + .args = act->Results()[1], + .line_num = exp->LineNumber()}; + default: + FATAL_RUNTIME_ERROR(exp->LineNumber()) + << "in call, expected a function, not " << *act->Results()[0]; + } } else { FATAL() << "in handle_value with Call pos " << act->Pos(); } - break; case Expression::Kind::IntrinsicExpression: CHECK(act->Pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} - frame->todo.Pop(1); switch (cast(*exp).Intrinsic()) { case IntrinsicExpression::IntrinsicKind::Print: Address pointer = GetFromEnv(exp->LineNumber(), "format_str"); @@ -682,81 +667,56 @@ void StepExp() { CHECK(pointee->Tag() == Value::Kind::StringValue); // TODO: This could eventually use something like llvm::formatv. llvm::outs() << cast(*pointee).Val(); - frame->todo.Push(global_arena->New(&TupleValue::Empty())); - break; + return Done{&TupleValue::Empty()}; } - break; case Expression::Kind::IntTypeLiteral: { CHECK(act->Pos() == 0); - const Value* v = global_arena->RawNew(); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(v)); - break; + return Done{global_arena->RawNew()}; } case Expression::Kind::BoolTypeLiteral: { CHECK(act->Pos() == 0); - const Value* v = global_arena->RawNew(); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(v)); - break; + return Done{global_arena->RawNew()}; } case Expression::Kind::TypeTypeLiteral: { CHECK(act->Pos() == 0); - const Value* v = global_arena->RawNew(); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(v)); - break; + return Done{global_arena->RawNew()}; } case Expression::Kind::FunctionTypeLiteral: { if (act->Pos() == 0) { - frame->todo.Push(global_arena->New( - cast(*exp).Parameter())); - act->IncrementPos(); + return Spawn{global_arena->New( + cast(*exp).Parameter())}; } else if (act->Pos() == 1) { // { { pt :: fn [] -> e :: C, E, F} :: S, H} // -> { { e :: fn pt -> []) :: C, E, F} :: S, H} - frame->todo.Push(global_arena->New( - cast(*exp).ReturnType())); - act->IncrementPos(); - } else if (act->Pos() == 2) { + return Spawn{global_arena->New( + cast(*exp).ReturnType())}; + } else { // { { rt :: fn pt -> [] :: C, E, F} :: S, H} // -> { fn pt -> rt :: {C, E, F} :: S, H} - const Value* v = global_arena->RawNew( + return Done{global_arena->RawNew( std::vector(), act->Results()[0], - act->Results()[1]); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(v)); + act->Results()[1])}; } - break; } case Expression::Kind::ContinuationTypeLiteral: { CHECK(act->Pos() == 0); - const Value* v = global_arena->RawNew(); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(v)); - break; + return Done{global_arena->RawNew()}; } case Expression::Kind::StringLiteral: CHECK(act->Pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} - frame->todo.Pop(1); - frame->todo.Push(global_arena->New( - global_arena->RawNew(cast(*exp).Val()))); - break; + return Done{ + global_arena->RawNew(cast(*exp).Val())}; case Expression::Kind::StringTypeLiteral: { CHECK(act->Pos() == 0); - const Value* v = global_arena->RawNew(); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(v)); - break; + return Done{global_arena->RawNew()}; } } // switch (exp->Tag) } -void StepPattern() { - Ptr frame = state->stack.Top(); - Ptr act = frame->todo.Top(); +Transition StepPattern() { + Ptr act = state->stack.Top()->todo.Top(); const Pattern* pattern = cast(*act).Pat(); if (tracing_output) { llvm::outs() << "--- step pattern " << *pattern << " --->\n"; @@ -764,34 +724,25 @@ void StepPattern() { switch (pattern->Tag()) { case Pattern::Kind::AutoPattern: { CHECK(act->Pos() == 0); - const Value* v = global_arena->RawNew(); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(v)); - break; + return Done{global_arena->RawNew()}; } case Pattern::Kind::BindingPattern: { const auto& binding = cast(*pattern); if (act->Pos() == 0) { - frame->todo.Push(global_arena->New(binding.Type())); - act->IncrementPos(); + return Spawn{global_arena->New(binding.Type())}; } else { - auto v = global_arena->RawNew( - binding.Name(), act->Results()[0]); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(v)); + return Done{global_arena->RawNew( + binding.Name(), act->Results()[0])}; } - break; } case Pattern::Kind::TuplePattern: { const auto& tuple = cast(*pattern); if (act->Pos() == 0) { if (tuple.Fields().empty()) { - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(&TupleValue::Empty())); + return Done{&TupleValue::Empty()}; } else { const Pattern* p1 = tuple.Fields()[0].pattern; - frame->todo.Push(global_arena->New(p1)); - act->IncrementPos(); + return Spawn{(global_arena->New(p1))}; } } else if (act->Pos() != static_cast(tuple.Fields().size())) { // { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S, @@ -799,47 +750,34 @@ void StepPattern() { // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, // H} const Pattern* elt = tuple.Fields()[act->Pos()].pattern; - frame->todo.Push(global_arena->New(elt)); - act->IncrementPos(); + return Spawn{global_arena->New(elt)}; } else { std::vector elements; for (size_t i = 0; i < tuple.Fields().size(); ++i) { elements.push_back( {.name = tuple.Fields()[i].name, .value = act->Results()[i]}); } - const Value* tuple_value = - global_arena->RawNew(std::move(elements)); - frame->todo.Pop(1); - frame->todo.Push(global_arena->New(tuple_value)); + return Done{global_arena->RawNew(std::move(elements))}; } - break; } case Pattern::Kind::AlternativePattern: { const auto& alternative = cast(*pattern); if (act->Pos() == 0) { - frame->todo.Push( - global_arena->New(alternative.ChoiceType())); - act->IncrementPos(); + return Spawn{ + global_arena->New(alternative.ChoiceType())}; } else if (act->Pos() == 1) { - frame->todo.Push( - global_arena->New(alternative.Arguments())); - act->IncrementPos(); + return Spawn{global_arena->New(alternative.Arguments())}; } else { CHECK(act->Pos() == 2); const auto& choice_type = cast(*act->Results()[0]); - frame->todo.Pop(1); - frame->todo.Push( - global_arena->New(global_arena->RawNew( - alternative.AlternativeName(), choice_type.Name(), - act->Results()[1]))); + return Done{global_arena->RawNew( + alternative.AlternativeName(), choice_type.Name(), + act->Results()[1])}; } - break; } case Pattern::Kind::ExpressionPattern: - frame->todo.Pop(1); - frame->todo.Push(global_arena->New( - cast(pattern)->Expression())); - break; + return Delegate{global_arena->New( + cast(pattern)->Expression())}; } } @@ -872,8 +810,7 @@ auto IsBlockAct(Ptr act) -> bool { } // State transitions for statements. - -void StepStmt() { +Transition StepStmt() { Ptr frame = state->stack.Top(); Ptr act = frame->todo.Top(); const Statement* stmt = cast(*act).Stmt(); @@ -888,9 +825,8 @@ void StepStmt() { if (act->Pos() == 0) { // { { (match (e) ...) :: C, E, F} :: S, H} // -> { { e :: (match ([]) ...) :: C, E, F} :: S, H} - frame->todo.Push( - global_arena->New(cast(*stmt).Exp())); - act->IncrementPos(); + return Spawn{ + global_arena->New(cast(*stmt).Exp())}; } else { // Regarding act->Pos(): // * odd: start interpreting the pattern of a clause @@ -904,8 +840,7 @@ void StepStmt() { auto clause_num = (act->Pos() - 1) / 2; if (clause_num >= static_cast(cast(*stmt).Clauses()->size())) { - frame->todo.Pop(1); - break; + return Done{}; } auto c = cast(*stmt).Clauses()->begin(); std::advance(c, clause_num); @@ -914,8 +849,7 @@ void StepStmt() { // start interpreting the pattern of the clause // { {v :: (match ([]) ...) :: C, E, F} :: S, H} // -> { {pi :: (match ([]) ...) :: C, E, F} :: S, H} - frame->todo.Push(global_arena->New(c->first)); - act->IncrementPos(); + return Spawn{global_arena->New(c->first)}; } else { // try to match auto v = act->Results()[0]; auto pat = act->Results()[clause_num + 1]; @@ -935,96 +869,86 @@ void StepStmt() { frame->todo.Pop(1); frame->todo.Push(body_act); frame->todo.Push(global_arena->New(c->second)); + return ManualTransition{}; } else { // this case did not match, moving on - act->IncrementPos(); - clause_num = (act->Pos() - 1) / 2; - if (clause_num == + int next_clause_num = act->Pos() / 2; + if (next_clause_num == static_cast(cast(*stmt).Clauses()->size())) { - frame->todo.Pop(1); + return Done{}; } + return RunAgain{}; } } } - break; case Statement::Kind::While: - if (act->Pos() == 0) { + if (act->Pos() % 2 == 0) { // { { (while (e) s) :: C, E, F} :: S, H} // -> { { e :: (while ([]) s) :: C, E, F} :: S, H} - frame->todo.Push( - global_arena->New(cast(*stmt).Cond())); - act->IncrementPos(); - } else if (cast(*act->Results()[0]).Val()) { + act->Clear(); + return Spawn{ + global_arena->New(cast(*stmt).Cond())}; + } else if (cast(*act->Results().back()).Val()) { // { {true :: (while ([]) s) :: C, E, F} :: S, H} // -> { { s :: (while (e) s) :: C, E, F } :: S, H} - frame->todo.Top()->Clear(); - frame->todo.Push( - global_arena->New(cast(*stmt).Body())); + return Spawn{ + global_arena->New(cast(*stmt).Body())}; } else { // { {false :: (while ([]) s) :: C, E, F} :: S, H} // -> { { C, E, F } :: S, H} - frame->todo.Top()->Clear(); - frame->todo.Pop(1); + return Done{}; } - break; - case Statement::Kind::Break: + case Statement::Kind::Break: { CHECK(act->Pos() == 0); // { { break; :: ... :: (while (e) s) :: C, E, F} :: S, H} // -> { { C, E', F} :: S, H} - frame->todo.Pop(1); - while (!frame->todo.IsEmpty() && !IsWhileAct(frame->todo.Top())) { - if (IsBlockAct(frame->todo.Top())) { - DeallocateScope(stmt->LineNumber(), frame->scopes.Top()); - frame->scopes.Pop(1); - } - frame->todo.Pop(1); + auto it = + std::find_if(frame->todo.begin(), frame->todo.end(), &IsWhileAct); + if (it == frame->todo.end()) { + FATAL_RUNTIME_ERROR(stmt->LineNumber()) + << "`break` not inside `while` statement"; } - frame->todo.Pop(1); - break; - case Statement::Kind::Continue: + ++it; + return UnwindTo{*it}; + } + case Statement::Kind::Continue: { CHECK(act->Pos() == 0); // { { continue; :: ... :: (while (e) s) :: C, E, F} :: S, H} // -> { { (while (e) s) :: C, E', F} :: S, H} - frame->todo.Pop(1); - while (!frame->todo.IsEmpty() && !IsWhileAct(frame->todo.Top())) { - if (IsBlockAct(frame->todo.Top())) { - DeallocateScope(stmt->LineNumber(), frame->scopes.Top()); - frame->scopes.Pop(1); - } - frame->todo.Pop(1); + auto it = + std::find_if(frame->todo.begin(), frame->todo.end(), &IsWhileAct); + if (it == frame->todo.end()) { + FATAL_RUNTIME_ERROR(stmt->LineNumber()) + << "`continue` not inside `while` statement"; } - break; + return UnwindTo{*it}; + } case Statement::Kind::Block: { if (act->Pos() == 0) { - if (cast(*stmt).Stmt()) { + const Block& block = cast(*stmt); + if (block.Stmt() != nullptr) { frame->scopes.Push(global_arena->New(CurrentEnv(state))); - frame->todo.Push( - global_arena->New(cast(*stmt).Stmt())); - act->IncrementPos(); - act->IncrementPos(); + return Spawn{global_arena->New(block.Stmt())}; } else { - frame->todo.Pop(); + return Done{}; } } else { Ptr scope = frame->scopes.Top(); - DeallocateScope(stmt->LineNumber(), scope); + DeallocateScope(scope); frame->scopes.Pop(1); - frame->todo.Pop(1); + return Done{}; } - break; } case Statement::Kind::VariableDefinition: if (act->Pos() == 0) { // { {(var x = e) :: C, E, F} :: S, H} // -> { {e :: (var x = []) :: C, E, F} :: S, H} - frame->todo.Push(global_arena->New( - cast(*stmt).Init())); - act->IncrementPos(); + return Spawn{global_arena->New( + cast(*stmt).Init())}; } else if (act->Pos() == 1) { - frame->todo.Push(global_arena->New( - cast(*stmt).Pat())); - act->IncrementPos(); - } else if (act->Pos() == 2) { + return Spawn{global_arena->New( + cast(*stmt).Pat())}; + } else { // { { v :: (x = []) :: C, E, F} :: S, H} // -> { { C, E(x := a), F} :: S, H(a := copy(v))} const Value* v = act->Results()[0]; @@ -1038,96 +962,83 @@ void StepStmt() { frame->scopes.Top()->values.Set(name, value); frame->scopes.Top()->locals.push_back(name); } - frame->todo.Pop(1); + return Done{}; } - break; case Statement::Kind::ExpressionStatement: if (act->Pos() == 0) { // { {e :: C, E, F} :: S, H} // -> { {e :: C, E, F} :: S, H} - frame->todo.Push(global_arena->New( - cast(*stmt).Exp())); - act->IncrementPos(); + return Spawn{global_arena->New( + cast(*stmt).Exp())}; } else { - frame->todo.Pop(1); + return Done{}; } - break; case Statement::Kind::Assign: if (act->Pos() == 0) { // { {(lv = e) :: C, E, F} :: S, H} // -> { {lv :: ([] = e) :: C, E, F} :: S, H} - frame->todo.Push( - global_arena->New(cast(*stmt).Lhs())); - act->IncrementPos(); + return Spawn{global_arena->New(cast(*stmt).Lhs())}; } else if (act->Pos() == 1) { // { { a :: ([] = e) :: C, E, F} :: S, H} // -> { { e :: (a = []) :: C, E, F} :: S, H} - frame->todo.Push( - global_arena->New(cast(*stmt).Rhs())); - act->IncrementPos(); - } else if (act->Pos() == 2) { + return Spawn{ + global_arena->New(cast(*stmt).Rhs())}; + } else { // { { v :: (a = []) :: C, E, F} :: S, H} // -> { { C, E, F} :: S, H(a := v)} auto pat = act->Results()[0]; auto val = act->Results()[1]; PatternAssignment(pat, val, stmt->LineNumber()); - frame->todo.Pop(1); + return Done{}; } - break; case Statement::Kind::If: if (act->Pos() == 0) { // { {(if (e) then_stmt else else_stmt) :: C, E, F} :: S, H} // -> { { e :: (if ([]) then_stmt else else_stmt) :: C, E, F} :: S, H} - frame->todo.Push( - global_arena->New(cast(*stmt).Cond())); - act->IncrementPos(); + return Spawn{ + global_arena->New(cast(*stmt).Cond())}; } else if (cast(*act->Results()[0]).Val()) { // { {true :: if ([]) then_stmt else else_stmt :: C, E, F} :: // S, H} // -> { { then_stmt :: C, E, F } :: S, H} - frame->todo.Pop(1); - frame->todo.Push( - global_arena->New(cast(*stmt).ThenStmt())); + return Delegate{ + global_arena->New(cast(*stmt).ThenStmt())}; } else if (cast(*stmt).ElseStmt()) { // { {false :: if ([]) then_stmt else else_stmt :: C, E, F} :: // S, H} // -> { { else_stmt :: C, E, F } :: S, H} - frame->todo.Pop(1); - frame->todo.Push( - global_arena->New(cast(*stmt).ElseStmt())); + return Delegate{ + global_arena->New(cast(*stmt).ElseStmt())}; } else { - frame->todo.Pop(1); + return Done{}; } - break; case Statement::Kind::Return: if (act->Pos() == 0) { // { {return e :: C, E, F} :: S, H} // -> { {e :: return [] :: C, E, F} :: S, H} - frame->todo.Push( - global_arena->New(cast(*stmt).Exp())); - act->IncrementPos(); + return Spawn{ + global_arena->New(cast(*stmt).Exp())}; } else { // { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H} // -> { {v :: C', E', F'} :: S, H} const Value* ret_val = CopyVal(act->Results()[0], stmt->LineNumber()); - DeallocateLocals(stmt->LineNumber(), frame); - state->stack.Pop(1); - frame = state->stack.Top(); - frame->todo.Push(global_arena->New(ret_val)); + return UnwindFunctionCall{ret_val}; } - break; - case Statement::Kind::Sequence: - CHECK(act->Pos() == 0); + case Statement::Kind::Sequence: { // { { (s1,s2) :: C, E, F} :: S, H} // -> { { s1 :: s2 :: C, E, F} :: S, H} - frame->todo.Pop(1); - if (cast(*stmt).Next()) { - frame->todo.Push( - global_arena->New(cast(*stmt).Next())); + const Sequence& seq = cast(*stmt); + if (act->Pos() == 0) { + return Spawn{global_arena->New(seq.Stmt())}; + } else { + if (seq.Next() != nullptr) { + return Delegate{ + global_arena->New(cast(*stmt).Next())}; + } else { + return Done{}; + } } - frame->todo.Push( - global_arena->New(cast(*stmt).Stmt())); - break; + } case Statement::Kind::Continuation: { CHECK(act->Pos() == 0); // Create a continuation object by creating a frame similar the @@ -1153,14 +1064,13 @@ void StepStmt() { continuation_address); // Pop the continuation statement. frame->todo.Pop(); - break; + return ManualTransition{}; } case Statement::Kind::Run: if (act->Pos() == 0) { // Evaluate the argument of the run statement. - frame->todo.Push( - global_arena->New(cast(*stmt).Argument())); - act->IncrementPos(); + return Spawn{ + global_arena->New(cast(*stmt).Argument())}; } else { frame->todo.Pop(1); // Push an expression statement action to ignore the result @@ -1177,8 +1087,8 @@ void StepStmt() { frame_iter != continuation_vector.rend(); ++frame_iter) { state->stack.Push(*frame_iter); } + return ManualTransition{}; } - break; case Statement::Kind::Await: CHECK(act->Pos() == 0); // Pause the current continuation @@ -1191,10 +1101,89 @@ void StepStmt() { state->heap.Write(*paused.back()->continuation, global_arena->RawNew(paused), stmt->LineNumber()); - break; + return ManualTransition{}; } } +// Visitor which implements the behavior associated with each transition type. +struct DoTransition { + void operator()(const Done& done) { + Ptr frame = state->stack.Top(); + if (frame->todo.Top()->Tag() != Action::Kind::StatementAction) { + CHECK(done.result != nullptr); + frame->todo.Pop(); + if (frame->todo.IsEmpty()) { + state->program_value = done.result; + } else { + frame->todo.Top()->AddResult(done.result); + } + } else { + CHECK(done.result == nullptr); + frame->todo.Pop(); + } + } + + void operator()(const Spawn& spawn) { + Ptr frame = state->stack.Top(); + frame->todo.Top()->IncrementPos(); + frame->todo.Push(spawn.child); + } + + void operator()(const Delegate& delegate) { + Ptr frame = state->stack.Top(); + frame->todo.Pop(); + frame->todo.Push(delegate.delegate); + } + + void operator()(const RunAgain&) { + state->stack.Top()->todo.Top()->IncrementPos(); + } + + void operator()(const UnwindTo& unwind_to) { + Ptr frame = state->stack.Top(); + // TODO: drop .Get() calls once `Ptr` has comparison operators + while (frame->todo.Top().Get() != unwind_to.new_top.Get()) { + if (IsBlockAct(frame->todo.Top())) { + DeallocateScope(frame->scopes.Top()); + frame->scopes.Pop(); + } + frame->todo.Pop(); + } + } + + void operator()(const UnwindFunctionCall& unwind) { + DeallocateLocals(state->stack.Top()); + state->stack.Pop(); + if (state->stack.Top()->todo.IsEmpty()) { + state->program_value = unwind.return_val; + } else { + state->stack.Top()->todo.Top()->AddResult(unwind.return_val); + } + } + + void operator()(const CallFunction& call) { + state->stack.Top()->todo.Pop(); + std::optional matches = + PatternMatch(call.function->Param(), call.args, call.line_num); + CHECK(matches.has_value()) + << "internal error in call_function, pattern match failed"; + // Create the new frame and push it on the stack + Env values = globals; + std::list params; + for (const auto& [name, value] : *matches) { + values.Set(name, value); + params.push_back(name); + } + auto scopes = Stack>(global_arena->New(values, params)); + auto todo = Stack>( + global_arena->New(call.function->Body())); + auto frame = global_arena->New(call.function->Name(), scopes, todo); + state->stack.Push(frame); + } + + void operator()(const ManualTransition&) {} +}; + // State transition. void Step() { Ptr frame = state->stack.Top(); @@ -1205,23 +1194,17 @@ void Step() { Ptr act = frame->todo.Top(); switch (act->Tag()) { - case Action::Kind::ValAction: { - const ValAction& val_act = cast(*frame->todo.Pop()); - Ptr act = frame->todo.Top(); - act->AddResult(val_act.Val()); - break; - } case Action::Kind::LValAction: - StepLvalue(); + std::visit(DoTransition(), StepLvalue()); break; case Action::Kind::ExpressionAction: - StepExp(); + std::visit(DoTransition(), StepExp()); break; case Action::Kind::PatternAction: - StepPattern(); + std::visit(DoTransition(), StepPattern()); break; case Action::Kind::StatementAction: - StepStmt(); + std::visit(DoTransition(), StepStmt()); break; } // switch } @@ -1248,43 +1231,47 @@ auto InterpProgram(const std::list>& fs) -> int { PrintState(llvm::outs()); } - while (state->stack.Count() > 1 || state->stack.Top()->todo.Count() > 1 || - state->stack.Top()->todo.Top()->Tag() != Action::Kind::ValAction) { + while (state->stack.Count() > 1 || !state->stack.Top()->todo.IsEmpty()) { Step(); if (tracing_output) { PrintState(llvm::outs()); } } - const Value* v = cast(*state->stack.Top()->todo.Top()).Val(); - return cast(*v).Val(); + return cast(**state->program_value).Val(); } // Interpret an expression at compile-time. auto InterpExp(Env values, const Expression* e) -> const Value* { + CHECK(state->program_value == std::nullopt); + auto program_value_guard = + llvm::make_scope_exit([] { state->program_value = std::nullopt; }); auto todo = Stack>(global_arena->New(e)); auto scopes = Stack>(global_arena->New(values)); state->stack = Stack>(global_arena->New("InterpExp", scopes, todo)); - while (state->stack.Count() > 1 || state->stack.Top()->todo.Count() > 1 || - state->stack.Top()->todo.Top()->Tag() != Action::Kind::ValAction) { + while (state->stack.Count() > 1 || !state->stack.Top()->todo.IsEmpty()) { Step(); } - return cast(*state->stack.Top()->todo.Top()).Val(); + CHECK(state->program_value != std::nullopt); + return *state->program_value; } // Interpret a pattern at compile-time. auto InterpPattern(Env values, const Pattern* p) -> const Value* { + CHECK(state->program_value == std::nullopt); + auto program_value_guard = + llvm::make_scope_exit([] { state->program_value = std::nullopt; }); auto todo = Stack>(global_arena->New(p)); auto scopes = Stack>(global_arena->New(values)); state->stack = Stack>( global_arena->New("InterpPattern", scopes, todo)); - while (state->stack.Count() > 1 || state->stack.Top()->todo.Count() > 1 || - state->stack.Top()->todo.Top()->Tag() != Action::Kind::ValAction) { + while (state->stack.Count() > 1 || !state->stack.Top()->todo.IsEmpty()) { Step(); } - return cast(*state->stack.Top()->todo.Top()).Val(); + CHECK(state->program_value != std::nullopt); + return *state->program_value; } } // namespace Carbon diff --git a/executable_semantics/interpreter/interpreter.h b/executable_semantics/interpreter/interpreter.h index 73cfa1c4d688..d726f7435da6 100644 --- a/executable_semantics/interpreter/interpreter.h +++ b/executable_semantics/interpreter/interpreter.h @@ -6,6 +6,7 @@ #define EXECUTABLE_SEMANTICS_INTERPRETER_INTERPRETER_H_ #include +#include #include #include @@ -25,6 +26,7 @@ using Env = Dictionary; struct State { Stack> stack; Heap heap; + std::optional program_value; }; extern State* state;