// 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::CurrentScope() -> Scope& { for (Nonnull action : todo_) { if (action->scope().has_value()) { return *action->scope(); } } FATAL() << "No current scope"; } auto Interpreter::CurrentEnv() -> Env { return 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: "; llvm::ListSeparator sep(" :: "); for (Nonnull action : todo_) { out << sep << *action; } 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], 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 auto& func_def = cast(d); Env new_env = *env; // Bring the deduced parameters into scope. for (const auto& deduced : func_def.deduced_parameters()) { AllocationId a = heap_.AllocateValue(arena_->New(deduced.name())); 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 Declaration::Kind::ClassDeclaration: { const ClassDefinition& class_def = cast(d).definition(); std::vector fields; std::vector methods; for (Nonnull m : class_def.members()) { switch (m->kind()) { case Member::Kind::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_def.name(), std::move(fields), std::move(methods)); AllocationId a = heap_.AllocateValue(st); env->Set(class_def.name(), a); break; } case Declaration::Kind::ChoiceDeclaration: { const auto& choice = cast(d); std::vector alts; for (const auto& 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 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. 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::UnwindTodoTop() -> Nonnull { Nonnull act = todo_.Pop(); if (act->scope().has_value()) { CHECK(!act->scope()->deallocated); for (const auto& l : act->scope()->locals) { std::optional a = act->scope()->values.Get(l); CHECK(a); heap_.Deallocate(*a); } act->scope()->deallocated = true; } return act; } 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()); return arena_->New(act->results()); } 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()) { AllocationId a = heap_.AllocateValue(v); 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) { 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, 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).value(), val, 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 (size_t i = 0; i < pat_tup.elements().size(); ++i) { PatternAssignment(pat_tup.elements()[i], val_tup.elements()[i], 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.choice_name() == pat_alt.choice_name() && val_alt.alt_name() == pat_alt.alt_name()) << "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 = todo_.Top(); const Expression& exp = cast(*act).expression(); if (trace_) { 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]).value(); 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]).value(); std::string f = std::to_string(cast(*act->results()[1]).value()); 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} return Spawn{arena_->New( cast(exp).fields()[act->pos()])}; } 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::Convert(Nonnull value, Nonnull destination_type) const -> Nonnull { switch (value->kind()) { case Value::Kind::IntValue: case Value::Kind::FunctionValue: case Value::Kind::PointerValue: 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: // 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)); } } } auto Interpreter::StepExp() -> Transition { Nonnull act = todo_.Top(); const Expression& exp = cast(*act).expression(); if (trace_) { 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} 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 Done{tuple.elements()[i]}; } } 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} return Spawn{arena_->New( cast(exp).fields()[act->pos()])}; } else { return Done{CreateTuple(act, &exp)}; } } case Expression::Kind::StructLiteral: { const auto& literal = cast(exp); if (act->pos() < static_cast(literal.fields().size())) { return Spawn{arena_->New( &literal.fields()[act->pos()].expression())}; } 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 { 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))}; } } 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).value())}; 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).value())}; 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::AlternativeConstructorValue: { const auto& alt = cast(*act->results()[0]); return Done{arena_->New( alt.alt_name(), alt.choice_name(), act->results()[1])}; } 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]; } } else if (act->pos() == 3) { if (act->results().size() < 3) { // Control fell through without explicit return. return Done{TupleValue::Empty()}; } else { return Done{act->results()[2]}; } } 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::Intrinsic::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).value(); 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).return_type())}; } 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).value())}; case Expression::Kind::StringTypeLiteral: { CHECK(act->pos() == 0); return Done{arena_->New()}; } } // switch (exp->kind) } auto Interpreter::StepPattern() -> Transition { Nonnull act = todo_.Top(); const Pattern& pattern = cast(*act).pattern(); if (trace_) { 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} return Spawn{arena_->New(tuple.fields()[act->pos()])}; } else { return Done{arena_->New(act->results())}; } } case Pattern::Kind::AlternativePattern: { const auto& alternative = cast(pattern); if (act->pos() == 0) { return Spawn{arena_->New(&alternative.choice_type())}; } 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.alternative_name(), choice_type.name(), act->results()[1])}; } } case Pattern::Kind::ExpressionPattern: return Delegate{arena_->New( &cast(pattern).expression())}; } } static auto IsRunAction(Nonnull action) -> bool { const auto* statement = dyn_cast(action); return statement != nullptr && llvm::isa(statement->statement()); } auto Interpreter::StepStmt() -> Transition { Nonnull act = todo_.Top(); 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 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} act->StartScope(Scope(CurrentEnv())); return Spawn{arena_->New(&match_stmt.expression())}; } else { int clause_num = act->pos() - 1; if (clause_num >= static_cast(match_stmt.clauses().size())) { return Done{}; } 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()->values.Set(name, value); act->scope()->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).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{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} return UnwindPast{.ast_node = &cast(stmt).loop()}; } 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} return UnwindTo{.ast_node = &cast(stmt).loop()}; } case Statement::Kind::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{}; } // Initialize a scope when starting a block. if (act->pos() == 0) { act->StartScope(Scope(CurrentEnv())); } // Process the next statement in the block. The position will be // incremented as part of Spawn. return Spawn{ arena_->New(block.statements()[act->pos()])}; } case Statement::Kind::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{arena_->New(&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 = CurrentScope(); current_scope.values.Set(name, value); current_scope.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).expression())}; } else { return Done{}; } case Statement::Kind::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{arena_->New(&assign.lhs())}; } else if (act->pos() == 1) { // { { a :: ([] = e) :: C, E, F} :: S, H} // -> { { e :: (a = []) :: C, E, F} :: S, H} return Spawn{arena_->New(&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{}; } } 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).condition())}; } else { 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{ arena_->New(&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{ arena_->New(*cast(stmt).else_block())}; } 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).expression())}; } else { // { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H} // -> { {v :: C', E', F'} :: S, H} // TODO(geoffromer): convert the result to the function's return type, // once #880 gives us a way to find that type. const FunctionDeclaration& function = cast(stmt).function(); return UnwindPast{.ast_node = *function.body(), .result = act->results()[0]}; } 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 continuation_stack = arena_->New>>(); continuation_stack->push_back( arena_->New(&cast(stmt).body())); continuation_stack->push_back( arena_->New(Scope(CurrentEnv()))); AllocationId continuation_address = heap_.AllocateValue( arena_->New(continuation_stack)); // Bind the continuation object to the continuation variable CurrentScope().values.Set( cast(stmt).continuation_variable(), continuation_address); return Done{}; } case Statement::Kind::Run: { auto& run = cast(stmt); if (act->pos() == 0) { // Evaluate the argument of the run statement. return Spawn{arena_->New(&run.argument())}; } else if (act->pos() == 1) { // Push the continuation onto the current stack. std::vector>& continuation_vector = cast(*act->results()[0]).stack(); while (!continuation_vector.empty()) { todo_.Push(continuation_vector.back()); continuation_vector.pop_back(); } act->set_pos(2); return ManualTransition{}; } else { return Done{}; } } case Statement::Kind::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]); CHECK(continuation.stack().empty()); // Update the continuation with the paused stack. continuation.stack() = std::move(paused); return ManualTransition{}; } } class Interpreter::DoTransition { public: // Does not take ownership of interpreter. explicit DoTransition(Interpreter* interpreter) : interpreter(interpreter) {} void operator()(const Done& done) { Nonnull act = interpreter->UnwindTodoTop(); 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()(const Spawn& spawn) { Nonnull action = interpreter->todo_.Top(); action->set_pos(action->pos() + 1); interpreter->todo_.Push(spawn.child); } void operator()(const Delegate& delegate) { Nonnull act = interpreter->todo_.Pop(); if (act->scope().has_value()) { delegate.delegate->StartScope(*act->scope()); } interpreter->todo_.Push(delegate.delegate); } void operator()(const RunAgain&) { Nonnull 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->UnwindTodoTop(); if (unwind_past.result.has_value()) { interpreter->todo_.Top()->AddResult(*unwind_past.result); } } void operator()(const CallFunction& call) { Nonnull 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_); for (const auto& [name, value] : *matches) { new_scope.values.Set(name, value); new_scope.locals.push_back(name); } interpreter->todo_.Push( interpreter->arena_->New(std::move(new_scope))); CHECK(call.function->body()) << "Calling a function that's missing a body"; interpreter->todo_.Push( interpreter->arena_->New(*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()); statement_action != nullptr && &statement_action->statement() == ast_node) { break; } interpreter->UnwindTodoTop(); } } Nonnull interpreter; }; // State transition. void Interpreter::Step() { Nonnull act = 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; 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]}}); } } // switch } auto Interpreter::ExecuteAction(Nonnull action, Env values, bool trace_steps) -> Nonnull { todo_ = {}; todo_.Push(arena_->New(Scope(values))); todo_.Push(action); while (todo_.Count() > 1) { Step(); if (trace_steps) { PrintState(llvm::outs()); } } CHECK(todo_.Top()->results().size() == 1); return todo_.Top()->results()[0]; } 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(arena_->New(call_main), globals_, trace_)) .value(); } auto Interpreter::InterpExp(Env values, Nonnull e) -> Nonnull { return ExecuteAction(arena_->New(e), values, /*trace_steps=*/false); } auto Interpreter::InterpPattern(Env values, Nonnull p) -> Nonnull { return ExecuteAction(arena_->New(p), values, /*trace_steps=*/false); } } // namespace Carbon