mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 07:14:42 +01:00
Stop allocating sub-Values on the Heap. (#648)
This minimizes use of the Heap, and moves us toward not using it at compile time.
This commit is contained in:
@@ -17,6 +17,7 @@ cc_library(
|
||||
":function_definition",
|
||||
":member",
|
||||
":struct_definition",
|
||||
"//executable_semantics/interpreter:address",
|
||||
"//executable_semantics/interpreter:containers",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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<std::string, const Value*>;
|
||||
using Env = Dictionary<std::string, Address>;
|
||||
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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 <cstdint>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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_
|
||||
@@ -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 <ostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
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<std::string> components;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // EXECUTABLE_SEMANTICS_INTERPRETER_FIELD_PATH_H_
|
||||
@@ -26,7 +26,6 @@ State* state = nullptr;
|
||||
auto PatternMatch(const Value* pat, const Value* val, Env,
|
||||
std::list<std::string>*, int) -> std::optional<Env>;
|
||||
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<TupleElement> 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<Frame*> 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<const Value*> 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<Env> 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<Env> 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<Frame*> 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();
|
||||
|
||||
@@ -47,16 +47,10 @@ struct Frame {
|
||||
Stack<Action*> 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<Address> continuation;
|
||||
|
||||
Frame(std::string n, Stack<Scope*> s, Stack<Action*> 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<const Value*> values_;
|
||||
std::vector<bool> alive_;
|
||||
|
||||
@@ -70,10 +70,8 @@ auto ReifyType(const Value* t, int line_num) -> const Expression* {
|
||||
case ValKind::TupleValue: {
|
||||
std::vector<FieldInitializer> 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<Address> 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<TupleElement> 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);
|
||||
|
||||
@@ -97,15 +97,13 @@ auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
auto FindTupleField(const std::string& name, const Value* tuple)
|
||||
-> std::optional<Address> {
|
||||
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<TupleElement> 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<std::string>::const_iterator path_begin,
|
||||
std::vector<std::string>::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<TupleElement> 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<TupleElement>& 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,34 +7,31 @@
|
||||
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#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<std::pair<std::string, const Value*>>;
|
||||
|
||||
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<Address>;
|
||||
|
||||
// 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<TupleElement> 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<TupleElement> 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<IntValue, FunctionValue, PointerValue, BoolValue, StructValue,
|
||||
AlternativeValue, TupleValue, IntType, BoolType, TypeType,
|
||||
|
||||
Reference in New Issue
Block a user