diff --git a/executable_semantics/interpreter/BUILD b/executable_semantics/interpreter/BUILD index a8c0f65a6f82..ec0046c068e5 100644 --- a/executable_semantics/interpreter/BUILD +++ b/executable_semantics/interpreter/BUILD @@ -32,6 +32,19 @@ cc_library( ], ) +cc_library( + name = "action_stack", + srcs = ["action_stack.cpp"], + hdrs = ["action_stack.h"], + deps = [ + ":action_and_value", + ":stack", + "//common:ostream", + "//executable_semantics/ast:statement", + "@llvm-project//llvm:Support", + ], +) + cc_library( name = "address", hdrs = ["address.h"], @@ -104,6 +117,7 @@ cc_library( ], deps = [ ":action_and_value", + ":action_stack", ":address", ":heap", "//common:check", diff --git a/executable_semantics/interpreter/action_stack.cpp b/executable_semantics/interpreter/action_stack.cpp new file mode 100644 index 000000000000..8cf48f61f288 --- /dev/null +++ b/executable_semantics/interpreter/action_stack.cpp @@ -0,0 +1,146 @@ +// 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 + +#include "executable_semantics/interpreter/action_stack.h" + +#include "executable_semantics/interpreter/action.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Casting.h" + +namespace Carbon { + +void ActionStack::Print(llvm::raw_ostream& out) const { + llvm::ListSeparator sep(" :: "); + for (const std::unique_ptr& action : todo_) { + out << sep << *action; + } +} + +void ActionStack::Start(std::unique_ptr action, Scope scope) { + result_ = std::nullopt; + todo_ = {}; + todo_.Push(std::make_unique(std::move(scope))); + todo_.Push(std::move(action)); +} + +auto ActionStack::CurrentScope() const -> Scope& { + for (const std::unique_ptr& action : todo_) { + if (action->scope().has_value()) { + return *action->scope(); + } + } + FATAL() << "No current scope"; +} + +void ActionStack::FinishAction() { + std::unique_ptr act = todo_.Pop(); + switch (act->kind()) { + case Action::Kind::ExpressionAction: + case Action::Kind::LValAction: + case Action::Kind::PatternAction: + FATAL() << "This kind of action must produce a result."; + case Action::Kind::ScopeAction: + FATAL() << "ScopeAction at top of stack"; + case Action::Kind::StatementAction: + PopScopes(); + CHECK(!IsEmpty()); + } +} + +void ActionStack::FinishAction(Nonnull result) { + std::unique_ptr act = todo_.Pop(); + switch (act->kind()) { + case Action::Kind::StatementAction: + FATAL() << "Statements cannot produce results."; + case Action::Kind::ScopeAction: + FATAL() << "ScopeAction at top of stack"; + case Action::Kind::ExpressionAction: + case Action::Kind::LValAction: + case Action::Kind::PatternAction: + PopScopes(); + SetResult(result); + } +} + +void ActionStack::Spawn(std::unique_ptr child) { + Action& action = *todo_.Top(); + action.set_pos(action.pos() + 1); + todo_.Push(std::move(child)); +} + +void ActionStack::Spawn(std::unique_ptr child, Scope scope) { + Action& action = *todo_.Top(); + action.set_pos(action.pos() + 1); + todo_.Push(std::make_unique(std::move(scope))); + todo_.Push(std::move(child)); +} + +void ActionStack::RunAgain() { + Action& action = *todo_.Top(); + action.set_pos(action.pos() + 1); +} + +void ActionStack::UnwindTo(Nonnull ast_node) { + while (true) { + if (const auto* statement_action = + llvm::dyn_cast(todo_.Top().get()); + statement_action != nullptr && + &statement_action->statement() == ast_node) { + break; + } + todo_.Pop(); + } +} + +void ActionStack::UnwindPast(Nonnull ast_node) { + UnwindTo(ast_node); + todo_.Pop(); + PopScopes(); +} + +void ActionStack::UnwindPast(Nonnull ast_node, + Nonnull result) { + UnwindPast(ast_node); + SetResult(result); +} + +void ActionStack::Resume(Nonnull continuation) { + Action& action = *todo_.Top(); + action.set_pos(action.pos() + 1); + continuation->stack().RestoreTo(todo_); +} + +static auto IsRunAction(const Action& action) -> bool { + const auto* statement = llvm::dyn_cast(&action); + return statement != nullptr && llvm::isa(statement->statement()); +} + +void ActionStack::Suspend() { + // Pause the current continuation + todo_.Pop(); + std::vector> paused; + while (!IsRunAction(*todo_.Top())) { + paused.push_back(todo_.Pop()); + } + const auto& continuation = + llvm::cast(*todo_.Top()->results()[0]); + // Update the continuation with the paused stack. + continuation.stack().StoreReversed(std::move(paused)); +} + +void ActionStack::PopScopes() { + while (!todo_.IsEmpty() && llvm::isa(*todo_.Top())) { + todo_.Pop(); + } +} + +void ActionStack::SetResult(Nonnull result) { + if (todo_.IsEmpty()) { + result_ = result; + } else { + todo_.Top()->AddResult(result); + } +} + +} // namespace Carbon diff --git a/executable_semantics/interpreter/action_stack.h b/executable_semantics/interpreter/action_stack.h new file mode 100644 index 000000000000..18be5c53217a --- /dev/null +++ b/executable_semantics/interpreter/action_stack.h @@ -0,0 +1,104 @@ +// 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 + +#ifndef EXECUTABLE_SEMANTICS_INTERPRETER_ACTION_STACK_H_ +#define EXECUTABLE_SEMANTICS_INTERPRETER_ACTION_STACK_H_ + +#include +#include + +#include "common/ostream.h" +#include "executable_semantics/ast/statement.h" +#include "executable_semantics/interpreter/action.h" +#include "executable_semantics/interpreter/value.h" + +namespace Carbon { + +// The stack of Actions currently being executed by the interpreter. +class ActionStack { + public: + // Constructs an empty ActionStack + ActionStack() = default; + + void Print(llvm::raw_ostream& out) const; + LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } + + // Starts execution with `action` at the top of the stack, in the given scope. + // `action` must be an `ExpressionAction` or `PatternAction`. + void Start(std::unique_ptr action, Scope scope); + + // True if the stack is empty. + auto IsEmpty() const -> bool { return todo_.IsEmpty(); } + + // The Action currently at the top of the stack. This will never be a + // ScopeAction. + auto CurrentAction() -> Action& { return *todo_.Top(); } + + // The scope that should be used to resolve name lookups in the current + // action. + auto CurrentScope() const -> Scope&; + + // The result produced by the `action` argument of the most recent + // `Start` call. *this must be empty, signifying that the action has been + // fully executed. + auto result() const -> Nonnull { return *result_; } + + // The following methods, called "transition methods", update the state of + // the ActionStack and/or the current Action to reflect the effects of + // executing a step of that Action. Execution of an Action step should always + // invoke exactly one transition method, as the very last operation. This is a + // matter of safety as well as convention: most transition methods modify the + // state of the current action, and some of them destroy it. To help enforce + // this requirement, we have a convention of calling these methods as part of + // return statements, e.g. `return todo_.FinishAction()`, even though they + // return void. + + // Finishes execution of the current Action. If `result` is specified, it + // represents the result of that Action. + void FinishAction(); + void FinishAction(Nonnull result); + + // Advances the current action one step, and push `child` onto the stack. + // If `scope` is specified, `child` will be executed in that scope. + void Spawn(std::unique_ptr child); + void Spawn(std::unique_ptr child, Scope scope); + + // Advances the current action one step. + void RunAgain(); + + // Unwinds Actions from the stack until the StatementAction associated with + // `ast_node` is at the top of the stack. + void UnwindTo(Nonnull ast_node); + + // Unwinds Actions from the stack until the StatementAction associated with + // `ast_node` has been removed from the stack. If `result` is specified, + // it represents the result of that Action (StatementActions normally cannot + // produce results, but the body of a function can). + void UnwindPast(Nonnull ast_node); + void UnwindPast(Nonnull ast_node, + Nonnull result); + + // Resumes execution of a suspended continuation. + void Resume(Nonnull continuation); + + // Suspends execution of the currently-executing continuation. + void Suspend(); + + private: + // Pop any ScopeActions from the top of the stack, propagating results as + // needed, to restore the invariant that todo_.Top() is not a ScopeAction. + void PopScopes(); + + // Set `result` as the result of the Action most recently removed from the + // stack. + void SetResult(Nonnull result); + + // TODO: consider defining a non-nullable unique_ptr-like type to use here. + Stack> todo_; + std::optional> result_; +}; + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_INTERPRETER_ACTION_STACK_H_ diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index a2c9e0dcfd3e..70b8d021cbd8 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -42,16 +42,7 @@ void Interpreter::PrintEnv(Env values, llvm::raw_ostream& out) { // State Operations // -auto Interpreter::CurrentScope() -> Scope& { - for (const std::unique_ptr& action : todo_) { - if (action->scope().has_value()) { - return *action->scope(); - } - } - FATAL() << "No current scope"; -} - -auto Interpreter::CurrentEnv() -> Env { return CurrentScope().values(); } +auto Interpreter::CurrentEnv() -> Env { return todo_.CurrentScope().values(); } // Returns the given name from the environment, printing an error if not found. auto Interpreter::GetFromEnv(SourceLocation source_loc, const std::string& name) @@ -64,11 +55,7 @@ auto Interpreter::GetFromEnv(SourceLocation source_loc, const std::string& name) } void Interpreter::PrintState(llvm::raw_ostream& out) { - out << "{\nstack: "; - llvm::ListSeparator sep(" :: "); - for (const std::unique_ptr& action : todo_) { - out << sep << *action; - } + out << "{\nstack: " << todo_; out << "\nheap: " << heap_; if (!todo_.IsEmpty()) { out << "\nvalues: "; @@ -355,8 +342,8 @@ void Interpreter::PatternAssignment(Nonnull pat, } } -auto Interpreter::StepLvalue() -> Transition { - Action& act = *todo_.Top(); +void Interpreter::StepLvalue() { + Action& act = todo_.CurrentAction(); const Expression& exp = cast(act).expression(); if (trace_) { llvm::outs() << "--- step lvalue " << exp << " (" << exp.source_loc() @@ -369,33 +356,33 @@ auto Interpreter::StepLvalue() -> Transition { Address pointer = GetFromEnv(exp.source_loc(), cast(exp).name()); Nonnull v = arena_->New(pointer); - return Done{v}; + return todo_.FinishAction(v); } case ExpressionKind::FieldAccessExpression: { if (act.pos() == 0) { // { {e.f :: C, E, F} :: S, H} // -> { e :: [].f :: C, E, F} :: S, H} - return Spawn{std::make_unique( - &cast(exp).aggregate())}; + return todo_.Spawn(std::make_unique( + &cast(exp).aggregate())); } else { // { v :: [].f :: C, E, F} :: S, H} // -> { { &v.f :: C, E, F} :: S, H } Address aggregate = cast(*act.results()[0]).value(); Address field = aggregate.SubobjectAddress( cast(exp).field()); - return Done{arena_->New(field)}; + return todo_.FinishAction(arena_->New(field)); } } case ExpressionKind::IndexExpression: { if (act.pos() == 0) { // { {e[i] :: C, E, F} :: S, H} // -> { e :: [][i] :: C, E, F} :: S, H} - return Spawn{std::make_unique( - &cast(exp).aggregate())}; + return todo_.Spawn(std::make_unique( + &cast(exp).aggregate())); } else if (act.pos() == 1) { - return Spawn{std::make_unique( - &cast(exp).offset())}; + return todo_.Spawn(std::make_unique( + &cast(exp).offset())); } else { // { v :: [][i] :: C, E, F} :: S, H} // -> { { &v[i] :: C, E, F} :: S, H } @@ -403,7 +390,7 @@ auto Interpreter::StepLvalue() -> Transition { std::string f = std::to_string(cast(*act.results()[1]).value()); Address field = aggregate.SubobjectAddress(f); - return Done{arena_->New(field)}; + return todo_.FinishAction(arena_->New(field)); } } case ExpressionKind::TupleLiteral: { @@ -413,10 +400,10 @@ auto Interpreter::StepLvalue() -> Transition { // H} // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, // H} - return Spawn{std::make_unique( - cast(exp).fields()[act.pos()])}; + return todo_.Spawn(std::make_unique( + cast(exp).fields()[act.pos()])); } else { - return Done{arena_->New(act.results())}; + return todo_.FinishAction(arena_->New(act.results())); } } case ExpressionKind::StructLiteral: @@ -505,8 +492,8 @@ auto Interpreter::Convert(Nonnull value, } } -auto Interpreter::StepExp() -> Transition { - Action& act = *todo_.Top(); +void Interpreter::StepExp() { + Action& act = todo_.CurrentAction(); const Expression& exp = cast(act).expression(); if (trace_) { llvm::outs() << "--- step exp " << exp << " (" << exp.source_loc() @@ -517,11 +504,11 @@ auto Interpreter::StepExp() -> Transition { if (act.pos() == 0) { // { { e[i] :: C, E, F} :: S, H} // -> { { e :: [][i] :: C, E, F} :: S, H} - return Spawn{std::make_unique( - &cast(exp).aggregate())}; + return todo_.Spawn(std::make_unique( + &cast(exp).aggregate())); } else if (act.pos() == 1) { - return Spawn{std::make_unique( - &cast(exp).offset())}; + return todo_.Spawn(std::make_unique( + &cast(exp).offset())); } else { // { { v :: [][i] :: C, E, F} :: S, H} // -> { { v_i :: C, E, F} : S, H} @@ -531,7 +518,7 @@ auto Interpreter::StepExp() -> Transition { FATAL_RUNTIME_ERROR_NO_LINE() << "index " << i << " out of range in " << tuple; } - return Done{tuple.elements()[i]}; + return todo_.FinishAction(tuple.elements()[i]); } } case ExpressionKind::TupleLiteral: { @@ -541,32 +528,33 @@ auto Interpreter::StepExp() -> Transition { // H} // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, // H} - return Spawn{std::make_unique( - cast(exp).fields()[act.pos()])}; + return todo_.Spawn(std::make_unique( + cast(exp).fields()[act.pos()])); } else { - return Done{arena_->New(act.results())}; + return todo_.FinishAction(arena_->New(act.results())); } } case ExpressionKind::StructLiteral: { const auto& literal = cast(exp); if (act.pos() < static_cast(literal.fields().size())) { - return Spawn{std::make_unique( - &literal.fields()[act.pos()].expression())}; + return todo_.Spawn(std::make_unique( + &literal.fields()[act.pos()].expression())); } else { - return Done{CreateStruct(literal.fields(), act.results())}; + return todo_.FinishAction( + CreateStruct(literal.fields(), act.results())); } } case ExpressionKind::StructTypeLiteral: { const auto& struct_type = cast(exp); if (act.pos() < static_cast(struct_type.fields().size())) { - return Spawn{std::make_unique( - &struct_type.fields()[act.pos()].expression())}; + return todo_.Spawn(std::make_unique( + &struct_type.fields()[act.pos()].expression())); } else { std::vector fields; for (size_t i = 0; i < struct_type.fields().size(); ++i) { fields.push_back({struct_type.fields()[i].name(), act.results()[i]}); } - return Done{arena_->New(std::move(fields))}; + return todo_.FinishAction(arena_->New(std::move(fields))); } } case ExpressionKind::FieldAccessExpression: { @@ -574,12 +562,13 @@ auto Interpreter::StepExp() -> Transition { if (act.pos() == 0) { // { { e.f :: C, E, F} :: S, H} // -> { { e :: [].f :: C, E, F} :: S, H} - return Spawn{std::make_unique(&access.aggregate())}; + return todo_.Spawn( + std::make_unique(&access.aggregate())); } else { // { { v :: [].f :: C, E, F} :: S, H} // -> { { v_f :: C, E, F} : S, H} - return Done{act.results()[0]->GetField( - arena_, FieldPath(access.field()), exp.source_loc())}; + return todo_.FinishAction(act.results()[0]->GetField( + arena_, FieldPath(access.field()), exp.source_loc())); } } case ExpressionKind::IdentifierExpression: { @@ -587,40 +576,43 @@ auto Interpreter::StepExp() -> Transition { const auto& ident = cast(exp); // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H} Address pointer = GetFromEnv(exp.source_loc(), ident.name()); - return Done{heap_.Read(pointer, exp.source_loc())}; + return todo_.FinishAction(heap_.Read(pointer, exp.source_loc())); } case ExpressionKind::IntLiteral: CHECK(act.pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} - return Done{arena_->New(cast(exp).value())}; + return todo_.FinishAction( + arena_->New(cast(exp).value())); case ExpressionKind::BoolLiteral: CHECK(act.pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} - return Done{arena_->New(cast(exp).value())}; + return todo_.FinishAction( + arena_->New(cast(exp).value())); case ExpressionKind::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} Nonnull arg = op.arguments()[act.pos()]; - return Spawn{std::make_unique(arg)}; + return todo_.Spawn(std::make_unique(arg)); } else { // { {v :: op(vs,[]) :: C, E, F} :: S, H} // -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H} - return Done{EvalPrim(op.op(), act.results(), exp.source_loc())}; + return todo_.FinishAction( + EvalPrim(op.op(), act.results(), exp.source_loc())); } } case ExpressionKind::CallExpression: if (act.pos() == 0) { // { {e1(e2) :: C, E, F} :: S, H} // -> { {e1 :: [](e2) :: C, E, F} :: S, H} - return Spawn{std::make_unique( - &cast(exp).function())}; + return todo_.Spawn(std::make_unique( + &cast(exp).function())); } else if (act.pos() == 1) { // { { v :: [](e) :: C, E, F} :: S, H} // -> { { e :: v([]) :: C, E, F} :: S, H} - return Spawn{std::make_unique( - &cast(exp).argument())}; + return todo_.Spawn(std::make_unique( + &cast(exp).argument())); } else if (act.pos() == 2) { // { { v2 :: v1([]) :: C, E, F} :: S, H} // -> { {C',E',F'} :: {C, E, F} :: S, H} @@ -628,15 +620,29 @@ auto Interpreter::StepExp() -> Transition { case Value::Kind::AlternativeConstructorValue: { const auto& alt = cast(*act.results()[0]); - return Done{arena_->New( - alt.alt_name(), alt.choice_name(), act.results()[1])}; + return todo_.FinishAction(arena_->New( + alt.alt_name(), alt.choice_name(), act.results()[1])); + } + case Value::Kind::FunctionValue: { + const FunctionDeclaration& function = + cast(*act.results()[0]).declaration(); + Nonnull converted_args = Convert( + act.results()[1], &function.param_pattern().static_type()); + std::optional matches = + PatternMatch(&function.param_pattern().value(), converted_args, + exp.source_loc()); + CHECK(matches.has_value()) + << "internal error in call_function, pattern match failed"; + Scope new_scope(globals_, &heap_); + for (const auto& [name, value] : *matches) { + new_scope.AddLocal(name, value); + } + CHECK(function.body().has_value()) + << "Calling a function that's missing a body"; + return todo_.Spawn( + std::make_unique(*function.body()), + std::move(new_scope)); } - case Value::Kind::FunctionValue: - return CallFunction{ - .function = - &cast(*act.results()[0]).declaration(), - .args = act.results()[1], - .source_loc = exp.source_loc()}; default: FATAL_RUNTIME_ERROR(exp.source_loc()) << "in call, expected a function, not " << *act.results()[0]; @@ -644,9 +650,9 @@ auto Interpreter::StepExp() -> Transition { } else if (act.pos() == 3) { if (act.results().size() < 3) { // Control fell through without explicit return. - return Done{TupleValue::Empty()}; + return todo_.FinishAction(TupleValue::Empty()); } else { - return Done{act.results()[2]}; + return todo_.FinishAction(act.results()[2]); } } else { FATAL() << "in handle_value with Call pos " << act.pos(); @@ -661,55 +667,56 @@ auto Interpreter::StepExp() -> Transition { CHECK(pointee->kind() == Value::Kind::StringValue); // TODO: This could eventually use something like llvm::formatv. llvm::outs() << cast(*pointee).value(); - return Done{TupleValue::Empty()}; + return todo_.FinishAction(TupleValue::Empty()); } case ExpressionKind::IntTypeLiteral: { CHECK(act.pos() == 0); - return Done{arena_->New()}; + return todo_.FinishAction(arena_->New()); } case ExpressionKind::BoolTypeLiteral: { CHECK(act.pos() == 0); - return Done{arena_->New()}; + return todo_.FinishAction(arena_->New()); } case ExpressionKind::TypeTypeLiteral: { CHECK(act.pos() == 0); - return Done{arena_->New()}; + return todo_.FinishAction(arena_->New()); } case ExpressionKind::FunctionTypeLiteral: { if (act.pos() == 0) { - return Spawn{std::make_unique( - &cast(exp).parameter())}; + return todo_.Spawn(std::make_unique( + &cast(exp).parameter())); } else if (act.pos() == 1) { // { { pt :: fn [] -> e :: C, E, F} :: S, H} // -> { { e :: fn pt -> []) :: C, E, F} :: S, H} - return Spawn{std::make_unique( - &cast(exp).return_type())}; + return todo_.Spawn(std::make_unique( + &cast(exp).return_type())); } else { // { { rt :: fn pt -> [] :: C, E, F} :: S, H} // -> { fn pt -> rt :: {C, E, F} :: S, H} - return Done{arena_->New( + return todo_.FinishAction(arena_->New( std::vector>(), act.results()[0], - act.results()[1])}; + act.results()[1])); } } case ExpressionKind::ContinuationTypeLiteral: { CHECK(act.pos() == 0); - return Done{arena_->New()}; + return todo_.FinishAction(arena_->New()); } case ExpressionKind::StringLiteral: CHECK(act.pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} - return Done{arena_->New(cast(exp).value())}; + return todo_.FinishAction( + arena_->New(cast(exp).value())); case ExpressionKind::StringTypeLiteral: { CHECK(act.pos() == 0); - return Done{arena_->New()}; + return todo_.FinishAction(arena_->New()); } } // switch (exp->kind) } -auto Interpreter::StepPattern() -> Transition { - Action& act = *todo_.Top(); +void Interpreter::StepPattern() { + Action& act = todo_.CurrentAction(); const Pattern& pattern = cast(act).pattern(); if (trace_) { llvm::outs() << "--- step pattern " << pattern << " (" @@ -718,15 +725,15 @@ auto Interpreter::StepPattern() -> Transition { switch (pattern.kind()) { case PatternKind::AutoPattern: { CHECK(act.pos() == 0); - return Done{arena_->New()}; + return todo_.FinishAction(arena_->New()); } case PatternKind::BindingPattern: { const auto& binding = cast(pattern); if (act.pos() == 0) { - return Spawn{std::make_unique(&binding.type())}; + return todo_.Spawn(std::make_unique(&binding.type())); } else { - return Done{arena_->New(binding.name(), - act.results()[0])}; + return todo_.FinishAction(arena_->New( + binding.name(), act.results()[0])); } } case PatternKind::TuplePattern: { @@ -736,40 +743,40 @@ auto Interpreter::StepPattern() -> Transition { // H} // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, // H} - return Spawn{ - std::make_unique(tuple.fields()[act.pos()])}; + return todo_.Spawn( + std::make_unique(tuple.fields()[act.pos()])); } else { - return Done{arena_->New(act.results())}; + return todo_.FinishAction(arena_->New(act.results())); } } case PatternKind::AlternativePattern: { const auto& alternative = cast(pattern); if (act.pos() == 0) { - return Spawn{ - std::make_unique(&alternative.choice_type())}; + return todo_.Spawn( + std::make_unique(&alternative.choice_type())); } else if (act.pos() == 1) { - return Spawn{std::make_unique(&alternative.arguments())}; + return todo_.Spawn( + std::make_unique(&alternative.arguments())); } else { CHECK(act.pos() == 2); const auto& choice_type = cast(*act.results()[0]); - return Done{arena_->New( + return todo_.FinishAction(arena_->New( alternative.alternative_name(), choice_type.name(), - act.results()[1])}; + act.results()[1])); } } case PatternKind::ExpressionPattern: - return Delegate{std::make_unique( - &cast(pattern).expression())}; + if (act.pos() == 0) { + return todo_.Spawn(std::make_unique( + &cast(pattern).expression())); + } else { + return todo_.FinishAction(act.results()[0]); + } } } -static auto IsRunAction(const Action& action) -> bool { - const auto* statement = dyn_cast(&action); - return statement != nullptr && llvm::isa(statement->statement()); -} - -auto Interpreter::StepStmt() -> Transition { - Action& act = *todo_.Top(); +void Interpreter::StepStmt() { + Action& act = todo_.CurrentAction(); const Statement& stmt = cast(act).statement(); if (trace_) { llvm::outs() << "--- step stmt "; @@ -783,12 +790,12 @@ auto Interpreter::StepStmt() -> Transition { // { { (match (e) ...) :: C, E, F} :: S, H} // -> { { e :: (match ([]) ...) :: C, E, F} :: S, H} act.StartScope(Scope(CurrentEnv(), &heap_)); - return Spawn{ - std::make_unique(&match_stmt.expression())}; + return todo_.Spawn( + std::make_unique(&match_stmt.expression())); } else { int clause_num = act.pos() - 1; if (clause_num >= static_cast(match_stmt.clauses().size())) { - return Done{}; + return todo_.FinishAction(); } auto c = match_stmt.clauses()[clause_num]; std::optional matches = @@ -802,9 +809,9 @@ auto Interpreter::StepStmt() -> Transition { for (const auto& [name, value] : *matches) { act.scope()->AddLocal(name, value); } - return Spawn{std::make_unique(&c.statement())}; + return todo_.Spawn(std::make_unique(&c.statement())); } else { - return RunAgain{}; + return todo_.RunAgain(); } } } @@ -813,40 +820,40 @@ auto Interpreter::StepStmt() -> Transition { // { { (while (e) s) :: C, E, F} :: S, H} // -> { { e :: (while ([]) s) :: C, E, F} :: S, H} act.Clear(); - return Spawn{ - std::make_unique(&cast(stmt).condition())}; + return todo_.Spawn( + std::make_unique(&cast(stmt).condition())); } else { Nonnull condition = Convert(act.results().back(), arena_->New()); if (cast(*condition).value()) { // { {true :: (while ([]) s) :: C, E, F} :: S, H} // -> { { s :: (while (e) s) :: C, E, F } :: S, H} - return Spawn{ - std::make_unique(&cast(stmt).body())}; + return todo_.Spawn( + std::make_unique(&cast(stmt).body())); } else { // { {false :: (while ([]) s) :: C, E, F} :: S, H} // -> { { C, E, F } :: S, H} - return Done{}; + return todo_.FinishAction(); } } case StatementKind::Break: { CHECK(act.pos() == 0); // { { break; :: ... :: (while (e) s) :: C, E, F} :: S, H} // -> { { C, E', F} :: S, H} - return UnwindPast{.ast_node = &cast(stmt).loop()}; + return todo_.UnwindPast(&cast(stmt).loop()); } case StatementKind::Continue: { CHECK(act.pos() == 0); // { { continue; :: ... :: (while (e) s) :: C, E, F} :: S, H} // -> { { (while (e) s) :: C, E', F} :: S, H} - return UnwindTo{.ast_node = &cast(stmt).loop()}; + return todo_.UnwindTo(&cast(stmt).loop()); } case StatementKind::Block: { const auto& block = cast(stmt); if (act.pos() >= static_cast(block.statements().size())) { // If the position is past the end of the block, end processing. Note // that empty blocks immediately end. - return Done{}; + return todo_.FinishAction(); } // Initialize a scope when starting a block. if (act.pos() == 0) { @@ -854,15 +861,16 @@ auto Interpreter::StepStmt() -> Transition { } // Process the next statement in the block. The position will be // incremented as part of Spawn. - return Spawn{ - std::make_unique(block.statements()[act.pos()])}; + return todo_.Spawn( + std::make_unique(block.statements()[act.pos()])); } case StatementKind::VariableDefinition: { const auto& definition = cast(stmt); if (act.pos() == 0) { // { {(var x = e) :: C, E, F} :: S, H} // -> { {e :: (var x = []) :: C, E, F} :: S, H} - return Spawn{std::make_unique(&definition.init())}; + return todo_.Spawn( + std::make_unique(&definition.init())); } else { // { { v :: (x = []) :: C, E, F} :: S, H} // -> { { C, E(x := a), F} :: S, H(a := copy(v))} @@ -876,79 +884,80 @@ auto Interpreter::StepStmt() -> Transition { << stmt.source_loc() << ": internal error in variable definition, match failed"; for (const auto& [name, value] : *matches) { - Scope& current_scope = CurrentScope(); + Scope& current_scope = todo_.CurrentScope(); current_scope.AddLocal(name, value); } - return Done{}; + return todo_.FinishAction(); } } case StatementKind::ExpressionStatement: if (act.pos() == 0) { // { {e :: C, E, F} :: S, H} // -> { {e :: C, E, F} :: S, H} - return Spawn{std::make_unique( - &cast(stmt).expression())}; + return todo_.Spawn(std::make_unique( + &cast(stmt).expression())); } else { - return Done{}; + return todo_.FinishAction(); } case StatementKind::Assign: { const auto& assign = cast(stmt); if (act.pos() == 0) { // { {(lv = e) :: C, E, F} :: S, H} // -> { {lv :: ([] = e) :: C, E, F} :: S, H} - return Spawn{std::make_unique(&assign.lhs())}; + return todo_.Spawn(std::make_unique(&assign.lhs())); } else if (act.pos() == 1) { // { { a :: ([] = e) :: C, E, F} :: S, H} // -> { { e :: (a = []) :: C, E, F} :: S, H} - return Spawn{std::make_unique(&assign.rhs())}; + return todo_.Spawn(std::make_unique(&assign.rhs())); } else { // { { v :: (a = []) :: C, E, F} :: S, H} // -> { { C, E, F} :: S, H(a := v)} auto pat = act.results()[0]; auto val = Convert(act.results()[1], &assign.lhs().static_type()); PatternAssignment(pat, val, stmt.source_loc()); - return Done{}; + return todo_.FinishAction(); } } case StatementKind::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} - return Spawn{ - std::make_unique(&cast(stmt).condition())}; - } else { + return todo_.Spawn( + std::make_unique(&cast(stmt).condition())); + } else if (act.pos() == 1) { Nonnull condition = Convert(act.results()[0], arena_->New()); if (cast(*condition).value()) { // { {true :: if ([]) then_stmt else else_stmt :: C, E, F} :: // S, H} // -> { { then_stmt :: C, E, F } :: S, H} - return Delegate{ - std::make_unique(&cast(stmt).then_block())}; + return todo_.Spawn( + std::make_unique(&cast(stmt).then_block())); } else if (cast(stmt).else_block()) { // { {false :: if ([]) then_stmt else else_stmt :: C, E, F} :: // S, H} // -> { { else_stmt :: C, E, F } :: S, H} - return Delegate{ - std::make_unique(*cast(stmt).else_block())}; + return todo_.Spawn( + std::make_unique(*cast(stmt).else_block())); } else { - return Done{}; + return todo_.FinishAction(); } + } else { + return todo_.FinishAction(); } case StatementKind::Return: if (act.pos() == 0) { // { {return e :: C, E, F} :: S, H} // -> { {e :: return [] :: C, E, F} :: S, H} - return Spawn{std::make_unique( - &cast(stmt).expression())}; + return todo_.Spawn(std::make_unique( + &cast(stmt).expression())); } else { // { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H} // -> { {v :: C', E', F'} :: S, H} const FunctionDeclaration& function = cast(stmt).function(); - return UnwindPast{ - .ast_node = *function.body(), - .result = Convert(act.results()[0], - &function.return_term().static_type())}; + return todo_.UnwindPast( + *function.body(), + Convert(act.results()[0], &function.return_term().static_type())); } case StatementKind::Continuation: { CHECK(act.pos() == 0); @@ -965,171 +974,55 @@ auto Interpreter::StepStmt() -> Transition { AllocationId continuation_address = heap_.AllocateValue(arena_->New(fragment)); // Bind the continuation object to the continuation variable - CurrentScope().AddLocal(cast(stmt).continuation_variable(), - continuation_address); - return Done{}; + todo_.CurrentScope().AddLocal( + cast(stmt).continuation_variable(), + continuation_address); + return todo_.FinishAction(); } case StatementKind::Run: { auto& run = cast(stmt); if (act.pos() == 0) { // Evaluate the argument of the run statement. - return Spawn{std::make_unique(&run.argument())}; + return todo_.Spawn(std::make_unique(&run.argument())); } else if (act.pos() == 1) { // Push the continuation onto the current stack. - cast(*act.results()[0]) - .stack() - .RestoreTo(todo_); - act.set_pos(2); - return ManualTransition{}; + return todo_.Resume(cast(act.results()[0])); } else { - return Done{}; + return todo_.FinishAction(); } } case StatementKind::Await: CHECK(act.pos() == 0); - // Pause the current continuation - todo_.Pop(); - std::vector> paused; - while (!IsRunAction(*todo_.Top())) { - paused.push_back(todo_.Pop()); - } - const auto& continuation = - cast(*todo_.Top()->results()[0]); - // Update the continuation with the paused stack. - continuation.stack().StoreReversed(std::move(paused)); - return ManualTransition{}; + return todo_.Suspend(); } } -class Interpreter::DoTransition { - public: - // Does not take ownership of interpreter. - explicit DoTransition(Interpreter* interpreter) : interpreter(interpreter) {} - - void operator()(const Done& done) { - std::unique_ptr act = interpreter->todo_.Pop(); - switch (act->kind()) { - case Action::Kind::ExpressionAction: - case Action::Kind::LValAction: - case Action::Kind::PatternAction: - CHECK(done.result.has_value()); - interpreter->todo_.Top()->AddResult(*done.result); - break; - case Action::Kind::StatementAction: - CHECK(!done.result.has_value()); - break; - case Action::Kind::ScopeAction: - if (done.result.has_value()) { - interpreter->todo_.Top()->AddResult(*done.result); - } - break; - } - } - - void operator()(Spawn spawn) { - Action& action = *interpreter->todo_.Top(); - action.set_pos(action.pos() + 1); - interpreter->todo_.Push(std::move(spawn.child)); - } - - void operator()(Delegate delegate) { - std::unique_ptr act = interpreter->todo_.Pop(); - if (act->scope().has_value()) { - delegate.delegate->StartScope(std::move(*act->scope())); - } - interpreter->todo_.Push(std::move(delegate.delegate)); - } - - void operator()(const RunAgain&) { - Action& action = *interpreter->todo_.Top(); - action.set_pos(action.pos() + 1); - } - - void operator()(const UnwindTo& unwind_to) { DoUnwindTo(unwind_to.ast_node); } - - void operator()(const UnwindPast& unwind_past) { - DoUnwindTo(unwind_past.ast_node); - // Unwind past the statement and return a result if needed. - interpreter->todo_.Pop(); - if (unwind_past.result.has_value()) { - interpreter->todo_.Top()->AddResult(*unwind_past.result); - } - } - - void operator()(const CallFunction& call) { - Action& action = *interpreter->todo_.Top(); - action.set_pos(action.pos() + 1); - Nonnull converted_args = interpreter->Convert( - call.args, &call.function->param_pattern().static_type()); - std::optional matches = - interpreter->PatternMatch(&call.function->param_pattern().value(), - converted_args, call.source_loc); - CHECK(matches.has_value()) - << "internal error in call_function, pattern match failed"; - // Create the new frame and push it on the stack - Scope new_scope(interpreter->globals_, &interpreter->heap_); - for (const auto& [name, value] : *matches) { - new_scope.AddLocal(name, value); - } - interpreter->todo_.Push( - std::make_unique(std::move(new_scope))); - CHECK(call.function->body()) << "Calling a function that's missing a body"; - interpreter->todo_.Push( - std::make_unique(*call.function->body())); - } - - void operator()(const ManualTransition&) {} - - private: - // Unwinds to the indicated node. - void DoUnwindTo(Nonnull ast_node) { - while (true) { - if (const auto* statement_action = - dyn_cast(interpreter->todo_.Top().get()); - statement_action != nullptr && - &statement_action->statement() == ast_node) { - break; - } - interpreter->todo_.Pop(); - } - } - - Nonnull interpreter; -}; - // State transition. void Interpreter::Step() { - Action& act = *todo_.Top(); + Action& act = todo_.CurrentAction(); switch (act.kind()) { case Action::Kind::LValAction: - std::visit(DoTransition(this), StepLvalue()); + StepLvalue(); break; case Action::Kind::ExpressionAction: - std::visit(DoTransition(this), StepExp()); + StepExp(); break; case Action::Kind::PatternAction: - std::visit(DoTransition(this), StepPattern()); + StepPattern(); break; case Action::Kind::StatementAction: - std::visit(DoTransition(this), StepStmt()); + StepStmt(); break; case Action::Kind::ScopeAction: - if (act.results().empty()) { - std::visit(DoTransition(this), Transition{Done{}}); - } else { - CHECK(act.results().size() == 1); - std::visit(DoTransition(this), Transition{Done{act.results()[0]}}); - } + FATAL() << "ScopeAction escaped ActionStack"; } // switch } auto Interpreter::ExecuteAction(std::unique_ptr action, Env values, bool trace_steps) -> Nonnull { - todo_ = {}; - todo_.Push(std::make_unique(Scope(values, &heap_))); - todo_.Push(std::move(action)); + todo_.Start(std::move(action), Scope(values, &heap_)); - while (todo_.Count() > 1) { + while (!todo_.IsEmpty()) { Step(); if (trace_steps) { PrintState(llvm::outs()); @@ -1141,8 +1034,7 @@ auto Interpreter::ExecuteAction(std::unique_ptr action, Env values, fragment->Clear(); } - CHECK(todo_.Top()->results().size() == 1); - return todo_.Top()->results()[0]; + return todo_.result(); } auto Interpreter::InterpProgram(llvm::ArrayRef> fs, diff --git a/executable_semantics/interpreter/interpreter.h b/executable_semantics/interpreter/interpreter.h index db9b4f502b6c..90754364e454 100644 --- a/executable_semantics/interpreter/interpreter.h +++ b/executable_semantics/interpreter/interpreter.h @@ -14,8 +14,8 @@ #include "executable_semantics/ast/expression.h" #include "executable_semantics/ast/pattern.h" #include "executable_semantics/interpreter/action.h" +#include "executable_semantics/interpreter/action_stack.h" #include "executable_semantics/interpreter/heap.h" -#include "executable_semantics/interpreter/stack.h" #include "executable_semantics/interpreter/value.h" #include "llvm/ADT/ArrayRef.h" @@ -52,87 +52,18 @@ class Interpreter { void PrintEnv(Env values, llvm::raw_ostream& out); private: - // 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. - - // Transition type which indicates that the current Action is now done. - struct Done { - // The value computed by the Action. Should always be nullopt for Statement - // Actions, and never null for any other kind of Action. - std::optional> result; - }; - - // 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 { - std::unique_ptr child; - }; - - // Transition type which spawns a new Action that replaces the current action - // on the todo stack. - struct Delegate { - std::unique_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` stack until it reaches the - // StatementAction associated with `ast_node`. Execution then resumes with - // that StatementAction. - struct UnwindTo { - Nonnull ast_node; - }; - - // Transition type which unwinds the `todo` stack down to and including the - // StatementAction associated with `ast_node`. If `result` is set, it will be - // treated as the result of that StatementAction. - struct UnwindPast { - Nonnull ast_node; - std::optional> result; - }; - - // 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 { - Nonnull function; - Nonnull args; - SourceLocation source_loc; - }; - - // 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; - - // Visitor which implements the behavior associated with each transition type. - class DoTransition; - friend class DoTransition; - void Step(); // State transitions for expressions. - auto StepExp() -> Transition; + void StepExp(); // State transitions for lvalues. - auto StepLvalue() -> Transition; + void StepLvalue(); // State transitions for patterns. - auto StepPattern() -> Transition; + void StepPattern(); // State transition for statements. - auto StepStmt() -> Transition; + void StepStmt(); void InitGlobals(llvm::ArrayRef> fs); - auto CurrentScope() -> Scope&; auto CurrentEnv() -> Env; auto GetFromEnv(SourceLocation source_loc, const std::string& name) -> Address; @@ -168,8 +99,7 @@ class Interpreter { // Globally-defined entities, such as functions, structs, or choices. Env globals_; - // TODO: consider defining a non-nullable unique_ptr-like type to use here. - Stack> todo_; + ActionStack todo_; Heap heap_; // The underlying states of continuation values. All StackFragments created