ソースを参照

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

Geoff Romer 5 年 前
コミット
cf7c97bf28

+ 8 - 29
executable_semantics/interpreter/interpreter.cpp

@@ -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
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<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);
 }
 

+ 0 - 2
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;
 

+ 33 - 29
executable_semantics/interpreter/value.cpp

@@ -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
executable_semantics/interpreter/value.h

@@ -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
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

+ 1 - 1
executable_semantics/testdata/global_variable5.golden

@@ -1,4 +1,4 @@
 10: type error in return
-expected: Tuple()
+expected: ()
 actual: Int
 EXIT CODE: 255