diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index 522b4b4ed4c9..dbdde1a73377 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -27,18 +27,6 @@ auto PatternMatch(const Value* pat, const Value* val, Env, std::list*, int) -> std::optional; void HandleValue(); -template -static auto FindField(const std::string& field, - const std::vector>& inits) - -> std::optional { - 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* 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); diff --git a/executable_semantics/interpreter/typecheck.cpp b/executable_semantics/interpreter/typecheck.cpp index 2340745945f7..e2c33ae03fe7 100644 --- a/executable_semantics/interpreter/typecheck.cpp +++ b/executable_semantics/interpreter/typecheck.cpp @@ -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>(); - 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
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>(); - auto arg_types = new VarValues(); + auto arg_types = new std::vector>(); 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
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>(); + 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); } diff --git a/executable_semantics/interpreter/typecheck.h b/executable_semantics/interpreter/typecheck.h index e3ee18c2683c..38e6846a10d0 100644 --- a/executable_semantics/interpreter/typecheck.h +++ b/executable_semantics/interpreter/typecheck.h @@ -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; diff --git a/executable_semantics/interpreter/value.cpp b/executable_semantics/interpreter/value.cpp index 5d2ca281ad22..2a83895c7f7c 100644 --- a/executable_semantics/interpreter/value.cpp +++ b/executable_semantics/interpreter/value.cpp @@ -4,6 +4,7 @@ #include "executable_semantics/interpreter/value.h" +#include #include #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
{ + 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>(); 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
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: diff --git a/executable_semantics/interpreter/value.h b/executable_semantics/interpreter/value.h index bdb10dbf1f90..03deb45b2fe4 100644 --- a/executable_semantics/interpreter/value.h +++ b/executable_semantics/interpreter/value.h @@ -6,6 +6,7 @@ #define EXECUTABLE_SEMANTICS_INTERPRETER_VALUE_H_ #include +#include #include #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
; + 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*; diff --git a/executable_semantics/testdata/fun6_fail_type.golden b/executable_semantics/testdata/fun6_fail_type.golden index 6812bf092fbd..2bf75beeaf9f 100644 --- a/executable_semantics/testdata/fun6_fail_type.golden +++ b/executable_semantics/testdata/fun6_fail_type.golden @@ -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 diff --git a/executable_semantics/testdata/global_variable5.golden b/executable_semantics/testdata/global_variable5.golden index e90cb516a289..95848156efd3 100644 --- a/executable_semantics/testdata/global_variable5.golden +++ b/executable_semantics/testdata/global_variable5.golden @@ -1,4 +1,4 @@ 10: type error in return -expected: Tuple() +expected: () actual: Int EXIT CODE: 255