Add support for struct types, following p0561. (#856)

This commit is contained in:
Geoff Romer
2021-09-29 16:14:27 -07:00
committed by GitHub
parent d157d96338
commit c4e40aaa86
20 changed files with 674 additions and 44 deletions
@@ -141,8 +141,8 @@ void Interpreter::InitEnv(const Declaration& d, Env* env) {
}
}
}
auto st = arena->New<ClassType>(class_def.name(), std::move(fields),
std::move(methods));
auto st = arena->New<NominalClassType>(
class_def.name(), std::move(fields), std::move(methods));
auto a = heap.AllocateValue(st);
env->Set(class_def.name(), a);
break;
@@ -211,6 +211,18 @@ auto Interpreter::CreateTuple(Nonnull<Action*> act,
return arena->New<TupleValue>(std::move(elements));
}
auto Interpreter::CreateStruct(const std::vector<FieldInitializer>& fields,
const std::vector<Nonnull<const Value*>>& values)
-> Nonnull<const Value*> {
CHECK(fields.size() == values.size());
std::vector<TupleElement> elements;
for (size_t i = 0; i < fields.size(); ++i) {
elements.push_back({.name = fields[i].name, .value = values[i]});
}
return arena->New<StructValue>(std::move(elements));
}
auto Interpreter::PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
SourceLocation loc) -> std::optional<Env> {
switch (p->Tag()) {
@@ -255,6 +267,24 @@ auto Interpreter::PatternMatch(Nonnull<const Value*> p, Nonnull<const Value*> v,
default:
FATAL() << "expected a tuple value in pattern, not " << *v;
}
case Value::Kind::StructValue: {
const auto& p_struct = cast<StructValue>(*p);
const auto& v_struct = cast<StructValue>(*v);
CHECK(p_struct.elements().size() == v_struct.elements().size());
Env values(arena);
for (size_t i = 0; i < p_struct.elements().size(); ++i) {
CHECK(p_struct.elements()[i].name == v_struct.elements()[i].name);
std::optional<Env> matches = PatternMatch(
p_struct.elements()[i].value, v_struct.elements()[i].value, loc);
if (!matches) {
return std::nullopt;
}
for (const auto& [name, value] : *matches) {
values.Set(name, value);
}
}
return values;
}
case Value::Kind::AlternativeValue:
switch (v->Tag()) {
case Value::Kind::AlternativeValue: {
@@ -426,6 +456,8 @@ auto Interpreter::StepLvalue() -> Transition {
return Done{CreateTuple(act, exp)};
}
}
case Expression::Kind::StructLiteral:
case Expression::Kind::StructTypeLiteral:
case Expression::Kind::IntLiteral:
case Expression::Kind::BoolLiteral:
case Expression::Kind::CallExpression:
@@ -492,6 +524,29 @@ auto Interpreter::StepExp() -> Transition {
return Done{CreateTuple(act, exp)};
}
}
case Expression::Kind::StructLiteral: {
const auto& literal = cast<StructLiteral>(*exp);
if (act->Pos() < static_cast<int>(literal.fields().size())) {
Nonnull<const Expression*> elt =
literal.fields()[act->Pos()].expression;
return Spawn{arena->New<ExpressionAction>(elt)};
} else {
return Done{CreateStruct(literal.fields(), act->Results())};
}
}
case Expression::Kind::StructTypeLiteral: {
const auto& struct_type = cast<StructTypeLiteral>(*exp);
if (act->Pos() < static_cast<int>(struct_type.fields().size())) {
return Spawn{arena->New<ExpressionAction>(
struct_type.fields()[act->Pos()].expression)};
} else {
VarValues fields;
for (size_t i = 0; i < struct_type.fields().size(); ++i) {
fields.push_back({struct_type.fields()[i].name, act->Results()[i]});
}
return Done{arena->New<StructType>(std::move(fields))};
}
}
case Expression::Kind::FieldAccessExpression: {
const auto& access = cast<FieldAccessExpression>(*exp);
if (act->Pos() == 0) {
@@ -548,10 +603,10 @@ auto Interpreter::StepExp() -> Transition {
// { { v2 :: v1([]) :: C, E, F} :: S, H}
// -> { {C',E',F'} :: {C, E, F} :: S, H}
switch (act->Results()[0]->Tag()) {
case Value::Kind::ClassType: {
case Value::Kind::NominalClassType: {
Nonnull<const Value*> arg =
CopyVal(arena, act->Results()[1], exp->SourceLoc());
return Done{arena->New<StructValue>(act->Results()[0], arg)};
return Done{arena->New<NominalClassValue>(act->Results()[0], arg)};
}
case Value::Kind::AlternativeConstructorValue: {
const auto& alt =
@@ -138,6 +138,9 @@ class Interpreter {
auto CreateTuple(Nonnull<Action*> act, Nonnull<const Expression*> exp)
-> Nonnull<const Value*>;
auto CreateStruct(const std::vector<FieldInitializer>& fields,
const std::vector<Nonnull<const Value*>>& values)
-> Nonnull<const Value*>;
auto EvalPrim(Operator op, const std::vector<Nonnull<const Value*>>& args,
SourceLocation loc) -> Nonnull<const Value*>;
@@ -84,8 +84,16 @@ auto TypeChecker::ReifyType(Nonnull<const Value*> t, SourceLocation loc)
}
return arena->New<TupleLiteral>(loc, args);
}
case Value::Kind::ClassType:
return arena->New<IdentifierExpression>(loc, cast<ClassType>(*t).Name());
case Value::Kind::StructType: {
std::vector<FieldInitializer> args;
for (const auto& [name, type] : cast<StructType>(*t).fields()) {
args.push_back(FieldInitializer(name, ReifyType(type, loc)));
}
return arena->New<StructTypeLiteral>(loc, args);
}
case Value::Kind::NominalClassType:
return arena->New<IdentifierExpression>(
loc, cast<NominalClassType>(*t).Name());
case Value::Kind::ChoiceType:
return arena->New<IdentifierExpression>(loc, cast<ChoiceType>(*t).Name());
case Value::Kind::PointerType:
@@ -109,6 +117,7 @@ auto TypeChecker::ReifyType(Nonnull<const Value*> t, SourceLocation loc)
case Value::Kind::PointerValue:
case Value::Kind::StringValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
FATAL() << "expected a type, not " << *t;
}
}
@@ -153,6 +162,27 @@ static auto ArgumentDeduction(SourceLocation loc, TypeEnv deduced,
}
return deduced;
}
case Value::Kind::StructType: {
if (arg->Tag() != Value::Kind::StructType) {
ExpectType(loc, "argument deduction", param, arg);
}
const auto& param_struct = cast<StructType>(*param);
const auto& arg_struct = cast<StructType>(*arg);
if (param_struct.fields().size() != arg_struct.fields().size()) {
ExpectType(loc, "argument deduction", param, arg);
}
for (size_t i = 0; i < param_struct.fields().size(); ++i) {
if (param_struct.fields()[i].first != arg_struct.fields()[i].first) {
FATAL_COMPILATION_ERROR(loc)
<< "mismatch in field names, " << param_struct.fields()[i].first
<< " != " << arg_struct.fields()[i].first;
}
deduced =
ArgumentDeduction(loc, deduced, param_struct.fields()[i].second,
arg_struct.fields()[i].second);
}
return deduced;
}
case Value::Kind::FunctionType: {
if (arg->Tag() != Value::Kind::FunctionType) {
ExpectType(loc, "argument deduction", param, arg);
@@ -178,7 +208,7 @@ static auto ArgumentDeduction(SourceLocation loc, TypeEnv deduced,
}
// For the following cases, we check for type equality.
case Value::Kind::ContinuationType:
case Value::Kind::ClassType:
case Value::Kind::NominalClassType:
case Value::Kind::ChoiceType:
case Value::Kind::IntType:
case Value::Kind::BoolType:
@@ -192,6 +222,7 @@ static auto ArgumentDeduction(SourceLocation loc, TypeEnv deduced,
case Value::Kind::FunctionValue:
case Value::Kind::PointerValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
case Value::Kind::BindingPlaceholderValue:
case Value::Kind::AlternativeConstructorValue:
@@ -221,6 +252,14 @@ auto TypeChecker::Substitute(TypeEnv dict, Nonnull<const Value*> type)
}
return arena->New<TupleValue>(elts);
}
case Value::Kind::StructType: {
VarValues fields;
for (const auto& [name, value] : cast<StructType>(*type).fields()) {
auto new_type = Substitute(dict, value);
fields.push_back({name, new_type});
}
return arena->New<StructType>(std::move(fields));
}
case Value::Kind::FunctionType: {
const auto& fn_type = cast<FunctionType>(*type);
auto param = Substitute(dict, fn_type.Param());
@@ -236,7 +275,7 @@ auto TypeChecker::Substitute(TypeEnv dict, Nonnull<const Value*> type)
case Value::Kind::IntType:
case Value::Kind::BoolType:
case Value::Kind::TypeType:
case Value::Kind::ClassType:
case Value::Kind::NominalClassType:
case Value::Kind::ChoiceType:
case Value::Kind::ContinuationType:
case Value::Kind::StringType:
@@ -247,6 +286,7 @@ auto TypeChecker::Substitute(TypeEnv dict, Nonnull<const Value*> type)
case Value::Kind::FunctionValue:
case Value::Kind::PointerValue:
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
case Value::Kind::BindingPlaceholderValue:
case Value::Kind::AlternativeConstructorValue:
@@ -305,13 +345,64 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e, TypeEnv types,
auto tuple_t = arena->New<TupleValue>(std::move(arg_types));
return TCExpression(tuple_e, tuple_t, new_types);
}
case Expression::Kind::StructLiteral: {
std::vector<FieldInitializer> new_args;
VarValues arg_types;
auto new_types = types;
for (const auto& arg : cast<StructLiteral>(*e).fields()) {
auto arg_res = TypeCheckExp(arg.expression, new_types, values);
new_types = arg_res.types;
new_args.push_back(FieldInitializer(arg.name, arg_res.exp));
arg_types.push_back({arg.name, arg_res.type});
}
auto new_e = arena->New<StructLiteral>(e->SourceLoc(), new_args);
auto type = arena->New<StructType>(std::move(arg_types));
return TCExpression(new_e, type, new_types);
}
case Expression::Kind::StructTypeLiteral: {
const auto& struct_type = cast<StructTypeLiteral>(*e);
std::vector<FieldInitializer> new_args;
auto new_types = types;
for (const auto& arg : struct_type.fields()) {
auto arg_res = TypeCheckExp(arg.expression, new_types, values);
new_types = arg_res.types;
Nonnull<const Value*> type = interpreter.InterpExp(values, arg_res.exp);
new_args.push_back(
FieldInitializer(arg.name, ReifyType(type, e->SourceLoc())));
}
auto new_e = arena->New<StructTypeLiteral>(e->SourceLoc(), new_args);
Nonnull<const Value*> type;
if (struct_type.fields().empty()) {
// `{}` is the type of `{}`, just as `()` is the type of `()`.
// This applies only if there are no fields, because (unlike with
// tuples) non-empty struct types are syntactically disjoint
// from non-empty struct values.
type = arena->New<StructType>();
} else {
type = arena->New<TypeType>();
}
return TCExpression(new_e, type, new_types);
}
case Expression::Kind::FieldAccessExpression: {
auto& access = cast<FieldAccessExpression>(*e);
auto res = TypeCheckExp(access.Aggregate(), types, values);
auto t = res.type;
switch (t->Tag()) {
case Value::Kind::ClassType: {
const auto& t_class = cast<ClassType>(*t);
case Value::Kind::StructType: {
const auto& struct_type = cast<StructType>(*t);
for (const auto& [field_name, field_type] : struct_type.fields()) {
if (access.Field() == field_name) {
Nonnull<Expression*> new_e = arena->New<FieldAccessExpression>(
access.SourceLoc(), res.exp, access.Field());
return TCExpression(new_e, field_type, res.types);
}
}
FATAL_COMPILATION_ERROR(access.SourceLoc())
<< "struct " << struct_type << " does not have a field named "
<< access.Field();
}
case Value::Kind::NominalClassType: {
const auto& t_class = cast<NominalClassType>(*t);
// Search for a field
for (auto& field : t_class.Fields()) {
if (access.Field() == field.first) {
@@ -951,8 +1042,8 @@ auto TypeChecker::TypeOfClassDef(const ClassDefinition* sd, TypeEnv /*types*/,
}
}
}
return arena->New<ClassType>(sd->name(), std::move(fields),
std::move(methods));
return arena->New<NominalClassType>(sd->name(), std::move(fields),
std::move(methods));
}
static auto GetName(const Declaration& d) -> const std::string& {
@@ -1042,7 +1133,7 @@ void TypeChecker::TopLevel(Nonnull<Declaration*> d, TypeCheckContext* tops) {
tops->values.Set(class_def.name(), a); // Is this obsolete?
std::vector<TupleElement> field_types;
for (const auto& [field_name, field_value] :
cast<ClassType>(*st).Fields()) {
cast<NominalClassType>(*st).Fields()) {
field_types.push_back({.name = field_name, .value = field_value});
}
auto fun_ty = arena->New<FunctionType>(
+100 -14
View File
@@ -44,6 +44,16 @@ auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool {
}
}
auto StructValue::FindField(const std::string& name) const
-> std::optional<Nonnull<const Value*>> {
for (const TupleElement& element : elements_) {
if (element.name == name) {
return element.value;
}
}
return std::nullopt;
}
auto TupleValue::FindField(const std::string& name) const
-> std::optional<Nonnull<const Value*>> {
for (const TupleElement& element : elements) {
@@ -62,7 +72,15 @@ auto GetMember(Nonnull<Arena*> arena, Nonnull<const Value*> v,
switch (v->Tag()) {
case Value::Kind::StructValue: {
std::optional<Nonnull<const Value*>> field =
cast<TupleValue>(*cast<StructValue>(*v).Inits()).FindField(f);
cast<StructValue>(*v).FindField(f);
if (field == std::nullopt) {
FATAL_RUNTIME_ERROR(loc) << "member " << f << " not in " << *v;
}
return *field;
}
case Value::Kind::NominalClassValue: {
std::optional<Nonnull<const Value*>> field =
cast<TupleValue>(*cast<NominalClassValue>(*v).Inits()).FindField(f);
if (field == std::nullopt) {
FATAL_RUNTIME_ERROR(loc) << "member " << f << " not in " << *v;
}
@@ -111,8 +129,22 @@ auto SetFieldImpl(Nonnull<Arena*> arena, Nonnull<const Value*> value,
}
switch (value->Tag()) {
case Value::Kind::StructValue: {
return SetFieldImpl(arena, cast<StructValue>(*value).Inits(), path_begin,
path_end, field_value, loc);
std::vector<TupleElement> elements = cast<StructValue>(*value).elements();
auto it = std::find_if(elements.begin(), elements.end(),
[path_begin](const TupleElement& element) {
return element.name == *path_begin;
});
if (it == elements.end()) {
FATAL_RUNTIME_ERROR(loc)
<< "field " << *path_begin << " not in " << *value;
}
it->value = SetFieldImpl(arena, it->value, path_begin + 1, path_end,
field_value, loc);
return arena->New<StructValue>(elements);
}
case Value::Kind::NominalClassValue: {
return SetFieldImpl(arena, cast<NominalClassValue>(*value).Inits(),
path_begin, path_end, field_value, loc);
}
case Value::Kind::TupleValue: {
std::vector<TupleElement> elements = cast<TupleValue>(*value).Elements();
@@ -167,8 +199,18 @@ void Value::Print(llvm::raw_ostream& out) const {
break;
}
case Value::Kind::StructValue: {
const auto& s = cast<StructValue>(*this);
out << cast<ClassType>(*s.Type()).Name() << *s.Inits();
const auto& struct_val = cast<StructValue>(*this);
out << "{";
llvm::ListSeparator sep;
for (const TupleElement& element : struct_val.elements()) {
out << sep << "." << element.name << " = " << *element.value;
}
out << "}";
break;
}
case Value::Kind::NominalClassValue: {
const auto& s = cast<NominalClassValue>(*this);
out << cast<NominalClassType>(*s.Type()).Name() << *s.Inits();
break;
}
case Value::Kind::TupleValue: {
@@ -228,8 +270,17 @@ void Value::Print(llvm::raw_ostream& out) const {
out << *fn_type.Param() << " -> " << *fn_type.Ret();
break;
}
case Value::Kind::ClassType:
out << "struct " << cast<ClassType>(*this).Name();
case Value::Kind::StructType: {
out << "{";
llvm::ListSeparator sep;
for (const auto& [name, type] : cast<StructType>(*this).fields()) {
out << sep << "." << name << ": " << *type;
}
out << "}";
break;
}
case Value::Kind::NominalClassType:
out << "class " << cast<NominalClassType>(*this).Name();
break;
case Value::Kind::ChoiceType:
out << "choice " << cast<ChoiceType>(*this).Name();
@@ -274,9 +325,17 @@ auto CopyVal(Nonnull<Arena*> arena, Nonnull<const Value*> val,
return arena->New<AlternativeValue>(alt.AltName(), alt.ChoiceName(), arg);
}
case Value::Kind::StructValue: {
const auto& s = cast<StructValue>(*val);
std::vector<TupleElement> elements;
for (const TupleElement& element : cast<StructValue>(*val).elements()) {
elements.push_back({.name = element.name,
.value = CopyVal(arena, element.value, loc)});
}
return arena->New<StructValue>(std::move(elements));
}
case Value::Kind::NominalClassValue: {
const auto& s = cast<NominalClassValue>(*val);
Nonnull<const Value*> inits = CopyVal(arena, s.Inits(), loc);
return arena->New<StructValue>(s.Type(), inits);
return arena->New<NominalClassValue>(s.Type(), inits);
}
case Value::Kind::IntValue:
return arena->New<IntValue>(cast<IntValue>(*val).Val());
@@ -315,8 +374,15 @@ auto CopyVal(Nonnull<Arena*> arena, Nonnull<const Value*> val,
return arena->New<StringType>();
case Value::Kind::StringValue:
return arena->New<StringValue>(cast<StringValue>(*val).Val());
case Value::Kind::StructType: {
VarValues fields;
for (const auto& [name, type] : cast<StructType>(*val).fields()) {
fields.push_back({name, CopyVal(arena, type, loc)});
}
return arena->New<StructType>(fields);
}
case Value::Kind::VariableType:
case Value::Kind::ClassType:
case Value::Kind::NominalClassType:
case Value::Kind::ChoiceType:
case Value::Kind::BindingPlaceholderValue:
case Value::Kind::AlternativeConstructorValue:
@@ -339,8 +405,24 @@ auto TypeEqual(Nonnull<const Value*> t1, Nonnull<const Value*> t2) -> bool {
return TypeEqual(fn1.Param(), fn2.Param()) &&
TypeEqual(fn1.Ret(), fn2.Ret());
}
case Value::Kind::ClassType:
return cast<ClassType>(*t1).Name() == cast<ClassType>(*t2).Name();
case Value::Kind::StructType: {
const auto& struct1 = cast<StructType>(*t1);
const auto& struct2 = cast<StructType>(*t2);
if (struct1.fields().size() != struct2.fields().size()) {
return false;
}
for (size_t i = 0; i < struct1.fields().size(); ++i) {
if (struct1.fields()[i].first != struct2.fields()[i].first ||
!TypeEqual(struct1.fields()[i].second,
struct2.fields()[i].second)) {
return false;
}
}
return true;
}
case Value::Kind::NominalClassType:
return cast<NominalClassType>(*t1).Name() ==
cast<NominalClassType>(*t2).Name();
case Value::Kind::ChoiceType:
return cast<ChoiceType>(*t1).Name() == cast<ChoiceType>(*t2).Name();
case Value::Kind::TupleValue: {
@@ -420,6 +502,9 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
case Value::Kind::TupleValue:
return FieldsValueEqual(cast<TupleValue>(*v1).Elements(),
cast<TupleValue>(*v2).Elements(), loc);
case Value::Kind::StructValue:
return FieldsValueEqual(cast<StructValue>(*v1).elements(),
cast<StructValue>(*v2).elements(), loc);
case Value::Kind::StringValue:
return cast<StringValue>(*v1).Val() == cast<StringValue>(*v2).Val();
case Value::Kind::IntType:
@@ -428,13 +513,14 @@ auto ValueEqual(Nonnull<const Value*> v1, Nonnull<const Value*> v2,
case Value::Kind::FunctionType:
case Value::Kind::PointerType:
case Value::Kind::AutoType:
case Value::Kind::ClassType:
case Value::Kind::StructType:
case Value::Kind::NominalClassType:
case Value::Kind::ChoiceType:
case Value::Kind::ContinuationType:
case Value::Kind::VariableType:
case Value::Kind::StringType:
return TypeEqual(v1, v2);
case Value::Kind::StructValue:
case Value::Kind::NominalClassValue:
case Value::Kind::AlternativeValue:
case Value::Kind::BindingPlaceholderValue:
case Value::Kind::AlternativeConstructorValue:
+68 -9
View File
@@ -37,6 +37,7 @@ class Value {
PointerValue,
BoolValue,
StructValue,
NominalClassValue,
AlternativeValue,
TupleValue,
IntType,
@@ -45,7 +46,8 @@ class Value {
FunctionType,
PointerType,
AutoType,
ClassType,
StructType,
NominalClassType,
ChoiceType,
ContinuationType, // The type of a continuation.
VariableType, // e.g., generic type parameters.
@@ -92,7 +94,11 @@ auto FindInVarValues(const std::string& field, const VarValues& inits)
-> std::optional<Nonnull<const Value*>>;
auto FieldsEqual(const VarValues& ts1, const VarValues& ts2) -> bool;
// A TupleElement represents the value of a single tuple field.
// A TupleElement represents the value of a single tuple or struct field.
//
// TODO(geoffromer): Rename this, and look for ways to eliminate duplication
// among TupleElement, VarValues::value_type, FieldInitializer,
// TuplePattern::Field, and any similar types.
struct TupleElement {
// The field name.
std::string name;
@@ -173,16 +179,48 @@ class BoolValue : public Value {
bool val;
};
// A function value.
// A non-empty value of a struct type.
//
// It can't be empty because `{}` is a struct type as well as a value of that
// type, so for consistency we always represent it as a StructType rather than
// let it oscillate unpredictably between the two. However, this means code
// that handles StructValue instances may also need to be able to handle
// StructType instances.
class StructValue : public Value {
public:
StructValue(Nonnull<const Value*> type, Nonnull<const Value*> inits)
: Value(Kind::StructValue), type(type), inits(inits) {}
explicit StructValue(std::vector<TupleElement> elements)
: Value(Kind::StructValue), elements_(std::move(elements)) {
CHECK(!elements_.empty())
<< "`{}` is represented as a StructType, not a StructValue.";
}
static auto classof(const Value* value) -> bool {
return value->Tag() == Kind::StructValue;
}
auto elements() const -> const std::vector<TupleElement>& {
return elements_;
}
// Returns the value of the field named `name` in this struct, or
// nullopt if there is no such field.
auto FindField(const std::string& name) const
-> std::optional<Nonnull<const Value*>>;
private:
std::vector<TupleElement> elements_;
};
// A value of a nominal class type.
class NominalClassValue : public Value {
public:
NominalClassValue(Nonnull<const Value*> type, Nonnull<const Value*> inits)
: Value(Kind::NominalClassValue), type(type), inits(inits) {}
static auto classof(const Value* value) -> bool {
return value->Tag() == Kind::NominalClassValue;
}
auto Type() const -> Nonnull<const Value*> { return type; }
auto Inits() const -> Nonnull<const Value*> { return inits; }
@@ -365,16 +403,37 @@ class AutoType : public Value {
};
// A struct type.
class ClassType : public Value {
//
// Code that handles this type may sometimes need to have special-case handling
// for `{}`, which is a struct value in addition to being a struct type.
class StructType : public Value {
public:
ClassType(std::string name, VarValues fields, VarValues methods)
: Value(Kind::ClassType),
StructType() : StructType(VarValues{}) {}
explicit StructType(VarValues fields)
: Value(Kind::StructType), fields_(std::move(fields)) {}
static auto classof(const Value* value) -> bool {
return value->Tag() == Kind::StructType;
}
auto fields() const -> const VarValues& { return fields_; }
private:
VarValues fields_;
};
// A class type.
class NominalClassType : public Value {
public:
NominalClassType(std::string name, VarValues fields, VarValues methods)
: Value(Kind::NominalClassType),
name(std::move(name)),
fields(std::move(fields)),
methods(std::move(methods)) {}
static auto classof(const Value* value) -> bool {
return value->Tag() == Kind::ClassType;
return value->Tag() == Kind::NominalClassType;
}
auto Name() const -> const std::string& { return name; }