diff --git a/executable_semantics/ast/BUILD b/executable_semantics/ast/BUILD index f43f338a3c87..5b2b4052cba3 100644 --- a/executable_semantics/ast/BUILD +++ b/executable_semantics/ast/BUILD @@ -17,6 +17,7 @@ cc_library( ":function_definition", ":member", ":struct_definition", + "//executable_semantics/interpreter:address", "//executable_semantics/interpreter:containers", ], ) diff --git a/executable_semantics/ast/declaration.h b/executable_semantics/ast/declaration.h index 2ef89a278a1a..ed4e4dd74a8a 100644 --- a/executable_semantics/ast/declaration.h +++ b/executable_semantics/ast/declaration.h @@ -11,6 +11,7 @@ #include "executable_semantics/ast/function_definition.h" #include "executable_semantics/ast/member.h" #include "executable_semantics/ast/struct_definition.h" +#include "executable_semantics/interpreter/address.h" #include "executable_semantics/interpreter/dictionary.h" namespace yy { @@ -21,7 +22,6 @@ namespace Carbon { struct Value; -using Address = unsigned int; using TypeEnv = Dictionary; using Env = Dictionary; diff --git a/executable_semantics/interpreter/BUILD b/executable_semantics/interpreter/BUILD index e07ece8d6a1c..376392c143e3 100644 --- a/executable_semantics/interpreter/BUILD +++ b/executable_semantics/interpreter/BUILD @@ -22,6 +22,7 @@ cc_library( "value.h", ], deps = [ + ":address", ":containers", "//common:check", "//executable_semantics:tracing_flag", @@ -33,6 +34,19 @@ cc_library( ], ) +cc_library( + name = "field_path", + hdrs = ["field_path.h"], +) + +cc_library( + name = "address", + hdrs = ["address.h"], + deps = [ + ":field_path", + ], +) + cc_library( name = "containers", srcs = [ diff --git a/executable_semantics/interpreter/address.h b/executable_semantics/interpreter/address.h new file mode 100644 index 000000000000..f8b19714928d --- /dev/null +++ b/executable_semantics/interpreter/address.h @@ -0,0 +1,68 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef EXECUTABLE_SEMANTICS_INTERPRETER_ADDRESS_H_ +#define EXECUTABLE_SEMANTICS_INTERPRETER_ADDRESS_H_ + +#include +#include +#include +#include + +#include "executable_semantics/interpreter/field_path.h" + +namespace Carbon { + +// An Address represents a memory address in the Carbon virtual machine. +// Addresses are used to access values stored in a Heap, and are obtained +// from a Heap (or by deriving them from other Addresses). +class Address { + public: + Address(const Address&) = default; + Address(Address&&) = default; + auto operator=(const Address&) -> Address& = default; + auto operator=(Address&&) -> Address& = default; + + // Returns true if the two addresses refer to the same memory location. + friend auto operator==(const Address& lhs, const Address& rhs) -> bool { + return lhs.index == rhs.index; + } + + friend auto operator!=(const Address& lhs, const Address& rhs) -> bool { + return !(lhs == rhs); + } + + // Prints a human-readable representation of `a` to `out`. + // + // Currently, that representation consists of an integer index identifying + // the whole memory allocation, and an optional FieldPath specifying a + // particular field within that allocation. + friend auto operator<<(std::ostream& out, const Address& a) -> std::ostream& { + out << "Address(" << a.index << ")" << a.field_path; + return out; + } + + // If *this represents the address of an object with a field named + // `field_name`, this method returns the address of that field. + auto SubobjectAddress(std::string field_name) const -> Address { + Address result = *this; + result.field_path.Append(std::move(field_name)); + return result; + } + + private: + // The representation of Address describes how to locate an object within + // the Heap, so its implementation details are tied to the implementation + // details of the Heap. + friend class Heap; + + explicit Address(uint64_t index) : index(index) {} + + uint64_t index; + FieldPath field_path; +}; + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_INTERPRETER_ADDRESS_H_ diff --git a/executable_semantics/interpreter/field_path.h b/executable_semantics/interpreter/field_path.h new file mode 100644 index 000000000000..926f5eb2df5c --- /dev/null +++ b/executable_semantics/interpreter/field_path.h @@ -0,0 +1,64 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef EXECUTABLE_SEMANTICS_INTERPRETER_FIELD_PATH_H_ +#define EXECUTABLE_SEMANTICS_INTERPRETER_FIELD_PATH_H_ + +#include +#include +#include + +namespace Carbon { + +// Given some initial Value, a FieldPath identifies a sub-Value within it, +// in much the same way that a file path identifies a file within some +// directory. FieldPaths are relative rather than absolute: the initial +// Value is specified by the context in which the FieldPath is used, not +// by the FieldPath itself. +// +// A FieldPath consists of a series of steps, which specify how to +// incrementally navigate from a Value to one of its fields. Currently +// there is only one kind of step, a string specifying a child field by name, +// but that may change as Carbon develops. Note that an empty FieldPath +// refers to the initial Value itself. +class FieldPath { + public: + // Constructs an empty FieldPath. + FieldPath() = default; + + // Constructs a FieldPath consisting of a single step. + explicit FieldPath(std::string name) : components({std::move(name)}) {} + + FieldPath(const FieldPath&) = default; + FieldPath(FieldPath&&) = default; + auto operator=(const FieldPath&) -> FieldPath& = default; + auto operator=(FieldPath&&) -> FieldPath& = default; + + // Returns whether *this is empty. + auto IsEmpty() const -> bool { return components.empty(); } + + // Appends `name` to the end of *this. + auto Append(std::string name) -> void { + components.push_back(std::move(name)); + } + + friend auto operator<<(std::ostream& out, const FieldPath& path) + -> std::ostream& { + for (const std::string& component : path.components) { + out << "." << component; + } + return out; + } + + private: + // The representation of FieldPath describes how to locate a Value within + // another Value, so its implementation details are tied to the implementation + // details of Value. + friend struct Value; + std::vector components; +}; + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_INTERPRETER_FIELD_PATH_H_ diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index a3d1d0d3e3aa..f5bf744483ea 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -26,7 +26,6 @@ State* state = nullptr; auto PatternMatch(const Value* pat, const Value* val, Env, std::list*, int) -> std::optional; auto Step() -> void; -auto GetMember(const Value* v, const std::string& f, int line_num) -> Address; // // Auxiliary Functions // @@ -37,27 +36,27 @@ auto Heap::AllocateValue(const Value* v) -> Address { // Consider whether to include a copy of the input v in this function // or to leave it up to the caller. CHECK(v != nullptr); - Address a = values_.size(); + Address a(values_.size()); values_.push_back(v); alive_.push_back(true); return a; } -auto Heap::Read(Address a, int line_num) -> const Value* { +auto Heap::Read(const Address& a, int line_num) -> const Value* { this->CheckAlive(a, line_num); - return values_[a]; + return values_[a.index]->GetField(a.field_path, line_num); } -auto Heap::Write(Address a, const Value* v, int line_num) -> void { +auto Heap::Write(const Address& a, const Value* v, int line_num) -> void { CHECK(v != nullptr); this->CheckAlive(a, line_num); - values_[a] = v; + values_[a.index] = values_[a.index]->SetField(a.field_path, v, line_num); } -void Heap::CheckAlive(Address address, int line_num) { - if (!alive_[address]) { +void Heap::CheckAlive(const Address& address, int line_num) { + if (!alive_[address.index]) { std::cerr << line_num << ": undefined behavior: access to dead value "; - PrintValue(values_[address], std::cerr); + PrintValue(values_[address.index], std::cerr); std::cerr << std::endl; exit(-1); } @@ -68,21 +67,16 @@ auto CopyVal(const Value* val, int line_num) -> const Value* { case ValKind::TupleValue: { std::vector elements; for (const TupleElement& element : val->GetTupleValue().elements) { - const Value* new_element = - CopyVal(state->heap.Read(element.address, line_num), line_num); - Address new_address = state->heap.AllocateValue(new_element); - elements.push_back({.name = element.name, .address = new_address}); + elements.push_back( + {.name = element.name, .value = CopyVal(element.value, line_num)}); } return Value::MakeTupleValue(std::move(elements)); } case ValKind::AlternativeValue: { - const Value* arg = CopyVal( - state->heap.Read(val->GetAlternativeValue().argument, line_num), - line_num); - Address argument_address = state->heap.AllocateValue(arg); + const Value* arg = CopyVal(val->GetAlternativeValue().argument, line_num); return Value::MakeAlternativeValue(val->GetAlternativeValue().alt_name, val->GetAlternativeValue().choice_name, - argument_address); + arg); } case ValKind::StructValue: { const Value* inits = CopyVal(val->GetStructValue().inits, line_num); @@ -128,28 +122,10 @@ auto CopyVal(const Value* val, int line_num) -> const Value* { } } -void Heap::DeallocateSubObjects(const Value* val) { - switch (val->tag()) { - case ValKind::AlternativeValue: - Deallocate(val->GetAlternativeValue().argument); - break; - case ValKind::StructValue: - DeallocateSubObjects(val->GetStructValue().inits); - break; - case ValKind::TupleValue: - for (const TupleElement& element : val->GetTupleValue().elements) { - Deallocate(element.address); - } - break; - default: - break; - } -} - -void Heap::Deallocate(Address address) { - if (alive_[address]) { - alive_[address] = false; - DeallocateSubObjects(values_[address]); +void Heap::Deallocate(const Address& address) { + CHECK(address.field_path.IsEmpty()); + if (alive_[address.index]) { + alive_[address.index] = false; } else { std::cerr << "runtime error, deallocating an already dead value" << std::endl; @@ -187,17 +163,17 @@ void PrintStack(Stack ls, std::ostream& out) { } void Heap::PrintHeap(std::ostream& out) { - for (Address i = 0; i < values_.size(); ++i) { - PrintAddress(i, out); + for (size_t i = 0; i < values_.size(); ++i) { + PrintAddress(Address(i), out); out << ", "; } } -auto Heap::PrintAddress(Address a, std::ostream& out) -> void { - if (!alive_[a]) { +auto Heap::PrintAddress(const Address& a, std::ostream& out) -> void { + if (!alive_[a.index]) { out << "!!"; } - PrintValue(values_[a], out); + PrintValue(values_[a.index], out); } auto CurrentEnv(State* state) -> Env { @@ -392,8 +368,7 @@ void CallFunction(int line_num, std::vector operas, const Value* arg = CopyVal(operas[1], line_num); const Value* av = Value::MakeAlternativeValue( operas[0]->GetAlternativeConstructorValue().alt_name, - operas[0]->GetAlternativeConstructorValue().choice_name, - state->heap.AllocateValue(arg)); + operas[0]->GetAlternativeConstructorValue().choice_name, arg); Frame* frame = state->stack.Top(); frame->todo.Push(Action::MakeValAction(av)); break; @@ -430,8 +405,7 @@ void CreateTuple(Frame* frame, Action* act, const Expression* exp) { auto f = exp->GetTupleLiteral().fields.begin(); for (auto i = act->results.begin(); i != act->results.end(); ++i, ++f) { - Address a = state->heap.AllocateValue(*i); // copy? - elements.push_back({.name = f->name, .address = a}); + elements.push_back({.name = f->name, .value = *i}); } const Value* tv = Value::MakeTupleValue(std::move(elements)); frame->todo.Pop(1); @@ -462,17 +436,19 @@ auto PatternMatch(const Value* p, const Value* v, Env values, << std::endl; exit(-1); } - for (const TupleElement& element : p->GetTupleValue().elements) { - auto a = FindTupleField(element.name, v); - if (a == std::nullopt) { - std::cerr << "runtime error: field " << element.name << "not in "; + for (const TupleElement& pattern_element : + p->GetTupleValue().elements) { + const Value* value_field = + v->GetTupleValue().FindField(pattern_element.name); + if (value_field == nullptr) { + std::cerr << "runtime error: field " << pattern_element.name + << "not in "; PrintValue(v, std::cerr); std::cerr << std::endl; exit(-1); } std::optional matches = PatternMatch( - state->heap.Read(element.address, line_num), - state->heap.Read(*a, line_num), values, vars, line_num); + pattern_element.value, value_field, values, vars, line_num); if (!matches) { return std::nullopt; } @@ -497,9 +473,8 @@ auto PatternMatch(const Value* p, const Value* v, Env values, return std::nullopt; } std::optional matches = PatternMatch( - state->heap.Read(p->GetAlternativeValue().argument, line_num), - state->heap.Read(v->GetAlternativeValue().argument, line_num), - values, vars, line_num); + p->GetAlternativeValue().argument, + v->GetAlternativeValue().argument, values, vars, line_num); if (!matches) { return std::nullopt; } @@ -553,16 +528,18 @@ void PatternAssignment(const Value* pat, const Value* val, int line_num) { << std::endl; exit(-1); } - for (const TupleElement& element : pat->GetTupleValue().elements) { - auto a = FindTupleField(element.name, val); - if (a == std::nullopt) { - std::cerr << "runtime error: field " << element.name << "not in "; + for (const TupleElement& pattern_element : + pat->GetTupleValue().elements) { + const Value* value_field = + val->GetTupleValue().FindField(pattern_element.name); + if (value_field == nullptr) { + std::cerr << "runtime error: field " << pattern_element.name + << "not in "; PrintValue(val, std::cerr); std::cerr << std::endl; exit(-1); } - PatternAssignment(state->heap.Read(element.address, line_num), - state->heap.Read(*a, line_num), line_num); + PatternAssignment(pattern_element.value, value_field, line_num); } break; } @@ -586,10 +563,8 @@ void PatternAssignment(const Value* pat, const Value* val, int line_num) { std::cerr << "internal error in pattern assignment" << std::endl; exit(-1); } - PatternAssignment( - state->heap.Read(pat->GetAlternativeValue().argument, line_num), - state->heap.Read(val->GetAlternativeValue().argument, line_num), - line_num); + PatternAssignment(pat->GetAlternativeValue().argument, + val->GetAlternativeValue().argument, line_num); break; } default: @@ -641,17 +616,17 @@ void StepLvalue() { if (act->pos == 0) { // { {e.f :: C, E, F} :: S, H} // -> { e :: [].f :: C, E, F} :: S, H} - frame->todo.Push(Action::MakeExpressionAction( - exp->GetFieldAccessExpression().aggregate)); + frame->todo.Push( + Action::MakeLValAction(exp->GetFieldAccessExpression().aggregate)); act->pos++; } else { // { v :: [].f :: C, E, F} :: S, H} // -> { { &v.f :: C, E, F} :: S, H } - const Value* str = act->results[0]; - Address a = GetMember(str, exp->GetFieldAccessExpression().field, - exp->line_num); + Address aggregate = act->results[0]->GetPointerValue(); + Address field = + aggregate.SubobjectAddress(exp->GetFieldAccessExpression().field); frame->todo.Pop(1); - frame->todo.Push(Action::MakeValAction(Value::MakePointerValue(a))); + frame->todo.Push(Action::MakeValAction(Value::MakePointerValue(field))); } break; } @@ -660,7 +635,7 @@ void StepLvalue() { // { {e[i] :: C, E, F} :: S, H} // -> { e :: [][i] :: C, E, F} :: S, H} frame->todo.Push( - Action::MakeExpressionAction(exp->GetIndexExpression().aggregate)); + Action::MakeLValAction(exp->GetIndexExpression().aggregate)); act->pos++; } else if (act->pos == 1) { frame->todo.Push( @@ -669,17 +644,11 @@ void StepLvalue() { } else if (act->pos == 2) { // { v :: [][i] :: C, E, F} :: S, H} // -> { { &v[i] :: C, E, F} :: S, H } - const Value* tuple = act->results[0]; + Address aggregate = act->results[0]->GetPointerValue(); std::string f = std::to_string(ToInteger(act->results[1])); - auto a = FindTupleField(f, tuple); - if (a == std::nullopt) { - std::cerr << "runtime error: field " << f << "not in "; - PrintValue(tuple, std::cerr); - std::cerr << std::endl; - exit(-1); - } + Address field = aggregate.SubobjectAddress(f); frame->todo.Pop(1); - frame->todo.Push(Action::MakeValAction(Value::MakePointerValue(*a))); + frame->todo.Push(Action::MakeValAction(Value::MakePointerValue(field))); } break; } @@ -767,16 +736,15 @@ void StepExp() { // { { v :: [][i] :: C, E, F} :: S, H} // -> { { v_i :: C, E, F} : S, H} std::string f = std::to_string(ToInteger(act->results[1])); - auto a = FindTupleField(f, tuple); - if (a == std::nullopt) { + const Value* field = tuple->GetTupleValue().FindField(f); + if (field == nullptr) { std::cerr << "runtime error, field " << f << " not in "; PrintValue(tuple, std::cerr); std::cerr << std::endl; exit(-1); } frame->todo.Pop(1); - const Value* element = state->heap.Read(*a, exp->line_num); - frame->todo.Push(Action::MakeValAction(element)); + frame->todo.Push(Action::MakeValAction(field)); break; } default: @@ -825,12 +793,10 @@ void StepExp() { } else { // { { v :: [].f :: C, E, F} :: S, H} // -> { { v_f :: C, E, F} : S, H} - Address element = - GetMember(act->results[0], exp->GetFieldAccessExpression().field, - exp->line_num); + const Value* element = act->results[0]->GetField( + FieldPath(exp->GetFieldAccessExpression().field), exp->line_num); frame->todo.Pop(1); - frame->todo.Push( - Action::MakeValAction(state->heap.Read(element, exp->line_num))); + frame->todo.Push(Action::MakeValAction(element)); } break; } @@ -1293,55 +1259,14 @@ void StepStmt() { std::vector paused; do { paused.push_back(state->stack.Pop()); - } while (!paused.back()->IsContinuation()); + } while (paused.back()->continuation == std::nullopt); // Update the continuation with the paused stack. - state->heap.Write(paused.back()->continuation, + state->heap.Write(*paused.back()->continuation, Value::MakeContinuationValue(paused), stmt->line_num); break; } } -auto GetMember(const Value* v, const std::string& f, int line_num) -> Address { - switch (v->tag()) { - case ValKind::StructValue: { - auto a = FindTupleField(f, v->GetStructValue().inits); - if (a == std::nullopt) { - std::cerr << "runtime error, member " << f << " not in "; - PrintValue(v, std::cerr); - std::cerr << std::endl; - exit(-1); - } - return *a; - } - case ValKind::TupleValue: { - auto a = FindTupleField(f, v); - if (a == std::nullopt) { - std::cerr << "field " << f << " not in "; - PrintValue(v, std::cerr); - std::cerr << std::endl; - exit(-1); - } - return *a; - } - case ValKind::ChoiceType: { - if (FindInVarValues(f, v->GetChoiceType().alternatives) == nullptr) { - std::cerr << "alternative " << f << " not in "; - PrintValue(v, std::cerr); - std::cerr << std::endl; - exit(-1); - } - auto ac = - Value::MakeAlternativeConstructorValue(f, v->GetChoiceType().name); - return state->heap.AllocateValue(ac); - } - default: - std::cerr << "field access not allowed for value "; - PrintValue(v, std::cerr); - std::cerr << std::endl; - exit(-1); - } -} - // State transition. void Step() { Frame* frame = state->stack.Top(); diff --git a/executable_semantics/interpreter/interpreter.h b/executable_semantics/interpreter/interpreter.h index d6608d1d60fd..87a2fa507a2b 100644 --- a/executable_semantics/interpreter/interpreter.h +++ b/executable_semantics/interpreter/interpreter.h @@ -47,16 +47,10 @@ struct Frame { Stack todo; // If this frame is the bottom frame of a continuation, then it stores // the address of the continuation. - // Otherwise the `continuation` field is the sentinel UINT_MAX. - Address continuation; - // Returns whether this frame is the bottom frame of a continuation. - auto IsContinuation() -> bool { return continuation != UINT_MAX; } + std::optional
continuation; Frame(std::string n, Stack s, Stack c) - : name(std::move(std::move(n))), - scopes(s), - todo(c), - continuation(UINT_MAX) {} + : name(std::move(std::move(n))), scopes(s), todo(c), continuation() {} }; // A Heap represents the abstract machine's dynamically allocated memory. @@ -70,30 +64,27 @@ class Heap { // Returns the value at the given address in the heap after // checking that it is alive. - auto Read(Address a, int line_num) -> const Value*; + auto Read(const Address& a, int line_num) -> const Value*; // Writes the given value at the address in the heap after // checking that the address is alive. - auto Write(Address a, const Value* v, int line_num) -> void; + auto Write(const Address& a, const Value* v, int line_num) -> void; // Put the given value on the heap and mark it as alive. auto AllocateValue(const Value* v) -> Address; // Marks the object at this address, and all of its sub-objects, as dead. - auto Deallocate(Address address) -> void; + auto Deallocate(const Address& address) -> void; // Print the value at the given address to the stream `out`. - auto PrintAddress(Address a, std::ostream& out) -> void; + auto PrintAddress(const Address& a, std::ostream& out) -> void; // Print all the values on the heap to the stream `out`. auto PrintHeap(std::ostream& out) -> void; private: // Signal an error if the address is no longer alive. - void CheckAlive(Address address, int line_num); - - // Marks all sub-objects of this value as dead. - void DeallocateSubObjects(const Value* val); + void CheckAlive(const Address& address, int line_num); std::vector values_; std::vector alive_; diff --git a/executable_semantics/interpreter/typecheck.cpp b/executable_semantics/interpreter/typecheck.cpp index 47c583f91474..d1d29b05ed18 100644 --- a/executable_semantics/interpreter/typecheck.cpp +++ b/executable_semantics/interpreter/typecheck.cpp @@ -70,10 +70,8 @@ auto ReifyType(const Value* t, int line_num) -> const Expression* { case ValKind::TupleValue: { std::vector args; for (const TupleElement& field : t->GetTupleValue().elements) { - args.push_back( - {.name = field.name, - .expression = ReifyType(state->heap.Read(field.address, line_num), - line_num)}); + args.push_back({.name = field.name, + .expression = ReifyType(field.value, line_num)}); } return Expression::MakeTupleLiteral(0, args); } @@ -169,15 +167,14 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, case ValKind::TupleValue: { auto i = ToInteger(InterpExp(values, e->GetIndexExpression().offset)); std::string f = std::to_string(i); - std::optional
field_address = FindTupleField(f, t); - if (field_address == std::nullopt) { + const Value* field_t = t->GetTupleValue().FindField(f); + if (field_t == nullptr) { std::cerr << e->line_num << ": compilation error, field " << f << " is not in the tuple "; PrintValue(t, std::cerr); std::cerr << std::endl; exit(-1); } - auto field_t = state->heap.Read(*field_address, e->line_num); auto new_e = Expression::MakeIndexExpression( e->line_num, res.exp, Expression::MakeIntLiteral(e->line_num, i)); return TCResult(new_e, field_t, res.types); @@ -217,16 +214,13 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, << " but got " << arg->name << std::endl; exit(-1); } - arg_expected = state->heap.Read( - expected->GetTupleValue().elements[i].address, e->line_num); + arg_expected = expected->GetTupleValue().elements[i].value; } auto arg_res = TypeCheckExp(arg->expression, new_types, values, arg_expected, context); new_types = arg_res.types; new_args.push_back({.name = arg->name, .expression = arg_res.exp}); - arg_types.push_back( - {.name = arg->name, - .address = state->heap.AllocateValue(arg_res.type)}); + arg_types.push_back({.name = arg->name, .value = arg_res.type}); } auto tuple_e = Expression::MakeTupleLiteral(e->line_num, new_args); auto tuple_t = Value::MakeTupleValue(std::move(arg_types)); @@ -264,9 +258,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, if (e->GetFieldAccessExpression().field == field.name) { auto new_e = Expression::MakeFieldAccessExpression( e->line_num, res.exp, e->GetFieldAccessExpression().field); - return TCResult(new_e, - state->heap.Read(field.address, e->line_num), - res.types); + return TCResult(new_e, field.value, res.types); } } std::cerr << e->line_num << ": compilation error, struct " @@ -777,8 +769,7 @@ auto StructDeclaration::TopLevel(TypeCheckContext& tops) const -> void { tops.values.Set(Name(), a); // Is this obsolete? std::vector field_types; for (const auto& [field_name, field_value] : st->GetStructType().fields) { - field_types.push_back({.name = field_name, - .address = state->heap.AllocateValue(field_value)}); + field_types.push_back({.name = field_name, .value = field_value}); } auto fun_ty = Value::MakeFunctionType( Value::MakeTupleValue(std::move(field_types)), st); diff --git a/executable_semantics/interpreter/value.cpp b/executable_semantics/interpreter/value.cpp index a5f01c09f7bf..8a097ebf8824 100644 --- a/executable_semantics/interpreter/value.cpp +++ b/executable_semantics/interpreter/value.cpp @@ -97,15 +97,13 @@ auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool { } } -auto FindTupleField(const std::string& name, const Value* tuple) - -> std::optional
{ - CHECK(tuple->tag() == ValKind::TupleValue); - for (const TupleElement& element : tuple->GetTupleValue().elements) { +auto TupleValue::FindField(const std::string& name) const -> const Value* { + for (const TupleElement& element : elements) { if (element.name == name) { - return element.address; + return element.value; } } - return std::nullopt; + return nullptr; } auto Value::MakeIntValue(int i) -> const Value* { @@ -148,7 +146,7 @@ auto Value::MakeTupleValue(std::vector elements) -> const Value* { } auto Value::MakeAlternativeValue(std::string alt_name, std::string choice_name, - Address argument) -> const Value* { + const Value* argument) -> const Value* { auto* v = new Value(); v->value = AlternativeValue({.alt_name = std::move(alt_name), .choice_name = std::move(choice_name), @@ -246,6 +244,106 @@ auto Value::MakeChoiceType(std::string name, VarValues alts) -> const Value* { return v; } +namespace { + +auto GetMember(const Value* v, const std::string& f, int line_num) + -> const Value* { + switch (v->tag()) { + case ValKind::StructValue: { + const Value* field = + v->GetStructValue().inits->GetTupleValue().FindField(f); + if (field == nullptr) { + std::cerr << "runtime error, member " << f << " not in "; + PrintValue(v, std::cerr); + std::cerr << std::endl; + exit(-1); + } + return field; + } + case ValKind::TupleValue: { + const Value* field = v->GetTupleValue().FindField(f); + if (field == nullptr) { + std::cerr << "field " << f << " not in "; + PrintValue(v, std::cerr); + std::cerr << std::endl; + exit(-1); + } + return field; + } + case ValKind::ChoiceType: { + if (FindInVarValues(f, v->GetChoiceType().alternatives) == nullptr) { + std::cerr << "alternative " << f << " not in "; + PrintValue(v, std::cerr); + std::cerr << std::endl; + exit(-1); + } + return Value::MakeAlternativeConstructorValue(f, v->GetChoiceType().name); + } + default: + std::cerr << "field access not allowed for value "; + PrintValue(v, std::cerr); + std::cerr << std::endl; + exit(-1); + } +} + +} // namespace + +auto Value::GetField(const FieldPath& path, int line_num) const + -> const Value* { + const Value* value = this; + for (const std::string& field : path.components) { + value = GetMember(value, field, line_num); + } + return value; +} + +namespace { + +auto SetFieldImpl(const Value* value, + std::vector::const_iterator path_begin, + std::vector::const_iterator path_end, + const Value* field_value, int line_num) -> const Value* { + if (path_begin == path_end) { + return field_value; + } + switch (value->tag()) { + case ValKind::StructValue: { + return SetFieldImpl(value->GetStructValue().inits, path_begin, path_end, + field_value, line_num); + } + case ValKind::TupleValue: { + std::vector elements = value->GetTupleValue().elements; + auto it = std::find_if(elements.begin(), elements.end(), + [path_begin](const TupleElement& element) { + return element.name == *path_begin; + }); + if (it == elements.end()) { + std::cerr << "field " << *path_begin << " not in "; + PrintValue(value, std::cerr); + std::cerr << std::endl; + exit(-1); + } + it->value = SetFieldImpl(it->value, path_begin + 1, path_end, field_value, + line_num); + return Value::MakeTupleValue(elements); + } + default: + std::cerr << "field access not allowed for value "; + PrintValue(value, std::cerr); + std::cerr << std::endl; + exit(-1); + } +} + +} // namespace + +auto Value::SetField(const FieldPath& path, const Value* field_value, + int line_num) const -> const Value* { + return SetFieldImpl(this, path.components.begin(), path.components.end(), + field_value, line_num); +} + auto PrintValue(const Value* val, std::ostream& out) -> void { switch (val->tag()) { case ValKind::AlternativeConstructorValue: { @@ -261,7 +359,7 @@ auto PrintValue(const Value* val, std::ostream& out) -> void { case ValKind::AlternativeValue: { out << "alt " << val->GetAlternativeValue().choice_name << "." << val->GetAlternativeValue().alt_name << " "; - state->heap.PrintAddress(val->GetAlternativeValue().argument, out); + PrintValue(val->GetAlternativeValue().argument, out); break; } case ValKind::StructValue: { @@ -280,7 +378,7 @@ auto PrintValue(const Value* val, std::ostream& out) -> void { } out << element.name << " = "; - state->heap.PrintAddress(element.address, out); + PrintValue(element.value, out); } out << ")"; break; @@ -364,9 +462,8 @@ auto TypeEqual(const Value* t1, const Value* t2) -> bool { t2->GetTupleValue().elements[i].name) { return false; } - if (!TypeEqual( - state->heap.Read(t1->GetTupleValue().elements[i].address, 0), - state->heap.Read(t2->GetTupleValue().elements[i].address, 0))) { + if (!TypeEqual(t1->GetTupleValue().elements[i].value, + t2->GetTupleValue().elements[i].value)) { return false; } } @@ -401,8 +498,7 @@ static auto FieldsValueEqual(const std::vector& ts1, if (iter == ts2.end()) { return false; } - if (!ValueEqual(state->heap.Read(element.address, line_num), - state->heap.Read(iter->address, line_num), line_num)) { + if (!ValueEqual(element.value, iter->value, line_num)) { return false; } } diff --git a/executable_semantics/interpreter/value.h b/executable_semantics/interpreter/value.h index 82c03d0e6368..dd398ba85f7f 100644 --- a/executable_semantics/interpreter/value.h +++ b/executable_semantics/interpreter/value.h @@ -7,34 +7,31 @@ #include #include +#include #include #include #include "executable_semantics/ast/statement.h" +#include "executable_semantics/interpreter/address.h" +#include "executable_semantics/interpreter/field_path.h" #include "executable_semantics/interpreter/stack.h" namespace Carbon { struct Value; -using Address = unsigned int; using VarValues = std::list>; auto FindInVarValues(const std::string& field, const VarValues& inits) -> const Value*; auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool; -// Finds the field in `*tuple` named `name`, and returns its address, or -// nullopt if there is no such field. `*tuple` must be a tuple value. -auto FindTupleField(const std::string& name, const Value* tuple) - -> std::optional
; - // A TupleElement represents the value of a single tuple field. struct TupleElement { // The field name. std::string name; - // Location of the field's value. - Address address; + // The field's value. + const Value* value; }; enum class ValKind { @@ -99,12 +96,16 @@ struct AlternativeValue { static constexpr ValKind Kind = ValKind::AlternativeValue; std::string alt_name; std::string choice_name; - Address argument; + const Value* argument; }; struct TupleValue { static constexpr ValKind Kind = ValKind::TupleValue; std::vector elements; + + // Returns the value of the field named `name` in this tuple, or + // null if there is no such field. + auto FindField(const std::string& name) const -> const Value*; }; struct BindingPlaceholderValue { @@ -177,8 +178,8 @@ struct Value { -> const Value*; static auto MakeTupleValue(std::vector elts) -> const Value*; static auto MakeAlternativeValue(std::string alt_name, - std::string choice_name, Address argument) - -> const Value*; + std::string choice_name, + const Value* argument) -> const Value*; static auto MakeAlternativeConstructorValue(std::string alt_name, std::string choice_name) -> const Value*; @@ -218,6 +219,15 @@ struct Value { return std::visit([](const auto& t) { return t.Kind; }, value); } + // Returns the sub-Value specified by `path`, which must be a valid field + // path for *this. + auto GetField(const FieldPath& path, int line_num) const -> const Value*; + + // Returns a copy of *this, but with the sub-Value specified by `path` + // set to `field_value`. `path` must be a valid field path for *this. + auto SetField(const FieldPath& path, const Value* field_value, + int line_num) const -> const Value*; + private: std::variant