Просмотр исходного кода

const-ify Value*s in preparation for value semantic transformation (#409)

Making Values const allows us to separate the actual mutations (search for "*&" in this change) from places where the Value is effectively passed by-value.
Dave Abrahams 5 лет назад
Родитель
Сommit
dd4e37e761

+ 1 - 1
executable_semantics/ast/declaration.h

@@ -18,7 +18,7 @@ namespace Carbon {
 struct Value;
 
 using Address = unsigned int;
-using TypeEnv = Dictionary<std::string, Value*>;
+using TypeEnv = Dictionary<std::string, const Value*>;
 using Env = Dictionary<std::string, Address>;
 
 struct TypeCheckContext {

+ 1 - 1
executable_semantics/interpreter/action.cpp

@@ -83,7 +83,7 @@ auto MakeStmtAct(Statement* s) -> Action* {
   return act;
 }
 
-auto MakeValAct(Value* v) -> Action* {
+auto MakeValAct(const Value* v) -> Action* {
   auto* act = new Action();
   act->tag = ActionKind::ValAction;
   act->u.val = v;

+ 4 - 4
executable_semantics/interpreter/action.h

@@ -29,11 +29,11 @@ struct Action {
   union {
     Expression* exp;  // for LValAction and ExpressionAction
     Statement* stmt;
-    Value* val;  // for finished actions with a value (ValAction)
+    const Value* val;  // for finished actions with a value (ValAction)
     Address delete_tmp;
   } u;
-  int pos;                      // position or state of the action
-  std::vector<Value*> results;  // results from subexpression
+  int pos;                            // position or state of the action
+  std::vector<const Value*> results;  // results from subexpression
 };
 
 void PrintAct(Action* act, std::ostream& out);
@@ -41,7 +41,7 @@ void PrintActList(Stack<Action*> ls, std::ostream& out);
 auto MakeExpAct(Expression* e) -> Action*;
 auto MakeLvalAct(Expression* e) -> Action*;
 auto MakeStmtAct(Statement* s) -> Action*;
-auto MakeValAct(Value* v) -> Action*;
+auto MakeValAct(const Value* v) -> Action*;
 auto MakeExpToLvalAct() -> Action*;
 auto MakeDeleteAct(Address a) -> Action*;
 

+ 44 - 42
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)
-    -> std::optional<Env>;
+auto PatternMatch(const Value* pat, const Value* val, Env,
+                  std::list<std::string>*, int) -> std::optional<Env>;
 void HandleValue();
 
 template <class T>
@@ -42,37 +42,37 @@ static auto FindField(const std::string& field,
 // Auxiliary Functions
 //
 
-auto AllocateValue(Value* v) -> Address {
+auto 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(v);
+  state->heap.push_back(new Value(*v));
   state->alive.push_back(true);
   return a;
 }
 
-auto CopyVal(Value* val, int line_num) -> Value* {
+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);
-        Value* elt = CopyVal(state->heap[i.second], line_num);
+        const Value* elt = CopyVal(state->heap[i.second], line_num);
         Address new_address = AllocateValue(elt);
         elts->push_back(make_pair(i.first, new_address));
       }
       return MakeTupleVal(elts);
     }
     case ValKind::AltV: {
-      Value* arg = CopyVal(state->heap[val->u.alt.argument], line_num);
+      const Value* arg = CopyVal(state->heap[val->u.alt.argument], line_num);
       Address argument_address = AllocateValue(arg);
       return MakeAltVal(*val->u.alt.alt_name, *val->u.alt.choice_name,
                         argument_address);
     }
     case ValKind::StructV: {
-      Value* inits = CopyVal(val->u.struct_val.inits, line_num);
+      const Value* inits = CopyVal(val->u.struct_val.inits, line_num);
       return MakeStructVal(val->u.struct_val.type, inits);
     }
     case ValKind::IntV:
@@ -119,7 +119,7 @@ auto CopyVal(Value* val, int line_num) -> Value* {
 void KillObject(Address address);
 
 // Marks all of the sub-objects of this value as dead.
-void KillSubObjects(Value* val) {
+void KillSubObjects(const Value* val) {
   switch (val->tag) {
     case ValKind::AltV:
       KillObject(val->u.alt.argument);
@@ -177,7 +177,7 @@ void PrintStack(Stack<Frame*> ls, std::ostream& out) {
   }
 }
 
-void PrintHeap(const std::vector<Value*>& heap, std::ostream& out) {
+void PrintHeap(const std::vector<const Value*>& heap, std::ostream& out) {
   for (auto& iter : heap) {
     if (iter) {
       PrintValue(iter, out);
@@ -208,7 +208,7 @@ void PrintState(std::ostream& out) {
 // More Auxiliary Functions
 //
 
-auto ValToInt(Value* v, int line_num) -> int {
+auto ValToInt(const Value* v, int line_num) -> int {
   switch (v->tag) {
     case ValKind::IntV:
       return v->u.integer;
@@ -219,7 +219,7 @@ auto ValToInt(Value* v, int line_num) -> int {
   }
 }
 
-auto ValToBool(Value* v, int line_num) -> int {
+auto ValToBool(const Value* v, int line_num) -> int {
   switch (v->tag) {
     case ValKind::BoolV:
       return v->u.boolean;
@@ -229,7 +229,7 @@ auto ValToBool(Value* v, int line_num) -> int {
   }
 }
 
-auto ValToPtr(Value* v, int line_num) -> Address {
+auto ValToPtr(const Value* v, int line_num) -> Address {
   CheckAlive(v->u.ptr, line_num);
   switch (v->tag) {
     case ValKind::PtrV:
@@ -242,8 +242,8 @@ auto ValToPtr(Value* v, int line_num) -> Address {
   }
 }
 
-auto EvalPrim(Operator op, const std::vector<Value*>& args, int line_num)
-    -> Value* {
+auto EvalPrim(Operator op, const std::vector<const Value*>& args, int line_num)
+    -> const Value* {
   switch (op) {
     case Operator::Neg:
       return MakeIntVal(-ValToInt(args[0], line_num));
@@ -325,7 +325,8 @@ auto VariableDeclaration::InitGlobals(Env& globals) const -> void {
 // where C is the body of the function,
 //       E is the environment (functions + parameters + locals)
 //       F is the function
-void CallFunction(int line_num, std::vector<Value*> operas, State* state) {
+void CallFunction(int line_num, std::vector<const Value*> operas,
+                  State* state) {
   switch (operas[0]->tag) {
     case ValKind::FunV: {
       // Bind arguments to parameters
@@ -345,15 +346,15 @@ void CallFunction(int line_num, std::vector<Value*> operas, State* state) {
       break;
     }
     case ValKind::StructTV: {
-      Value* arg = CopyVal(operas[1], line_num);
-      Value* sv = MakeStructVal(operas[0], arg);
+      const Value* arg = CopyVal(operas[1], line_num);
+      const Value* sv = MakeStructVal(operas[0], arg);
       Frame* frame = state->stack.Top();
       frame->todo.Push(MakeValAct(sv));
       break;
     }
     case ValKind::AltConsV: {
-      Value* arg = CopyVal(operas[1], line_num);
-      Value* av =
+      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));
       Frame* frame = state->stack.Top();
@@ -394,7 +395,7 @@ void CreateTuple(Frame* frame, Action* act, Expression* /*exp*/) {
     Address a = AllocateValue(*i);  // copy?
     elts->push_back(make_pair(f->first, a));
   }
-  Value* tv = MakeTupleVal(elts);
+  const Value* tv = MakeTupleVal(elts);
   frame->todo.Pop(1);
   frame->todo.Push(MakeValAct(tv));
 }
@@ -404,8 +405,9 @@ void CreateTuple(Frame* frame, Action* act, Expression* /*exp*/) {
 //
 // 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> {
+auto PatternMatch(const Value* p, const Value* v, Env env,
+                  std::list<std::string>* vars, int line_num)
+    -> std::optional<Env> {
   switch (p->tag) {
     case ValKind::VarPatV: {
       Address a = AllocateValue(CopyVal(v, line_num));
@@ -491,7 +493,7 @@ auto PatternMatch(Value* p, Value* v, Env env, std::list<std::string>* vars,
   }
 }
 
-void PatternAssignment(Value* pat, Value* val, int line_num) {
+void PatternAssignment(const Value* pat, const Value* val, int line_num) {
   switch (pat->tag) {
     case ValKind::PtrV:
       state->heap[ValToPtr(pat, line_num)] = val;
@@ -579,7 +581,7 @@ void StepLvalue() {
                   << *(exp->u.variable.name) << "`" << std::endl;
         exit(-1);
       }
-      Value* v = MakePtrVal(*pointer);
+      const Value* v = MakePtrVal(*pointer);
       CheckAlive(*pointer, exp->line_num);
       frame->todo.Pop();
       frame->todo.Push(MakeValAct(v));
@@ -676,7 +678,7 @@ void StepExp() {
                   << *(exp->u.variable.name) << "`" << std::endl;
         exit(-1);
       }
-      Value* pointee = state->heap[*pointer];
+      const Value* pointee = state->heap[*pointer];
       frame->todo.Pop(1);
       frame->todo.Push(MakeValAct(pointee));
       break;
@@ -700,7 +702,7 @@ void StepExp() {
       } else {
         //    { {v :: op(]) :: C, E, F} :: S, H}
         // -> { {eval_prim(op, ()) :: C, E, F} :: S, H}
-        Value* v =
+        const Value* v =
             EvalPrim(exp->u.primitive_op.op, act->results, exp->line_num);
         frame->todo.Pop(2);
         frame->todo.Push(MakeValAct(v));
@@ -713,25 +715,25 @@ void StepExp() {
       act->pos++;
       break;
     case ExpressionKind::IntT: {
-      Value* v = MakeIntTypeVal();
+      const Value* v = MakeIntTypeVal();
       frame->todo.Pop(1);
       frame->todo.Push(MakeValAct(v));
       break;
     }
     case ExpressionKind::BoolT: {
-      Value* v = MakeBoolTypeVal();
+      const Value* v = MakeBoolTypeVal();
       frame->todo.Pop(1);
       frame->todo.Push(MakeValAct(v));
       break;
     }
     case ExpressionKind::AutoT: {
-      Value* v = MakeAutoTypeVal();
+      const Value* v = MakeAutoTypeVal();
       frame->todo.Pop(1);
       frame->todo.Push(MakeValAct(v));
       break;
     }
     case ExpressionKind::TypeT: {
-      Value* v = MakeTypeTypeVal();
+      const Value* v = MakeTypeTypeVal();
       frame->todo.Pop(1);
       frame->todo.Push(MakeValAct(v));
       break;
@@ -878,7 +880,7 @@ void StepStmt() {
 }
 
 auto GetMember(Address a, const std::string& f) -> Address {
-  Value* v = state->heap[a];
+  const Value* v = state->heap[a];
   switch (v->tag) {
     case ValKind::StructV: {
       auto a = FindField(f, *v->u.struct_val.inits->u.tuple.elts);
@@ -980,7 +982,7 @@ void HandleValue() {
         case ExpressionKind::GetField: {
           //    { v :: [].f :: C, E, F} :: S, H}
           // -> { { &v.f :: C, E, F} :: S, H }
-          Value* str = act->results[0];
+          const Value* str = act->results[0];
           Address a =
               GetMember(ValToPtr(str, exp->line_num), *exp->u.get_field.field);
           frame->todo.Pop(2);
@@ -994,7 +996,7 @@ void HandleValue() {
           } else if (act->pos == 2) {
             //    { v :: [][i] :: C, E, F} :: S, H}
             // -> { { &v[i] :: C, E, F} :: S, H }
-            Value* tuple = act->results[0];
+            const Value* tuple = act->results[0];
             std::string f = std::to_string(ToInteger(act->results[1]));
             auto a = FindField(f, *tuple->u.tuple.elts);
             if (a == std::nullopt) {
@@ -1107,7 +1109,7 @@ void HandleValue() {
           } else {
             //    { {v :: op(vs,[]) :: C, E, F} :: S, H}
             // -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H}
-            Value* v =
+            const Value* v =
                 EvalPrim(exp->u.primitive_op.op, act->results, exp->line_num);
             frame->todo.Pop(2);
             frame->todo.Push(MakeValAct(v));
@@ -1136,7 +1138,7 @@ void HandleValue() {
           if (act->pos == 2) {
             //    { { rt :: fn pt -> [] :: C, E, F} :: S, H}
             // -> { fn pt -> rt :: {C, E, F} :: S, H}
-            Value* v = MakeFunTypeVal(act->results[0], act->results[1]);
+            const Value* v = MakeFunTypeVal(act->results[0], act->results[1]);
             frame->todo.Pop(2);
             frame->todo.Push(MakeValAct(v));
           } else {
@@ -1173,8 +1175,8 @@ void HandleValue() {
           } else if (act->pos == 2) {
             //    { { v :: (x = []) :: C, E, F} :: S, H}
             // -> { { C, E(x := a), F} :: S, H(a := copy(v))}
-            Value* v = act->results[0];
-            Value* p = act->results[1];
+            const Value* v = act->results[0];
+            const Value* p = act->results[1];
 
             std::optional<Env> env_with_matches =
                 PatternMatch(p, v, frame->scopes.Top()->env,
@@ -1302,7 +1304,7 @@ void HandleValue() {
         case StatementKind::Return: {
           //    { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H}
           // -> { {v :: C', E', F'} :: S, H}
-          Value* ret_val = CopyVal(val_act->u.val, stmt->line_num);
+          const Value* ret_val = CopyVal(val_act->u.val, stmt->line_num);
           KillLocals(stmt->line_num, frame);
           state->stack.Pop(1);
           frame = state->stack.Top();
@@ -1389,12 +1391,12 @@ auto InterpProgram(std::list<Declaration>* fs) -> int {
       PrintState(std::cout);
     }
   }
-  Value* v = state->stack.Top()->todo.Top()->u.val;
+  const Value* v = state->stack.Top()->todo.Top()->u.val;
   return ValToInt(v, 0);
 }
 
 // Interpret an expression at compile-time.
-auto InterpExp(Env env, Expression* e) -> Value* {
+auto InterpExp(Env env, Expression* e) -> const Value* {
   auto todo = Stack(MakeExpAct(e));
   auto* scope = new Scope(env, std::list<std::string>());
   auto* frame = new Frame("InterpExp", Stack(scope), todo);
@@ -1405,7 +1407,7 @@ auto InterpExp(Env env, Expression* e) -> Value* {
          state->stack.Top()->todo.Top()->tag != ActionKind::ValAction) {
     Step();
   }
-  Value* v = state->stack.Top()->todo.Top()->u.val;
+  const Value* v = state->stack.Top()->todo.Top()->u.val;
   return v;
 }
 

+ 5 - 5
executable_semantics/interpreter/interpreter.h

@@ -40,21 +40,21 @@ struct Frame {
 
 struct State {
   Stack<Frame*> stack;
-  std::vector<Value*> heap;
+  std::vector<const Value*> heap;
   std::vector<bool> alive;
 };
 
 extern State* state;
 
 void PrintEnv(Env env);
-auto AllocateValue(Value* v) -> Address;
-auto CopyVal(Value* val, int line_num) -> Value*;
-auto ToInteger(Value* v) -> int;
+auto AllocateValue(const Value* v) -> Address;
+auto CopyVal(const Value* val, int line_num) -> const Value*;
+auto ToInteger(const Value* v) -> int;
 
 /***** Interpreters *****/
 
 auto InterpProgram(std::list<Declaration>* fs) -> int;
-auto InterpExp(Env env, Expression* e) -> Value*;
+auto InterpExp(Env env, Expression* e) -> const Value*;
 
 }  // namespace Carbon
 

+ 21 - 20
executable_semantics/interpreter/typecheck.cpp

@@ -16,8 +16,8 @@
 
 namespace Carbon {
 
-void ExpectType(int line_num, const std::string& context, Value* expected,
-                Value* actual) {
+void ExpectType(int line_num, const std::string& context, const Value* expected,
+                const Value* actual) {
   if (!TypeEqual(expected, actual)) {
     std::cerr << line_num << ": type error in " << context << std::endl;
     std::cerr << "expected: ";
@@ -40,12 +40,12 @@ void PrintTypeEnv(TypeEnv env, std::ostream& out) {
 }
 
 // Convert tuples to tuple types.
-auto ToType(int line_num, Value* val) -> Value* {
+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) {
-        Value* ty = ToType(line_num, state->heap[elt.second]);
+        const Value* ty = ToType(line_num, state->heap[elt.second]);
         fields->push_back(std::make_pair(elt.first, ty));
       }
       return MakeTupleTypeVal(fields);
@@ -53,7 +53,7 @@ auto ToType(int line_num, Value* val) -> Value* {
     case ValKind::TupleTV: {
       auto fields = new VarValues();
       for (auto& field : *val->u.tuple_type.fields) {
-        Value* ty = ToType(line_num, field.second);
+        const Value* ty = ToType(line_num, field.second);
         fields->push_back(std::make_pair(field.first, ty));
       }
       return MakeTupleTypeVal(fields);
@@ -86,7 +86,7 @@ auto ToType(int line_num, Value* val) -> Value* {
 }
 
 // Reify type to type expression.
-auto ReifyType(Value* t, int line_num) -> Expression* {
+auto ReifyType(const Value* t, int line_num) -> Expression* {
   switch (t->tag) {
     case ValKind::VarTV:
       return MakeVar(0, *t->u.var_type);
@@ -137,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, const Value* expected,
                   TCContext context) -> TCResult {
   switch (e->tag) {
     case ExpressionKind::PatternVariable: {
@@ -198,7 +198,7 @@ auto TypeCheckExp(Expression* e, TypeEnv env, Env ct_env, Value* expected,
       int i = 0;
       for (auto arg = e->u.tuple.fields->begin();
            arg != e->u.tuple.fields->end(); ++arg, ++i) {
-        Value* arg_expected = nullptr;
+        const Value* arg_expected = nullptr;
         if (expected && expected->tag == ValKind::TupleTV) {
           arg_expected =
               FindInVarValues(arg->first, expected->u.tuple_type.fields);
@@ -281,7 +281,7 @@ auto TypeCheckExp(Expression* e, TypeEnv env, Env ct_env, Value* expected,
       }
     }
     case ExpressionKind::Variable: {
-      std::optional<Value*> type = env.Get(*(e->u.variable.name));
+      std::optional<const Value*> type = env.Get(*(e->u.variable.name));
       if (type) {
         return TCResult(e, *type, env);
       } else {
@@ -296,7 +296,7 @@ auto TypeCheckExp(Expression* e, TypeEnv env, Env ct_env, Value* expected,
       return TCResult(e, MakeBoolTypeVal(), env);
     case ExpressionKind::PrimitiveOp: {
       auto es = new std::vector<Expression*>();
-      std::vector<Value*> ts;
+      std::vector<const Value*> ts;
       auto new_env = env;
       for (auto& argument : *e->u.primitive_op.arguments) {
         auto res = TypeCheckExp(argument, env, ct_env, nullptr,
@@ -389,8 +389,8 @@ 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)
+auto TypecheckCase(const Value* expected, Expression* pat, Statement* body,
+                   TypeEnv env, Env ct_env, const Value*& ret_type)
     -> std::pair<Expression*, Statement*> {
   auto pat_res =
       TypeCheckExp(pat, env, ct_env, expected, TCContext::PatternContext);
@@ -405,8 +405,8 @@ 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)
-    -> TCStatement {
+auto TypeCheckStmt(Statement* s, TypeEnv env, Env ct_env,
+                   const Value*& ret_type) -> TCStatement {
   if (!s) {
     return TCStatement(s, env);
   }
@@ -443,7 +443,7 @@ auto TypeCheckStmt(Statement* s, TypeEnv env, Env ct_env, Value* ret_type)
     case StatementKind::VariableDefinition: {
       auto res = TypeCheckExp(s->u.variable_definition.init, env, ct_env,
                               nullptr, TCContext::ValueContext);
-      Value* rhs_ty = res.type;
+      const Value* rhs_ty = res.type;
       auto lhs_res = TypeCheckExp(s->u.variable_definition.pat, env, ct_env,
                                   rhs_ty, TCContext::PatternContext);
       Statement* new_s =
@@ -494,7 +494,7 @@ auto TypeCheckStmt(Statement* s, TypeEnv env, Env ct_env, Value* ret_type)
         // The following infers the return type from the first 'return'
         // statement. This will get more difficult with subtyping, when we
         // should infer the least-upper bound of all the 'return' statements.
-        *ret_type = *res.type;
+        ret_type = res.type;
       } else {
         ExpectType(s->line_num, "return", ret_type, res.type);
       }
@@ -587,7 +587,7 @@ auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv env, Env ct_env)
 }
 
 auto TypeOfFunDef(TypeEnv env, Env ct_env, const FunctionDefinition* fun_def)
-    -> Value* {
+    -> const Value* {
   auto param_res = TypeCheckExp(fun_def->param_pattern, env, ct_env, nullptr,
                                 TCContext::PatternContext);
   auto param_type = ToType(fun_def->line_num, param_res.type);
@@ -600,7 +600,7 @@ auto TypeOfFunDef(TypeEnv env, Env ct_env, const FunctionDefinition* fun_def)
 }
 
 auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*env*/, Env ct_top)
-    -> Value* {
+    -> const Value* {
   auto fields = new VarValues();
   auto methods = new VarValues();
   for (auto m = sd->members->begin(); m != sd->members->end(); ++m) {
@@ -652,7 +652,7 @@ auto VariableDeclaration::TypeChecked(TypeEnv env, Env ct_env) const
     -> Declaration {
   TCResult type_checked_initializer =
       TypeCheckExp(initializer, env, ct_env, nullptr, TCContext::ValueContext);
-  Value* declared_type = ToType(source_location, InterpExp(ct_env, type));
+  const Value* declared_type = ToType(source_location, InterpExp(ct_env, type));
   ExpectType(source_location, "initializer of variable", declared_type,
              type_checked_initializer.type);
   return *this;
@@ -706,7 +706,8 @@ 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 {
-  Value* declared_type = ToType(source_location, InterpExp(tops.values, type));
+  const Value* declared_type =
+      ToType(source_location, InterpExp(tops.values, type));
   tops.types.Set(Name(), declared_type);
 }
 

+ 7 - 6
executable_semantics/interpreter/typecheck.h

@@ -14,17 +14,18 @@
 
 namespace Carbon {
 
-using TypeEnv = Dictionary<std::string, Value*>;
+using TypeEnv = Dictionary<std::string, const Value*>;
 
 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, const Value* t, TypeEnv env)
+      : exp(e), type(t), env(env) {}
 
   Expression* exp;
-  Value* type;
+  const Value* type;
   TypeEnv env;
 };
 
@@ -35,12 +36,12 @@ struct TCStatement {
   TypeEnv env;
 };
 
-auto ToType(int line_num, Value* val) -> Value*;
+auto ToType(int line_num, const Value* val) -> const Value*;
 
-auto TypeCheckExp(Expression* e, TypeEnv env, Env ct_env, Value* expected,
+auto TypeCheckExp(Expression* e, TypeEnv env, Env ct_env, const Value* expected,
                   TCContext context) -> TCResult;
 
-auto TypeCheckStmt(Statement*, TypeEnv, Env, Value*) -> TCStatement;
+auto TypeCheckStmt(Statement*, TypeEnv, Env, Value const*&) -> TCStatement;
 
 auto TypeCheckFunDef(struct FunctionDefinition*, TypeEnv)
     -> struct FunctionDefinition*;

+ 29 - 26
executable_semantics/interpreter/value.cpp

@@ -10,7 +10,8 @@
 
 namespace Carbon {
 
-auto FindInVarValues(const std::string& field, VarValues* inits) -> Value* {
+auto FindInVarValues(const std::string& field, VarValues* inits)
+    -> const Value* {
   for (auto& i : *inits) {
     if (i.first == field) {
       return i.second;
@@ -36,21 +37,22 @@ auto FieldsEqual(VarValues* ts1, VarValues* ts2) -> bool {
   }
 }
 
-auto MakeIntVal(int i) -> Value* {
+auto MakeIntVal(int i) -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::IntV;
   v->u.integer = i;
   return v;
 }
 
-auto MakeBoolVal(bool b) -> Value* {
+auto MakeBoolVal(bool b) -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::BoolV;
   v->u.boolean = b;
   return v;
 }
 
-auto MakeFunVal(std::string name, Value* param, Statement* body) -> Value* {
+auto MakeFunVal(std::string name, const Value* param, Statement* body)
+    -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::FunV;
   v->u.fun.name = new std::string(std::move(name));
@@ -59,14 +61,14 @@ auto MakeFunVal(std::string name, Value* param, Statement* body) -> Value* {
   return v;
 }
 
-auto MakePtrVal(Address addr) -> Value* {
+auto MakePtrVal(Address addr) -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::PtrV;
   v->u.ptr = addr;
   return v;
 }
 
-auto MakeStructVal(Value* type, Value* inits) -> Value* {
+auto MakeStructVal(const Value* type, const Value* inits) -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::StructV;
   v->u.struct_val.type = type;
@@ -75,7 +77,7 @@ auto MakeStructVal(Value* type, Value* inits) -> Value* {
 }
 
 auto MakeTupleVal(std::vector<std::pair<std::string, Address>>* elts)
-    -> Value* {
+    -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::TupleV;
   v->u.tuple.elts = elts;
@@ -83,7 +85,7 @@ auto MakeTupleVal(std::vector<std::pair<std::string, Address>>* elts)
 }
 
 auto MakeAltVal(std::string alt_name, std::string choice_name, Address argument)
-    -> Value* {
+    -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::AltV;
   v->u.alt.alt_name = new std::string(std::move(alt_name));
@@ -92,7 +94,8 @@ auto MakeAltVal(std::string alt_name, std::string choice_name, Address argument)
   return v;
 }
 
-auto MakeAltCons(std::string alt_name, std::string choice_name) -> Value* {
+auto MakeAltCons(std::string alt_name, std::string choice_name)
+    -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::AltConsV;
   v->u.alt.alt_name = new std::string(std::move(alt_name));
@@ -100,7 +103,7 @@ auto MakeAltCons(std::string alt_name, std::string choice_name) -> Value* {
   return v;
 }
 
-auto MakeVarPatVal(std::string name, Value* type) -> Value* {
+auto MakeVarPatVal(std::string name, const Value* type) -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::VarPatV;
   v->u.var_pat.name = new std::string(std::move(name));
@@ -108,38 +111,38 @@ auto MakeVarPatVal(std::string name, Value* type) -> Value* {
   return v;
 }
 
-auto MakeVarTypeVal(std::string name) -> Value* {
+auto MakeVarTypeVal(std::string name) -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::VarTV;
   v->u.var_type = new std::string(std::move(name));
   return v;
 }
 
-auto MakeIntTypeVal() -> Value* {
+auto MakeIntTypeVal() -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::IntTV;
   return v;
 }
 
-auto MakeBoolTypeVal() -> Value* {
+auto MakeBoolTypeVal() -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::BoolTV;
   return v;
 }
 
-auto MakeTypeTypeVal() -> Value* {
+auto MakeTypeTypeVal() -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::TypeTV;
   return v;
 }
 
-auto MakeAutoTypeVal() -> Value* {
+auto MakeAutoTypeVal() -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::AutoTV;
   return v;
 }
 
-auto MakeFunTypeVal(Value* param, Value* ret) -> Value* {
+auto MakeFunTypeVal(const Value* param, const Value* ret) -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::FunctionTV;
   v->u.fun_type.param = param;
@@ -147,7 +150,7 @@ auto MakeFunTypeVal(Value* param, Value* ret) -> Value* {
   return v;
 }
 
-auto MakePtrTypeVal(Value* type) -> Value* {
+auto MakePtrTypeVal(const Value* type) -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::PointerTV;
   v->u.ptr_type.type = type;
@@ -155,7 +158,7 @@ auto MakePtrTypeVal(Value* type) -> Value* {
 }
 
 auto MakeStructTypeVal(std::string name, VarValues* fields, VarValues* methods)
-    -> Value* {
+    -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::StructTV;
   v->u.struct_type.name = new std::string(std::move(name));
@@ -164,14 +167,14 @@ auto MakeStructTypeVal(std::string name, VarValues* fields, VarValues* methods)
   return v;
 }
 
-auto MakeTupleTypeVal(VarValues* fields) -> Value* {
+auto MakeTupleTypeVal(VarValues* fields) -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::TupleTV;
   v->u.tuple_type.fields = fields;
   return v;
 }
 
-auto MakeVoidTypeVal() -> Value* {
+auto MakeVoidTypeVal() -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::TupleTV;
   v->u.tuple_type.fields = new VarValues();
@@ -179,8 +182,8 @@ auto MakeVoidTypeVal() -> Value* {
 }
 
 auto MakeChoiceTypeVal(std::string name,
-                       std::list<std::pair<std::string, Value*>>* alts)
-    -> Value* {
+                       std::list<std::pair<std::string, const Value*>>* alts)
+    -> const Value* {
   auto* v = new Value();
   v->tag = ValKind::ChoiceTV;
   // Transitional leak: when we get rid of all pointers, this will disappear.
@@ -189,7 +192,7 @@ auto MakeChoiceTypeVal(std::string name,
   return v;
 }
 
-void PrintValue(Value* val, std::ostream& out) {
+void PrintValue(const Value* val, std::ostream& out) {
   switch (val->tag) {
     case ValKind::AltConsV: {
       out << *val->u.alt_cons.choice_name << "." << *val->u.alt_cons.alt_name;
@@ -291,7 +294,7 @@ void PrintValue(Value* val, std::ostream& out) {
   }
 }
 
-auto TypeEqual(Value* t1, Value* t2) -> bool {
+auto TypeEqual(const Value* t1, const Value* t2) -> bool {
   if (t1->tag != t2->tag) {
     return false;
   }
@@ -334,7 +337,7 @@ static auto FieldsValueEqual(VarValues* ts1, VarValues* ts2, int line_num)
   return true;
 }
 
-auto ValueEqual(Value* v1, Value* v2, int line_num) -> bool {
+auto ValueEqual(const Value* v1, const Value* v2, int line_num) -> bool {
   if (v1->tag != v2->tag) {
     return false;
   }
@@ -357,7 +360,7 @@ auto ValueEqual(Value* v1, Value* v2, int line_num) -> bool {
   }
 }
 
-auto ToInteger(Value* v) -> int {
+auto ToInteger(const Value* v) -> int {
   switch (v->tag) {
     case ValKind::IntV:
       return v->u.integer;

+ 39 - 36
executable_semantics/interpreter/value.h

@@ -14,9 +14,10 @@ namespace Carbon {
 
 struct Value;
 using Address = unsigned int;
-using VarValues = std::list<std::pair<std::string, Value*>>;
+using VarValues = std::list<std::pair<std::string, const Value*>>;
 
-auto FindInVarValues(const std::string& field, VarValues* inits) -> Value*;
+auto FindInVarValues(const std::string& field, VarValues* inits)
+    -> const Value*;
 auto FieldsEqual(VarValues* ts1, VarValues* ts2) -> bool;
 
 enum class ValKind {
@@ -48,12 +49,12 @@ struct Value {
     bool boolean;
     struct {
       std::string* name;
-      Value* param;
+      const Value* param;
       Statement* body;
     } fun;
     struct {
-      Value* type;
-      Value* inits;
+      const Value* type;
+      const Value* inits;
     } struct_val;
     struct {
       std::string* alt_name;
@@ -71,14 +72,14 @@ struct Value {
     std::string* var_type;
     struct {
       std::string* name;
-      Value* type;
+      const Value* type;
     } var_pat;
     struct {
-      Value* param;
-      Value* ret;
+      const Value* param;
+      const Value* ret;
     } fun_type;
     struct {
-      Value* type;
+      const Value* type;
     } ptr_type;
     struct {
       std::string* name;
@@ -95,42 +96,44 @@ struct Value {
     } choice_type;
     struct {
       std::list<std::string*>* params;
-      Value* type;
+      const Value* type;
     } implicit;
   } u;
 };
 
-auto MakeIntVal(int i) -> Value*;
-auto MakeBoolVal(bool b) -> Value*;
-auto MakeFunVal(std::string name, Value* param, Statement* body) -> Value*;
-auto MakePtrVal(Address addr) -> Value*;
-auto MakeStructVal(Value* type, Value* inits) -> Value*;
-auto MakeTupleVal(std::vector<std::pair<std::string, Address>>* elts) -> Value*;
+auto MakeIntVal(int i) -> const Value*;
+auto MakeBoolVal(bool b) -> const Value*;
+auto MakeFunVal(std::string name, const Value* param, Statement* body)
+    -> const Value*;
+auto MakePtrVal(Address addr) -> const Value*;
+auto MakeStructVal(const Value* type, const Value* inits) -> const Value*;
+auto MakeTupleVal(std::vector<std::pair<std::string, Address>>* elts)
+    -> const Value*;
 auto MakeAltVal(std::string alt_name, std::string choice_name, Address argument)
-    -> Value*;
-auto MakeAltCons(std::string alt_name, std::string choice_name) -> Value*;
-
-auto MakeVarPatVal(std::string name, Value* type) -> Value*;
-
-auto MakeVarTypeVal(std::string name) -> Value*;
-auto MakeIntTypeVal() -> Value*;
-auto MakeAutoTypeVal() -> Value*;
-auto MakeBoolTypeVal() -> Value*;
-auto MakeTypeTypeVal() -> Value*;
-auto MakeFunTypeVal(Value* param, Value* ret) -> Value*;
-auto MakePtrTypeVal(Value* type) -> Value*;
+    -> const Value*;
+auto MakeAltCons(std::string alt_name, std::string choice_name) -> const Value*;
+
+auto MakeVarPatVal(std::string name, const Value* type) -> const Value*;
+
+auto MakeVarTypeVal(std::string name) -> const Value*;
+auto MakeIntTypeVal() -> const Value*;
+auto MakeAutoTypeVal() -> const Value*;
+auto MakeBoolTypeVal() -> const Value*;
+auto MakeTypeTypeVal() -> const Value*;
+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)
-    -> Value*;
-auto MakeTupleTypeVal(VarValues* fields) -> Value*;
-auto MakeVoidTypeVal() -> Value*;
-auto MakeChoiceTypeVal(std::string name, VarValues* alts) -> Value*;
+    -> const Value*;
+auto MakeTupleTypeVal(VarValues* fields) -> const Value*;
+auto MakeVoidTypeVal() -> const Value*;
+auto MakeChoiceTypeVal(std::string name, VarValues* alts) -> const Value*;
 
-void PrintValue(Value* val, std::ostream& out);
+void PrintValue(const Value* val, std::ostream& out);
 
-auto TypeEqual(Value* t1, Value* t2) -> bool;
-auto ValueEqual(Value* v1, Value* v2, int line_num) -> bool;
+auto TypeEqual(const Value* t1, const Value* t2) -> bool;
+auto ValueEqual(const Value* v1, const Value* v2, int line_num) -> bool;
 
-auto ToInteger(Value* v) -> int;
+auto ToInteger(const Value* v) -> int;
 void CheckAlive(Address a, int line_num);
 
 }  // namespace Carbon