Unify tuple types with tuples-of-types in the interpreter (#442)

This commit is contained in:
Geoff Romer
2021-04-12 16:13:00 -07:00
committed by GitHub
parent 22656c8e09
commit cf7c97bf28
7 changed files with 82 additions and 149 deletions
@@ -27,18 +27,6 @@ auto PatternMatch(const Value* pat, const Value* val, Env,
std::list<std::string>*, int) -> std::optional<Env>;
void HandleValue();
template <class T>
static auto FindField(const std::string& field,
const std::vector<std::pair<std::string, T>>& inits)
-> std::optional<T> {
for (const auto& i : inits) {
if (i.first == field) {
return i.second;
}
}
return std::nullopt;
}
//
// Auxiliary Functions
//
@@ -105,14 +93,6 @@ auto CopyVal(const Value* val, int line_num) -> const Value* {
return MakeAutoTypeVal();
case ValKind::ContinuationTV:
return MakeContinuationTypeVal();
case ValKind::TupleTV: {
auto new_fields = new VarValues();
for (auto& field : *val->u.tuple_type.fields) {
auto v = CopyVal(field.second, line_num);
new_fields->push_back(make_pair(field.first, v));
}
return MakeTupleTypeVal(new_fields);
}
case ValKind::StructTV:
case ValKind::ChoiceTV:
case ValKind::VarPatV:
@@ -300,7 +280,7 @@ void InitGlobals(std::list<Declaration>* fs) {
auto ChoiceDeclaration::InitGlobals(Env& globals) const -> void {
auto alts = new VarValues();
for (auto kv : alternatives) {
auto t = ToType(this->line_num, InterpExp(Env(), kv.second));
auto t = InterpExp(Env(), kv.second);
alts->push_back(make_pair(kv.first, t));
}
auto ct = MakeChoiceTypeVal(name, alts);
@@ -315,8 +295,7 @@ auto StructDeclaration::InitGlobals(Env& globals) const -> void {
++i) {
switch ((*i)->tag) {
case MemberKind::FieldMember: {
auto t =
ToType(definition.line_num, InterpExp(Env(), (*i)->u.field.type));
auto t = InterpExp(Env(), (*i)->u.field.type);
fields->push_back(make_pair(*(*i)->u.field.name, t));
break;
}
@@ -446,7 +425,7 @@ auto PatternMatch(const Value* p, const Value* v, Env values,
exit(-1);
}
for (auto& elt : *p->u.tuple.elts) {
auto a = FindField(elt.first, *v->u.tuple.elts);
auto a = FindTupleField(elt.first, v);
if (a == std::nullopt) {
std::cerr << "runtime error: field " << elt.first << "not in ";
PrintValue(v, std::cerr);
@@ -530,7 +509,7 @@ void PatternAssignment(const Value* pat, const Value* val, int line_num) {
exit(-1);
}
for (auto& elt : *pat->u.tuple.elts) {
auto a = FindField(elt.first, *val->u.tuple.elts);
auto a = FindTupleField(elt.first, val);
if (a == std::nullopt) {
std::cerr << "runtime error: field " << elt.first << "not in ";
PrintValue(val, std::cerr);
@@ -954,7 +933,7 @@ auto GetMember(Address a, const std::string& f) -> Address {
const Value* v = state->heap[a];
switch (v->tag) {
case ValKind::StructV: {
auto a = FindField(f, *v->u.struct_val.inits->u.tuple.elts);
auto a = FindTupleField(f, v->u.struct_val.inits);
if (a == std::nullopt) {
std::cerr << "runtime error, member " << f << " not in ";
PrintValue(v, std::cerr);
@@ -964,7 +943,7 @@ auto GetMember(Address a, const std::string& f) -> Address {
return *a;
}
case ValKind::TupleV: {
auto a = FindField(f, *v->u.tuple.elts);
auto a = FindTupleField(f, v);
if (a == std::nullopt) {
std::cerr << "field " << f << " not in ";
PrintValue(v, std::cerr);
@@ -1069,7 +1048,7 @@ void HandleValue() {
// -> { { &v[i] :: C, E, F} :: S, H }
const Value* tuple = act->results[0];
std::string f = std::to_string(ToInteger(act->results[1]));
auto a = FindField(f, *tuple->u.tuple.elts);
auto a = FindTupleField(f, tuple);
if (a == std::nullopt) {
std::cerr << "runtime error: field " << f << "not in ";
PrintValue(tuple, std::cerr);
@@ -1139,7 +1118,7 @@ void HandleValue() {
// { { 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 = FindField(f, *tuple->u.tuple.elts);
auto a = FindTupleField(f, tuple);
if (a == std::nullopt) {
std::cerr << "runtime error, field " << f << " not in ";
PrintValue(tuple, std::cerr);
+32 -79
View File
@@ -39,53 +39,6 @@ void PrintTypeEnv(TypeEnv types, std::ostream& out) {
}
}
// Convert tuples to tuple types.
auto ToType(int line_num, const Value* val) -> const Value* {
switch (val->tag) {
case ValKind::TupleV: {
auto fields = new VarValues();
for (auto& elt : *val->u.tuple.elts) {
const Value* ty = ToType(line_num, state->heap[elt.second]);
fields->push_back(std::make_pair(elt.first, ty));
}
return MakeTupleTypeVal(fields);
}
case ValKind::TupleTV: {
auto fields = new VarValues();
for (auto& field : *val->u.tuple_type.fields) {
const Value* ty = ToType(line_num, field.second);
fields->push_back(std::make_pair(field.first, ty));
}
return MakeTupleTypeVal(fields);
}
case ValKind::PointerTV: {
return MakePtrTypeVal(ToType(line_num, val->u.ptr_type.type));
}
case ValKind::FunctionTV: {
return MakeFunTypeVal(ToType(line_num, val->u.fun_type.param),
ToType(line_num, val->u.fun_type.ret));
}
case ValKind::VarPatV: {
return MakeVarPatVal(*val->u.var_pat.name,
ToType(line_num, val->u.var_pat.type));
}
case ValKind::ChoiceTV:
case ValKind::StructTV:
case ValKind::TypeTV:
case ValKind::VarTV:
case ValKind::BoolTV:
case ValKind::IntTV:
case ValKind::AutoTV:
case ValKind::ContinuationTV:
return val;
default:
std::cerr << line_num << ": in ToType, expected a type, not ";
PrintValue(val, std::cerr);
std::cerr << std::endl;
exit(-1);
}
}
// Reify type to type expression.
auto ReifyType(const Value* t, int line_num) -> Expression* {
switch (t->tag) {
@@ -102,11 +55,11 @@ auto ReifyType(const Value* t, int line_num) -> Expression* {
case ValKind::FunctionTV:
return MakeFunType(0, ReifyType(t->u.fun_type.param, line_num),
ReifyType(t->u.fun_type.ret, line_num));
case ValKind::TupleTV: {
case ValKind::TupleV: {
auto args = new std::vector<std::pair<std::string, Expression*>>();
for (auto& field : *t->u.tuple_type.fields) {
for (auto& field : *t->u.tuple.elts) {
args->push_back(
make_pair(field.first, ReifyType(field.second, line_num)));
{field.first, ReifyType(state->heap[field.second], line_num)});
}
return MakeTuple(0, args);
}
@@ -152,8 +105,7 @@ auto TypeCheckExp(Expression* e, TypeEnv types, Env values,
<< std::endl;
exit(-1);
}
auto t =
ToType(e->line_num, InterpExp(values, e->u.pattern_variable.type));
auto t = InterpExp(values, e->u.pattern_variable.type);
if (t->tag == ValKind::AutoTV) {
if (expected == nullptr) {
std::cerr << e->line_num
@@ -174,17 +126,18 @@ auto TypeCheckExp(Expression* e, TypeEnv types, Env values,
TCContext::ValueContext);
auto t = res.type;
switch (t->tag) {
case ValKind::TupleTV: {
case ValKind::TupleV: {
auto i = ToInteger(InterpExp(values, e->u.index.offset));
std::string f = std::to_string(i);
auto field_t = FindInVarValues(f, t->u.tuple_type.fields);
if (field_t == nullptr) {
std::optional<Address> field_address = FindTupleField(f, t);
if (field_address == std::nullopt) {
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[*field_address];
auto new_e = MakeIndex(e->line_num, res.exp, MakeInt(e->line_num, i));
return TCResult(new_e, field_t, res.types);
}
@@ -196,29 +149,30 @@ auto TypeCheckExp(Expression* e, TypeEnv types, Env values,
}
case ExpressionKind::Tuple: {
auto new_args = new std::vector<std::pair<std::string, Expression*>>();
auto arg_types = new VarValues();
auto arg_types = new std::vector<std::pair<std::string, Address>>();
auto new_types = types;
int i = 0;
for (auto arg = e->u.tuple.fields->begin();
arg != e->u.tuple.fields->end(); ++arg, ++i) {
const Value* arg_expected = nullptr;
if (expected && expected->tag == ValKind::TupleTV) {
arg_expected =
FindInVarValues(arg->first, expected->u.tuple_type.fields);
if (arg_expected == nullptr) {
if (expected && expected->tag == ValKind::TupleV) {
std::optional<Address> expected_field =
FindTupleField(arg->first, expected);
if (expected_field == std::nullopt) {
std::cerr << e->line_num << ": compilation error, missing field "
<< arg->first << std::endl;
exit(-1);
}
arg_expected = state->heap[*expected_field];
}
auto arg_res =
TypeCheckExp(arg->second, new_types, values, arg_expected, context);
new_types = arg_res.types;
new_args->push_back(std::make_pair(arg->first, arg_res.exp));
arg_types->push_back(std::make_pair(arg->first, arg_res.type));
arg_types->push_back({arg->first, AllocateValue(arg_res.type)});
}
auto tuple_e = MakeTuple(e->line_num, new_args);
auto tuple_t = MakeTupleTypeVal(arg_types);
auto tuple_t = MakeTupleVal(arg_types);
return TCResult(tuple_e, tuple_t, new_types);
}
case ExpressionKind::GetField: {
@@ -247,12 +201,12 @@ auto TypeCheckExp(Expression* e, TypeEnv types, Env values,
<< *t->u.struct_type.name << " does not have a field named "
<< *e->u.get_field.field << std::endl;
exit(-1);
case ValKind::TupleTV:
for (auto& field : *t->u.tuple_type.fields) {
case ValKind::TupleV:
for (auto& field : *t->u.tuple.elts) {
if (*e->u.get_field.field == field.first) {
auto new_e =
MakeGetField(e->line_num, res.exp, *e->u.get_field.field);
return TCResult(new_e, field.second, res.types);
return TCResult(new_e, state->heap[field.second], res.types);
}
}
std::cerr << e->line_num << ": compilation error, struct "
@@ -364,10 +318,8 @@ auto TypeCheckExp(Expression* e, TypeEnv types, Env values,
switch (context) {
case TCContext::ValueContext:
case TCContext::TypeContext: {
auto pt = ToType(e->line_num,
InterpExp(values, e->u.function_type.parameter));
auto rt = ToType(e->line_num,
InterpExp(values, e->u.function_type.return_type));
auto pt = InterpExp(values, e->u.function_type.parameter);
auto rt = InterpExp(values, e->u.function_type.return_type);
auto new_e = MakeFunType(e->line_num, ReifyType(pt, e->line_num),
ReifyType(rt, e->line_num));
return TCResult(new_e, MakeTypeTypeVal(), types);
@@ -610,7 +562,7 @@ auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv types, Env values)
-> struct FunctionDefinition* {
auto param_res = TypeCheckExp(f->param_pattern, types, values, nullptr,
TCContext::PatternContext);
auto return_type = ToType(f->line_num, InterpExp(values, f->return_type));
auto return_type = InterpExp(values, f->return_type);
if (f->name == "main") {
ExpectType(f->line_num, "return type of `main`", MakeIntTypeVal(),
return_type);
@@ -627,13 +579,12 @@ auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def)
-> const Value* {
auto param_res = TypeCheckExp(fun_def->param_pattern, types, values, nullptr,
TCContext::PatternContext);
auto param_type = ToType(fun_def->line_num, param_res.type);
auto ret = InterpExp(values, fun_def->return_type);
if (ret->tag == ValKind::AutoTV) {
auto f = TypeCheckFunDef(fun_def, types, values);
ret = InterpExp(values, f->return_type);
}
return MakeFunTypeVal(param_type, ret);
return MakeFunTypeVal(param_res.type, ret);
}
auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*types*/, Env ct_top)
@@ -642,7 +593,7 @@ auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*types*/, Env ct_top)
auto methods = new VarValues();
for (auto m = sd->members->begin(); m != sd->members->end(); ++m) {
if ((*m)->tag == MemberKind::FieldMember) {
auto t = ToType(sd->line_num, InterpExp(ct_top, (*m)->u.field.type));
auto t = InterpExp(ct_top, (*m)->u.field.type);
fields->push_back(std::make_pair(*(*m)->u.field.name, t));
}
}
@@ -689,7 +640,7 @@ auto VariableDeclaration::TypeChecked(TypeEnv types, Env values) const
-> Declaration {
TCResult type_checked_initializer = TypeCheckExp(
initializer, types, values, nullptr, TCContext::ValueContext);
const Value* declared_type = ToType(source_location, InterpExp(values, type));
const Value* declared_type = InterpExp(values, type);
ExpectType(source_location, "initializer of variable", declared_type,
type_checked_initializer.type);
return *this;
@@ -723,15 +674,18 @@ auto StructDeclaration::TopLevel(TypeCheckContext& tops) const -> void {
auto st = TypeOfStructDef(&definition, tops.types, tops.values);
Address a = AllocateValue(st);
tops.values.Set(Name(), a); // Is this obsolete?
auto params = MakeTupleTypeVal(st->u.struct_type.fields);
auto fun_ty = MakeFunTypeVal(params, st);
auto field_types = new std::vector<std::pair<std::string, Address>>();
for (const auto& [field_name, field_value] : *st->u.struct_type.fields) {
field_types->push_back({field_name, AllocateValue(field_value)});
}
auto fun_ty = MakeFunTypeVal(MakeTupleVal(field_types), st);
tops.types.Set(Name(), fun_ty);
}
auto ChoiceDeclaration::TopLevel(TypeCheckContext& tops) const -> void {
auto alts = new VarValues();
for (auto a : alternatives) {
auto t = ToType(line_num, InterpExp(tops.values, a.second));
auto t = InterpExp(tops.values, a.second);
alts->push_back(std::make_pair(a.first, t));
}
auto ct = MakeChoiceTypeVal(name, alts);
@@ -743,8 +697,7 @@ auto ChoiceDeclaration::TopLevel(TypeCheckContext& tops) const -> void {
// Associate the variable name with it's declared type in the
// compile-time symbol table.
auto VariableDeclaration::TopLevel(TypeCheckContext& tops) const -> void {
const Value* declared_type =
ToType(source_location, InterpExp(tops.values, type));
const Value* declared_type = InterpExp(tops.values, type);
tops.types.Set(Name(), declared_type);
}
@@ -36,8 +36,6 @@ struct TCStatement {
TypeEnv types;
};
auto ToType(int line_num, const Value* val) -> const Value*;
auto TypeCheckExp(Expression* e, TypeEnv types, Env values,
const Value* expected, TCContext context) -> TCResult;
+33 -29
View File
@@ -4,6 +4,7 @@
#include "executable_semantics/interpreter/value.h"
#include <cassert>
#include <iostream>
#include "executable_semantics/interpreter/interpreter.h"
@@ -37,6 +38,17 @@ auto FieldsEqual(VarValues* ts1, VarValues* ts2) -> bool {
}
}
auto FindTupleField(const std::string& name, const Value* tuple)
-> std::optional<Address> {
assert(tuple->tag == ValKind::TupleV);
for (const auto& i : *tuple->u.tuple.elts) {
if (i.first == name) {
return i.second;
}
}
return std::nullopt;
}
auto MakeIntVal(int i) -> const Value* {
auto* v = new Value();
v->tag = ValKind::IntV;
@@ -183,17 +195,10 @@ auto MakeStructTypeVal(std::string name, VarValues* fields, VarValues* methods)
return v;
}
auto MakeTupleTypeVal(VarValues* fields) -> const Value* {
auto* v = new Value();
v->tag = ValKind::TupleTV;
v->u.tuple_type.fields = fields;
return v;
}
auto MakeVoidTypeVal() -> const Value* {
auto* v = new Value();
v->tag = ValKind::TupleTV;
v->u.tuple_type.fields = new VarValues();
v->tag = ValKind::TupleV;
v->u.tuple.elts = new std::vector<std::pair<std::string, Address>>();
return v;
}
@@ -288,22 +293,6 @@ void PrintValue(const Value* val, std::ostream& out) {
case ValKind::VarTV:
out << *val->u.var_type;
break;
case ValKind::TupleTV: {
out << "Tuple(";
bool add_commas = false;
for (const auto& elt : *val->u.tuple_type.fields) {
if (add_commas) {
out << ", ";
} else {
add_commas = true;
}
out << elt.first << " = ";
PrintValue(elt.second, out);
}
out << ")";
break;
}
case ValKind::StructTV:
out << "struct " << *val->u.struct_type.name;
break;
@@ -337,14 +326,30 @@ auto TypeEqual(const Value* t1, const Value* t2) -> bool {
return *t1->u.struct_type.name == *t2->u.struct_type.name;
case ValKind::ChoiceTV:
return *t1->u.choice_type.name == *t2->u.choice_type.name;
case ValKind::TupleTV:
return FieldsEqual(t1->u.tuple_type.fields, t2->u.tuple_type.fields);
case ValKind::TupleV: {
if (t1->u.tuple.elts->size() != t2->u.tuple.elts->size()) {
return false;
}
for (size_t i = 0; i < t1->u.tuple.elts->size(); ++i) {
std::optional<Address> t2_field =
FindTupleField((*t1->u.tuple.elts)[i].first, t2);
if (t2_field == std::nullopt) {
return false;
}
if (!TypeEqual(state->heap[(*t1->u.tuple.elts)[i].second],
state->heap[*t2_field])) {
return false;
}
}
return true;
}
case ValKind::IntTV:
case ValKind::BoolTV:
case ValKind::ContinuationTV:
return true;
default:
return false;
std::cerr << "TypeEqual used to compare non-type values" << std::endl;
exit(-1);
}
}
@@ -370,7 +375,6 @@ auto ValueEqual(const Value* v1, const Value* v2, int line_num) -> bool {
case ValKind::FunctionTV:
case ValKind::PointerTV:
case ValKind::AutoTV:
case ValKind::TupleTV:
case ValKind::StructTV:
case ValKind::ChoiceTV:
case ValKind::ContinuationTV:
+6 -7
View File
@@ -6,6 +6,7 @@
#define EXECUTABLE_SEMANTICS_INTERPRETER_VALUE_H_
#include <list>
#include <optional>
#include <vector>
#include "executable_semantics/ast/statement.h"
@@ -21,6 +22,11 @@ auto FindInVarValues(const std::string& field, VarValues* inits)
-> const Value*;
auto FieldsEqual(VarValues* ts1, 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>;
enum class ValKind {
IntV,
FunV,
@@ -36,7 +42,6 @@ enum class ValKind {
FunctionTV,
PointerTV,
AutoTV,
TupleTV,
StructTV,
ChoiceTV,
ContinuationTV, // The type of a continuation.
@@ -102,11 +107,6 @@ struct Value {
VarValues* methods;
} struct_type;
struct {
std::string* name;
VarValues* fields;
} tuple_type;
struct {
std::string* name;
VarValues* alternatives;
@@ -151,7 +151,6 @@ auto MakeFunTypeVal(const Value* param, const Value* ret) -> const Value*;
auto MakePtrTypeVal(const Value* type) -> const Value*;
auto MakeStructTypeVal(std::string name, VarValues* fields, VarValues* methods)
-> const Value*;
auto MakeTupleTypeVal(VarValues* fields) -> const Value*;
auto MakeVoidTypeVal() -> const Value*;
auto MakeChoiceTypeVal(std::string name, VarValues* alts) -> const Value*;
+2 -2
View File
@@ -1,4 +1,4 @@
10: type error in call
expected: Tuple(0 = Int, 1 = Int)
actual: Tuple(0 = Tuple(0 = Int, 1 = Int))
expected: (0 = Int@0, 1 = Int@1)
actual: (0 = (0 = Int@6, 1 = Int@7)@8)
EXIT CODE: 255
+1 -1
View File
@@ -1,4 +1,4 @@
10: type error in return
expected: Tuple()
expected: ()
actual: Int
EXIT CODE: 255