From de0571196674d0d73f7f59b068cbb0582babe740 Mon Sep 17 00:00:00 2001 From: Geoff Romer Date: Mon, 12 Jul 2021 11:24:30 -0700 Subject: [PATCH] Migrate Value to std::variant (#635) Also: - Switch data members, and associated parameters and return types to be values/references rather than pointers, where applicable. - Move Expression's TagVisitor to anonymous namespace, to avoid name collision. - Switch to trailing return syntax (per style guide). --- executable_semantics/ast/expression.cpp | 15 ++ executable_semantics/ast/expression.h | 15 +- .../interpreter/interpreter.cpp | 113 ++++---- .../interpreter/typecheck.cpp | 82 +++--- executable_semantics/interpreter/value.cpp | 249 ++++++++---------- executable_semantics/interpreter/value.h | 138 ++++++---- 6 files changed, 316 insertions(+), 296 deletions(-) diff --git a/executable_semantics/ast/expression.cpp b/executable_semantics/ast/expression.cpp index 43f727bdf2da..32748860246c 100644 --- a/executable_semantics/ast/expression.cpp +++ b/executable_semantics/ast/expression.cpp @@ -8,6 +8,21 @@ namespace Carbon { +namespace { + +struct TagVisitor { + template + auto operator()(const Alternative&) -> ExpressionKind { + return Alternative::Kind; + } +}; + +} // namespace + +auto Expression::tag() const -> ExpressionKind { + return std::visit(TagVisitor(), value); +} + auto Expression::GetIdentifierExpression() const -> const IdentifierExpression& { return std::get(value); diff --git a/executable_semantics/ast/expression.h b/executable_semantics/ast/expression.h index 653b46da4120..2a703b3640bc 100644 --- a/executable_semantics/ast/expression.h +++ b/executable_semantics/ast/expression.h @@ -135,7 +135,7 @@ struct TypeTypeLiteral { struct Expression { int line_num; - inline auto tag() const -> ExpressionKind; + auto tag() const -> ExpressionKind; static auto MakeIdentifierExpression(int line_num, std::string var) -> const Expression*; @@ -187,19 +187,6 @@ struct Expression { void PrintExp(const Expression* exp); -// Implementation details only beyond this point - -struct TagVisitor { - template - auto operator()(const Alternative&) -> ExpressionKind { - return Alternative::Kind; - } -}; - -auto Expression::tag() const -> ExpressionKind { - return std::visit(TagVisitor(), value); -} - } // namespace Carbon #endif // EXECUTABLE_SEMANTICS_AST_EXPRESSION_H_ diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index 3ac11b178ef0..142bf2e8e782 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -65,25 +65,25 @@ void Heap::CheckAlive(Address address, int line_num) { } auto CopyVal(const Value* val, int line_num) -> const Value* { - switch (val->tag) { + switch (val->tag()) { case ValKind::TupleValue: { - auto* elements = new std::vector(); - for (const TupleElement& element : *val->GetTupleValue().elements) { + std::vector elements; + for (const TupleElement& element : val->GetTupleValue().elements) { const Value* new_element = CopyVal(state->heap.Read(element.address, line_num), line_num); Address new_address = state->heap.AllocateValue(new_element); - elements->push_back({.name = element.name, .address = new_address}); + elements.push_back({.name = element.name, .address = new_address}); } - return Value::MakeTupleValue(elements); + return Value::MakeTupleValue(std::move(elements)); } case ValKind::AlternativeValue: { const Value* arg = CopyVal( state->heap.Read(val->GetAlternativeValue().argument, line_num), line_num); Address argument_address = state->heap.AllocateValue(arg); - return Value::MakeAlternativeValue( - *val->GetAlternativeValue().alt_name, - *val->GetAlternativeValue().choice_name, argument_address); + return Value::MakeAlternativeValue(val->GetAlternativeValue().alt_name, + val->GetAlternativeValue().choice_name, + argument_address); } case ValKind::StructValue: { const Value* inits = CopyVal(val->GetStructValue().inits, line_num); @@ -94,7 +94,7 @@ auto CopyVal(const Value* val, int line_num) -> const Value* { case ValKind::BoolValue: return Value::MakeBoolValue(val->GetBoolValue()); case ValKind::FunctionValue: - return Value::MakeFunctionValue(*val->GetFunctionValue().name, + return Value::MakeFunctionValue(val->GetFunctionValue().name, val->GetFunctionValue().param, val->GetFunctionValue().body); case ValKind::PointerValue: @@ -130,7 +130,7 @@ auto CopyVal(const Value* val, int line_num) -> const Value* { } void Heap::DeallocateSubObjects(const Value* val) { - switch (val->tag) { + switch (val->tag()) { case ValKind::AlternativeValue: Deallocate(val->GetAlternativeValue().argument); break; @@ -138,7 +138,7 @@ void Heap::DeallocateSubObjects(const Value* val) { DeallocateSubObjects(val->GetStructValue().inits); break; case ValKind::TupleValue: - for (const TupleElement& element : *val->GetTupleValue().elements) { + for (const TupleElement& element : val->GetTupleValue().elements) { Deallocate(element.address); } break; @@ -224,7 +224,7 @@ void PrintState(std::ostream& out) { // auto ValToInt(const Value* v, int line_num) -> int { - switch (v->tag) { + switch (v->tag()) { case ValKind::IntValue: return v->GetIntValue(); default: @@ -235,7 +235,7 @@ auto ValToInt(const Value* v, int line_num) -> int { } auto ValToBool(const Value* v, int line_num) -> int { - switch (v->tag) { + switch (v->tag()) { case ValKind::BoolValue: return v->GetBoolValue(); default: @@ -245,7 +245,7 @@ auto ValToBool(const Value* v, int line_num) -> int { } auto ValToPtr(const Value* v, int line_num) -> Address { - switch (v->tag) { + switch (v->tag()) { case ValKind::PointerValue: return v->GetPointerValue(); default: @@ -261,8 +261,8 @@ auto ValToPtr(const Value* v, int line_num) -> Address { // - Precondition: continuation->tag == ValKind::ContinuationV. auto ContinuationToVector(const Value* continuation, int sourceLocation) -> std::vector { - if (continuation->tag == ValKind::ContinuationValue) { - return *continuation->GetContinuationValue().stack; + if (continuation->tag() == ValKind::ContinuationValue) { + return continuation->GetContinuationValue().stack; } else { std::cerr << sourceLocation << ": runtime error: expected an integer" << std::endl; @@ -312,30 +312,31 @@ void InitGlobals(std::list* fs) { } auto ChoiceDeclaration::InitGlobals(Env& globals) const -> void { - auto alts = new VarValues(); + VarValues alts; for (const auto& [name, signature] : alternatives) { auto t = InterpExp(Env(), signature); - alts->push_back(make_pair(name, t)); + alts.push_back(make_pair(name, t)); } - auto ct = Value::MakeChoiceType(name, alts); + auto ct = Value::MakeChoiceType(name, std::move(alts)); auto a = state->heap.AllocateValue(ct); globals.Set(name, a); } auto StructDeclaration::InitGlobals(Env& globals) const -> void { - auto fields = new VarValues(); - auto methods = new VarValues(); + VarValues fields; + VarValues methods; for (auto i = definition.members->begin(); i != definition.members->end(); ++i) { switch ((*i)->tag) { case MemberKind::FieldMember: { auto t = InterpExp(Env(), (*i)->u.field.type); - fields->push_back(make_pair(*(*i)->u.field.name, t)); + fields.push_back(make_pair(*(*i)->u.field.name, t)); break; } } } - auto st = Value::MakeStructType(*definition.name, fields, methods); + auto st = Value::MakeStructType(*definition.name, std::move(fields), + std::move(methods)); auto a = state->heap.AllocateValue(st); globals.Set(*definition.name, a); } @@ -361,7 +362,7 @@ auto VariableDeclaration::InitGlobals(Env& globals) const -> void { // F is the function void CallFunction(int line_num, std::vector operas, State* state) { - switch (operas[0]->tag) { + switch (operas[0]->tag()) { case ValKind::FunctionValue: { // Bind arguments to parameters std::list params; @@ -376,7 +377,7 @@ void CallFunction(int line_num, std::vector operas, // Create the new frame and push it on the stack auto* scope = new Scope(*matches, params); auto* frame = - new Frame(*operas[0]->GetFunctionValue().name, Stack(scope), + new Frame(operas[0]->GetFunctionValue().name, Stack(scope), Stack(MakeStmtAct(operas[0]->GetFunctionValue().body))); state->stack.Push(frame); break; @@ -391,8 +392,8 @@ void CallFunction(int line_num, std::vector operas, case ValKind::AlternativeConstructorValue: { const Value* arg = CopyVal(operas[1], line_num); const Value* av = Value::MakeAlternativeValue( - *operas[0]->GetAlternativeConstructorValue().alt_name, - *operas[0]->GetAlternativeConstructorValue().choice_name, + operas[0]->GetAlternativeConstructorValue().alt_name, + operas[0]->GetAlternativeConstructorValue().choice_name, state->heap.AllocateValue(arg)); Frame* frame = state->stack.Top(); frame->todo.Push(MakeValAct(av)); @@ -426,13 +427,13 @@ void DeallocateLocals(int line_num, Frame* frame) { void CreateTuple(Frame* frame, Action* act, const Expression* /*exp*/) { // { { (v1,...,vn) :: C, E, F} :: S, H} // -> { { `(v1,...,vn) :: C, E, F} :: S, H} - auto elements = new std::vector(); + std::vector elements; auto f = act->u.exp->GetTupleLiteral().fields.begin(); for (auto i = act->results.begin(); i != act->results.end(); ++i, ++f) { Address a = state->heap.AllocateValue(*i); // copy? - elements->push_back({.name = f->name, .address = a}); + elements.push_back({.name = f->name, .address = a}); } - const Value* tv = Value::MakeTupleValue(elements); + const Value* tv = Value::MakeTupleValue(std::move(elements)); frame->todo.Pop(1); frame->todo.Push(MakeValAct(tv)); } @@ -445,23 +446,23 @@ void CreateTuple(Frame* frame, Action* act, const Expression* /*exp*/) { auto PatternMatch(const Value* p, const Value* v, Env values, std::list* vars, int line_num) -> std::optional { - switch (p->tag) { + switch (p->tag()) { case ValKind::BindingPlaceholderValue: { Address a = state->heap.AllocateValue(CopyVal(v, line_num)); - vars->push_back(*p->GetBindingPlaceholderValue().name); - values.Set(*p->GetBindingPlaceholderValue().name, a); + vars->push_back(p->GetBindingPlaceholderValue().name); + values.Set(p->GetBindingPlaceholderValue().name, a); return values; } case ValKind::TupleValue: - switch (v->tag) { + switch (v->tag()) { case ValKind::TupleValue: { - if (p->GetTupleValue().elements->size() != - v->GetTupleValue().elements->size()) { + if (p->GetTupleValue().elements.size() != + v->GetTupleValue().elements.size()) { std::cerr << "runtime error: arity mismatch in tuple pattern match" << std::endl; exit(-1); } - for (const TupleElement& element : *p->GetTupleValue().elements) { + for (const TupleElement& element : p->GetTupleValue().elements) { auto a = FindTupleField(element.name, v); if (a == std::nullopt) { std::cerr << "runtime error: field " << element.name << "not in "; @@ -487,12 +488,12 @@ auto PatternMatch(const Value* p, const Value* v, Env values, exit(-1); } case ValKind::AlternativeValue: - switch (v->tag) { + switch (v->tag()) { case ValKind::AlternativeValue: { - if (*p->GetAlternativeValue().choice_name != - *v->GetAlternativeValue().choice_name || - *p->GetAlternativeValue().alt_name != - *v->GetAlternativeValue().alt_name) { + if (p->GetAlternativeValue().choice_name != + v->GetAlternativeValue().choice_name || + p->GetAlternativeValue().alt_name != + v->GetAlternativeValue().alt_name) { return std::nullopt; } std::optional matches = PatternMatch( @@ -513,7 +514,7 @@ auto PatternMatch(const Value* p, const Value* v, Env values, exit(-1); } case ValKind::FunctionType: - switch (v->tag) { + switch (v->tag()) { case ValKind::FunctionType: { std::optional matches = PatternMatch(p->GetFunctionType().param, @@ -538,21 +539,21 @@ auto PatternMatch(const Value* p, const Value* v, Env values, } void PatternAssignment(const Value* pat, const Value* val, int line_num) { - switch (pat->tag) { + switch (pat->tag()) { case ValKind::PointerValue: state->heap.Write(ValToPtr(pat, line_num), CopyVal(val, line_num), line_num); break; case ValKind::TupleValue: { - switch (val->tag) { + switch (val->tag()) { case ValKind::TupleValue: { - if (pat->GetTupleValue().elements->size() != - val->GetTupleValue().elements->size()) { + if (pat->GetTupleValue().elements.size() != + val->GetTupleValue().elements.size()) { std::cerr << "runtime error: arity mismatch in tuple pattern match" << std::endl; exit(-1); } - for (const TupleElement& element : *pat->GetTupleValue().elements) { + for (const TupleElement& element : pat->GetTupleValue().elements) { auto a = FindTupleField(element.name, val); if (a == std::nullopt) { std::cerr << "runtime error: field " << element.name << "not in "; @@ -576,12 +577,12 @@ void PatternAssignment(const Value* pat, const Value* val, int line_num) { break; } case ValKind::AlternativeValue: { - switch (val->tag) { + switch (val->tag()) { case ValKind::AlternativeValue: { - if (*pat->GetAlternativeValue().choice_name != - *val->GetAlternativeValue().choice_name || - *pat->GetAlternativeValue().alt_name != - *val->GetAlternativeValue().alt_name) { + if (pat->GetAlternativeValue().choice_name != + val->GetAlternativeValue().choice_name || + pat->GetAlternativeValue().alt_name != + val->GetAlternativeValue().alt_name) { std::cerr << "internal error in pattern assignment" << std::endl; exit(-1); } @@ -756,7 +757,7 @@ void StepExp() { act->pos++; } else if (act->pos == 2) { auto tuple = act->results[0]; - switch (tuple->tag) { + switch (tuple->tag()) { case ValKind::TupleValue: { // { { v :: [][i] :: C, E, F} :: S, H} // -> { { v_i :: C, E, F} : S, H} @@ -1286,7 +1287,7 @@ void StepStmt() { auto GetMember(Address a, const std::string& f, int line_num) -> Address { const Value* v = state->heap.Read(a, line_num); - switch (v->tag) { + switch (v->tag()) { case ValKind::StructValue: { auto a = FindTupleField(f, v->GetStructValue().inits); if (a == std::nullopt) { @@ -1315,7 +1316,7 @@ auto GetMember(Address a, const std::string& f, int line_num) -> Address { exit(-1); } auto ac = - Value::MakeAlternativeConstructorValue(f, *v->GetChoiceType().name); + Value::MakeAlternativeConstructorValue(f, v->GetChoiceType().name); return state->heap.AllocateValue(ac); } default: diff --git a/executable_semantics/interpreter/typecheck.cpp b/executable_semantics/interpreter/typecheck.cpp index db4cd83984a6..32b2affff715 100644 --- a/executable_semantics/interpreter/typecheck.cpp +++ b/executable_semantics/interpreter/typecheck.cpp @@ -32,7 +32,7 @@ void ExpectType(int line_num, const std::string& context, const Value* expected, void ExpectPointerType(int line_num, const std::string& context, const Value* actual) { - if (actual->tag != ValKind::PointerType) { + if (actual->tag() != ValKind::PointerType) { std::cerr << line_num << ": type error in " << context << std::endl; std::cerr << "expected a pointer type\n"; std::cerr << "actual: "; @@ -54,7 +54,7 @@ void PrintTypeEnv(TypeEnv types, std::ostream& out) { // Reify type to type expression. auto ReifyType(const Value* t, int line_num) -> const Expression* { - switch (t->tag) { + switch (t->tag()) { case ValKind::IntType: return Expression::MakeIntTypeLiteral(0); case ValKind::BoolType: @@ -69,7 +69,7 @@ auto ReifyType(const Value* t, int line_num) -> const Expression* { ReifyType(t->GetFunctionType().ret, line_num)); case ValKind::TupleValue: { std::vector args; - for (const TupleElement& field : *t->GetTupleValue().elements) { + for (const TupleElement& field : t->GetTupleValue().elements) { args.push_back( {.name = field.name, .expression = ReifyType(state->heap.Read(field.address, line_num), @@ -78,9 +78,9 @@ auto ReifyType(const Value* t, int line_num) -> const Expression* { return Expression::MakeTupleLiteral(0, args); } case ValKind::StructType: - return Expression::MakeIdentifierExpression(0, *t->GetStructType().name); + return Expression::MakeIdentifierExpression(0, t->GetStructType().name); case ValKind::ChoiceType: - return Expression::MakeIdentifierExpression(0, *t->GetChoiceType().name); + return Expression::MakeIdentifierExpression(0, t->GetChoiceType().name); case ValKind::PointerType: return Expression::MakePrimitiveOperatorExpression( 0, Operator::Ptr, {ReifyType(t->GetPointerType().type, line_num)}); @@ -143,7 +143,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, exit(-1); } auto t = InterpExp(values, e->GetBindingExpression().type); - if (t->tag == ValKind::AutoType) { + if (t->tag() == ValKind::AutoType) { if (expected == nullptr) { std::cerr << e->line_num << ": compilation error, auto not allowed here" @@ -165,7 +165,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, auto res = TypeCheckExp(e->GetIndexExpression().aggregate, types, values, nullptr, TCContext::ValueContext); auto t = res.type; - switch (t->tag) { + switch (t->tag()) { case ValKind::TupleValue: { auto i = ToInteger(InterpExp(values, e->GetIndexExpression().offset)); std::string f = std::to_string(i); @@ -190,15 +190,15 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, } case ExpressionKind::TupleLiteral: { std::vector new_args; - auto arg_types = new std::vector(); + std::vector arg_types; auto new_types = types; - if (expected && expected->tag != ValKind::TupleValue) { + if (expected && expected->tag() != ValKind::TupleValue) { std::cerr << e->line_num << ": compilation error, didn't expect a tuple" << std::endl; exit(-1); } if (expected && e->GetTupleLiteral().fields.size() != - expected->GetTupleValue().elements->size()) { + expected->GetTupleValue().elements.size()) { std::cerr << e->line_num << ": compilation error, tuples of different length" << std::endl; @@ -208,38 +208,38 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, for (auto arg = e->GetTupleLiteral().fields.begin(); arg != e->GetTupleLiteral().fields.end(); ++arg, ++i) { const Value* arg_expected = nullptr; - if (expected && expected->tag == ValKind::TupleValue) { - if ((*expected->GetTupleValue().elements)[i].name != arg->name) { + if (expected && expected->tag() == ValKind::TupleValue) { + if (expected->GetTupleValue().elements[i].name != arg->name) { std::cerr << e->line_num << ": compilation error, field names do not match, " << "expected " - << (*expected->GetTupleValue().elements)[i].name + << expected->GetTupleValue().elements[i].name << " but got " << arg->name << std::endl; exit(-1); } arg_expected = state->heap.Read( - (*expected->GetTupleValue().elements)[i].address, e->line_num); + expected->GetTupleValue().elements[i].address, e->line_num); } auto arg_res = TypeCheckExp(arg->expression, new_types, values, arg_expected, context); new_types = arg_res.types; new_args.push_back({.name = arg->name, .expression = arg_res.exp}); - arg_types->push_back( + arg_types.push_back( {.name = arg->name, .address = state->heap.AllocateValue(arg_res.type)}); } auto tuple_e = Expression::MakeTupleLiteral(e->line_num, new_args); - auto tuple_t = Value::MakeTupleValue(arg_types); + auto tuple_t = Value::MakeTupleValue(std::move(arg_types)); return TCResult(tuple_e, tuple_t, new_types); } case ExpressionKind::FieldAccessExpression: { auto res = TypeCheckExp(e->GetFieldAccessExpression().aggregate, types, values, nullptr, TCContext::ValueContext); auto t = res.type; - switch (t->tag) { + switch (t->tag()) { case ValKind::StructType: // Search for a field - for (auto& field : *t->GetStructType().fields) { + for (auto& field : t->GetStructType().fields) { if (e->GetFieldAccessExpression().field == field.first) { const Expression* new_e = Expression::MakeFieldAccessExpression( e->line_num, res.exp, e->GetFieldAccessExpression().field); @@ -247,7 +247,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, } } // Search for a method - for (auto& method : *t->GetStructType().methods) { + for (auto& method : t->GetStructType().methods) { if (e->GetFieldAccessExpression().field == method.first) { const Expression* new_e = Expression::MakeFieldAccessExpression( e->line_num, res.exp, e->GetFieldAccessExpression().field); @@ -255,12 +255,12 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, } } std::cerr << e->line_num << ": compilation error, struct " - << *t->GetStructType().name + << t->GetStructType().name << " does not have a field named " << e->GetFieldAccessExpression().field << std::endl; exit(-1); case ValKind::TupleValue: - for (const TupleElement& field : *t->GetTupleValue().elements) { + for (const TupleElement& field : t->GetTupleValue().elements) { if (e->GetFieldAccessExpression().field == field.name) { auto new_e = Expression::MakeFieldAccessExpression( e->line_num, res.exp, e->GetFieldAccessExpression().field); @@ -270,13 +270,13 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, } } std::cerr << e->line_num << ": compilation error, struct " - << *t->GetStructType().name + << t->GetStructType().name << " does not have a field named " << e->GetFieldAccessExpression().field << std::endl; exit(-1); case ValKind::ChoiceType: - for (auto vt = t->GetChoiceType().alternatives->begin(); - vt != t->GetChoiceType().alternatives->end(); ++vt) { + for (auto vt = t->GetChoiceType().alternatives.begin(); + vt != t->GetChoiceType().alternatives.end(); ++vt) { if (e->GetFieldAccessExpression().field == vt->first) { const Expression* new_e = Expression::MakeFieldAccessExpression( e->line_num, res.exp, e->GetFieldAccessExpression().field); @@ -285,7 +285,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, } } std::cerr << e->line_num << ": compilation error, struct " - << *t->GetStructType().name + << t->GetStructType().name << " does not have a field named " << e->GetFieldAccessExpression().field << std::endl; exit(-1); @@ -374,7 +374,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values, case ExpressionKind::CallExpression: { auto fun_res = TypeCheckExp(e->GetCallExpression().function, types, values, nullptr, TCContext::ValueContext); - switch (fun_res.type->tag) { + switch (fun_res.type->tag()) { case ValKind::FunctionType: { auto fun_t = fun_res.type; auto arg_res = @@ -545,7 +545,7 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values, case StatementKind::Return: { auto res = TypeCheckExp(s->GetReturn(), types, values, nullptr, TCContext::ValueContext); - if (ret_type->tag == ValKind::AutoType) { + if (ret_type->tag() == ValKind::AutoType) { // 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. @@ -679,7 +679,7 @@ auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def) auto param_res = TypeCheckExp(fun_def->param_pattern, types, values, nullptr, TCContext::PatternContext); auto ret = InterpExp(values, fun_def->return_type); - if (ret->tag == ValKind::AutoType) { + if (ret->tag() == ValKind::AutoType) { auto f = TypeCheckFunDef(fun_def, types, values); ret = InterpExp(values, f->return_type); } @@ -688,15 +688,16 @@ auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def) auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*types*/, Env ct_top) -> const Value* { - auto fields = new VarValues(); - auto methods = new VarValues(); + VarValues fields; + VarValues methods; for (auto m = sd->members->begin(); m != sd->members->end(); ++m) { if ((*m)->tag == MemberKind::FieldMember) { auto t = InterpExp(ct_top, (*m)->u.field.type); - fields->push_back(std::make_pair(*(*m)->u.field.name, t)); + fields.push_back(std::make_pair(*(*m)->u.field.name, t)); } } - return Value::MakeStructType(*sd->name, fields, methods); + return Value::MakeStructType(*sd->name, std::move(fields), + std::move(methods)); } auto FunctionDeclaration::Name() const -> std::string { @@ -774,22 +775,23 @@ auto StructDeclaration::TopLevel(TypeCheckContext& tops) const -> void { auto st = TypeOfStructDef(&definition, tops.types, tops.values); Address a = state->heap.AllocateValue(st); tops.values.Set(Name(), a); // Is this obsolete? - auto field_types = new std::vector(); - for (const auto& [field_name, field_value] : *st->GetStructType().fields) { - field_types->push_back({.name = field_name, - .address = state->heap.AllocateValue(field_value)}); + std::vector field_types; + for (const auto& [field_name, field_value] : st->GetStructType().fields) { + field_types.push_back({.name = field_name, + .address = state->heap.AllocateValue(field_value)}); } - auto fun_ty = Value::MakeFunctionType(Value::MakeTupleValue(field_types), st); + auto fun_ty = Value::MakeFunctionType( + Value::MakeTupleValue(std::move(field_types)), st); tops.types.Set(Name(), fun_ty); } auto ChoiceDeclaration::TopLevel(TypeCheckContext& tops) const -> void { - auto alts = new VarValues(); + VarValues alts; for (const auto& [name, signature] : alternatives) { auto t = InterpExp(tops.values, signature); - alts->push_back(std::make_pair(name, t)); + alts.push_back(std::make_pair(name, t)); } - auto ct = Value::MakeChoiceType(name, alts); + auto ct = Value::MakeChoiceType(name, std::move(alts)); Address a = state->heap.AllocateValue(ct); tops.values.Set(Name(), a); // Is this obsolete? tops.types.Set(Name(), ct); diff --git a/executable_semantics/interpreter/value.cpp b/executable_semantics/interpreter/value.cpp index 517c07b35c96..528d04771c1e 100644 --- a/executable_semantics/interpreter/value.cpp +++ b/executable_semantics/interpreter/value.cpp @@ -12,79 +12,80 @@ namespace Carbon { -int Value::GetIntValue() const { - CHECK(tag == ValKind::IntValue); - return u.integer; +namespace { + +struct TagVisitor { + template + auto operator()(const Alternative&) -> ValKind { + return Alternative::Kind; + } +}; + +} // namespace + +auto Value::tag() const -> ValKind { return std::visit(TagVisitor(), value); } + +auto Value::GetIntValue() const -> int { + return std::get(value).value; } -bool Value::GetBoolValue() const { - CHECK(tag == ValKind::BoolValue); - return u.boolean; +auto Value::GetBoolValue() const -> bool { + return std::get(value).value; } -FunctionValue Value::GetFunctionValue() const { - CHECK(tag == ValKind::FunctionValue); - return u.fun; +auto Value::GetFunctionValue() const -> const FunctionValue& { + return std::get(value); } -StructValue Value::GetStructValue() const { - CHECK(tag == ValKind::StructValue); - return u.struct_val; +auto Value::GetStructValue() const -> const StructValue& { + return std::get(value); } -AlternativeConstructorValue Value::GetAlternativeConstructorValue() const { - CHECK(tag == ValKind::AlternativeConstructorValue); - return u.alt_cons; +auto Value::GetAlternativeConstructorValue() const + -> const AlternativeConstructorValue& { + return std::get(value); } -AlternativeValue Value::GetAlternativeValue() const { - CHECK(tag == ValKind::AlternativeValue); - return u.alt; +auto Value::GetAlternativeValue() const -> const AlternativeValue& { + return std::get(value); } -TupleValue Value::GetTupleValue() const { - CHECK(tag == ValKind::TupleValue); - return u.tuple; +auto Value::GetTupleValue() const -> const TupleValue& { + return std::get(value); } -Address Value::GetPointerValue() const { - CHECK(tag == ValKind::PointerValue); - return u.ptr; +auto Value::GetPointerValue() const -> Address { + return std::get(value).value; } -BindingPlaceholderValue Value::GetBindingPlaceholderValue() const { - CHECK(tag == ValKind::BindingPlaceholderValue); - return u.var_pat; +auto Value::GetBindingPlaceholderValue() const + -> const BindingPlaceholderValue& { + return std::get(value); } -FunctionType Value::GetFunctionType() const { - CHECK(tag == ValKind::FunctionType); - return u.fun_type; +auto Value::GetFunctionType() const -> const FunctionType& { + return std::get(value); } -PointerType Value::GetPointerType() const { - CHECK(tag == ValKind::PointerType); - return u.ptr_type; +auto Value::GetPointerType() const -> const PointerType& { + return std::get(value); } -StructType Value::GetStructType() const { - CHECK(tag == ValKind::StructType); - return u.struct_type; +auto Value::GetStructType() const -> const StructType& { + return std::get(value); } -ChoiceType Value::GetChoiceType() const { - CHECK(tag == ValKind::ChoiceType); - return u.choice_type; +auto Value::GetChoiceType() const -> const ChoiceType& { + return std::get(value); } -ContinuationValue Value::GetContinuationValue() const { - CHECK(tag == ValKind::ContinuationValue); - return u.continuation; +auto Value::GetContinuationValue() const -> const ContinuationValue& { + return std::get(value); } -auto FindInVarValues(const std::string& field, VarValues* inits) +auto FindInVarValues(const std::string& field, const VarValues& inits) -> const Value* { - for (auto& i : *inits) { + for (auto& i : inits) { if (i.first == field) { return i.second; } @@ -92,9 +93,9 @@ auto FindInVarValues(const std::string& field, VarValues* inits) return nullptr; } -auto FieldsEqual(VarValues* ts1, VarValues* ts2) -> bool { - if (ts1->size() == ts2->size()) { - for (auto& iter1 : *ts1) { +auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool { + if (ts1.size() == ts2.size()) { + for (auto& iter1 : ts1) { auto t2 = FindInVarValues(iter1.first, ts2); if (t2 == nullptr) { return false; @@ -111,8 +112,8 @@ auto FieldsEqual(VarValues* ts1, VarValues* ts2) -> bool { auto FindTupleField(const std::string& name, const Value* tuple) -> std::optional
{ - CHECK(tuple->tag == ValKind::TupleValue); - for (const TupleElement& element : *tuple->GetTupleValue().elements) { + CHECK(tuple->tag() == ValKind::TupleValue); + for (const TupleElement& element : tuple->GetTupleValue().elements) { if (element.name == name) { return element.address; } @@ -122,59 +123,49 @@ auto FindTupleField(const std::string& name, const Value* tuple) auto Value::MakeIntValue(int i) -> const Value* { auto* v = new Value(); - v->tag = ValKind::IntValue; - v->u.integer = i; + v->value = IntValue({.value = i}); return v; } auto Value::MakeBoolValue(bool b) -> const Value* { auto* v = new Value(); - v->tag = ValKind::BoolValue; - v->u.boolean = b; + v->value = BoolValue({.value = b}); return v; } auto Value::MakeFunctionValue(std::string name, const Value* param, const Statement* body) -> const Value* { auto* v = new Value(); - v->tag = ValKind::FunctionValue; - v->u.fun.name = new std::string(std::move(name)); - v->u.fun.param = param; - v->u.fun.body = body; + v->value = + FunctionValue({.name = std::move(name), .param = param, .body = body}); return v; } auto Value::MakePointerValue(Address addr) -> const Value* { auto* v = new Value(); - v->tag = ValKind::PointerValue; - v->u.ptr = addr; + v->value = PointerValue({.value = addr}); return v; } auto Value::MakeStructValue(const Value* type, const Value* inits) -> const Value* { auto* v = new Value(); - v->tag = ValKind::StructValue; - v->u.struct_val.type = type; - v->u.struct_val.inits = inits; + v->value = StructValue({.type = type, .inits = inits}); return v; } -auto Value::MakeTupleValue(std::vector* elements) - -> const Value* { +auto Value::MakeTupleValue(std::vector elements) -> const Value* { auto* v = new Value(); - v->tag = ValKind::TupleValue; - v->u.tuple.elements = elements; + v->value = TupleValue({.elements = std::move(elements)}); return v; } auto Value::MakeAlternativeValue(std::string alt_name, std::string choice_name, Address argument) -> const Value* { auto* v = new Value(); - v->tag = ValKind::AlternativeValue; - v->u.alt.alt_name = new std::string(std::move(alt_name)); - v->u.alt.choice_name = new std::string(std::move(choice_name)); - v->u.alt.argument = argument; + v->value = AlternativeValue({.alt_name = std::move(alt_name), + .choice_name = std::move(choice_name), + .argument = argument}); return v; } @@ -182,9 +173,8 @@ auto Value::MakeAlternativeConstructorValue(std::string alt_name, std::string choice_name) -> const Value* { auto* v = new Value(); - v->tag = ValKind::AlternativeConstructorValue; - v->u.alt.alt_name = new std::string(std::move(alt_name)); - v->u.alt.choice_name = new std::string(std::move(choice_name)); + v->value = AlternativeConstructorValue( + {.alt_name = std::move(alt_name), .choice_name = std::move(choice_name)}); return v; } @@ -192,122 +182,110 @@ auto Value::MakeAlternativeConstructorValue(std::string alt_name, // of the stack. auto Value::MakeContinuationValue(std::vector stack) -> Value* { auto* v = new Value(); - v->tag = ValKind::ContinuationValue; - v->u.continuation.stack = new std::vector(stack); + v->value = ContinuationValue({.stack = std::move(stack)}); return v; } auto Value::MakeBindingPlaceholderValue(std::string name, const Value* type) -> const Value* { auto* v = new Value(); - v->tag = ValKind::BindingPlaceholderValue; - v->u.var_pat.name = new std::string(std::move(name)); - v->u.var_pat.type = type; + v->value = BindingPlaceholderValue({.name = std::move(name), .type = type}); return v; } auto Value::MakeIntType() -> const Value* { auto* v = new Value(); - v->tag = ValKind::IntType; + v->value = IntType(); return v; } auto Value::MakeBoolType() -> const Value* { auto* v = new Value(); - v->tag = ValKind::BoolType; + v->value = BoolType(); return v; } auto Value::MakeTypeType() -> const Value* { auto* v = new Value(); - v->tag = ValKind::TypeType; + v->value = TypeType(); return v; } // Return a Continuation type. auto Value::MakeContinuationType() -> const Value* { auto* v = new Value(); - v->tag = ValKind::ContinuationType; + v->value = ContinuationType(); return v; } auto Value::MakeAutoType() -> const Value* { auto* v = new Value(); - v->tag = ValKind::AutoType; + v->value = AutoType(); return v; } auto Value::MakeFunctionType(const Value* param, const Value* ret) -> const Value* { auto* v = new Value(); - v->tag = ValKind::FunctionType; - v->u.fun_type.param = param; - v->u.fun_type.ret = ret; + v->value = FunctionType({.param = param, .ret = ret}); return v; } auto Value::MakePointerType(const Value* type) -> const Value* { auto* v = new Value(); - v->tag = ValKind::PointerType; - v->u.ptr_type.type = type; + v->value = PointerType({.type = type}); return v; } -auto Value::MakeStructType(std::string name, VarValues* fields, - VarValues* methods) -> const Value* { +auto Value::MakeStructType(std::string name, VarValues fields, + VarValues methods) -> const Value* { auto* v = new Value(); - v->tag = ValKind::StructType; - v->u.struct_type.name = new std::string(std::move(name)); - v->u.struct_type.fields = fields; - v->u.struct_type.methods = methods; + v->value = StructType({.name = std::move(name), + .fields = std::move(fields), + .methods = std::move(methods)}); return v; } auto Value::MakeUnitTypeVal() -> const Value* { auto* v = new Value(); - v->tag = ValKind::TupleValue; - v->u.tuple.elements = new std::vector(); + v->value = TupleValue({.elements = {}}); return v; } -auto Value::MakeChoiceType( - std::string name, std::list>* alts) - -> const Value* { +auto Value::MakeChoiceType(std::string name, VarValues alts) -> const Value* { auto* v = new Value(); - v->tag = ValKind::ChoiceType; - // Transitional leak: when we get rid of all pointers, this will disappear. - v->u.choice_type.name = new std::string(name); - v->u.choice_type.alternatives = alts; + v->value = + ChoiceType({.name = std::move(name), .alternatives = std::move(alts)}); return v; } auto PrintValue(const Value* val, std::ostream& out) -> void { - switch (val->tag) { + switch (val->tag()) { case ValKind::AlternativeConstructorValue: { - out << *val->GetAlternativeConstructorValue().choice_name << "." - << *val->GetAlternativeConstructorValue().alt_name; + out << val->GetAlternativeConstructorValue().choice_name << "." + << val->GetAlternativeConstructorValue().alt_name; break; } case ValKind::BindingPlaceholderValue: { PrintValue(val->GetBindingPlaceholderValue().type, out); - out << ": " << *val->GetBindingPlaceholderValue().name; + out << ": " << val->GetBindingPlaceholderValue().name; break; } case ValKind::AlternativeValue: { - out << "alt " << *val->GetAlternativeValue().choice_name << "." - << *val->GetAlternativeValue().alt_name << " "; + out << "alt " << val->GetAlternativeValue().choice_name << "." + << val->GetAlternativeValue().alt_name << " "; state->heap.PrintAddress(val->GetAlternativeValue().argument, out); break; } case ValKind::StructValue: { - out << *val->GetStructValue().type->GetStructType().name; + out << val->GetStructValue().type->GetStructType().name; PrintValue(val->GetStructValue().inits, out); break; } case ValKind::TupleValue: { out << "("; bool add_commas = false; - for (const TupleElement& element : *val->GetTupleValue().elements) { + for (const TupleElement& element : val->GetTupleValue().elements) { if (add_commas) { out << ", "; } else { @@ -327,7 +305,7 @@ auto PrintValue(const Value* val, std::ostream& out) -> void { out << std::boolalpha << val->GetBoolValue(); break; case ValKind::FunctionValue: - out << "fun<" << *val->GetFunctionValue().name << ">"; + out << "fun<" << val->GetFunctionValue().name << ">"; break; case ValKind::PointerValue: out << "ptr<" << val->GetPointerValue() << ">"; @@ -358,14 +336,14 @@ auto PrintValue(const Value* val, std::ostream& out) -> void { PrintValue(val->GetFunctionType().ret, out); break; case ValKind::StructType: - out << "struct " << *val->GetStructType().name; + out << "struct " << val->GetStructType().name; break; case ValKind::ChoiceType: - out << "choice " << *val->GetChoiceType().name; + out << "choice " << val->GetChoiceType().name; break; case ValKind::ContinuationValue: out << "continuation[["; - for (Frame* frame : *val->GetContinuationValue().stack) { + for (Frame* frame : val->GetContinuationValue().stack) { PrintFrame(frame, out); out << " :: "; } @@ -375,10 +353,10 @@ auto PrintValue(const Value* val, std::ostream& out) -> void { } auto TypeEqual(const Value* t1, const Value* t2) -> bool { - if (t1->tag != t2->tag) { + if (t1->tag() != t2->tag()) { return false; } - switch (t1->tag) { + switch (t1->tag()) { case ValKind::PointerType: return TypeEqual(t1->GetPointerType().type, t2->GetPointerType().type); case ValKind::FunctionType: @@ -386,23 +364,22 @@ auto TypeEqual(const Value* t1, const Value* t2) -> bool { t2->GetFunctionType().param) && TypeEqual(t1->GetFunctionType().ret, t2->GetFunctionType().ret); case ValKind::StructType: - return *t1->GetStructType().name == *t2->GetStructType().name; + return t1->GetStructType().name == t2->GetStructType().name; case ValKind::ChoiceType: - return *t1->GetChoiceType().name == *t2->GetChoiceType().name; + return t1->GetChoiceType().name == t2->GetChoiceType().name; case ValKind::TupleValue: { - if (t1->GetTupleValue().elements->size() != - t2->GetTupleValue().elements->size()) { + if (t1->GetTupleValue().elements.size() != + t2->GetTupleValue().elements.size()) { return false; } - for (size_t i = 0; i < t1->GetTupleValue().elements->size(); ++i) { - if ((*t1->GetTupleValue().elements)[i].name != - (*t2->GetTupleValue().elements)[i].name) { + for (size_t i = 0; i < t1->GetTupleValue().elements.size(); ++i) { + if (t1->GetTupleValue().elements[i].name != + t2->GetTupleValue().elements[i].name) { return false; } if (!TypeEqual( - state->heap.Read((*t1->GetTupleValue().elements)[i].address, 0), - state->heap.Read((*t2->GetTupleValue().elements)[i].address, - 0))) { + state->heap.Read(t1->GetTupleValue().elements[i].address, 0), + state->heap.Read(t2->GetTupleValue().elements[i].address, 0))) { return false; } } @@ -424,17 +401,17 @@ auto TypeEqual(const Value* t1, const Value* t2) -> bool { // Returns true if all the fields of the two tuples contain equal values // and returns false otherwise. -static auto FieldsValueEqual(std::vector* ts1, - std::vector* ts2, int line_num) +static auto FieldsValueEqual(const std::vector& ts1, + const std::vector& ts2, int line_num) -> bool { - if (ts1->size() != ts2->size()) { + if (ts1.size() != ts2.size()) { return false; } - for (const TupleElement& element : *ts1) { + for (const TupleElement& element : ts1) { auto iter = std::find_if( - ts2->begin(), ts2->end(), + ts2.begin(), ts2.end(), [&](const TupleElement& e2) { return e2.name == element.name; }); - if (iter == ts2->end()) { + if (iter == ts2.end()) { return false; } if (!ValueEqual(state->heap.Read(element.address, line_num), @@ -449,10 +426,10 @@ static auto FieldsValueEqual(std::vector* ts1, // // This function implements the `==` operator of Carbon. auto ValueEqual(const Value* v1, const Value* v2, int line_num) -> bool { - if (v1->tag != v2->tag) { + if (v1->tag() != v2->tag()) { return false; } - switch (v1->tag) { + switch (v1->tag()) { case ValKind::IntValue: return v1->GetIntValue() == v2->GetIntValue(); case ValKind::BoolValue: @@ -487,7 +464,7 @@ auto ValueEqual(const Value* v1, const Value* v2, int line_num) -> bool { } auto ToInteger(const Value* v) -> int { - switch (v->tag) { + switch (v->tag()) { case ValKind::IntValue: return v->GetIntValue(); default: diff --git a/executable_semantics/interpreter/value.h b/executable_semantics/interpreter/value.h index 6bca15b2bd52..6caf48497fd3 100644 --- a/executable_semantics/interpreter/value.h +++ b/executable_semantics/interpreter/value.h @@ -7,6 +7,7 @@ #include #include +#include #include #include "executable_semantics/ast/statement.h" @@ -18,9 +19,9 @@ struct Value; using Address = unsigned int; using VarValues = std::list>; -auto FindInVarValues(const std::string& field, VarValues* inits) +auto FindInVarValues(const std::string& field, const VarValues& inits) -> const Value*; -auto FieldsEqual(VarValues* ts1, VarValues* ts2) -> bool; +auto FieldsEqual(const VarValues& ts1, const 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. @@ -60,63 +61,109 @@ enum class ValKind { struct Frame; // used by continuation +struct IntValue { + static constexpr ValKind Kind = ValKind::IntValue; + int value; +}; + struct FunctionValue { - std::string* name; + static constexpr ValKind Kind = ValKind::FunctionValue; + std::string name; const Value* param; const Statement* body; }; +struct PointerValue { + static constexpr ValKind Kind = ValKind::PointerValue; + Address value; +}; + +struct BoolValue { + static constexpr ValKind Kind = ValKind::BoolValue; + bool value; +}; + struct StructValue { + static constexpr ValKind Kind = ValKind::StructValue; const Value* type; const Value* inits; }; struct AlternativeConstructorValue { - std::string* alt_name; - std::string* choice_name; + static constexpr ValKind Kind = ValKind::AlternativeConstructorValue; + std::string alt_name; + std::string choice_name; }; struct AlternativeValue { - std::string* alt_name; - std::string* choice_name; + static constexpr ValKind Kind = ValKind::AlternativeValue; + std::string alt_name; + std::string choice_name; Address argument; }; struct TupleValue { - std::vector* elements; + static constexpr ValKind Kind = ValKind::TupleValue; + std::vector elements; }; struct BindingPlaceholderValue { - std::string* name; + static constexpr ValKind Kind = ValKind::BindingPlaceholderValue; + std::string name; const Value* type; }; +struct IntType { + static constexpr ValKind Kind = ValKind::IntType; +}; + +struct BoolType { + static constexpr ValKind Kind = ValKind::BoolType; +}; + +struct TypeType { + static constexpr ValKind Kind = ValKind::TypeType; +}; + struct FunctionType { + static constexpr ValKind Kind = ValKind::FunctionType; const Value* param; const Value* ret; }; struct PointerType { + static constexpr ValKind Kind = ValKind::PointerType; const Value* type; }; +struct AutoType { + static constexpr ValKind Kind = ValKind::AutoType; +}; + struct StructType { - std::string* name; - VarValues* fields; - VarValues* methods; + static constexpr ValKind Kind = ValKind::StructType; + std::string name; + VarValues fields; + VarValues methods; }; struct ChoiceType { - std::string* name; - VarValues* alternatives; + static constexpr ValKind Kind = ValKind::ChoiceType; + std::string name; + VarValues alternatives; +}; + +struct ContinuationType { + static constexpr ValKind Kind = ValKind::ContinuationType; }; struct ContinuationValue { - std::vector* stack; + static constexpr ValKind Kind = ValKind::ContinuationValue; + std::vector stack; }; struct Value { - ValKind tag; + auto tag() const -> ValKind; // Constructors @@ -130,7 +177,7 @@ struct Value { static auto MakePointerValue(Address addr) -> const Value*; static auto MakeStructValue(const Value* type, const Value* inits) -> const Value*; - static auto MakeTupleValue(std::vector* elts) -> const Value*; + static auto MakeTupleValue(std::vector elts) -> const Value*; static auto MakeAlternativeValue(std::string alt_name, std::string choice_name, Address argument) -> const Value*; @@ -147,44 +194,35 @@ struct Value { static auto MakeFunctionType(const Value* param, const Value* ret) -> const Value*; static auto MakePointerType(const Value* type) -> const Value*; - static auto MakeStructType(std::string name, VarValues* fields, - VarValues* methods) -> const Value*; + static auto MakeStructType(std::string name, VarValues fields, + VarValues methods) -> const Value*; static auto MakeUnitTypeVal() -> const Value*; - static auto MakeChoiceType(std::string name, VarValues* alts) -> const Value*; + static auto MakeChoiceType(std::string name, VarValues alts) -> const Value*; // Access to alternatives - int GetIntValue() const; - bool GetBoolValue() const; - FunctionValue GetFunctionValue() const; - StructValue GetStructValue() const; - AlternativeConstructorValue GetAlternativeConstructorValue() const; - AlternativeValue GetAlternativeValue() const; - TupleValue GetTupleValue() const; - Address GetPointerValue() const; - BindingPlaceholderValue GetBindingPlaceholderValue() const; - FunctionType GetFunctionType() const; - PointerType GetPointerType() const; - StructType GetStructType() const; - ChoiceType GetChoiceType() const; - ContinuationValue GetContinuationValue() const; + auto GetIntValue() const -> int; + auto GetBoolValue() const -> bool; + auto GetFunctionValue() const -> const FunctionValue&; + auto GetStructValue() const -> const StructValue&; + auto GetAlternativeConstructorValue() const + -> const AlternativeConstructorValue&; + auto GetAlternativeValue() const -> const AlternativeValue&; + auto GetTupleValue() const -> const TupleValue&; + auto GetPointerValue() const -> Address; + auto GetBindingPlaceholderValue() const -> const BindingPlaceholderValue&; + auto GetFunctionType() const -> const FunctionType&; + auto GetPointerType() const -> const PointerType&; + auto GetStructType() const -> const StructType&; + auto GetChoiceType() const -> const ChoiceType&; + auto GetContinuationValue() const -> const ContinuationValue&; private: - union { - int integer; - bool boolean; - FunctionValue fun; - StructValue struct_val; - AlternativeConstructorValue alt_cons; - AlternativeValue alt; - TupleValue tuple; - Address ptr; - BindingPlaceholderValue var_pat; - FunctionType fun_type; - PointerType ptr_type; - StructType struct_type; - ChoiceType choice_type; - ContinuationValue continuation; - } u; + std::variant + value; }; void PrintValue(const Value* val, std::ostream& out);