mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-01 08:24:59 +01:00
improved checking for liveness when reading and writing memory (#448)
* improved checking for liveness when reading and writing memory * moving some functions to be methods of State * finished moving functions into State * Update executable_semantics/interpreter/interpreter.h Co-authored-by: Geoff Romer <gromer@google.com> * moved some comments, other minor edits Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
committed by
GitHub
co-authored by
Geoff Romer
parent
7e81fb9c62
commit
27fd9de5bf
@@ -31,32 +31,52 @@ void HandleValue();
|
||||
// Auxiliary Functions
|
||||
//
|
||||
|
||||
auto AllocateValue(const Value* v) -> Address {
|
||||
auto State::AllocateValue(const Value* v) -> Address {
|
||||
// Putting the following two side effects together in this function
|
||||
// ensures that we don't do anything else in between, which is really bad!
|
||||
// Consider whether to include a copy of the input v in this function
|
||||
// or to leave it up to the caller.
|
||||
Address a = state->heap.size();
|
||||
state->heap.push_back(new Value(*v));
|
||||
state->alive.push_back(true);
|
||||
Address a = heap.size();
|
||||
heap.push_back(new Value(*v));
|
||||
this->alive.push_back(true);
|
||||
return a;
|
||||
}
|
||||
|
||||
auto State::ReadFromMemory(Address a, int line_num) -> const Value* {
|
||||
this->CheckAlive(a, line_num);
|
||||
return heap[a];
|
||||
}
|
||||
|
||||
auto State::WriteToMemory(Address a, const Value* v, int line_num) -> void {
|
||||
this->CheckAlive(a, line_num);
|
||||
heap[a] = v;
|
||||
}
|
||||
|
||||
void State::CheckAlive(Address address, int line_num) {
|
||||
if (!this->alive[address]) {
|
||||
std::cerr << line_num << ": undefined behavior: access to dead value ";
|
||||
PrintValue(heap[address], std::cerr);
|
||||
std::cerr << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
auto CopyVal(const Value* val, int line_num) -> const Value* {
|
||||
switch (val->tag) {
|
||||
case ValKind::TupleV: {
|
||||
auto elts = new std::vector<std::pair<std::string, Address>>();
|
||||
for (auto& i : *val->u.tuple.elts) {
|
||||
CheckAlive(i.second, line_num);
|
||||
const Value* elt = CopyVal(state->heap[i.second], line_num);
|
||||
Address new_address = AllocateValue(elt);
|
||||
const Value* elt =
|
||||
CopyVal(state->ReadFromMemory(i.second, line_num), line_num);
|
||||
Address new_address = state->AllocateValue(elt);
|
||||
elts->push_back(make_pair(i.first, new_address));
|
||||
}
|
||||
return MakeTupleVal(elts);
|
||||
}
|
||||
case ValKind::AltV: {
|
||||
const Value* arg = CopyVal(state->heap[val->u.alt.argument], line_num);
|
||||
Address argument_address = AllocateValue(arg);
|
||||
const Value* arg = CopyVal(
|
||||
state->ReadFromMemory(val->u.alt.argument, line_num), line_num);
|
||||
Address argument_address = state->AllocateValue(arg);
|
||||
return MakeAltVal(*val->u.alt.alt_name, *val->u.alt.choice_name,
|
||||
argument_address);
|
||||
}
|
||||
@@ -102,20 +122,18 @@ auto CopyVal(const Value* val, int line_num) -> const Value* {
|
||||
}
|
||||
}
|
||||
|
||||
void KillObject(Address address);
|
||||
|
||||
// Marks all of the sub-objects of this value as dead.
|
||||
void KillSubObjects(const Value* val) {
|
||||
switch (val->tag) {
|
||||
case ValKind::AltV:
|
||||
KillObject(val->u.alt.argument);
|
||||
state->KillObject(val->u.alt.argument);
|
||||
break;
|
||||
case ValKind::StructV:
|
||||
KillSubObjects(val->u.struct_val.inits);
|
||||
break;
|
||||
case ValKind::TupleV:
|
||||
for (auto& elt : *val->u.tuple.elts) {
|
||||
KillObject(elt.second);
|
||||
state->KillObject(elt.second);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -123,11 +141,10 @@ void KillSubObjects(const Value* val) {
|
||||
}
|
||||
}
|
||||
|
||||
// Marks the object at this address, and all of its sub-objects, as dead.
|
||||
void KillObject(Address address) {
|
||||
if (state->alive[address]) {
|
||||
state->alive[address] = false;
|
||||
KillSubObjects(state->heap[address]);
|
||||
void State::KillObject(Address address) {
|
||||
if (this->alive[address]) {
|
||||
this->alive[address] = false;
|
||||
KillSubObjects(heap[address]);
|
||||
} else {
|
||||
std::cerr << "runtime error, killing an already dead value" << std::endl;
|
||||
exit(-1);
|
||||
@@ -135,9 +152,9 @@ void KillObject(Address address) {
|
||||
}
|
||||
|
||||
void PrintEnv(Env values, std::ostream& out) {
|
||||
for (const auto& [name, value] : values) {
|
||||
for (const auto& [name, address] : values) {
|
||||
out << name << ": ";
|
||||
PrintValue(state->heap[value], out);
|
||||
state->PrintAddress(address, out);
|
||||
out << ", ";
|
||||
}
|
||||
}
|
||||
@@ -163,7 +180,7 @@ void PrintStack(Stack<Frame*> ls, std::ostream& out) {
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHeap(const std::vector<const Value*>& heap, std::ostream& out) {
|
||||
void State::PrintHeap(std::ostream& out) {
|
||||
for (auto& iter : heap) {
|
||||
if (iter) {
|
||||
PrintValue(iter, out);
|
||||
@@ -184,7 +201,7 @@ void PrintState(std::ostream& out) {
|
||||
out << "stack: ";
|
||||
PrintStack(state->stack, out);
|
||||
out << std::endl << "heap: ";
|
||||
PrintHeap(state->heap, out);
|
||||
state->PrintHeap(out);
|
||||
if (!state->stack.IsEmpty() && !state->stack.Top()->scopes.IsEmpty()) {
|
||||
out << std::endl << "values: ";
|
||||
PrintEnv(CurrentEnv(state), out);
|
||||
@@ -218,7 +235,6 @@ auto ValToBool(const Value* v, int line_num) -> int {
|
||||
}
|
||||
|
||||
auto ValToPtr(const Value* v, int line_num) -> Address {
|
||||
CheckAlive(v->u.ptr, line_num);
|
||||
switch (v->tag) {
|
||||
case ValKind::PtrV:
|
||||
return v->u.ptr;
|
||||
@@ -284,7 +300,7 @@ auto ChoiceDeclaration::InitGlobals(Env& globals) const -> void {
|
||||
alts->push_back(make_pair(kv.first, t));
|
||||
}
|
||||
auto ct = MakeChoiceTypeVal(name, alts);
|
||||
auto a = AllocateValue(ct);
|
||||
auto a = state->AllocateValue(ct);
|
||||
globals.Set(name, a);
|
||||
}
|
||||
|
||||
@@ -302,7 +318,7 @@ auto StructDeclaration::InitGlobals(Env& globals) const -> void {
|
||||
}
|
||||
}
|
||||
auto st = MakeStructTypeVal(*definition.name, fields, methods);
|
||||
auto a = AllocateValue(st);
|
||||
auto a = state->AllocateValue(st);
|
||||
globals.Set(*definition.name, a);
|
||||
}
|
||||
|
||||
@@ -310,7 +326,7 @@ auto FunctionDeclaration::InitGlobals(Env& globals) const -> void {
|
||||
Env values;
|
||||
auto pt = InterpExp(values, definition->param_pattern);
|
||||
auto f = MakeFunVal(definition->name, pt, definition->body);
|
||||
Address a = AllocateValue(f);
|
||||
Address a = state->AllocateValue(f);
|
||||
globals.Set(definition->name, a);
|
||||
}
|
||||
|
||||
@@ -318,7 +334,7 @@ auto FunctionDeclaration::InitGlobals(Env& globals) const -> void {
|
||||
// result of evaluating the initializer.
|
||||
auto VariableDeclaration::InitGlobals(Env& globals) const -> void {
|
||||
auto v = InterpExp(globals, initializer);
|
||||
Address a = AllocateValue(v);
|
||||
Address a = state->AllocateValue(v);
|
||||
globals.Set(name, a);
|
||||
}
|
||||
|
||||
@@ -355,9 +371,9 @@ void CallFunction(int line_num, std::vector<const Value*> operas,
|
||||
}
|
||||
case ValKind::AltConsV: {
|
||||
const Value* arg = CopyVal(operas[1], line_num);
|
||||
const Value* av =
|
||||
MakeAltVal(*operas[0]->u.alt_cons.alt_name,
|
||||
*operas[0]->u.alt_cons.choice_name, AllocateValue(arg));
|
||||
const Value* av = MakeAltVal(*operas[0]->u.alt_cons.alt_name,
|
||||
*operas[0]->u.alt_cons.choice_name,
|
||||
state->AllocateValue(arg));
|
||||
Frame* frame = state->stack.Top();
|
||||
frame->todo.Push(MakeValAct(av));
|
||||
break;
|
||||
@@ -377,7 +393,7 @@ void KillScope(int line_num, Scope* scope) {
|
||||
std::cerr << "internal error in KillScope" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
KillObject(*a);
|
||||
state->KillObject(*a);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -393,7 +409,7 @@ void CreateTuple(Frame* frame, Action* act, const Expression* /*exp*/) {
|
||||
auto elts = new std::vector<std::pair<std::string, Address>>();
|
||||
auto f = act->u.exp->u.tuple.fields->begin();
|
||||
for (auto i = act->results.begin(); i != act->results.end(); ++i, ++f) {
|
||||
Address a = AllocateValue(*i); // copy?
|
||||
Address a = state->AllocateValue(*i); // copy?
|
||||
elts->push_back(make_pair(f->first, a));
|
||||
}
|
||||
const Value* tv = MakeTupleVal(elts);
|
||||
@@ -411,7 +427,7 @@ auto PatternMatch(const Value* p, const Value* v, Env values,
|
||||
-> std::optional<Env> {
|
||||
switch (p->tag) {
|
||||
case ValKind::VarPatV: {
|
||||
Address a = AllocateValue(CopyVal(v, line_num));
|
||||
Address a = state->AllocateValue(CopyVal(v, line_num));
|
||||
vars->push_back(*p->u.var_pat.name);
|
||||
values.Set(*p->u.var_pat.name, a);
|
||||
return values;
|
||||
@@ -432,9 +448,9 @@ auto PatternMatch(const Value* p, const Value* v, Env values,
|
||||
std::cerr << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
std::optional<Env> matches =
|
||||
PatternMatch(state->heap[elt.second], state->heap[*a], values,
|
||||
vars, line_num);
|
||||
std::optional<Env> matches = PatternMatch(
|
||||
state->ReadFromMemory(elt.second, line_num),
|
||||
state->ReadFromMemory(*a, line_num), values, vars, line_num);
|
||||
if (!matches) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -456,9 +472,10 @@ auto PatternMatch(const Value* p, const Value* v, Env values,
|
||||
*p->u.alt.alt_name != *v->u.alt.alt_name) {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<Env> matches = PatternMatch(
|
||||
state->heap[p->u.alt.argument], state->heap[v->u.alt.argument],
|
||||
values, vars, line_num);
|
||||
std::optional<Env> matches =
|
||||
PatternMatch(state->ReadFromMemory(p->u.alt.argument, line_num),
|
||||
state->ReadFromMemory(v->u.alt.argument, line_num),
|
||||
values, vars, line_num);
|
||||
if (!matches) {
|
||||
return std::nullopt;
|
||||
}
|
||||
@@ -498,7 +515,8 @@ auto PatternMatch(const Value* p, const Value* v, Env values,
|
||||
void PatternAssignment(const Value* pat, const Value* val, int line_num) {
|
||||
switch (pat->tag) {
|
||||
case ValKind::PtrV:
|
||||
state->heap[ValToPtr(pat, line_num)] = CopyVal(val, line_num);
|
||||
state->WriteToMemory(ValToPtr(pat, line_num), CopyVal(val, line_num),
|
||||
line_num);
|
||||
break;
|
||||
case ValKind::TupleV: {
|
||||
switch (val->tag) {
|
||||
@@ -516,8 +534,8 @@ void PatternAssignment(const Value* pat, const Value* val, int line_num) {
|
||||
std::cerr << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
PatternAssignment(state->heap[elt.second], state->heap[*a],
|
||||
line_num);
|
||||
PatternAssignment(state->ReadFromMemory(elt.second, line_num),
|
||||
state->ReadFromMemory(*a, line_num), line_num);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -539,8 +557,9 @@ 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[pat->u.alt.argument],
|
||||
state->heap[val->u.alt.argument], line_num);
|
||||
PatternAssignment(
|
||||
state->ReadFromMemory(pat->u.alt.argument, line_num),
|
||||
state->ReadFromMemory(val->u.alt.argument, line_num), line_num);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -584,7 +603,6 @@ void StepLvalue() {
|
||||
exit(-1);
|
||||
}
|
||||
const Value* v = MakePtrVal(*pointer);
|
||||
CheckAlive(*pointer, exp->line_num);
|
||||
frame->todo.Pop();
|
||||
frame->todo.Push(MakeValAct(v));
|
||||
break;
|
||||
@@ -681,7 +699,7 @@ void StepExp() {
|
||||
<< *(exp->u.variable.name) << "`" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
const Value* pointee = state->heap[*pointer];
|
||||
const Value* pointee = state->ReadFromMemory(*pointer, exp->line_num);
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(MakeValAct(pointee));
|
||||
break;
|
||||
@@ -901,7 +919,7 @@ void StepStmt() {
|
||||
todo.Push(MakeStmtAct(stmt->u.continuation.body));
|
||||
Frame* continuation_frame = new Frame("__continuation", scopes, todo);
|
||||
Address continuation_address =
|
||||
AllocateValue(MakeContinuation({continuation_frame}));
|
||||
state->AllocateValue(MakeContinuation({continuation_frame}));
|
||||
// Store the continuation's address in the frame.
|
||||
continuation_frame->continuation = continuation_address;
|
||||
// Bind the continuation object to the continuation variable
|
||||
@@ -924,13 +942,14 @@ void StepStmt() {
|
||||
paused.push_back(state->stack.Pop());
|
||||
} while (!paused.back()->IsContinuation());
|
||||
// Update the continuation with the paused stack.
|
||||
state->heap[paused.back()->continuation] = MakeContinuation(paused);
|
||||
state->WriteToMemory(paused.back()->continuation,
|
||||
MakeContinuation(paused), stmt->line_num);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto GetMember(Address a, const std::string& f) -> Address {
|
||||
const Value* v = state->heap[a];
|
||||
auto GetMember(Address a, const std::string& f, int line_num) -> Address {
|
||||
const Value* v = state->ReadFromMemory(a, line_num);
|
||||
switch (v->tag) {
|
||||
case ValKind::StructV: {
|
||||
auto a = FindTupleField(f, v->u.struct_val.inits);
|
||||
@@ -960,7 +979,7 @@ auto GetMember(Address a, const std::string& f) -> Address {
|
||||
exit(-1);
|
||||
}
|
||||
auto ac = MakeAltCons(f, *v->u.choice_type.name);
|
||||
return AllocateValue(ac);
|
||||
return state->AllocateValue(ac);
|
||||
}
|
||||
default:
|
||||
std::cerr << "field access not allowed for value ";
|
||||
@@ -1013,13 +1032,13 @@ void HandleValue() {
|
||||
}
|
||||
switch (act->tag) {
|
||||
case ActionKind::DeleteTmpAction: {
|
||||
KillObject(act->u.delete_tmp);
|
||||
state->KillObject(act->u.delete_tmp);
|
||||
frame->todo.Pop(2);
|
||||
frame->todo.Push(val_act);
|
||||
break;
|
||||
}
|
||||
case ActionKind::ExpToLValAction: {
|
||||
Address a = AllocateValue(act->results[0]);
|
||||
Address a = state->AllocateValue(act->results[0]);
|
||||
auto del = MakeDeleteAct(a);
|
||||
frame->todo.Pop(2);
|
||||
InsertDelete(del, frame->todo);
|
||||
@@ -1033,8 +1052,8 @@ void HandleValue() {
|
||||
// { v :: [].f :: C, E, F} :: S, H}
|
||||
// -> { { &v.f :: C, E, F} :: S, H }
|
||||
const Value* str = act->results[0];
|
||||
Address a =
|
||||
GetMember(ValToPtr(str, exp->line_num), *exp->u.get_field.field);
|
||||
Address a = GetMember(ValToPtr(str, exp->line_num),
|
||||
*exp->u.get_field.field, exp->line_num);
|
||||
frame->todo.Pop(2);
|
||||
frame->todo.Push(MakeValAct(MakePtrVal(a)));
|
||||
break;
|
||||
@@ -1126,7 +1145,8 @@ void HandleValue() {
|
||||
exit(-1);
|
||||
}
|
||||
frame->todo.Pop(2);
|
||||
frame->todo.Push(MakeValAct(state->heap[*a]));
|
||||
const Value* element = state->ReadFromMemory(*a, exp->line_num);
|
||||
frame->todo.Push(MakeValAct(element));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -1143,9 +1163,10 @@ void HandleValue() {
|
||||
// { { v :: [].f :: C, E, F} :: S, H}
|
||||
// -> { { v_f :: C, E, F} : S, H}
|
||||
auto a = GetMember(ValToPtr(act->results[0], exp->line_num),
|
||||
*exp->u.get_field.field);
|
||||
*exp->u.get_field.field, exp->line_num);
|
||||
const Value* element = state->ReadFromMemory(a, exp->line_num);
|
||||
frame->todo.Pop(2);
|
||||
frame->todo.Push(MakeValAct(state->heap[a]));
|
||||
frame->todo.Push(MakeValAct(element));
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::PrimitiveOp: {
|
||||
|
||||
Reference in New Issue
Block a user