// 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/interpreter.h" #include #include #include #include #include #include #include "common/check.h" #include "executable_semantics/ast/expression.h" #include "executable_semantics/ast/function_definition.h" #include "executable_semantics/common/arena.h" #include "executable_semantics/common/error.h" #include "executable_semantics/common/tracing_flag.h" #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 { // // Auxiliary Functions // void Interpreter::PrintEnv(Env values, llvm::raw_ostream& out) { llvm::ListSeparator sep; for (const auto& [name, address] : values) { out << sep << name << ": "; heap.PrintAddress(address, out); } } // // State Operations // auto Interpreter::CurrentEnv() -> Env { Nonnull frame = stack.Top(); return frame->scopes.Top()->values; } // Returns the given name from the environment, printing an error if not found. auto Interpreter::GetFromEnv(SourceLocation source_loc, const std::string& name) -> Address { std::optional
pointer = CurrentEnv().Get(name); if (!pointer) { FATAL_RUNTIME_ERROR(source_loc) << "could not find `" << name << "`"; } return *pointer; } void Interpreter::PrintState(llvm::raw_ostream& out) { out << "{\nstack: "; llvm::ListSeparator sep(" :: "); for (const auto& frame : stack) { out << sep << *frame; } out << "\nheap: " << heap; if (!stack.IsEmpty() && !stack.Top()->scopes.IsEmpty()) { out << "\nvalues: "; PrintEnv(CurrentEnv(), out); } out << "\n}\n"; } auto Interpreter::EvalPrim(Operator op, const std::vector>& args, SourceLocation source_loc) -> Nonnull { switch (op) { case Operator::Neg: return arena->New(-cast(*args[0]).Val()); case Operator::Add: return arena->New(cast(*args[0]).Val() + cast(*args[1]).Val()); case Operator::Sub: return arena->New(cast(*args[0]).Val() - cast(*args[1]).Val()); case Operator::Mul: return arena->New(cast(*args[0]).Val() * cast(*args[1]).Val()); case Operator::Not: return arena->New(!cast(*args[0]).Val()); case Operator::And: return arena->New(cast(*args[0]).Val() && cast(*args[1]).Val()); case Operator::Or: return arena->New(cast(*args[0]).Val() || cast(*args[1]).Val()); case Operator::Eq: return arena->New(ValueEqual(args[0], args[1], source_loc)); case Operator::Ptr: return arena->New(args[0]); case Operator::Deref: FATAL() << "dereference not implemented yet"; } } void Interpreter::InitEnv(const Declaration& d, Env* env) { switch (d.kind()) { case Declaration::Kind::FunctionDeclaration: { const FunctionDefinition& func_def = cast(d).definition(); Env new_env = *env; // Bring the deduced parameters into scope. for (const auto& deduced : func_def.deduced_parameters()) { Address a = heap.AllocateValue(arena->New(deduced.name)); new_env.Set(deduced.name, a); } auto pt = InterpPattern(new_env, &func_def.param_pattern()); auto f = arena->New(func_def.name(), pt, func_def.body()); Address a = heap.AllocateValue(f); env->Set(func_def.name(), a); break; } case Declaration::Kind::ClassDeclaration: { const ClassDefinition& class_def = cast(d).definition(); VarValues fields; VarValues methods; for (Nonnull m : class_def.members()) { switch (m->kind()) { case Member::Kind::FieldMember: { Nonnull binding = cast(*m).Binding(); Nonnull type_expression = cast(*binding->Type()).Expression(); auto type = InterpExp(Env(arena), type_expression); fields.push_back(make_pair(*binding->Name(), type)); break; } } } auto st = arena->New( class_def.name(), std::move(fields), std::move(methods)); auto a = heap.AllocateValue(st); env->Set(class_def.name(), a); break; } case Declaration::Kind::ChoiceDeclaration: { const auto& choice = cast(d); VarValues alts; for (const auto& alternative : choice.alternatives()) { auto t = InterpExp(Env(arena), &alternative.signature()); alts.push_back(make_pair(alternative.name(), t)); } auto ct = arena->New(choice.name(), std::move(alts)); auto a = heap.AllocateValue(ct); env->Set(choice.name(), a); break; } case Declaration::Kind::VariableDeclaration: { const auto& var = cast(d); // Adds an entry in `globals` mapping the variable's name to the // result of evaluating the initializer. auto v = InterpExp(*env, &var.initializer()); Address a = heap.AllocateValue(v); env->Set(*var.binding().Name(), a); break; } } } void Interpreter::InitGlobals( const std::vector>& fs) { for (const auto d : fs) { InitEnv(*d, &globals); } } void Interpreter::DeallocateScope(Nonnull scope) { for (const auto& l : scope->locals) { std::optional
a = scope->values.Get(l); CHECK(a); heap.Deallocate(*a); } } void Interpreter::DeallocateLocals(Nonnull frame) { while (!frame->scopes.IsEmpty()) { DeallocateScope(frame->scopes.Top()); frame->scopes.Pop(); } } auto Interpreter::CreateTuple(Nonnull act, Nonnull exp) -> Nonnull { // { { (v1,...,vn) :: C, E, F} :: S, H} // -> { { `(v1,...,vn) :: C, E, F} :: S, H} const auto& tup_lit = cast(*exp); CHECK(act->results().size() == tup_lit.Fields().size()); std::vector elements; for (size_t i = 0; i < act->results().size(); ++i) { elements.push_back( {.name = tup_lit.Fields()[i].name, .value = act->results()[i]}); } return arena->New(std::move(elements)); } auto Interpreter::CreateStruct(const std::vector& fields, const std::vector>& values) -> Nonnull { CHECK(fields.size() == values.size()); std::vector elements; for (size_t i = 0; i < fields.size(); ++i) { elements.push_back({.name = fields[i].name, .value = values[i]}); } return arena->New(std::move(elements)); } auto Interpreter::PatternMatch(Nonnull p, Nonnull v, SourceLocation source_loc) -> std::optional { switch (p->kind()) { case Value::Kind::BindingPlaceholderValue: { const auto& placeholder = cast(*p); Env values(arena); if (placeholder.Name().has_value()) { Address a = heap.AllocateValue(CopyVal(arena, v, source_loc)); values.Set(*placeholder.Name(), a); } return values; } case Value::Kind::TupleValue: switch (v->kind()) { case Value::Kind::TupleValue: { const auto& p_tup = cast(*p); const auto& v_tup = cast(*v); if (p_tup.Elements().size() != v_tup.Elements().size()) { FATAL_PROGRAM_ERROR(source_loc) << "arity mismatch in tuple pattern match:\n pattern: " << p_tup << "\n value: " << v_tup; } Env values(arena); for (size_t i = 0; i < p_tup.Elements().size(); ++i) { if (p_tup.Elements()[i].name != v_tup.Elements()[i].name) { FATAL_PROGRAM_ERROR(source_loc) << "Tuple field name '" << v_tup.Elements()[i].name << "' does not match pattern field name '" << p_tup.Elements()[i].name << "'"; } std::optional matches = PatternMatch(p_tup.Elements()[i].value, v_tup.Elements()[i].value, source_loc); if (!matches) { return std::nullopt; } for (const auto& [name, value] : *matches) { values.Set(name, value); } } // for return values; } default: FATAL() << "expected a tuple value in pattern, not " << *v; } case Value::Kind::StructValue: { const auto& p_struct = cast(*p); const auto& v_struct = cast(*v); CHECK(p_struct.elements().size() == v_struct.elements().size()); Env values(arena); for (size_t i = 0; i < p_struct.elements().size(); ++i) { CHECK(p_struct.elements()[i].name == v_struct.elements()[i].name); std::optional matches = PatternMatch(p_struct.elements()[i].value, v_struct.elements()[i].value, source_loc); if (!matches) { return std::nullopt; } for (const auto& [name, value] : *matches) { values.Set(name, value); } } return values; } case Value::Kind::AlternativeValue: switch (v->kind()) { case Value::Kind::AlternativeValue: { const auto& p_alt = cast(*p); const auto& v_alt = cast(*v); if (p_alt.ChoiceName() != v_alt.ChoiceName() || p_alt.AltName() != v_alt.AltName()) { return std::nullopt; } return PatternMatch(p_alt.Argument(), v_alt.Argument(), source_loc); } default: FATAL() << "expected a choice alternative in pattern, not " << *v; } case Value::Kind::FunctionType: switch (v->kind()) { case Value::Kind::FunctionType: { const auto& p_fn = cast(*p); const auto& v_fn = cast(*v); std::optional param_matches = PatternMatch(p_fn.Param(), v_fn.Param(), source_loc); if (!param_matches) { return std::nullopt; } std::optional ret_matches = PatternMatch(p_fn.Ret(), v_fn.Ret(), source_loc); if (!ret_matches) { return std::nullopt; } Env values = *param_matches; for (const auto& [name, value] : *ret_matches) { values.Set(name, value); } return values; } default: return std::nullopt; } case Value::Kind::AutoType: // `auto` matches any type, without binding any new names. We rely // on the typechecker to ensure that `v` is a type. return Env(arena); default: if (ValueEqual(p, v, source_loc)) { return Env(arena); } else { return std::nullopt; } } } void Interpreter::PatternAssignment(Nonnull pat, Nonnull val, SourceLocation source_loc) { switch (pat->kind()) { case Value::Kind::PointerValue: heap.Write(cast(*pat).Val(), CopyVal(arena, val, source_loc), source_loc); break; case Value::Kind::TupleValue: { switch (val->kind()) { case Value::Kind::TupleValue: { const auto& pat_tup = cast(*pat); const auto& val_tup = cast(*val); if (pat_tup.Elements().size() != val_tup.Elements().size()) { FATAL_RUNTIME_ERROR(source_loc) << "arity mismatch in tuple pattern assignment:\n pattern: " << pat_tup << "\n value: " << val_tup; } for (const TupleElement& pattern_element : pat_tup.Elements()) { std::optional> value_field = val_tup.FindField(pattern_element.name); if (!value_field) { FATAL_RUNTIME_ERROR(source_loc) << "field " << pattern_element.name << "not in " << *val; } PatternAssignment(pattern_element.value, *value_field, source_loc); } break; } default: FATAL() << "expected a tuple value on right-hand-side, not " << *val; } break; } case Value::Kind::AlternativeValue: { switch (val->kind()) { case Value::Kind::AlternativeValue: { const auto& pat_alt = cast(*pat); const auto& val_alt = cast(*val); CHECK(val_alt.ChoiceName() == pat_alt.ChoiceName() && val_alt.AltName() == pat_alt.AltName()) << "internal error in pattern assignment"; PatternAssignment(pat_alt.Argument(), val_alt.Argument(), source_loc); break; } default: FATAL() << "expected an alternative in left-hand-side, not " << *val; } break; } default: CHECK(ValueEqual(pat, val, source_loc)) << "internal error in pattern assignment"; } } auto Interpreter::StepLvalue() -> Transition { Nonnull act = stack.Top()->todo.Top(); Nonnull exp = cast(*act).Exp(); if (tracing_output) { llvm::outs() << "--- step lvalue " << *exp << " (" << exp->source_loc() << ") --->\n"; } switch (exp->kind()) { case Expression::Kind::IdentifierExpression: { // { {x :: C, E, F} :: S, H} // -> { {E(x) :: C, E, F} :: S, H} Address pointer = GetFromEnv(exp->source_loc(), cast(*exp).Name()); Nonnull v = arena->New(pointer); 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} return Spawn{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()); return Done{arena->New(field)}; } } case Expression::Kind::IndexExpression: { if (act->pos() == 0) { // { {e[i] :: C, E, F} :: S, H} // -> { e :: [][i] :: C, E, F} :: S, H} return Spawn{ arena->New(cast(*exp).Aggregate())}; } else if (act->pos() == 1) { return Spawn{ 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); return Done{arena->New(field)}; } } case Expression::Kind::TupleLiteral: { if (act->pos() < static_cast(cast(*exp).Fields().size())) { // { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S, // H} // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, // H} Nonnull elt = cast(*exp).Fields()[act->pos()].expression; return Spawn{arena->New(elt)}; } else { return Done{CreateTuple(act, exp)}; } } case Expression::Kind::StructLiteral: case Expression::Kind::StructTypeLiteral: case Expression::Kind::IntLiteral: case Expression::Kind::BoolLiteral: case Expression::Kind::CallExpression: case Expression::Kind::PrimitiveOperatorExpression: case Expression::Kind::IntTypeLiteral: case Expression::Kind::BoolTypeLiteral: case Expression::Kind::TypeTypeLiteral: case Expression::Kind::FunctionTypeLiteral: case Expression::Kind::ContinuationTypeLiteral: case Expression::Kind::StringLiteral: case Expression::Kind::StringTypeLiteral: case Expression::Kind::IntrinsicExpression: FATAL_RUNTIME_ERROR_NO_LINE() << "Can't treat expression as lvalue: " << *exp; } } auto Interpreter::StepExp() -> Transition { Nonnull act = stack.Top()->todo.Top(); Nonnull exp = cast(*act).Exp(); if (tracing_output) { llvm::outs() << "--- step exp " << *exp << " (" << exp->source_loc() << ") --->\n"; } switch (exp->kind()) { case Expression::Kind::IndexExpression: { if (act->pos() == 0) { // { { e[i] :: C, E, F} :: S, H} // -> { { e :: [][i] :: C, E, F} :: S, H} return Spawn{arena->New( cast(*exp).Aggregate())}; } else if (act->pos() == 1) { return Spawn{ 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 " << *act->results()[0]; } std::string f = std::to_string(cast(*act->results()[1]).Val()); std::optional> field = tuple->FindField(f); if (!field) { FATAL_RUNTIME_ERROR_NO_LINE() << "field " << f << " not in " << *tuple; } return Done{*field}; } } case Expression::Kind::TupleLiteral: { if (act->pos() < static_cast(cast(*exp).Fields().size())) { // { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S, // H} // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, // H} Nonnull elt = cast(*exp).Fields()[act->pos()].expression; return Spawn{arena->New(elt)}; } else { return Done{CreateTuple(act, exp)}; } } case Expression::Kind::StructLiteral: { const auto& literal = cast(*exp); if (act->pos() < static_cast(literal.fields().size())) { Nonnull elt = literal.fields()[act->pos()].expression; return Spawn{arena->New(elt)}; } else { return Done{CreateStruct(literal.fields(), act->results())}; } } case Expression::Kind::StructTypeLiteral: { const auto& struct_type = cast(*exp); if (act->pos() < static_cast(struct_type.fields().size())) { return Spawn{arena->New( struct_type.fields()[act->pos()].expression)}; } else { VarValues 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))}; } } 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} return Spawn{arena->New(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())}; } } 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->source_loc(), ident.Name()); return Done{heap.Read(pointer, exp->source_loc())}; } case Expression::Kind::IntLiteral: CHECK(act->pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} return Done{arena->New(cast(*exp).Val())}; case Expression::Kind::BoolLiteral: CHECK(act->pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} return Done{arena->New(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} Nonnull arg = op.Arguments()[act->pos()]; return Spawn{arena->New(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())}; } } case Expression::Kind::CallExpression: if (act->pos() == 0) { // { {e1(e2) :: C, E, F} :: S, H} // -> { {e1 :: [](e2) :: C, E, F} :: S, H} return Spawn{arena->New( cast(*exp).Function())}; } else if (act->pos() == 1) { // { { v :: [](e) :: C, E, F} :: S, H} // -> { { e :: v([]) :: C, E, F} :: S, H} return Spawn{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} switch (act->results()[0]->kind()) { case Value::Kind::NominalClassType: { Nonnull arg = CopyVal(arena, act->results()[1], exp->source_loc()); return Done{arena->New(act->results()[0], arg)}; } case Value::Kind::AlternativeConstructorValue: { const auto& alt = cast(*act->results()[0]); Nonnull arg = CopyVal(arena, act->results()[1], exp->source_loc()); return Done{arena->New(alt.AltName(), alt.ChoiceName(), arg)}; } case Value::Kind::FunctionValue: return CallFunction{ // TODO: Think about a cleaner way to cast between Ptr types. // (multiple TODOs) .function = Nonnull( cast(act->results()[0])), .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]; } } else { FATAL() << "in handle_value with Call pos " << act->pos(); } case Expression::Kind::IntrinsicExpression: CHECK(act->pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} switch (cast(*exp).Intrinsic()) { case IntrinsicExpression::IntrinsicKind::Print: Address pointer = GetFromEnv(exp->source_loc(), "format_str"); Nonnull pointee = heap.Read(pointer, exp->source_loc()); CHECK(pointee->kind() == Value::Kind::StringValue); // TODO: This could eventually use something like llvm::formatv. llvm::outs() << cast(*pointee).Val(); return Done{TupleValue::Empty()}; } case Expression::Kind::IntTypeLiteral: { CHECK(act->pos() == 0); return Done{arena->New()}; } case Expression::Kind::BoolTypeLiteral: { CHECK(act->pos() == 0); return Done{arena->New()}; } case Expression::Kind::TypeTypeLiteral: { CHECK(act->pos() == 0); return Done{arena->New()}; } case Expression::Kind::FunctionTypeLiteral: { if (act->pos() == 0) { return Spawn{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} return Spawn{arena->New( cast(*exp).ReturnType())}; } else { // { { rt :: fn pt -> [] :: C, E, F} :: S, H} // -> { fn pt -> rt :: {C, E, F} :: S, H} return Done{arena->New(std::vector(), act->results()[0], act->results()[1])}; } } case Expression::Kind::ContinuationTypeLiteral: { CHECK(act->pos() == 0); return Done{arena->New()}; } case Expression::Kind::StringLiteral: CHECK(act->pos() == 0); // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} return Done{arena->New(cast(*exp).Val())}; case Expression::Kind::StringTypeLiteral: { CHECK(act->pos() == 0); return Done{arena->New()}; } } // switch (exp->kind) } auto Interpreter::StepPattern() -> Transition { Nonnull act = stack.Top()->todo.Top(); Nonnull pattern = cast(*act).Pat(); if (tracing_output) { llvm::outs() << "--- step pattern " << *pattern << " (" << pattern->source_loc() << ") --->\n"; } switch (pattern->kind()) { case Pattern::Kind::AutoPattern: { CHECK(act->pos() == 0); return Done{arena->New()}; } case Pattern::Kind::BindingPattern: { const auto& binding = cast(*pattern); if (act->pos() == 0) { return Spawn{arena->New(binding.Type())}; } else { return Done{arena->New(binding.Name(), act->results()[0])}; } } case Pattern::Kind::TuplePattern: { const auto& tuple = cast(*pattern); if (act->pos() < static_cast(tuple.Fields().size())) { // { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S, // H} // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, // H} Nonnull elt = tuple.Fields()[act->pos()].pattern; return Spawn{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]}); } return Done{arena->New(std::move(elements))}; } } case Pattern::Kind::AlternativePattern: { const auto& alternative = cast(*pattern); if (act->pos() == 0) { return Spawn{arena->New(alternative.ChoiceType())}; } else if (act->pos() == 1) { return Spawn{arena->New(alternative.Arguments())}; } else { CHECK(act->pos() == 2); const auto& choice_type = cast(*act->results()[0]); return Done{arena->New(alternative.AlternativeName(), choice_type.Name(), act->results()[1])}; } } case Pattern::Kind::ExpressionPattern: return Delegate{arena->New( cast(*pattern).Expression())}; } } static auto IsWhileAct(Nonnull act) -> bool { switch (act->kind()) { case Action::Kind::StatementAction: switch (cast(*act).Stmt()->kind()) { case Statement::Kind::While: return true; default: return false; } default: return false; } } static auto HasLocalScope(Nonnull act) -> bool { switch (act->kind()) { case Action::Kind::StatementAction: switch (cast(*act).Stmt()->kind()) { case Statement::Kind::Block: case Statement::Kind::Match: return true; default: return false; } default: return false; } } auto Interpreter::StepStmt() -> Transition { Nonnull frame = stack.Top(); Nonnull act = frame->todo.Top(); Nonnull stmt = cast(*act).Stmt(); if (tracing_output) { llvm::outs() << "--- step stmt "; stmt->PrintDepth(1, llvm::outs()); llvm::outs() << " (" << stmt->source_loc() << ") --->\n"; } switch (stmt->kind()) { case Statement::Kind::Match: { const auto& match_stmt = cast(*stmt); if (act->pos() == 0) { // { { (match (e) ...) :: C, E, F} :: S, H} // -> { { e :: (match ([]) ...) :: C, E, F} :: S, H} frame->scopes.Push(arena->New(CurrentEnv())); return Spawn{arena->New(&match_stmt.expression())}; } else { // Regarding act->pos(): // * odd: start interpreting the pattern of a clause // * even: finished interpreting the pattern, now try to match // // Regarding act->results(): // * 0: the value that we're matching // * 1: the pattern for clause 0 // * 2: the pattern for clause 1 // * ... auto clause_num = (act->pos() - 1) / 2; if (clause_num >= static_cast(match_stmt.clauses().size())) { DeallocateScope(frame->scopes.Top()); frame->scopes.Pop(); return Done{}; } auto c = match_stmt.clauses()[clause_num]; if (act->pos() % 2 == 1) { // start interpreting the pattern of the clause // { {v :: (match ([]) ...) :: C, E, F} :: S, H} // -> { {pi :: (match ([]) ...) :: C, E, F} :: S, H} return Spawn{arena->New(&c.pattern())}; } else { // try to match auto v = act->results()[0]; auto pat = act->results()[clause_num + 1]; std::optional matches = PatternMatch(pat, v, stmt->source_loc()); if (matches) { // we have a match, start the body // Ensure we don't process any more clauses. act->set_pos(2 * match_stmt.clauses().size() + 1); for (const auto& [name, value] : *matches) { frame->scopes.Top()->values.Set(name, value); frame->scopes.Top()->locals.push_back(name); } return Spawn{arena->New(&c.statement())}; } else { return RunAgain{}; } } } } case Statement::Kind::While: if (act->pos() % 2 == 0) { // { { (while (e) s) :: C, E, F} :: S, H} // -> { { e :: (while ([]) s) :: C, E, F} :: S, H} act->Clear(); return Spawn{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} return Spawn{arena->New(cast(*stmt).Body())}; } else { // { {false :: (while ([]) s) :: C, E, F} :: S, H} // -> { { C, E, F } :: S, H} return Done{}; } case Statement::Kind::Break: { CHECK(act->pos() == 0); // { { break; :: ... :: (while (e) s) :: C, E, F} :: S, H} // -> { { C, E', F} :: S, H} auto it = std::find_if(frame->todo.begin(), frame->todo.end(), &IsWhileAct); if (it == frame->todo.end()) { FATAL_RUNTIME_ERROR(stmt->source_loc()) << "`break` not inside `while` statement"; } ++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} auto it = std::find_if(frame->todo.begin(), frame->todo.end(), &IsWhileAct); if (it == frame->todo.end()) { FATAL_RUNTIME_ERROR(stmt->source_loc()) << "`continue` not inside `while` statement"; } return UnwindTo{*it}; } case Statement::Kind::Block: { if (act->pos() == 0) { const Block& block = cast(*stmt); if (block.Stmt()) { frame->scopes.Push(arena->New(CurrentEnv())); return Spawn{arena->New(*block.Stmt())}; } else { return Done{}; } } else { Nonnull scope = frame->scopes.Top(); DeallocateScope(scope); frame->scopes.Pop(1); return Done{}; } } case Statement::Kind::VariableDefinition: if (act->pos() == 0) { // { {(var x = e) :: C, E, F} :: S, H} // -> { {e :: (var x = []) :: C, E, F} :: S, H} return Spawn{arena->New( cast(*stmt).Init())}; } else if (act->pos() == 1) { return Spawn{ arena->New(cast(*stmt).Pat())}; } else { // { { v :: (x = []) :: C, E, F} :: S, H} // -> { { C, E(x := a), F} :: S, H(a := copy(v))} Nonnull v = act->results()[0]; Nonnull p = act->results()[1]; std::optional matches = PatternMatch(p, v, stmt->source_loc()); CHECK(matches) << stmt->source_loc() << ": internal error in variable definition, match failed"; for (const auto& [name, value] : *matches) { frame->scopes.Top()->values.Set(name, value); frame->scopes.Top()->locals.push_back(name); } return Done{}; } case Statement::Kind::ExpressionStatement: if (act->pos() == 0) { // { {e :: C, E, F} :: S, H} // -> { {e :: C, E, F} :: S, H} return Spawn{arena->New( cast(*stmt).Exp())}; } else { return Done{}; } case Statement::Kind::Assign: if (act->pos() == 0) { // { {(lv = e) :: C, E, F} :: S, H} // -> { {lv :: ([] = e) :: C, E, F} :: S, H} return Spawn{arena->New(cast(*stmt).Lhs())}; } else if (act->pos() == 1) { // { { a :: ([] = e) :: C, E, F} :: S, H} // -> { { e :: (a = []) :: C, E, F} :: S, H} return Spawn{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->source_loc()); return Done{}; } 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} return Spawn{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} return Delegate{ 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} return Delegate{ arena->New(*cast(*stmt).ElseStmt())}; } else { return Done{}; } case Statement::Kind::Return: if (act->pos() == 0) { // { {return e :: C, E, F} :: S, H} // -> { {e :: return [] :: C, E, F} :: S, H} return Spawn{arena->New(cast(*stmt).Exp())}; } else { // { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H} // -> { {v :: C', E', F'} :: S, H} Nonnull ret_val = CopyVal(arena, act->results()[0], stmt->source_loc()); return UnwindFunctionCall{ret_val}; } case Statement::Kind::Sequence: { // { { (s1,s2) :: C, E, F} :: S, H} // -> { { s1 :: s2 :: C, E, F} :: S, H} const Sequence& seq = cast(*stmt); if (act->pos() == 0) { return Spawn{arena->New(seq.Stmt())}; } else { if (seq.Next()) { return Delegate{ arena->New(*cast(*stmt).Next())}; } else { return Done{}; } } } case Statement::Kind::Continuation: { CHECK(act->pos() == 0); // Create a continuation object by creating a frame similar the // way one is created in a function call. auto scopes = Stack>(arena->New(CurrentEnv())); Stack> todo; todo.Push(arena->New( arena->New(arena, stmt->source_loc()))); todo.Push(arena->New(cast(*stmt).Body())); auto continuation_frame = arena->New("__continuation", scopes, todo); Address continuation_address = heap.AllocateValue(arena->New( std::vector>({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()->values.Set( cast(*stmt).ContinuationVariable(), continuation_address); // Pop the continuation statement. frame->todo.Pop(); return ManualTransition{}; } case Statement::Kind::Run: if (act->pos() == 0) { // Evaluate the argument of the run statement. return Spawn{arena->New(cast(*stmt).Argument())}; } else { frame->todo.Pop(1); // Push an expression statement action to ignore the result // value from the continuation. auto ignore_result = arena->New(arena->New( stmt->source_loc(), arena->New(stmt->source_loc()))); frame->todo.Push(ignore_result); // Push the continuation onto the current stack. const std::vector>& continuation_vector = cast(*act->results()[0]).Stack(); for (auto frame_iter = continuation_vector.rbegin(); frame_iter != continuation_vector.rend(); ++frame_iter) { stack.Push(*frame_iter); } return ManualTransition{}; } case Statement::Kind::Await: CHECK(act->pos() == 0); // Pause the current continuation frame->todo.Pop(); std::vector> paused; do { 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(paused), stmt->source_loc()); return ManualTransition{}; } } class Interpreter::DoTransition { public: // Does not take ownership of interpreter. DoTransition(Interpreter* interpreter) : interpreter(interpreter) {} void operator()(const Done& done) { Nonnull frame = interpreter->stack.Top(); if (frame->todo.Top()->kind() != Action::Kind::StatementAction) { CHECK(done.result); frame->todo.Pop(); if (frame->todo.IsEmpty()) { interpreter->program_value = *done.result; } else { frame->todo.Top()->AddResult(*done.result); } } else { CHECK(!done.result); frame->todo.Pop(); } } void operator()(const Spawn& spawn) { Nonnull frame = interpreter->stack.Top(); Nonnull action = frame->todo.Top(); action->set_pos(action->pos() + 1); frame->todo.Push(spawn.child); } void operator()(const Delegate& delegate) { Nonnull frame = interpreter->stack.Top(); frame->todo.Pop(); frame->todo.Push(delegate.delegate); } void operator()(const RunAgain&) { Nonnull action = interpreter->stack.Top()->todo.Top(); action->set_pos(action->pos() + 1); } void operator()(const UnwindTo& unwind_to) { Nonnull frame = interpreter->stack.Top(); while (frame->todo.Top() != unwind_to.new_top) { if (HasLocalScope(frame->todo.Top())) { interpreter->DeallocateScope(frame->scopes.Top()); frame->scopes.Pop(); } frame->todo.Pop(); } } void operator()(const UnwindFunctionCall& unwind) { interpreter->DeallocateLocals(interpreter->stack.Top()); interpreter->stack.Pop(); if (interpreter->stack.Top()->todo.IsEmpty()) { interpreter->program_value = unwind.return_val; } else { interpreter->stack.Top()->todo.Top()->AddResult(unwind.return_val); } } void operator()(const CallFunction& call) { interpreter->stack.Top()->todo.Pop(); std::optional matches = interpreter->PatternMatch( call.function->Param(), call.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 Env values = interpreter->globals; std::vector params; for (const auto& [name, value] : *matches) { values.Set(name, value); params.push_back(name); } auto scopes = Stack>(interpreter->arena->New(values, params)); CHECK(call.function->Body()) << "Calling a function that's missing a body"; auto todo = Stack>( interpreter->arena->New(*call.function->Body())); auto frame = interpreter->arena->New(call.function->Name(), scopes, todo); interpreter->stack.Push(frame); } void operator()(const ManualTransition&) {} private: Nonnull interpreter; }; // State transition. void Interpreter::Step() { Nonnull frame = stack.Top(); if (frame->todo.IsEmpty()) { FATAL_RUNTIME_ERROR_NO_LINE() << "fell off end of function " << frame->name << " without `return`"; } Nonnull act = frame->todo.Top(); switch (act->kind()) { case Action::Kind::LValAction: std::visit(DoTransition(this), StepLvalue()); break; case Action::Kind::ExpressionAction: std::visit(DoTransition(this), StepExp()); break; case Action::Kind::PatternAction: std::visit(DoTransition(this), StepPattern()); break; case Action::Kind::StatementAction: std::visit(DoTransition(this), StepStmt()); break; } // switch } auto Interpreter::InterpProgram( const std::vector>& fs, Nonnull call_main) -> int { // Check that the interpreter is in a clean state. CHECK(globals.IsEmpty()); CHECK(stack.IsEmpty()); CHECK(program_value == std::nullopt); if (tracing_output) { llvm::outs() << "********** initializing globals **********\n"; } InitGlobals(fs); auto todo = Stack>(arena->New(call_main)); auto scopes = Stack>(arena->New(globals)); stack = Stack>(arena->New("top", scopes, todo)); if (tracing_output) { llvm::outs() << "********** calling main function **********\n"; PrintState(llvm::outs()); } while (stack.Count() > 1 || !stack.Top()->todo.IsEmpty()) { Step(); if (tracing_output) { PrintState(llvm::outs()); } } return cast(**program_value).Val(); } auto Interpreter::InterpExp(Env values, Nonnull e) -> Nonnull { CHECK(program_value == std::nullopt); auto program_value_guard = llvm::make_scope_exit([&] { program_value = std::nullopt; }); auto todo = Stack>(arena->New(e)); auto scopes = Stack>(arena->New(values)); stack = Stack>(arena->New("InterpExp", scopes, todo)); while (stack.Count() > 1 || !stack.Top()->todo.IsEmpty()) { Step(); } CHECK(program_value != std::nullopt); return *program_value; } auto Interpreter::InterpPattern(Env values, Nonnull p) -> Nonnull { CHECK(program_value == std::nullopt); auto program_value_guard = llvm::make_scope_exit([&] { program_value = std::nullopt; }); auto todo = Stack>(arena->New(p)); auto scopes = Stack>(arena->New(values)); stack = Stack>(arena->New("InterpPattern", scopes, todo)); while (stack.Count() > 1 || !stack.Top()->todo.IsEmpty()) { Step(); } CHECK(program_value != std::nullopt); return *program_value; } } // namespace Carbon