Explorar el Código

replace uses of AssocList with Dictionary (#344)

Co-authored-by: Geoff Romer <gromer@google.com>
Co-authored-by: Dave Abrahams <dabrahams@google.com>
Jeremy G. Siek hace 5 años
padre
commit
05db2012ab

+ 1 - 0
executable_semantics/ast/BUILD

@@ -14,6 +14,7 @@ cc_library(
         ":function_definition",
         ":member",
         ":struct_definition",
+        "//executable_semantics/interpreter:containers",
     ],
 )
 

+ 17 - 18
executable_semantics/ast/declaration.h

@@ -11,18 +11,18 @@
 #include "executable_semantics/ast/function_definition.h"
 #include "executable_semantics/ast/member.h"
 #include "executable_semantics/ast/struct_definition.h"
+#include "executable_semantics/interpreter/dictionary.h"
 
 namespace Carbon {
 
 struct Value;
-template <class K, class V>
-struct AssocList;
+
 using Address = unsigned int;
-using TypeEnv = AssocList<std::string, Value*>;
-using Env = AssocList<std::string, Address>;
+using TypeEnv = Dictionary<std::string, Value*>;
+using Env = Dictionary<std::string, Address>;
 
 /// TODO:explain this. Also name it if necessary. Consult with jsiek.
-using ExecutionEnvironment = std::pair<TypeEnv*, Env*>;
+using ExecutionEnvironment = std::pair<TypeEnv, Env>;
 
 /// An existential AST declaration satisfying the Declaration concept.
 class Declaration {
@@ -38,10 +38,10 @@ class Declaration {
  public:  // Declaration concept API, in addition to ValueSemantic.
   void Print() const { box->Print(); }
   auto Name() const -> std::string { return box->Name(); }
-  auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration {
+  auto TypeChecked(TypeEnv env, Env ct_env) const -> Declaration {
     return box->TypeChecked(env, ct_env);
   }
-  void InitGlobals(Env*& globals) const { return box->InitGlobals(globals); }
+  void InitGlobals(Env& globals) const { return box->InitGlobals(globals); }
   auto TopLevel(ExecutionEnvironment& e) const -> void {
     return box->TopLevel(e);
   }
@@ -60,9 +60,8 @@ class Declaration {
     virtual ~Box() {}
     virtual auto Print() const -> void = 0;
     virtual auto Name() const -> std::string = 0;
-    virtual auto TypeChecked(TypeEnv* env, Env* ct_env) const
-        -> Declaration = 0;
-    virtual auto InitGlobals(Env*& globals) const -> void = 0;
+    virtual auto TypeChecked(TypeEnv env, Env ct_env) const -> Declaration = 0;
+    virtual auto InitGlobals(Env& globals) const -> void = 0;
     virtual auto TopLevel(ExecutionEnvironment&) const -> void = 0;
   };
 
@@ -75,10 +74,10 @@ class Declaration {
 
     auto Print() const -> void override { return content.Print(); }
     auto Name() const -> std::string override { return content.Name(); }
-    auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration override {
+    auto TypeChecked(TypeEnv env, Env ct_env) const -> Declaration override {
       return content.TypeChecked(env, ct_env);
     }
-    auto InitGlobals(Env*& globals) const -> void override {
+    auto InitGlobals(Env& globals) const -> void override {
       content.InitGlobals(globals);
     }
     auto TopLevel(ExecutionEnvironment& e) const -> void override {
@@ -98,8 +97,8 @@ struct FunctionDeclaration {
 
   auto Print() const -> void;
   auto Name() const -> std::string;
-  auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration;
-  auto InitGlobals(Env*& globals) const -> void;
+  auto TypeChecked(TypeEnv env, Env ct_env) const -> Declaration;
+  auto InitGlobals(Env& globals) const -> void;
   auto TopLevel(ExecutionEnvironment&) const -> void;
 };
 
@@ -110,8 +109,8 @@ struct StructDeclaration {
 
   void Print() const;
   auto Name() const -> std::string;
-  auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration;
-  void InitGlobals(Env*& globals) const;
+  auto TypeChecked(TypeEnv env, Env ct_env) const -> Declaration;
+  void InitGlobals(Env& globals) const;
   auto TopLevel(ExecutionEnvironment&) const -> void;
 };
 
@@ -126,8 +125,8 @@ struct ChoiceDeclaration {
 
   void Print() const;
   auto Name() const -> std::string;
-  auto TypeChecked(TypeEnv* env, Env* ct_env) const -> Declaration;
-  void InitGlobals(Env*& globals) const;
+  auto TypeChecked(TypeEnv env, Env ct_env) const -> Declaration;
+  void InitGlobals(Env& globals) const;
   auto TopLevel(ExecutionEnvironment&) const -> void;
 };
 

+ 12 - 3
executable_semantics/interpreter/BUILD

@@ -17,14 +17,12 @@ cc_library(
     ],
     hdrs = [
         "action.h",
-        "assoc_list.h",
         "interpreter.h",
-        "list_node.h",
-        "stack.h",
         "typecheck.h",
         "value.h",
     ],
     deps = [
+        ":containers",
         "//executable_semantics:tracing_flag",
         "//executable_semantics/ast:declaration",
         "//executable_semantics/ast:expression",
@@ -33,3 +31,14 @@ cc_library(
         "//executable_semantics/ast:statement",
     ],
 )
+
+cc_library(
+    name = "containers",
+    srcs = [
+        "list_node.h",
+    ],
+    hdrs = [
+        "dictionary.h",
+        "stack.h",
+    ],
+)

+ 0 - 38
executable_semantics/interpreter/assoc_list.h

@@ -1,38 +0,0 @@
-// 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_ASSOC_LIST_H_
-#define EXECUTABLE_SEMANTICS_INTERPRETER_ASSOC_LIST_H_
-
-#include <iostream>
-#include <list>
-#include <string>
-
-namespace Carbon {
-
-template <class K, class V>
-struct AssocList {
-  AssocList(K k, V v, AssocList* n) : key(k), value(v), next(n) {}
-
-  K key;
-  V value;
-  AssocList* next;
-};
-
-template <class K, class V>
-auto Lookup(int line_num, AssocList<K, V>* alist, const K& key,
-            void (*print_key)(const K&)) -> V {
-  if (alist == NULL) {
-    std::cerr << line_num << ": could not find `" << key << "`" << std::endl;
-    exit(-1);
-  } else if (alist->key == key) {
-    return alist->value;
-  } else {
-    return Lookup(line_num, alist->next, key, print_key);
-  }
-}
-
-}  // namespace Carbon
-
-#endif  // EXECUTABLE_SEMANTICS_INTERPRETER_ASSOC_LIST_H_

+ 105 - 67
executable_semantics/interpreter/interpreter.cpp

@@ -22,8 +22,8 @@ namespace Carbon {
 
 State* state = nullptr;
 
-auto PatternMatch(Value* pat, Value* val, Env*, std::list<std::string>*, int)
-    -> Env*;
+auto PatternMatch(Value* pat, Value* val, Env, std::list<std::string>*, int)
+    -> std::optional<Env>;
 void HandleValue();
 
 template <class T>
@@ -38,7 +38,9 @@ static auto FindField(const std::string& field,
   return std::nullopt;
 }
 
-/**** Auxiliary Functions ****/
+//
+// Auxiliary Functions
+//
 
 auto AllocateValue(Value* v) -> Address {
   // Putting the following two side effects together in this function
@@ -135,16 +137,17 @@ void KillValue(Value* val) {
   }
 }
 
-void PrintEnv(Env* env, std::ostream& out) {
-  if (env) {
-    std::cout << env->key << ": ";
-    PrintValue(state->heap[env->value], out);
-    std::cout << ", ";
-    PrintEnv(env->next, out);
+void PrintEnv(Env env, std::ostream& out) {
+  for (const auto& [name, value] : env) {
+    out << name << ": ";
+    PrintValue(state->heap[value], out);
+    out << ", ";
   }
 }
 
-/***** Frame and State Operations *****/
+//
+// Frame and State Operations
+//
 
 void PrintFrame(Frame* frame, std::ostream& out) {
   out << frame->name;
@@ -174,7 +177,7 @@ void PrintHeap(const std::vector<Value*>& heap, std::ostream& out) {
   }
 }
 
-auto CurrentEnv(State* state) -> Env* {
+auto CurrentEnv(State* state) -> Env {
   Frame* frame = state->stack.Top();
   return frame->scopes.Top()->env;
 }
@@ -190,7 +193,9 @@ void PrintState(std::ostream& out) {
   out << std::endl << "}" << std::endl;
 }
 
-/***** Auxiliary Functions *****/
+//
+// More Auxiliary Functions
+//
 
 auto ValToInt(Value* v, int line_num) -> int {
   CheckAlive(v, line_num);
@@ -252,27 +257,27 @@ auto EvalPrim(Operator op, const std::vector<Value*>& args, int line_num)
   }
 }
 
-Env* globals;
+// Globally-defined entities, such as functions, structs, choices.
+Env globals;
 
 void InitGlobals(std::list<Declaration>* fs) {
-  globals = nullptr;
   for (auto const& d : *fs) {
     d.InitGlobals(globals);
   }
 }
 
-auto ChoiceDeclaration::InitGlobals(Env*& globals) const -> void {
+auto ChoiceDeclaration::InitGlobals(Env& globals) const -> void {
   auto alts = new VarValues();
   for (auto kv : alternatives) {
-    auto t = ToType(line_num, InterpExp(nullptr, kv.second));
+    auto t = ToType(line_num, InterpExp(Env(), kv.second));
     alts->push_back(make_pair(kv.first, t));
   }
   auto ct = MakeChoiceTypeVal(name, alts);
   auto a = AllocateValue(ct);
-  globals = new Env(name, a, globals);
+  globals.Set(name, a);
 }
 
-auto StructDeclaration::InitGlobals(Env*& globals) const -> void {
+auto StructDeclaration::InitGlobals(Env& globals) const -> void {
   auto fields = new VarValues();
   auto methods = new VarValues();
   for (auto i = definition.members->begin(); i != definition.members->end();
@@ -280,7 +285,7 @@ auto StructDeclaration::InitGlobals(Env*& globals) const -> void {
     switch ((*i)->tag) {
       case MemberKind::FieldMember: {
         auto t =
-            ToType(definition.line_num, InterpExp(nullptr, (*i)->u.field.type));
+            ToType(definition.line_num, InterpExp(Env(), (*i)->u.field.type));
         fields->push_back(make_pair(*(*i)->u.field.name, t));
         break;
       }
@@ -288,15 +293,15 @@ auto StructDeclaration::InitGlobals(Env*& globals) const -> void {
   }
   auto st = MakeStructTypeVal(*definition.name, fields, methods);
   auto a = AllocateValue(st);
-  globals = new Env(*definition.name, a, globals);
+  globals.Set(*definition.name, a);
 }
 
-auto FunctionDeclaration::InitGlobals(Env*& globals) const -> void {
-  Env* env = nullptr;
+auto FunctionDeclaration::InitGlobals(Env& globals) const -> void {
+  Env env;
   auto pt = InterpExp(env, definition->param_pattern);
   auto f = MakeFunVal(definition->name, pt, definition->body);
   Address a = AllocateValue(f);
-  globals = new Env(definition->name, a, globals);
+  globals.Set(definition->name, a);
 }
 
 //    { S, H} -> { { C, E, F} :: S, H}
@@ -309,15 +314,15 @@ void CallFunction(int line_num, std::vector<Value*> operas, State* state) {
     case ValKind::FunV: {
       // Bind arguments to parameters
       std::list<std::string> params;
-      Env* env = PatternMatch(operas[0]->u.fun.param, operas[1], globals,
-                              &params, line_num);
-      if (!env) {
+      std::optional<Env> envWithMatches = PatternMatch(
+          operas[0]->u.fun.param, operas[1], globals, &params, line_num);
+      if (!envWithMatches) {
         std::cerr << "internal error in call_function, pattern match failed"
                   << std::endl;
         exit(-1);
       }
       // Create the new frame and push it on the stack
-      auto* scope = new Scope(env, params);
+      auto* scope = new Scope(*envWithMatches, params);
       auto* frame = new Frame(*operas[0]->u.fun.name, Stack(scope),
                               Stack(MakeStmtAct(operas[0]->u.fun.body)));
       state->stack.Push(frame);
@@ -348,8 +353,12 @@ void CallFunction(int line_num, std::vector<Value*> operas, State* state) {
 
 void KillScope(int line_num, Scope* scope) {
   for (const auto& l : scope->locals) {
-    Address a = Lookup(line_num, scope->env, l, PrintErrorString);
-    KillValue(state->heap[a]);
+    std::optional<Address> a = scope->env.Get(l);
+    if (!a) {
+      std::cerr << "internal error in KillScope" << std::endl;
+      exit(-1);
+    }
+    KillValue(state->heap[*a]);
   }
 }
 
@@ -395,9 +404,13 @@ auto ToValue(Expression* value) -> Value* {
   }
 }
 
-// Returns 0 if the value doesn't match the pattern.
-auto PatternMatch(Value* p, Value* v, Env* env, std::list<std::string>* vars,
-                  int line_num) -> Env* {
+// Returns an updated environment that includes the bindings of
+//    pattern variables to their matched values, if matching succeeds.
+//
+// The names of the pattern variables are added to the vars parameter.
+// Returns nullopt if the value doesn't match the pattern.
+auto PatternMatch(Value* p, Value* v, Env env, std::list<std::string>* vars,
+                  int line_num) -> std::optional<Env> {
   if (tracing_output) {
     std::cout << "pattern_match(";
     PrintValue(p, std::cout);
@@ -409,7 +422,8 @@ auto PatternMatch(Value* p, Value* v, Env* env, std::list<std::string>* vars,
     case ValKind::VarPatV: {
       Address a = AllocateValue(CopyVal(v, line_num));
       vars->push_back(*p->u.var_pat.name);
-      return new Env(*p->u.var_pat.name, a, env);
+      env.Set(*p->u.var_pat.name, a);
+      return env;
     }
     case ValKind::TupleV:
       switch (v->tag) {
@@ -427,9 +441,13 @@ auto PatternMatch(Value* p, Value* v, Env* env, std::list<std::string>* vars,
               std::cerr << std::endl;
               exit(-1);
             }
-            env = PatternMatch(state->heap[elt.second], state->heap[*a], env,
-                               vars, line_num);
-          }
+            std::optional<Env> envWithMatches = PatternMatch(
+                state->heap[elt.second], state->heap[*a], env, vars, line_num);
+            if (!envWithMatches) {
+              return std::nullopt;
+            }
+            env = *envWithMatches;
+          }  // for
           return env;
         }
         default:
@@ -444,10 +462,14 @@ auto PatternMatch(Value* p, Value* v, Env* env, std::list<std::string>* vars,
         case ValKind::AltV: {
           if (*p->u.alt.choice_name != *v->u.alt.choice_name ||
               *p->u.alt.alt_name != *v->u.alt.alt_name) {
-            return nullptr;
+            return std::nullopt;
           }
-          env = PatternMatch(p->u.alt.arg, v->u.alt.arg, env, vars, line_num);
-          return env;
+          std::optional<Env> envWithMatches =
+              PatternMatch(p->u.alt.arg, v->u.alt.arg, env, vars, line_num);
+          if (!envWithMatches) {
+            return std::nullopt;
+          }
+          return *envWithMatches;
         }
         default:
           std::cerr
@@ -459,20 +481,23 @@ auto PatternMatch(Value* p, Value* v, Env* env, std::list<std::string>* vars,
       }
     case ValKind::FunctionTV:
       switch (v->tag) {
-        case ValKind::FunctionTV:
-          env = PatternMatch(p->u.fun_type.param, v->u.fun_type.param, env,
-                             vars, line_num);
-          env = PatternMatch(p->u.fun_type.ret, v->u.fun_type.ret, env, vars,
-                             line_num);
-          return env;
+        case ValKind::FunctionTV: {
+          std::optional<Env> envWithMatches = PatternMatch(
+              p->u.fun_type.param, v->u.fun_type.param, env, vars, line_num);
+          if (!envWithMatches) {
+            return std::nullopt;
+          }
+          return PatternMatch(p->u.fun_type.ret, v->u.fun_type.ret,
+                              *envWithMatches, vars, line_num);
+        }
         default:
-          return nullptr;
+          return std::nullopt;
       }
     default:
       if (ValueEqual(p, v, line_num)) {
         return env;
       } else {
-        return nullptr;
+        return std::nullopt;
       }
   }
 }
@@ -542,7 +567,7 @@ void PatternAssignment(Value* pat, Value* val, int line_num) {
   }
 }
 
-/***** state transitions for lvalues *****/
+// State transitions for lvalues.
 
 void StepLvalue() {
   Frame* frame = state->stack.Top();
@@ -557,9 +582,14 @@ void StepLvalue() {
     case ExpressionKind::Variable: {
       //    { {x :: C, E, F} :: S, H}
       // -> { {E(x) :: C, E, F} :: S, H}
-      Address a = Lookup(exp->line_num, CurrentEnv(state),
-                         *(exp->u.variable.name), PrintErrorString);
-      Value* v = MakePtrVal(a);
+      std::optional<Address> pointer =
+          CurrentEnv(state).Get(*(exp->u.variable.name));
+      if (!pointer) {
+        std::cerr << exp->line_num << ": could not find `"
+                  << *(exp->u.variable.name) << "`" << std::endl;
+        exit(-1);
+      }
+      Value* v = MakePtrVal(*pointer);
       CheckAlive(v, exp->line_num);
       frame->todo.Pop();
       frame->todo.Push(MakeValAct(v));
@@ -604,7 +634,7 @@ void StepLvalue() {
   }
 }
 
-/***** state transitions for expressions *****/
+// State transitions for expressions.
 
 void StepExp() {
   Frame* frame = state->stack.Top();
@@ -649,11 +679,16 @@ void StepExp() {
     }
     case ExpressionKind::Variable: {
       // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H}
-      Address a = Lookup(exp->line_num, CurrentEnv(state),
-                         *(exp->u.variable.name), PrintErrorString);
-      Value* v = state->heap[a];
+      std::optional<Address> pointer =
+          CurrentEnv(state).Get(*(exp->u.variable.name));
+      if (!pointer) {
+        std::cerr << exp->line_num << ": could not find `"
+                  << *(exp->u.variable.name) << "`" << std::endl;
+        exit(-1);
+      }
+      Value* pointee = state->heap[*pointer];
       frame->todo.Pop(1);
-      frame->todo.Push(MakeValAct(v));
+      frame->todo.Push(MakeValAct(pointee));
       break;
     }
     case ExpressionKind::Integer:
@@ -719,8 +754,6 @@ void StepExp() {
   }  // switch (exp->tag)
 }
 
-/***** state transitions for statements *****/
-
 auto IsWhileAct(Action* act) -> bool {
   switch (act->tag) {
     case ActionKind::StatementAction:
@@ -749,6 +782,8 @@ auto IsBlockAct(Action* act) -> bool {
   }
 }
 
+// State transitions for statements.
+
 void StepStmt() {
   Frame* frame = state->stack.Top();
   Action* act = frame->todo.Top();
@@ -918,7 +953,7 @@ void InsertDelete(Action* del, Stack<Action*>& todo) {
   }
 }
 
-/***** State transition for handling a value *****/
+// State transition for handling a value.
 
 void HandleValue() {
   Frame* frame = state->stack.Top();
@@ -1150,17 +1185,18 @@ void HandleValue() {
             // -> { { C, E(x := a), F} :: S, H(a := copy(v))}
             Value* v = act->results[0];
             Value* p = act->results[1];
-            // Address a = AllocateValue(CopyVal(v));
-            frame->scopes.Top()->env =
+
+            std::optional<Env> envWithMatches =
                 PatternMatch(p, v, frame->scopes.Top()->env,
                              &frame->scopes.Top()->locals, stmt->line_num);
-            if (!frame->scopes.Top()->env) {
+            if (!envWithMatches) {
               std::cerr
                   << stmt->line_num
                   << ": internal error in variable definition, match failed"
                   << std::endl;
               exit(-1);
             }
+            frame->scopes.Top()->env = *envWithMatches;
             frame->todo.Pop(2);
           }
           break;
@@ -1244,9 +1280,10 @@ void HandleValue() {
             auto pat = act->results[clause_num + 1];
             auto env = CurrentEnv(state);
             std::list<std::string> vars;
-            Env* new_env = PatternMatch(pat, v, env, &vars, stmt->line_num);
-            if (new_env) {  // we have a match, start the body
-              auto* new_scope = new Scope(new_env, vars);
+            std::optional<Env> envWithMatches =
+                PatternMatch(pat, v, env, &vars, stmt->line_num);
+            if (envWithMatches) {  // we have a match, start the body
+              auto* new_scope = new Scope(*envWithMatches, vars);
               frame->scopes.Push(new_scope);
               Statement* body_block = MakeBlock(stmt->line_num, c->second);
               Action* body_act = MakeStmtAct(body_block);
@@ -1255,11 +1292,12 @@ void HandleValue() {
               frame->todo.Push(body_act);
               frame->todo.Push(MakeStmtAct(c->second));
             } else {
+              // this case did not match, moving on
               act->pos++;
               clause_num = (act->pos - 1) / 2;
               if (clause_num <
                   static_cast<int>(stmt->u.match_stmt.clauses->size())) {
-                // move on to the next clause
+                // interpret the next clause
                 c = stmt->u.match_stmt.clauses->begin();
                 std::advance(c, clause_num);
                 frame->todo.Pop(1);
@@ -1366,7 +1404,7 @@ auto InterpProgram(std::list<Declaration>* fs) -> int {
 }
 
 // Interpret an expression at compile-time.
-auto InterpExp(Env* env, Expression* e) -> Value* {
+auto InterpExp(Env env, Expression* e) -> Value* {
   auto todo = Stack(MakeExpAct(e));
   auto* scope = new Scope(env, std::list<std::string>());
   auto* frame = new Frame("InterpExp", Stack(scope), todo);

+ 6 - 6
executable_semantics/interpreter/interpreter.h

@@ -11,19 +11,19 @@
 
 #include "executable_semantics/ast/declaration.h"
 #include "executable_semantics/interpreter/action.h"
-#include "executable_semantics/interpreter/assoc_list.h"
+#include "executable_semantics/interpreter/dictionary.h"
 #include "executable_semantics/interpreter/stack.h"
 #include "executable_semantics/interpreter/value.h"
 
 namespace Carbon {
 
-using Env = AssocList<std::string, Address>;
+using Env = Dictionary<std::string, Address>;
 
 /***** Scopes *****/
 
 struct Scope {
-  Scope(Env* e, std::list<std::string> l) : env(e), locals(std::move(l)) {}
-  Env* env;
+  Scope(Env e, std::list<std::string> l) : env(e), locals(std::move(l)) {}
+  Env env;
   std::list<std::string> locals;
 };
 
@@ -45,7 +45,7 @@ struct State {
 
 extern State* state;
 
-void PrintEnv(Env* env);
+void PrintEnv(Env env);
 auto AllocateValue(Value* v) -> Address;
 auto CopyVal(Value* val, int line_num) -> Value*;
 auto ToInteger(Value* v) -> int;
@@ -53,7 +53,7 @@ auto ToInteger(Value* v) -> int;
 /***** Interpreters *****/
 
 auto InterpProgram(std::list<Declaration>* fs) -> int;
-auto InterpExp(Env* env, Expression* e) -> Value*;
+auto InterpExp(Env env, Expression* e) -> Value*;
 
 }  // namespace Carbon
 

+ 30 - 26
executable_semantics/interpreter/typecheck.cpp

@@ -31,12 +31,11 @@ void ExpectType(int line_num, const std::string& context, Value* expected,
 
 void PrintErrorString(const std::string& s) { std::cerr << s; }
 
-void PrintTypeEnv(TypeEnv* env, std::ostream& out) {
-  if (env) {
-    out << env->key << ": ";
-    PrintValue(env->value, out);
+void PrintTypeEnv(TypeEnv env, std::ostream& out) {
+  for (const auto& [name, value] : env) {
+    out << name << ": ";
+    PrintValue(value, out);
     out << ", ";
-    PrintTypeEnv(env->next, out);
   }
 }
 
@@ -138,7 +137,7 @@ auto ReifyType(Value* t, int line_num) -> Expression* {
 //    and it is used to implement `auto`, otherwise it is null.
 // context says what kind of position this expression is nested in,
 //    whether it's a position that expects a value, a pattern, or a type.
-auto TypeCheckExp(Expression* e, TypeEnv* env, Env* ct_env, Value* expected,
+auto TypeCheckExp(Expression* e, TypeEnv env, Env ct_env, Value* expected,
                   TCContext context) -> TCResult {
   switch (e->tag) {
     case ExpressionKind::PatternVariable: {
@@ -164,8 +163,8 @@ auto TypeCheckExp(Expression* e, TypeEnv* env, Env* ct_env, Value* expected,
       }
       auto new_e = MakeVarPat(e->line_num, *e->u.pattern_variable.name,
                               ReifyType(t, e->line_num));
-      return TCResult(new_e, t,
-                      new TypeEnv(*e->u.pattern_variable.name, t, env));
+      env.Set(*e->u.pattern_variable.name, t);
+      return TCResult(new_e, t, env);
     }
     case ExpressionKind::Index: {
       auto res = TypeCheckExp(e->u.get_field.aggregate, env, ct_env, nullptr,
@@ -282,9 +281,14 @@ auto TypeCheckExp(Expression* e, TypeEnv* env, Env* ct_env, Value* expected,
       }
     }
     case ExpressionKind::Variable: {
-      auto t =
-          Lookup(e->line_num, env, *(e->u.variable.name), PrintErrorString);
-      return TCResult(e, t, env);
+      std::optional<Value*> type = env.Get(*(e->u.variable.name));
+      if (type) {
+        return TCResult(e, *type, env);
+      } else {
+        std::cerr << e->line_num << ": could not find `"
+                  << *(e->u.variable.name) << "`" << std::endl;
+        exit(-1);
+      }
     }
     case ExpressionKind::Integer:
       return TCResult(e, MakeIntTypeVal(), env);
@@ -386,7 +390,7 @@ auto TypeCheckExp(Expression* e, TypeEnv* env, Env* ct_env, Value* expected,
 }
 
 auto TypecheckCase(Value* expected, Expression* pat, Statement* body,
-                   TypeEnv* env, Env* ct_env, Value* ret_type)
+                   TypeEnv env, Env ct_env, Value* ret_type)
     -> std::pair<Expression*, Statement*> {
   auto pat_res =
       TypeCheckExp(pat, env, ct_env, expected, TCContext::PatternContext);
@@ -401,7 +405,7 @@ auto TypecheckCase(Value* expected, Expression* pat, Statement* body,
 // It is the declared return type of the enclosing function definition.
 // If the return type is "auto", then the return type is inferred from
 // the first return statement.
-auto TypeCheckStmt(Statement* s, TypeEnv* env, Env* ct_env, Value* ret_type)
+auto TypeCheckStmt(Statement* s, TypeEnv env, Env ct_env, Value* ret_type)
     -> TCStatement {
   if (!s) {
     return TCStatement(s, env);
@@ -565,7 +569,7 @@ auto CheckOrEnsureReturn(Statement* stmt, bool void_return, int line_num)
   }
 }
 
-auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv* env, Env* ct_env)
+auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv env, Env ct_env)
     -> struct FunctionDefinition* {
   auto param_res = TypeCheckExp(f->param_pattern, env, ct_env, nullptr,
                                 TCContext::PatternContext);
@@ -582,7 +586,7 @@ auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv* env, Env* ct_env)
                     f->param_pattern, body);
 }
 
-auto TypeOfFunDef(TypeEnv* env, Env* ct_env, const FunctionDefinition* fun_def)
+auto TypeOfFunDef(TypeEnv env, Env ct_env, const FunctionDefinition* fun_def)
     -> Value* {
   auto param_res = TypeCheckExp(fun_def->param_pattern, env, ct_env, nullptr,
                                 TCContext::PatternContext);
@@ -595,7 +599,7 @@ auto TypeOfFunDef(TypeEnv* env, Env* ct_env, const FunctionDefinition* fun_def)
   return MakeFunTypeVal(param_type, ret);
 }
 
-auto TypeOfStructDef(const StructDefinition* sd, TypeEnv* /*env*/, Env* ct_top)
+auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*env*/, Env ct_top)
     -> Value* {
   auto fields = new VarValues();
   auto methods = new VarValues();
@@ -616,7 +620,7 @@ auto StructDeclaration::Name() const -> std::string { return *definition.name; }
 
 auto ChoiceDeclaration::Name() const -> std::string { return name; }
 
-auto StructDeclaration::TypeChecked(TypeEnv* env, Env* ct_env) const
+auto StructDeclaration::TypeChecked(TypeEnv env, Env ct_env) const
     -> Declaration {
   auto fields = new std::list<Member*>();
   for (auto& m : *definition.members) {
@@ -628,18 +632,18 @@ auto StructDeclaration::TypeChecked(TypeEnv* env, Env* ct_env) const
   return StructDeclaration(definition.line_num, *definition.name, fields);
 }
 
-auto FunctionDeclaration::TypeChecked(TypeEnv* env, Env* ct_env) const
+auto FunctionDeclaration::TypeChecked(TypeEnv env, Env ct_env) const
     -> Declaration {
   return FunctionDeclaration(TypeCheckFunDef(definition, env, ct_env));
 }
 
-auto ChoiceDeclaration::TypeChecked(TypeEnv* env, Env* ct_env) const
+auto ChoiceDeclaration::TypeChecked(TypeEnv env, Env ct_env) const
     -> Declaration {
   return *this;  // TODO.
 }
 
-auto TopLevel(std::list<Declaration>* fs) -> std::pair<TypeEnv*, Env*> {
-  ExecutionEnvironment tops = {nullptr, nullptr};
+auto TopLevel(std::list<Declaration>* fs) -> std::pair<TypeEnv, Env> {
+  ExecutionEnvironment tops;
   bool found_main = false;
 
   for (auto const& d : *fs) {
@@ -659,16 +663,16 @@ auto TopLevel(std::list<Declaration>* fs) -> std::pair<TypeEnv*, Env*> {
 
 auto FunctionDeclaration::TopLevel(ExecutionEnvironment& tops) const -> void {
   auto t = TypeOfFunDef(tops.first, tops.second, definition);
-  tops.first = new TypeEnv(Name(), t, tops.first);
+  tops.first.Set(Name(), t);
 }
 
 auto StructDeclaration::TopLevel(ExecutionEnvironment& tops) const -> void {
   auto st = TypeOfStructDef(&definition, tops.first, tops.second);
   Address a = AllocateValue(st);
-  tops.second = new Env(Name(), a, tops.second);  // Is this obsolete?
+  tops.second.Set(Name(), a);  // Is this obsolete?
   auto params = MakeTupleTypeVal(st->u.struct_type.fields);
   auto fun_ty = MakeFunTypeVal(params, st);
-  tops.first = new TypeEnv(Name(), fun_ty, tops.first);
+  tops.first.Set(Name(), fun_ty);
 }
 
 auto ChoiceDeclaration::TopLevel(ExecutionEnvironment& tops) const -> void {
@@ -679,8 +683,8 @@ auto ChoiceDeclaration::TopLevel(ExecutionEnvironment& tops) const -> void {
   }
   auto ct = MakeChoiceTypeVal(name, alts);
   Address a = AllocateValue(ct);
-  tops.second = new Env(Name(), a, tops.second);  // Is this obsolete?
-  tops.first = new TypeEnv(Name(), ct, tops.first);
+  tops.second.Set(Name(), a);  // Is this obsolete?
+  tops.first.Set(Name(), ct);
 }
 
 }  // namespace Carbon

+ 11 - 11
executable_semantics/interpreter/typecheck.h

@@ -9,43 +9,43 @@
 
 #include "executable_semantics/ast/expression.h"
 #include "executable_semantics/ast/statement.h"
-#include "executable_semantics/interpreter/assoc_list.h"
+#include "executable_semantics/interpreter/dictionary.h"
 #include "executable_semantics/interpreter/interpreter.h"
 
 namespace Carbon {
 
-using TypeEnv = AssocList<std::string, Value*>;
+using TypeEnv = Dictionary<std::string, Value*>;
 
-void PrintTypeEnv(TypeEnv* env);
+void PrintTypeEnv(TypeEnv env);
 
 enum class TCContext { ValueContext, PatternContext, TypeContext };
 
 struct TCResult {
-  TCResult(Expression* e, Value* t, TypeEnv* env) : exp(e), type(t), env(env) {}
+  TCResult(Expression* e, Value* t, TypeEnv env) : exp(e), type(t), env(env) {}
 
   Expression* exp;
   Value* type;
-  TypeEnv* env;
+  TypeEnv env;
 };
 
 struct TCStatement {
-  TCStatement(Statement* s, TypeEnv* e) : stmt(s), env(e) {}
+  TCStatement(Statement* s, TypeEnv e) : stmt(s), env(e) {}
 
   Statement* stmt;
-  TypeEnv* env;
+  TypeEnv env;
 };
 
 auto ToType(int line_num, Value* val) -> Value*;
 
-auto TypeCheckExp(Expression* e, TypeEnv* env, Env* ct_env, Value* expected,
+auto TypeCheckExp(Expression* e, TypeEnv env, Env ct_env, Value* expected,
                   TCContext context) -> TCResult;
 
-auto TypeCheckStmt(Statement*, TypeEnv*, Env*, Value*) -> TCStatement;
+auto TypeCheckStmt(Statement*, TypeEnv, Env, Value*) -> TCStatement;
 
-auto TypeCheckFunDef(struct FunctionDefinition*, TypeEnv*)
+auto TypeCheckFunDef(struct FunctionDefinition*, TypeEnv)
     -> struct FunctionDefinition*;
 
-auto TopLevel(std::list<Declaration>* fs) -> std::pair<TypeEnv*, Env*>;
+auto TopLevel(std::list<Declaration>* fs) -> std::pair<TypeEnv, Env>;
 
 void PrintErrorString(const std::string& s);
 

+ 3 - 3
executable_semantics/syntax/syntax_helpers.cpp

@@ -28,9 +28,9 @@ void ExecProgram(std::list<Declaration>* fs) {
     std::cout << "********** type checking **********" << std::endl;
   }
   state = new State();  // Compile-time state.
-  std::pair<TypeEnv*, Env*> p = TopLevel(fs);
-  TypeEnv* top = p.first;
-  Env* ct_top = p.second;
+  std::pair<TypeEnv, Env> p = TopLevel(fs);
+  TypeEnv top = p.first;
+  Env ct_top = p.second;
   std::list<Declaration> new_decls;
   for (const auto& decl : *fs) {
     new_decls.push_back(decl.TypeChecked(top, ct_top));