// 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/declaration.h" #include "executable_semantics/ast/expression.h" #include "executable_semantics/common/arena.h" #include "executable_semantics/common/error.h" #include "executable_semantics/interpreter/action.h" #include "executable_semantics/interpreter/stack.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, allocation] : values) { out << sep << name << ": "; heap_.PrintAllocation(allocation, out); } } // // State Operations // 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) -> Address { std::optional pointer = CurrentEnv().Get(name); if (!pointer) { FATAL_RUNTIME_ERROR(source_loc) << "could not find `" << name << "`"; } return Address(*pointer); } void Interpreter::PrintState(llvm::raw_ostream& out) { out << "{\nstack: " << todo_; out << "\nheap: " << heap_; if (!todo_.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]).value()); case Operator::Add: return arena_->New(cast(*args[0]).value() + cast(*args[1]).value()); case Operator::Sub: return arena_->New(cast(*args[0]).value() - cast(*args[1]).value()); case Operator::Mul: return arena_->New(cast(*args[0]).value() * cast(*args[1]).value()); case Operator::Not: return arena_->New(!cast(*args[0]).value()); case Operator::And: return arena_->New(cast(*args[0]).value() && cast(*args[1]).value()); case Operator::Or: return arena_->New(cast(*args[0]).value() || cast(*args[1]).value()); case Operator::Eq: return arena_->New(ValueEqual(args[0], args[1])); 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 DeclarationKind::FunctionDeclaration: { const auto& func_def = cast(d); Env new_env = *env; // Bring the deduced parameters into scope. for (Nonnull deduced : func_def.deduced_parameters()) { AllocationId a = heap_.AllocateValue(arena_->New(deduced)); new_env.Set(deduced->name(), a); } Nonnull f = arena_->New(&func_def); AllocationId a = heap_.AllocateValue(f); env->Set(func_def.name(), a); break; } case DeclarationKind::ClassDeclaration: { const auto& class_decl = cast(d); std::vector fields; std::vector methods; for (Nonnull m : class_decl.members()) { switch (m->kind()) { case MemberKind::FieldMember: { const BindingPattern& binding = cast(*m).binding(); const Expression& type_expression = cast(binding.type()).expression(); auto type = InterpExp(Env(arena_), &type_expression); fields.push_back({.name = binding.name(), .value = type}); break; } } } auto st = arena_->New( class_decl.name(), std::move(fields), std::move(methods)); AllocationId a = heap_.AllocateValue(st); env->Set(class_decl.name(), a); break; } case DeclarationKind::ChoiceDeclaration: { const auto& choice = cast(d); std::vector alts; for (Nonnull alternative : choice.alternatives()) { auto t = InterpExp(Env(arena_), &alternative->signature()); alts.push_back({.name = alternative->name(), .value = t}); } auto ct = arena_->New(choice.name(), std::move(alts)); AllocationId a = heap_.AllocateValue(ct); env->Set(choice.name(), a); break; } case DeclarationKind::VariableDeclaration: { const auto& var = cast(d); // Adds an entry in `globals` mapping the variable's name to the // result of evaluating the initializer. Nonnull v = Convert(InterpExp(*env, &var.initializer()), &var.static_type()); AllocationId a = heap_.AllocateValue(v); env->Set(var.binding().name(), a); break; } } } void Interpreter::InitGlobals(llvm::ArrayRef> fs) { for (const auto d : fs) { InitEnv(*d, &globals_); } } 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.named_entity().has_value()) { AllocationId a = heap_.AllocateValue(v); values.Set(std::string(placeholder.named_entity()->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) { std::optional matches = PatternMatch( p_tup.elements()[i], v_tup.elements()[i], 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.choice_name() != v_alt.choice_name() || p_alt.alt_name() != v_alt.alt_name()) { 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.parameters(), &v_fn.parameters(), source_loc); if (!param_matches) { return std::nullopt; } std::optional ret_matches = PatternMatch( &p_fn.return_type(), &v_fn.return_type(), 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)) { return Env(arena_); } else { return std::nullopt; } } } void Interpreter::StepLvalue() { Action& act = todo_.CurrentAction(); const Expression& exp = cast(act).expression(); if (trace_) { llvm::outs() << "--- step lvalue " << exp << " (" << exp.source_loc() << ") --->\n"; } switch (exp.kind()) { case ExpressionKind::IdentifierExpression: { // { {x :: C, E, F} :: S, H} // -> { {E(x) :: C, E, F} :: S, H} CHECK(cast(exp).has_named_entity()) << "Identifier '" << exp << "' at " << exp.source_loc() << " was not resolved"; Address pointer = GetFromEnv(exp.source_loc(), cast(exp).name()); Nonnull v = arena_->New(pointer); 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 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]).address(); Address field = aggregate.SubobjectAddress( cast(exp).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 todo_.Spawn(std::make_unique( &cast(exp).aggregate())); } else if (act.pos() == 1) { return todo_.Spawn(std::make_unique( &cast(exp).offset())); } else { // { v :: [][i] :: C, E, F} :: S, H} // -> { { &v[i] :: C, E, F} :: S, H } Address aggregate = cast(*act.results()[0]).address(); std::string f = std::to_string(cast(*act.results()[1]).value()); Address field = aggregate.SubobjectAddress(f); return todo_.FinishAction(arena_->New(field)); } } case ExpressionKind::TupleLiteral: case ExpressionKind::StructLiteral: case ExpressionKind::StructTypeLiteral: case ExpressionKind::IntLiteral: case ExpressionKind::BoolLiteral: case ExpressionKind::CallExpression: case ExpressionKind::PrimitiveOperatorExpression: case ExpressionKind::IntTypeLiteral: case ExpressionKind::BoolTypeLiteral: case ExpressionKind::TypeTypeLiteral: case ExpressionKind::FunctionTypeLiteral: case ExpressionKind::ContinuationTypeLiteral: case ExpressionKind::StringLiteral: case ExpressionKind::StringTypeLiteral: case ExpressionKind::IntrinsicExpression: FATAL() << "Can't treat expression as lvalue: " << exp; case ExpressionKind::UnimplementedExpression: FATAL() << "Unimplemented: " << exp; } } auto Interpreter::Convert(Nonnull value, Nonnull destination_type) const -> Nonnull { switch (value->kind()) { case Value::Kind::IntValue: case Value::Kind::FunctionValue: case Value::Kind::LValue: case Value::Kind::BoolValue: case Value::Kind::NominalClassValue: case Value::Kind::AlternativeValue: case Value::Kind::IntType: case Value::Kind::BoolType: case Value::Kind::TypeType: case Value::Kind::FunctionType: case Value::Kind::PointerType: case Value::Kind::AutoType: case Value::Kind::StructType: case Value::Kind::NominalClassType: case Value::Kind::ChoiceType: case Value::Kind::ContinuationType: case Value::Kind::VariableType: case Value::Kind::BindingPlaceholderValue: case Value::Kind::AlternativeConstructorValue: case Value::Kind::ContinuationValue: case Value::Kind::StringType: case Value::Kind::StringValue: case Value::Kind::TypeOfClassType: case Value::Kind::TypeOfChoiceType: // TODO: add `CHECK(TypeEqual(type, value->dynamic_type()))`, once we // have Value::dynamic_type. return value; case Value::Kind::StructValue: { const auto& struct_val = cast(*value); switch (destination_type->kind()) { case Value::Kind::StructType: { const auto& destination_struct_type = cast(*destination_type); std::vector new_elements; for (const auto& [field_name, field_type] : destination_struct_type.fields()) { std::optional> old_value = struct_val.FindField(field_name); new_elements.push_back( {.name = field_name, .value = Convert(*old_value, field_type)}); } return arena_->New(std::move(new_elements)); } case Value::Kind::NominalClassType: return arena_->New(destination_type, value); default: FATAL() << "Can't convert value " << *value << " to type " << *destination_type; } } case Value::Kind::TupleValue: { const auto& tuple = cast(value); const auto& destination_tuple_type = cast(destination_type); CHECK(tuple->elements().size() == destination_tuple_type->elements().size()); std::vector> new_elements; for (size_t i = 0; i < tuple->elements().size(); ++i) { new_elements.push_back(Convert(tuple->elements()[i], destination_tuple_type->elements()[i])); } return arena_->New(std::move(new_elements)); } } } void Interpreter::StepExp() { Action& act = todo_.CurrentAction(); const Expression& exp = cast(act).expression(); if (trace_) { llvm::outs() << "--- step exp " << exp << " (" << exp.source_loc() << ") --->\n"; } switch (exp.kind()) { case ExpressionKind::IndexExpression: { if (act.pos() == 0) { // { { e[i] :: C, E, F} :: S, H} // -> { { e :: [][i] :: C, E, F} :: S, H} return todo_.Spawn(std::make_unique( &cast(exp).aggregate())); } else if (act.pos() == 1) { return todo_.Spawn(std::make_unique( &cast(exp).offset())); } else { // { { v :: [][i] :: C, E, F} :: S, H} // -> { { v_i :: C, E, F} : S, H} const auto& tuple = cast(*act.results()[0]); int i = cast(*act.results()[1]).value(); if (i < 0 || i >= static_cast(tuple.elements().size())) { FATAL_RUNTIME_ERROR_NO_LINE() << "index " << i << " out of range in " << tuple; } return todo_.FinishAction(tuple.elements()[i]); } } case ExpressionKind::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} return todo_.Spawn(std::make_unique( cast(exp).fields()[act.pos()])); } else { return todo_.FinishAction(arena_->New(act.results())); } } case ExpressionKind::StructLiteral: { const auto& literal = cast(exp); if (act.pos() < static_cast(literal.fields().size())) { return todo_.Spawn(std::make_unique( &literal.fields()[act.pos()].expression())); } else { 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 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 todo_.FinishAction(arena_->New(std::move(fields))); } } case ExpressionKind::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 todo_.Spawn( std::make_unique(&access.aggregate())); } else { // { { v :: [].f :: C, E, F} :: S, H} // -> { { v_f :: C, E, F} : S, H} return todo_.FinishAction(act.results()[0]->GetField( arena_, FieldPath(access.field()), exp.source_loc())); } } case ExpressionKind::IdentifierExpression: { CHECK(act.pos() == 0); const auto& ident = cast(exp); CHECK(ident.has_named_entity()) << "Identifier '" << exp << "' at " << exp.source_loc() << " was not resolved"; // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H} Address pointer = GetFromEnv(exp.source_loc(), ident.name()); 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 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 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 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 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 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 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} switch (act.results()[0]->kind()) { case Value::Kind::AlternativeConstructorValue: { const auto& alt = cast(*act.results()[0]); 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)); } default: FATAL_RUNTIME_ERROR(exp.source_loc()) << "in call, expected a function, not " << *act.results()[0]; } } else if (act.pos() == 3) { if (act.results().size() < 3) { // Control fell through without explicit return. return todo_.FinishAction(TupleValue::Empty()); } else { return todo_.FinishAction(act.results()[2]); } } else { FATAL() << "in handle_value with Call pos " << act.pos(); } case ExpressionKind::IntrinsicExpression: { const auto& intrinsic = cast(exp); if (act.pos() == 0) { return todo_.Spawn( std::make_unique(&intrinsic.args())); } // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} switch (cast(exp).intrinsic()) { case IntrinsicExpression::Intrinsic::Print: { const auto& args = cast(*act.results()[0]); // TODO: This could eventually use something like llvm::formatv. llvm::outs() << cast(*args.elements()[0]).value(); return todo_.FinishAction(TupleValue::Empty()); } } } case ExpressionKind::IntTypeLiteral: { CHECK(act.pos() == 0); return todo_.FinishAction(arena_->New()); } case ExpressionKind::BoolTypeLiteral: { CHECK(act.pos() == 0); return todo_.FinishAction(arena_->New()); } case ExpressionKind::TypeTypeLiteral: { CHECK(act.pos() == 0); return todo_.FinishAction(arena_->New()); } case ExpressionKind::FunctionTypeLiteral: { if (act.pos() == 0) { 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 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 todo_.FinishAction(arena_->New( std::vector>(), act.results()[0], act.results()[1])); } } case ExpressionKind::ContinuationTypeLiteral: { CHECK(act.pos() == 0); 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 todo_.FinishAction( arena_->New(cast(exp).value())); case ExpressionKind::StringTypeLiteral: { CHECK(act.pos() == 0); return todo_.FinishAction(arena_->New()); } case ExpressionKind::UnimplementedExpression: FATAL() << "Unimplemented: " << exp; } // switch (exp->kind) } void Interpreter::StepPattern() { Action& act = todo_.CurrentAction(); const Pattern& pattern = cast(act).pattern(); if (trace_) { llvm::outs() << "--- step pattern " << pattern << " (" << pattern.source_loc() << ") --->\n"; } switch (pattern.kind()) { case PatternKind::AutoPattern: { CHECK(act.pos() == 0); return todo_.FinishAction(arena_->New()); } case PatternKind::BindingPattern: { const auto& binding = cast(pattern); if (binding.name() != AnonymousName) { return todo_.FinishAction( arena_->New(&binding)); } else { return todo_.FinishAction(arena_->New()); } } case PatternKind::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} return todo_.Spawn( std::make_unique(tuple.fields()[act.pos()])); } else { return todo_.FinishAction(arena_->New(act.results())); } } case PatternKind::AlternativePattern: { const auto& alternative = cast(pattern); if (act.pos() == 0) { return todo_.Spawn( std::make_unique(&alternative.choice_type())); } else if (act.pos() == 1) { return todo_.Spawn( std::make_unique(&alternative.arguments())); } else { CHECK(act.pos() == 2); const auto& choice_type = cast(*act.results()[0]); return todo_.FinishAction(arena_->New( alternative.alternative_name(), choice_type.name(), act.results()[1])); } } case PatternKind::ExpressionPattern: if (act.pos() == 0) { return todo_.Spawn(std::make_unique( &cast(pattern).expression())); } else { return todo_.FinishAction(act.results()[0]); } } } void Interpreter::StepStmt() { Action& act = todo_.CurrentAction(); const Statement& stmt = cast(act).statement(); if (trace_) { llvm::outs() << "--- step stmt "; stmt.PrintDepth(1, llvm::outs()); llvm::outs() << " (" << stmt.source_loc() << ") --->\n"; } switch (stmt.kind()) { case StatementKind::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} act.StartScope(Scope(CurrentEnv(), &heap_)); 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 todo_.FinishAction(); } auto c = match_stmt.clauses()[clause_num]; std::optional matches = PatternMatch(&c.pattern().value(), Convert(act.results()[0], &c.pattern().static_type()), stmt.source_loc()); if (matches) { // We have a match, start the body. // Ensure we don't process any more clauses. act.set_pos(match_stmt.clauses().size() + 1); for (const auto& [name, value] : *matches) { act.scope()->AddLocal(name, value); } return todo_.Spawn(std::make_unique(&c.statement())); } else { return todo_.RunAgain(); } } } case StatementKind::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 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 todo_.Spawn( std::make_unique(&cast(stmt).body())); } else { // { {false :: (while ([]) s) :: C, E, F} :: S, H} // -> { { C, E, F } :: S, H} 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 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 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 todo_.FinishAction(); } // Initialize a scope when starting a block. if (act.pos() == 0) { act.StartScope(Scope(CurrentEnv(), &heap_)); } // Process the next statement in the block. The position will be // incremented as part of Spawn. 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 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))} Nonnull v = Convert(act.results()[0], &definition.pattern().static_type()); Nonnull p = &cast(stmt).pattern().value(); 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) { Scope& current_scope = todo_.CurrentScope(); current_scope.AddLocal(name, value); } return todo_.FinishAction(); } } case StatementKind::ExpressionStatement: if (act.pos() == 0) { // { {e :: C, E, F} :: S, H} // -> { {e :: C, E, F} :: S, H} return todo_.Spawn(std::make_unique( &cast(stmt).expression())); } else { 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 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 todo_.Spawn(std::make_unique(&assign.rhs())); } else { // { { v :: (a = []) :: C, E, F} :: S, H} // -> { { C, E, F} :: S, H(a := v)} const auto& lval = cast(*act.results()[0]); Nonnull rval = Convert(act.results()[1], &assign.lhs().static_type()); heap_.Write(lval.address(), rval, stmt.source_loc()); 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 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 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 todo_.Spawn( std::make_unique(*cast(stmt).else_block())); } else { 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 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 todo_.UnwindPast( *function.body(), Convert(act.results()[0], &function.return_term().static_type())); } case StatementKind::Continuation: { CHECK(act.pos() == 0); // Create a continuation object by creating a frame similar the // way one is created in a function call. auto fragment = arena_->New(); stack_fragments_.push_back(fragment); std::vector> reversed_todo; reversed_todo.push_back( std::make_unique(&cast(stmt).body())); reversed_todo.push_back( std::make_unique(Scope(CurrentEnv(), &heap_))); fragment->StoreReversed(std::move(reversed_todo)); AllocationId continuation_address = heap_.AllocateValue(arena_->New(fragment)); // Bind the continuation object to the continuation variable todo_.CurrentScope().AddLocal(cast(stmt).name(), continuation_address); return todo_.FinishAction(); } case StatementKind::Run: { auto& run = cast(stmt); if (act.pos() == 0) { // Evaluate the argument of the run statement. return todo_.Spawn(std::make_unique(&run.argument())); } else if (act.pos() == 1) { // Push the continuation onto the current stack. return todo_.Resume(cast(act.results()[0])); } else { return todo_.FinishAction(); } } case StatementKind::Await: CHECK(act.pos() == 0); return todo_.Suspend(); } } // State transition. void Interpreter::Step() { Action& act = todo_.CurrentAction(); switch (act.kind()) { case Action::Kind::LValAction: StepLvalue(); break; case Action::Kind::ExpressionAction: StepExp(); break; case Action::Kind::PatternAction: StepPattern(); break; case Action::Kind::StatementAction: StepStmt(); break; case Action::Kind::ScopeAction: FATAL() << "ScopeAction escaped ActionStack"; } // switch } auto Interpreter::ExecuteAction(std::unique_ptr action, Env values, bool trace_steps) -> Nonnull { todo_.Start(std::move(action), Scope(values, &heap_)); while (!todo_.IsEmpty()) { Step(); if (trace_steps) { PrintState(llvm::outs()); } } // Clean up any remaining suspended continuations. for (Nonnull fragment : stack_fragments_) { fragment->Clear(); } return todo_.result(); } auto Interpreter::InterpProgram(llvm::ArrayRef> fs, Nonnull call_main) -> int { // Check that the interpreter is in a clean state. CHECK(globals_.IsEmpty()); CHECK(todo_.IsEmpty()); if (trace_) { llvm::outs() << "********** initializing globals **********\n"; } InitGlobals(fs); if (trace_) { llvm::outs() << "********** calling main function **********\n"; PrintState(llvm::outs()); } return cast( *ExecuteAction(std::make_unique(call_main), globals_, trace_)) .value(); } auto Interpreter::InterpExp(Env values, Nonnull e) -> Nonnull { return ExecuteAction(std::make_unique(e), values, /*trace_steps=*/false); } auto Interpreter::InterpPattern(Env values, Nonnull p) -> Nonnull { return ExecuteAction(std::make_unique(p), values, /*trace_steps=*/false); } } // namespace Carbon