diff --git a/executable_semantics/ast/member.cpp b/executable_semantics/ast/member.cpp index 293440277cf6..58afdba48ed3 100644 --- a/executable_semantics/ast/member.cpp +++ b/executable_semantics/ast/member.cpp @@ -5,26 +5,17 @@ #include "executable_semantics/ast/member.h" #include "executable_semantics/common/arena.h" +#include "llvm/Support/Casting.h" namespace Carbon { -auto Member::MakeFieldMember(int line_num, const BindingPattern* binding) - -> Member* { - auto m = global_arena->New(); - m->line_num = line_num; - m->value = FieldMember({.binding = binding}); - return m; -} - -auto Member::GetFieldMember() const -> const FieldMember& { - return std::get(value); -} +using llvm::cast; void Member::Print(llvm::raw_ostream& out) const { - switch (tag()) { - case MemberKind::FieldMember: - const auto& field = GetFieldMember(); - out << "var " << field.binding << ";\n"; + switch (Tag()) { + case Kind::FieldMember: + const auto& field = cast(*this); + out << "var " << field.Binding() << ";\n"; break; } } diff --git a/executable_semantics/ast/member.h b/executable_semantics/ast/member.h index acfd078db154..e629c14dd8bc 100644 --- a/executable_semantics/ast/member.h +++ b/executable_semantics/ast/member.h @@ -14,35 +14,58 @@ namespace Carbon { -enum class MemberKind { FieldMember }; +// Abstract base class of all AST nodes representing patterns. +// +// Member and its derived classes support LLVM-style RTTI, including +// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every +// class derived from Member must provide a `classof` operation, and +// every concrete derived class must have a corresponding enumerator +// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for +// details. +class Member { + public: + enum class Kind { FieldMember }; -struct FieldMember { - static constexpr MemberKind Kind = MemberKind::FieldMember; + Member(const Member&) = delete; + Member& operator=(const Member&) = delete; + + // Returns the enumerator corresponding to the most-derived type of this + // object. + auto Tag() const -> Kind { return tag; } + + auto LineNumber() const -> int { return line_num; } + + void Print(llvm::raw_ostream& out) const; + + protected: + // Constructs a Member representing syntax at the given line number. + // `tag` must be the enumerator corresponding to the most-derived type being + // constructed. + Member(Kind tag, int line_num) : tag(tag), line_num(line_num) {} + + private: + const Kind tag; + int line_num; +}; + +class FieldMember : public Member { + public: + FieldMember(int line_num, const BindingPattern* binding) + : Member(Kind::FieldMember, line_num), binding(binding) {} + + static auto classof(const Member* member) -> bool { + return member->Tag() == Kind::FieldMember; + } + + auto Binding() const -> const BindingPattern* { return binding; } + + private: // TODO: split this into a non-optional name and a type, initialized by // a constructor that takes a BindingPattern and handles errors like a // missing name. const BindingPattern* binding; }; -struct Member { - static auto MakeFieldMember(int line_num, const BindingPattern* binding) - -> Member*; - - auto GetFieldMember() const -> const FieldMember&; - - void Print(llvm::raw_ostream& out) const; - LLVM_DUMP_METHOD void Dump() const { Print(llvm::errs()); } - - inline auto tag() const -> MemberKind { - return std::visit([](const auto& t) { return t.Kind; }, value); - } - - int line_num; - - private: - std::variant value; -}; - } // namespace Carbon #endif // EXECUTABLE_SEMANTICS_AST_MEMBER_H_ diff --git a/executable_semantics/interpreter/interpreter.cpp b/executable_semantics/interpreter/interpreter.cpp index 0c7aa7809f02..5ecd9ce8983b 100644 --- a/executable_semantics/interpreter/interpreter.cpp +++ b/executable_semantics/interpreter/interpreter.cpp @@ -132,9 +132,9 @@ void InitEnv(const Declaration& d, Env* env) { VarValues fields; VarValues methods; for (const Member* m : struct_def.members) { - switch (m->tag()) { - case MemberKind::FieldMember: { - const BindingPattern* binding = m->GetFieldMember().binding; + switch (m->Tag()) { + case Member::Kind::FieldMember: { + const BindingPattern* binding = cast(*m).Binding(); const Expression* type_expression = cast(binding->Type())->Expression(); auto type = InterpExp(Env(), type_expression); diff --git a/executable_semantics/interpreter/typecheck.cpp b/executable_semantics/interpreter/typecheck.cpp index 5c1d0a381588..355f8ef99dc4 100644 --- a/executable_semantics/interpreter/typecheck.cpp +++ b/executable_semantics/interpreter/typecheck.cpp @@ -896,9 +896,9 @@ static auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*types*/, VarValues fields; VarValues methods; for (const Member* m : sd->members) { - switch (m->tag()) { - case MemberKind::FieldMember: { - const BindingPattern* binding = m->GetFieldMember().binding; + switch (m->Tag()) { + case Member::Kind::FieldMember: { + const BindingPattern* binding = cast(*m).Binding(); if (!binding->Name().has_value()) { FATAL_COMPILATION_ERROR(binding->LineNumber()) << "Struct members must have names"; @@ -950,8 +950,8 @@ auto MakeTypeChecked(const Declaration& d, const TypeEnv& types, cast(d).Definition(); std::list fields; for (Member* m : struct_def.members) { - switch (m->tag()) { - case MemberKind::FieldMember: + switch (m->Tag()) { + case Member::Kind::FieldMember: // TODO: Interpret the type expression and store the result. fields.push_back(m); break; diff --git a/executable_semantics/syntax/parser.ypp b/executable_semantics/syntax/parser.ypp index 95365983e3fe..ad5cf86297a8 100644 --- a/executable_semantics/syntax/parser.ypp +++ b/executable_semantics/syntax/parser.ypp @@ -530,7 +530,7 @@ variable_declaration: identifier ":" pattern { $$ = global_arena->New(yylineno, $1, $3); } ; member: VAR variable_declaration ";" - { $$ = Member::MakeFieldMember(yylineno, $2); } + { $$ = global_arena->New(yylineno, $2); } ; member_list: // Empty