فهرست منبع

Replace RTTI generation script with macros. (#2703)

This keeps all of our C++ code written in C++ source files, and avoids the increasing size of the RTTI generation script we'd started to see in #2699.

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
Richard Smith 3 سال پیش
والد
کامیت
807ef98758

+ 1 - 0
.clang-format

@@ -14,3 +14,4 @@ PointerAlignment: Left
 # We abuse control macros for formatting other kinds of macros.
 SpaceBeforeParens: ControlStatementsExceptControlMacros
 IfMacros: ['CARBON_DEFINE_RAW_ENUM_CLASS']
+StatementMacros: ['ABSTRACT']

+ 31 - 7
common/enum_base.h

@@ -83,7 +83,7 @@ class EnumBase {
 
   // Prints this value using its name.
   void Print(llvm::raw_ostream& out) const {
-    out << reinterpret_cast<const EnumType*>(this)->name();
+    out << static_cast<const EnumType*>(this)->name();
   }
 
  protected:
@@ -135,8 +135,12 @@ class EnumBase {
 // Use this to compute the `Internal::EnumBase` specialization for a Carbon enum
 // class. It both computes the name of the raw enum and ensures all the
 // namespaces are correct.
-#define CARBON_ENUM_BASE(EnumClassName)       \
-  ::Carbon::Internal::EnumBase<EnumClassName, \
+#define CARBON_ENUM_BASE(EnumClassName) \
+  CARBON_ENUM_BASE_CRTP(EnumClassName, EnumClassName)
+// This variant handles the case where the external name for the Carbon enum is
+// not the same as the name by which we refer to it from this context.
+#define CARBON_ENUM_BASE_CRTP(EnumClassName, LocalTypeNameForEnumClass) \
+  ::Carbon::Internal::EnumBase<LocalTypeNameForEnumClass,               \
                                ::Carbon::Internal::EnumClassName##RawEnum>
 
 // Use this within the Carbon enum class body to generate named constant
@@ -149,6 +153,29 @@ class EnumBase {
   constexpr EnumClassName EnumClassName::Name =              \
       EnumClassName::Create(RawEnumType::Name);
 
+// Alternatively, use this within the Carbon enum class body to declare and
+// define each named constant. Due to type completeness constraints, this will
+// only work if the enum-like class is templated.
+//
+// This requires the template to have a member named `Base` that names the
+// `EnumBase` base class.
+#define CARBON_INLINE_ENUM_CONSTANT_DEFINITION(Name)     \
+  static constexpr const typename Base::EnumType& Name = \
+      Base::Create(Base::RawEnumType::Name);
+
+// Use this to define a custom `name()` function for an enum-like class. Usage:
+//
+//   CARBON_ENUM_NAME_FUNCTION(MyEnum) {
+//     // Return a StringRef based on the value of *this.
+//   }
+//
+// You should usually use CARBON_DEFINE_ENUM_CLASS_NAMES instead.
+#define CARBON_ENUM_NAME_FUNCTION(EnumClassName)                              \
+  template <>                                                                 \
+  auto                                                                        \
+  Internal::EnumBase<EnumClassName, Internal::EnumClassName##RawEnum>::name() \
+      const->llvm::StringRef
+
 // Use this in the `.cpp` file for an enum class to start the definition of the
 // constant names array for each enumerator. It is followed by the desired
 // constant initializer.
@@ -164,10 +191,7 @@ class EnumBase {
                                                                               \
   /* Now define an explicit function specialization for the `name` method, as \
    * it can now reference our specialized array. */                           \
-  template <>                                                                 \
-  auto                                                                        \
-  Internal::EnumBase<EnumClassName, Internal::EnumClassName##RawEnum>::name() \
-      const->llvm::StringRef {                                                \
+  CARBON_ENUM_NAME_FUNCTION(EnumClassName) {                                  \
     return names[static_cast<int>(value_)];                                   \
   }                                                                           \
                                                                               \

+ 0 - 5
explorer/BUILD

@@ -41,8 +41,3 @@ cc_binary(
         "@llvm-project//llvm:Support",
     ],
 )
-
-py_binary(
-    name = "gen_rtti",
-    srcs = ["gen_rtti.py"],
-)

+ 21 - 35
explorer/ast/BUILD

@@ -4,40 +4,6 @@
 
 package(default_visibility = ["//explorer:__subpackages__"])
 
-AST_HDRS = [
-    "address.h",
-    "ast.h",
-    "ast_node.h",
-    "bindings.h",
-    "clone_context.h",
-    "declaration.h",
-    "element.h",
-    "element_path.h",
-    "expression.h",
-    "impl_binding.h",
-    "pattern.h",
-    "return_term.h",
-    "statement.h",
-    "value.h",
-    "value_node.h",
-    "value_transform.h",
-]
-
-genrule(
-    name = "ast_rtti",
-    srcs = ["ast_rtti.txt"] + AST_HDRS,
-    outs = [
-        "ast_rtti.h",
-        "ast_rtti.cpp",
-    ],
-    cmd = "./$(location //explorer:gen_rtti)" +
-          " $(location ast_rtti.txt)" +
-          " $(location ast_rtti.h) $(location ast_rtti.cpp)" +
-          " $(rootpath ast_rtti.h)" +
-          "".join([" $(rootpath " + f + ")" for f in AST_HDRS]),
-    tools = ["//explorer:gen_rtti"],
-)
-
 cc_library(
     name = "ast",
     srcs = [
@@ -53,7 +19,26 @@ cc_library(
         "statement.cpp",
         "value.cpp",
     ],
-    hdrs = AST_HDRS + ["ast_rtti.h"],
+    hdrs = [
+        "address.h",
+        "ast.h",
+        "ast_kinds.h",
+        "ast_node.h",
+        "ast_rtti.h",
+        "bindings.h",
+        "clone_context.h",
+        "declaration.h",
+        "element.h",
+        "element_path.h",
+        "expression.h",
+        "impl_binding.h",
+        "pattern.h",
+        "return_term.h",
+        "statement.h",
+        "value.h",
+        "value_node.h",
+        "value_transform.h",
+    ],
     textual_hdrs = [
         "value_kinds.def",
     ],
@@ -62,6 +47,7 @@ cc_library(
         ":paren_contents",
         ":value_category",
         "//common:check",
+        "//common:enum_base",
         "//common:error",
         "//common:indirect_value",
         "//common:ostream",

+ 4 - 4
explorer/ast/README.md

@@ -25,10 +25,10 @@ concrete type of the object, and a `FooKind` value can be safely `static_cast`ed
 to `BarKind` if that value represents a type that's derived from both `Foo` and
 `Bar`.
 
-We rely on code generation to help enforce those invariants, so every node type
-must be described in [`ast_rtti.txt`](ast_rtti.txt). See the documentation in
-[`gen_rtti.py`](../gen_rtti.py), the code generation script, for details about
-the file format and generated code.
+These `FooKind` enums are generated from a description of the class hierarchy
+that is provided by [X macros](https://en.wikipedia.org/wiki/X_Macro) defined in
+[`ast_kinds.h`](ast_kinds.h) that specify the classes derived from each AST base
+class. Those macros must be kept up to date as the class hierarchy changes.
 
 The AST class hierarchy is structured in a fairly unsurprising way, with
 abstract classes such as `Statement` and `Expression`, and concrete classes

+ 164 - 0
explorer/ast/ast_kinds.h

@@ -0,0 +1,164 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+// This file defines a number of X-macros that can be used to walk the AST
+// class hierarchy. The main entry points are:
+//
+// -   `CARBON_AST_FOR_EACH_FINAL_CLASS(ACTION)`
+//     Invokes `ACTION` on each leaf class in the AST class hierarchy.
+// -   `CARBON_AST_FOR_EACH_ABSTRACT_CLASS(ACTION)`
+//     Invokes `ACTION` on each non-leaf class in the AST class hierarchy.
+// -   `CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(BASE, ACTION)`
+//     Invokes `ACTION` on each leaf class in the AST class hierarchy that
+//     derives from `BASE`.
+// -   `CARBON_AST_FOR_EACH_ABSTRACT_CLASS_BELOW(BASE, ACTION)`
+//     Invokes `ACTION` on each non-leaf class in the AST class hierarchy that
+//     derives from `BASE`.
+//
+// These macros are implemented in terms of `CARBON_Foo_KINDS` X-macros. Each of
+// these macros takes two macro names as arguments:
+//
+// -   `CARBON_Foo_KINDS(ABSTRACT, FINAL)`
+//     Invokes `ABSTRACT(Class)` for each abstract class that inherits from
+//     `Foo`.
+//     Invokes `FINAL(Class)` for each final class that inherits from `Foo`.
+//
+// A fake root class, `AST_RTTI`, is also provided, so that
+// `CARBON_AST_RTTI_KINDS` can be used to walk all AST classes.
+#ifndef CARBON_EXPLORER_AST_AST_KINDS_H_
+#define CARBON_EXPLORER_AST_AST_KINDS_H_
+
+#define CARBON_RTTI_NOOP_ACTION(X)
+
+#define CARBON_AST_FOR_EACH_ABSTRACT_CLASS_BELOW(ROOT, ACTION) \
+  CARBON_##ROOT##_KINDS(ACTION, CARBON_RTTI_NOOP_ACTION)
+#define CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(ROOT, ACTION) \
+  CARBON_##ROOT##_KINDS(CARBON_RTTI_NOOP_ACTION, ACTION)
+#define CARBON_AST_FOR_EACH_ABSTRACT_CLASS(ACTION) \
+  CARBON_AST_FOR_EACH_ABSTRACT_CLASS_BELOW(AST_RTTI, ACTION)
+#define CARBON_AST_FOR_EACH_FINAL_CLASS(ACTION) \
+  CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(AST_RTTI, ACTION)
+
+// Class hierarchy description follows, with one _KINDS macro for each abstract
+// base class.
+
+#define CARBON_AST_RTTI_KINDS(ABSTRACT, FINAL)            \
+  CARBON_AstNode_KINDS(ABSTRACT, FINAL) ABSTRACT(AstNode) \
+  CARBON_Element_KINDS(ABSTRACT, FINAL) ABSTRACT(Element)
+
+#define CARBON_AstNode_KINDS(ABSTRACT, FINAL)                     \
+  CARBON_Pattern_KINDS(ABSTRACT, FINAL) ABSTRACT(Pattern)         \
+  CARBON_Declaration_KINDS(ABSTRACT, FINAL) ABSTRACT(Declaration) \
+  FINAL(ImplBinding)                                              \
+  FINAL(AlternativeSignature)                                     \
+  CARBON_Statement_KINDS(ABSTRACT, FINAL) ABSTRACT(Statement)     \
+  CARBON_Expression_KINDS(ABSTRACT, FINAL) ABSTRACT(Expression)   \
+  CARBON_WhereClause_KINDS(ABSTRACT, FINAL) ABSTRACT(WhereClause)
+
+#define CARBON_Pattern_KINDS(ABSTRACT, FINAL) \
+  FINAL(AutoPattern)                          \
+  FINAL(VarPattern)                           \
+  FINAL(AddrPattern)                          \
+  FINAL(BindingPattern)                       \
+  FINAL(GenericBinding)                       \
+  FINAL(TuplePattern)                         \
+  FINAL(AlternativePattern)                   \
+  FINAL(ExpressionPattern)
+
+#define CARBON_Declaration_KINDS(ABSTRACT, FINAL)         \
+  FINAL(NamespaceDeclaration)                             \
+  CARBON_CallableDeclaration_KINDS(ABSTRACT, FINAL)       \
+      ABSTRACT(CallableDeclaration)                       \
+  FINAL(SelfDeclaration)                                  \
+  FINAL(ClassDeclaration)                                 \
+  FINAL(MixinDeclaration)                                 \
+  FINAL(MixDeclaration)                                   \
+  FINAL(ChoiceDeclaration)                                \
+  FINAL(VariableDeclaration)                              \
+  CARBON_ConstraintTypeDeclaration_KINDS(ABSTRACT, FINAL) \
+      ABSTRACT(ConstraintTypeDeclaration)                 \
+  FINAL(InterfaceExtendsDeclaration)                      \
+  FINAL(InterfaceImplDeclaration)                         \
+  FINAL(AssociatedConstantDeclaration)                    \
+  FINAL(ImplDeclaration)                                  \
+  FINAL(MatchFirstDeclaration)                            \
+  FINAL(AliasDeclaration)
+
+#define CARBON_CallableDeclaration_KINDS(ABSTRACT, FINAL) \
+  FINAL(FunctionDeclaration)                              \
+  FINAL(DestructorDeclaration)
+
+#define CARBON_ConstraintTypeDeclaration_KINDS(ABSTRACT, FINAL) \
+  FINAL(InterfaceDeclaration)                                   \
+  FINAL(ConstraintDeclaration)
+
+#define CARBON_Statement_KINDS(ABSTRACT, FINAL)         \
+  FINAL(ExpressionStatement)                            \
+  FINAL(Assign)                                         \
+  FINAL(IncrementDecrement)                             \
+  FINAL(VariableDefinition)                             \
+  FINAL(If)                                             \
+  CARBON_Return_KINDS(ABSTRACT, FINAL) ABSTRACT(Return) \
+  FINAL(Block)                                          \
+  FINAL(While)                                          \
+  FINAL(Break)                                          \
+  FINAL(Continue)                                       \
+  FINAL(Match)                                          \
+  FINAL(Continuation)                                   \
+  FINAL(Run)                                            \
+  FINAL(Await)                                          \
+  FINAL(For)
+
+#define CARBON_Return_KINDS(ABSTRACT, FINAL) \
+  FINAL(ReturnVar)                           \
+  FINAL(ReturnExpression)
+
+#define CARBON_Expression_KINDS(ABSTRACT, FINAL)       \
+  FINAL(BoolTypeLiteral)                               \
+  FINAL(BoolLiteral)                                   \
+  FINAL(CallExpression)                                \
+  CARBON_ConstantValueLiteral_KINDS(ABSTRACT, FINAL)   \
+      ABSTRACT(ConstantValueLiteral)                   \
+  CARBON_MemberAccessExpression_KINDS(ABSTRACT, FINAL) \
+      ABSTRACT(MemberAccessExpression)                 \
+  FINAL(IndexExpression)                               \
+  FINAL(IntTypeLiteral)                                \
+  FINAL(ContinuationTypeLiteral)                       \
+  FINAL(IntLiteral)                                    \
+  FINAL(OperatorExpression)                            \
+  FINAL(StringLiteral)                                 \
+  FINAL(StringTypeLiteral)                             \
+  FINAL(TupleLiteral)                                  \
+  FINAL(StructLiteral)                                 \
+  FINAL(TypeTypeLiteral)                               \
+  FINAL(IdentifierExpression)                          \
+  FINAL(DotSelfExpression)                             \
+  FINAL(IntrinsicExpression)                           \
+  FINAL(IfExpression)                                  \
+  FINAL(WhereExpression)                               \
+  FINAL(BuiltinConvertExpression)                      \
+  FINAL(UnimplementedExpression)
+
+#define CARBON_ConstantValueLiteral_KINDS(ABSTRACT, FINAL) \
+  FINAL(FunctionTypeLiteral)                               \
+  FINAL(StructTypeLiteral)                                 \
+  FINAL(ArrayTypeLiteral)                                  \
+  FINAL(ValueLiteral)
+
+#define CARBON_MemberAccessExpression_KINDS(ABSTRACT, FINAL) \
+  FINAL(SimpleMemberAccessExpression)                        \
+  FINAL(CompoundMemberAccessExpression)                      \
+  FINAL(BaseAccessExpression)
+
+#define CARBON_WhereClause_KINDS(ABSTRACT, FINAL) \
+  FINAL(ImplsWhereClause)                         \
+  FINAL(EqualsWhereClause)                        \
+  FINAL(RewriteWhereClause)
+
+#define CARBON_Element_KINDS(ABSTRACT, FINAL) \
+  FINAL(NamedElement)                         \
+  FINAL(PositionalElement)                    \
+  FINAL(BaseElement)
+
+#endif  // CARBON_EXPLORER_AST_AST_KINDS_H_

+ 20 - 0
explorer/ast/ast_rtti.cpp

@@ -0,0 +1,20 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#include "explorer/ast/ast_rtti.h"
+
+namespace Carbon {
+
+// Define kind names for the base enumeration type.
+CARBON_DEFINE_ENUM_CLASS_NAMES(AstRttiNodeKind) = {
+    CARBON_AST_FOR_EACH_FINAL_CLASS(CARBON_ENUM_CLASS_NAME_STRING)};
+
+// For other kind enumerations, reuse the same table.
+#define DEFINE_NAME_FUNCTION(C)                                        \
+  CARBON_ENUM_NAME_FUNCTION(C##Kind) {                                 \
+    return AstRttiNodeKind(static_cast<const C##Kind&>(*this)).name(); \
+  }
+CARBON_AST_FOR_EACH_ABSTRACT_CLASS(DEFINE_NAME_FUNCTION)
+
+}  // namespace Carbon

+ 99 - 0
explorer/ast/ast_rtti.h

@@ -0,0 +1,99 @@
+// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
+// Exceptions. See /LICENSE for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+
+#ifndef CARBON_EXPLORER_AST_AST_RTTI_H_
+#define CARBON_EXPLORER_AST_AST_RTTI_H_
+
+#include "common/enum_base.h"
+#include "explorer/ast/ast_kinds.h"
+
+namespace Carbon {
+
+// Assign numbers to all AST types.
+CARBON_DEFINE_RAW_ENUM_CLASS(AstRttiNodeKind, int) {
+#define DEFINE_ENUMERATOR(E) E,
+  CARBON_AST_FOR_EACH_FINAL_CLASS(DEFINE_ENUMERATOR)
+#undef DEFINE_ENUMERATOR
+};
+
+// An enumerated type defining the kinds of AST nodes.
+class AstRttiNodeKind : public CARBON_ENUM_BASE(AstRttiNodeKind) {
+ public:
+  using IsCarbonAstRttiNodeKind = void;
+
+  AstRttiNodeKind() = default;
+
+  // All our other RTTI node kinds implicitly convert to this one.
+  // TODO: We could support conversion to the base class's kind for all node
+  // kinds if we find a reason to do so.
+  template <typename Kind, typename = typename Kind::IsCarbonAstRttiNodeKind>
+  /*implicit*/ AstRttiNodeKind(Kind kind)
+      : AstRttiNodeKind(FromInt(kind.AsInt())) {}
+
+  // Expose the integer value of this node. This is used to set the values of
+  // other enumerations to match, and to implement range checks.
+  using EnumBase::AsInt;
+
+  CARBON_AST_FOR_EACH_FINAL_CLASS(CARBON_ENUM_CONSTANT_DECLARATION)
+};
+
+// Define the constant members for AstRttiNodeKind.
+#define CONSTANT_DEFINITION(E) \
+  CARBON_ENUM_CONSTANT_DEFINITION(AstRttiNodeKind, E)
+CARBON_AST_FOR_EACH_FINAL_CLASS(CONSTANT_DEFINITION)
+#undef CONSTANT_DEFINITION
+
+// Define Kind enumerations for all base classes.
+#define DEFINE_KIND_ENUM(C)                                                 \
+  CARBON_DEFINE_RAW_ENUM_CLASS(C##Kind, int) {                              \
+    CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(C, DEFINE_ENUMERATOR)             \
+  };                                                                        \
+  template <typename Derived>                                               \
+  class C##KindTemplate : public CARBON_ENUM_BASE_CRTP(C##Kind, Derived) {  \
+   private:                                                                 \
+    using Base = CARBON_ENUM_BASE_CRTP(C##Kind, Derived);                   \
+    friend class AstRttiNodeKind;                                           \
+                                                                            \
+   public:                                                                  \
+    using IsCarbonAstRttiNodeKind = void;                                   \
+                                                                            \
+    C##KindTemplate() = default;                                            \
+                                                                            \
+    /* This type can be explicitly converted from the generic node kind. */ \
+    explicit C##KindTemplate(AstRttiNodeKind base_kind)                     \
+        : Base(Base::FromInt(base_kind.AsInt())) {}                         \
+                                                                            \
+    CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(                                  \
+        C, CARBON_INLINE_ENUM_CONSTANT_DEFINITION)                          \
+  };                                                                        \
+  class C##Kind : public C##KindTemplate<C##Kind> {                         \
+    using C##KindTemplate<C##Kind>::C##KindTemplate;                        \
+  };
+#define DEFINE_ENUMERATOR(E) E = AstRttiNodeKind::E.AsInt(),
+CARBON_AST_FOR_EACH_ABSTRACT_CLASS(DEFINE_KIND_ENUM)
+#undef DEFINE_KIND_ENUM
+#undef DEFINE_ENUMERATOR
+
+// Define InheritsFrom functions for each abstract class.
+#define DEFINE_INHERITS_FROM_FUNCTION_ABSTRACT(C)             \
+  inline bool InheritsFrom##C(Carbon::AstRttiNodeKind kind) { \
+    return CARBON_AST_FOR_EACH_FINAL_CLASS_BELOW(             \
+        C, INHERITS_FROM_CLASS_TEST) false;                   \
+  }
+#define INHERITS_FROM_CLASS_TEST(C) kind == Carbon::AstRttiNodeKind::C ||
+CARBON_AST_FOR_EACH_ABSTRACT_CLASS(DEFINE_INHERITS_FROM_FUNCTION_ABSTRACT)
+#undef DEFINE_INHERITS_FROM_FUNCTION_ABSTRACT
+#undef INHERITS_FROM_CLASS_TEST
+
+// Define trivial InheritsFrom functions for each final class.
+#define DEFINE_INHERITS_FROM_FUNCTION_FINAL(C)                \
+  inline bool InheritsFrom##C(Carbon::AstRttiNodeKind kind) { \
+    return kind == Carbon::AstRttiNodeKind::C;                \
+  }
+CARBON_AST_FOR_EACH_FINAL_CLASS(DEFINE_INHERITS_FROM_FUNCTION_FINAL)
+#undef DEFINE_INHERITS_FROM_FUNCTION_FINAL
+
+}  // namespace Carbon
+
+#endif  // CARBON_EXPLORER_AST_AST_RTTI_H_

+ 0 - 92
explorer/ast/ast_rtti.txt

@@ -1,92 +0,0 @@
-# Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-# Exceptions. See /LICENSE for license information.
-# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-
-root class AstNode;
-abstract class Pattern : AstNode;
-  class AutoPattern : Pattern;
-  class VarPattern : Pattern;
-  class AddrPattern : Pattern;
-  class BindingPattern : Pattern;
-  class GenericBinding : Pattern;
-  class TuplePattern : Pattern;
-  class AlternativePattern : Pattern;
-  class ExpressionPattern : Pattern;
-abstract class Declaration : AstNode;
-  class NamespaceDeclaration : Declaration;
-  abstract class CallableDeclaration : Declaration;
-    class FunctionDeclaration : CallableDeclaration;
-    class DestructorDeclaration : CallableDeclaration;
-  class SelfDeclaration : Declaration;
-  class ClassDeclaration : Declaration;
-  class MixinDeclaration : Declaration;
-  class MixDeclaration : Declaration;
-  class ChoiceDeclaration : Declaration;
-  class VariableDeclaration : Declaration;
-  abstract class ConstraintTypeDeclaration : Declaration;
-    class InterfaceDeclaration : ConstraintTypeDeclaration;
-    class ConstraintDeclaration : ConstraintTypeDeclaration;
-  class InterfaceExtendsDeclaration : Declaration;
-  class InterfaceImplDeclaration : Declaration;
-  class AssociatedConstantDeclaration : Declaration;
-  class ImplDeclaration : Declaration;
-  class MatchFirstDeclaration : Declaration;
-  class AliasDeclaration : Declaration;
-class ImplBinding : AstNode;
-class AlternativeSignature : AstNode;
-abstract class Statement : AstNode;
-  class ExpressionStatement : Statement;
-  class Assign : Statement;
-  class IncrementDecrement : Statement;
-  class VariableDefinition : Statement;
-  class If : Statement;
-  abstract class Return : Statement;
-    class ReturnVar : Return;
-    class ReturnExpression : Return;
-  class Block : Statement;
-  class While : Statement;
-  class Break : Statement;
-  class Continue : Statement;
-  class Match : Statement;
-  class Continuation : Statement;
-  class Run : Statement;
-  class Await : Statement;
-  class For : Statement;
-abstract class Expression : AstNode;
-  class BoolTypeLiteral : Expression;
-  class BoolLiteral : Expression;
-  class CallExpression : Expression;
-  abstract class ConstantValueLiteral : Expression;
-    class FunctionTypeLiteral : ConstantValueLiteral;
-    class StructTypeLiteral : ConstantValueLiteral;
-    class ArrayTypeLiteral : ConstantValueLiteral;
-    class ValueLiteral : ConstantValueLiteral;
-  abstract class MemberAccessExpression : Expression;
-    class SimpleMemberAccessExpression : MemberAccessExpression;
-    class CompoundMemberAccessExpression : MemberAccessExpression;
-    class BaseAccessExpression : MemberAccessExpression;
-  class IndexExpression : Expression;
-  class IntTypeLiteral : Expression;
-  class ContinuationTypeLiteral : Expression;
-  class IntLiteral : Expression;
-  class OperatorExpression : Expression;
-  class StringLiteral : Expression;
-  class StringTypeLiteral : Expression;
-  class TupleLiteral : Expression;
-  class StructLiteral : Expression;
-  class TypeTypeLiteral : Expression;
-  class IdentifierExpression : Expression;
-  class DotSelfExpression : Expression;
-  class IntrinsicExpression : Expression;
-  class IfExpression : Expression;
-  class WhereExpression : Expression;
-  class BuiltinConvertExpression : Expression;
-  class UnimplementedExpression : Expression;
-abstract class WhereClause : AstNode;
-  class ImplsWhereClause : WhereClause;
-  class EqualsWhereClause : WhereClause;
-  class RewriteWhereClause : WhereClause;
-root class Element;
-  class NamedElement : Element;
-  class PositionalElement : Element;
-  class BaseElement : Element;

+ 14 - 2
explorer/ast/clone_context.cpp

@@ -4,6 +4,7 @@
 
 #include "explorer/ast/clone_context.h"
 
+#include "explorer/ast/ast_kinds.h"
 #include "explorer/ast/ast_node.h"
 #include "explorer/ast/value_transform.h"
 
@@ -17,8 +18,19 @@ auto CloneContext::CloneBase(Nonnull<const AstNode*> node)
                               : "node was remapped before it was cloned: ")
                       << *node;
 
-  // The implementation is generated in ast_rtti.cpp.
-  CloneImpl(*arena_, *this, *node, &it->second);
+  // TODO: Generate a Visit member on AstNode and use it here to avoid these
+  // macros.
+  switch (node->kind()) {
+#define IGNORE(C)
+#define CLONE_CASE(C)                                         \
+  case AstNodeKind::C:                                        \
+    arena_->New<C>(Arena::WriteAddressTo{&it->second}, *this, \
+                   static_cast<const C&>(*node));             \
+    break;
+    CARBON_AstNode_KINDS(IGNORE, CLONE_CASE)
+#undef CLONE_CASE
+#undef IGNORE
+  }
 
   // Cloning may have invalidated our iterator; redo lookup.
   auto* result = nodes_[node];

+ 1 - 0
explorer/ast/clone_context.h

@@ -17,6 +17,7 @@
 
 namespace Carbon {
 
+class AstNode;
 class Element;
 class Value;
 

+ 0 - 340
explorer/gen_rtti.py

@@ -1,340 +0,0 @@
-#!/usr/bin/env python3
-
-"""Generates C++ header to support LLVM-style RTTI for a class hierarchy.
-
-This script should be run through the //explorer:gen_rtti build target.
-
-# Background
-
-A C++ class hierarchy supported by this script consists of *abstract* classes,
-which can be inherited from but can't be instantiated, and *concrete* classes,
-which can be instantiated but can't be inherited from. Classes can inherit from
-at most one other class in the hierarchy; a class that doesn't inherit from
-any other class is called a *root* class, and it cannot be concrete.
-
-# Input format
-
-This script's input file declares every class in the hierarchy, and specifies
-the parent of each non-root class. The input file consists of comment lines
-starting with `#`, whitespace lines, and one `;`-terminated line for each class.
-The core of a line is `class` followed by the class name. `class` can be
-prefixed with `root` or `abstract` to specify the corresponding kind of class;
-if there is no prefix, the class is concrete. If the class is not a root class,
-the name is followed by `:` and then the name of the class it inherits from. A
-class cannot inherit from a class defined later in the file.
-
-For example:
-
-root class R;
-abstract class A : R;
-abstract class B : R;
-class C : A;
-class D : B;
-class E : A;
-
-# Output
-
-For each abstract class `Foo`, the generated header file will contain
-`enum class FooKind`, which has an enumerator for each concrete class derived
-from `Foo`, with a name that matches the concrete class name.
-
-For each non-root class `Foo` whose root class is `Root`, the generated header
-file will also contain a function `bool InheritsFromFoo(RootKind kind)`, which
-returns true if the value of `kind` corresponds to a class that is derived from
-`Foo`. This function can be used to implement `Foo::classof`.
-
-All enumerators that represent the same concrete class will have the same
-numeric value, so you can use `static_cast` to convert between the enum types
-for different classes that have a common root, so long as the enumerator value
-is present in both types. As a result, `InheritsFromFoo` can be used to
-determine whether casting to `FooKind` is safe.
-
-This also generates the dispatch function for the clone constructor, which
-behaves like a virtual function.
-"""
-
-__copyright__ = """
-Part of the Carbon Language project, under the Apache License v2.0 with LLVM
-Exceptions. See /LICENSE for license information.
-SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-"""
-
-import enum
-import re
-import sys
-from typing import Dict, List, Optional, Tuple
-
-
-class Class:
-    """Metadata about a class from the input file.
-
-    This consists of information
-
-    Attributes set at construction:
-      name: The class name.
-      kind: The class kind (root, abstract, or concrete)
-      ancestors: A list of Class objects representing the class's ancestors,
-        starting with the root and ending with the current class's parent.
-      _children: A list of Class objects representing the classes that are
-        derived directly from this one.
-
-    Attributes set by Finalize():
-      id (CONCRETE only): The class's numeric ID, which will become its
-        enumerator value in the generated C++ code.
-      id_range (ROOT and ABSTRACT only): A pair such that a Class
-        object `c` represents a concrete class derived from `self` if and only
-        if c.id >= self.id_range[0] and c.id < self.id_range[1].
-      leaves (ROOT only): A list of all concrete classes derived from this one,
-        indexed by their IDs.
-    """
-
-    class Kind(enum.Enum):
-        ROOT = enum.auto()
-        ABSTRACT = enum.auto()
-        CONCRETE = enum.auto()
-
-    def __init__(
-        self, name: str, kind: Kind, parent: Optional["Class"]
-    ) -> None:
-        self.name = name
-        self.kind = kind
-
-        assert (parent is None) == (kind == Class.Kind.ROOT)
-        self.ancestors: List[Class] = []
-        if parent is not None:
-            self.ancestors = parent.ancestors + [parent]
-
-        if self.kind == Class.Kind.CONCRETE:
-            self.id: Optional[int] = None
-        else:
-            self.id_range: Optional[Tuple[int, int]] = None
-            if self.kind == Class.Kind.ROOT:
-                self.leaves: List[Class] = []
-
-        if self.kind != Class.Kind.CONCRETE:
-            self._children: List[Class] = []
-
-        if parent:
-            parent._children.append(self)
-
-    def Parent(self) -> "Class":
-        """Returns this Class's parent."""
-        return self.ancestors[-1]
-
-    def Root(self) -> "Class":
-        """Returns the root Class of this hierarchy."""
-        if self.kind == Class.Kind.ROOT:
-            return self
-        else:
-            return self.ancestors[0]
-
-    def _RegisterLeaf(self, leaf: "Class") -> None:
-        """Records that `leaf` is derived from self.
-
-        Also recursively updates the parent of self. leaf.id must already be
-        populated, and leaves must be registered in order of ID. This operation
-        is idempotent.
-        """
-        already_visited = False
-        assert leaf.id is not None
-        if self.kind == Class.Kind.ROOT:
-            if leaf.id == len(self.leaves):
-                self.leaves.append(leaf)
-            else:
-                assert leaf.id + 1 == len(self.leaves)
-                assert self.leaves[leaf.id] == leaf
-                already_visited = True
-        if self.kind in [Class.Kind.ROOT, Class.Kind.ABSTRACT]:
-            if self not in leaf.ancestors:
-                sys.exit(
-                    f"{leaf.name} derived from {self.name}, but has a"
-                    + " different root"
-                )
-            if not self.id_range:
-                self.id_range = (leaf.id, leaf.id + 1)
-            elif self.id_range[1] == leaf.id:
-                self.id_range = (self.id_range[0], self.id_range[1] + 1)
-            else:
-                assert self.id_range[1] == leaf.id + 1
-                already_visited = True
-
-        if not already_visited:
-            if self.kind != Class.Kind.ROOT:
-                self.Parent()._RegisterLeaf(leaf)
-
-    def Finalize(self) -> None:
-        """Populates additional attributes for `self` and derived Classes.
-
-        Each Class can only be finalized once, after which no additional Classes
-        can be derived from it.
-        """
-        if self.kind == Class.Kind.CONCRETE:
-            self.id = len(self.Root().leaves)
-            self._RegisterLeaf(self)
-        else:
-            for child in self._children:
-                child.Finalize()
-
-
-_LINE_PATTERN = r"""(?P<prefix> \w*) \s*
-                 class \s+
-                 (?P<name> \w+)
-                 (?: \s*:\s* (?P<parent> \w+)
-                 )?
-                 ;$"""
-
-
-def main() -> None:
-    input_filename = sys.argv[1]
-    header_filename = sys.argv[2]
-    cpp_filename = sys.argv[3]
-    ast_headers = sys.argv[4:]
-
-    with open(input_filename) as file:
-        lines = file.readlines()
-
-    classes: Dict[str, Class] = {}
-    for line_num, line in enumerate(lines, 1):
-        if line.startswith("#") or line.strip() == "":
-            continue
-        match_result = re.match(_LINE_PATTERN, line.strip(), re.VERBOSE)
-        if not match_result:
-            sys.exit(f"Invalid format on line {line_num}")
-
-        prefix = match_result.group("prefix")
-        if prefix == "":
-            kind = Class.Kind.CONCRETE
-        elif prefix == "root":
-            kind = Class.Kind.ROOT
-        elif prefix == "abstract":
-            kind = Class.Kind.ABSTRACT
-        else:
-            sys.exit(f"Unrecognized class prefix '{prefix}' on line {line_num}")
-
-        parent = None
-        if match_result.group("parent"):
-            if kind == Class.Kind.ROOT:
-                sys.exit(f"Root class cannot have parent on line {line_num}")
-            parent_name = match_result.group("parent")
-            parent = classes[parent_name]
-            if not parent:
-                sys.exit(f"Unknown class '{parent_name}' on line {line_num}")
-            if parent.kind == Class.Kind.CONCRETE:
-                sys.exit(f"{parent.name} cannot be a parent on line {line_num}")
-        else:
-            if kind != Class.Kind.ROOT:
-                sys.exit(
-                    f"Non-root class must have a parent on line {line_num}"
-                )
-
-        classes[match_result.group("name")] = Class(
-            match_result.group("name"), kind, parent
-        )
-
-    for node in classes.values():
-        if node.kind == Class.Kind.ROOT:
-            node.Finalize()
-
-    header_file = open(header_filename, "w")
-    sys.stdout = header_file
-
-    print(f"// Generated from {input_filename} by explorer/gen_rtti.py\n")
-    trans_table = str.maketrans({"/": "_", ".": "_"})
-    guard_macro = input_filename.upper().translate(trans_table) + "_"
-    print(f"#ifndef {guard_macro}")
-    print(f"#define {guard_macro}")
-    print("\n#include <string_view>")
-    print("\nnamespace Carbon {\n")
-
-    for node in classes.values():
-        if node.kind != Class.Kind.CONCRETE:
-            assert node.id_range is not None
-            ids = range(node.id_range[0], node.id_range[1])
-            print(f"enum class {node.name}Kind {{")
-            for id in ids:
-                print(f"  {node.Root().leaves[id].name} = {id},")
-            print("};\n")
-
-            print(f"std::string_view {node.name}KindName({node.name}Kind k);\n")
-
-        if node.kind in [Class.Kind.ABSTRACT, Class.Kind.CONCRETE]:
-            print(
-                f"inline bool InheritsFrom{node.name}({node.Root().name}Kind"
-                + " kind) {"
-            )
-            if node.kind == Class.Kind.ABSTRACT:
-                assert node.id_range is not None
-                if node.id_range[0] == node.id_range[1]:
-                    print("  return false;")
-                else:
-                    range_begin = node.Root().leaves[node.id_range[0]].name
-                    print(
-                        f"  return kind >= {node.Root().name}Kind"
-                        + f"::{range_begin}"
-                    )
-                    if node.id_range[1] < len(node.Root().leaves):
-                        range_end = node.Root().leaves[node.id_range[1]].name
-                        print(
-                            f"      && kind < {node.Root().name}Kind"
-                            + f"::{range_end}"
-                        )
-                    print("      ;")
-            elif node.kind == Class.Kind.CONCRETE:
-                print(
-                    f"    return kind == {node.Root().name}Kind::{node.name};"
-                )
-            print("}\n")
-
-    print("class Arena;")
-    print("class AstNode;")
-    print("class CloneContext;")
-    print("void CloneImpl(Arena& arena, CloneContext& context,")
-    print("               const AstNode& node, AstNode** result);\n")
-
-    print("}  // namespace Carbon\n")
-    print(f"#endif  // {guard_macro}")
-
-    header_file.close()
-
-    cpp_file = open(cpp_filename, "w")
-    sys.stdout = cpp_file
-
-    print(f"// Generated from {input_filename} by explorer/gen_rtti.py\n")
-    for h in ast_headers:
-        print(f'#include "{h}"')
-    print("\nnamespace Carbon {\n")
-    for node in classes.values():
-        if node.kind != Class.Kind.CONCRETE:
-            assert node.id_range is not None
-            ids = range(node.id_range[0], node.id_range[1])
-            print(f"std::string_view {node.name}KindName({node.name}Kind k) {{")
-            print("  switch(k) {")
-            for id in ids:
-                name = node.Root().leaves[id].name
-                desc = " ".join(
-                    w.lower() for w in re.sub(r"([A-Z])", r" \1", name).split()
-                )
-                print(f'    case {node.name}Kind::{name}: return "{desc}";')
-            print("  }")
-            print("}\n")
-
-    print("void CloneImpl(Arena& arena, CloneContext& context,")
-    print("               const AstNode& node, AstNode** result) {")
-    print("  switch(node.kind()) {")
-    for node in classes.values():
-        if node.kind == Class.Kind.CONCRETE and node.Root().name == "AstNode":
-            print(f"    case AstNodeKind::{node.name}:")
-            print(
-                f"      return arena.New<{node.name}>("
-                + "Arena::WriteAddressTo{result}, context, "
-                + f"static_cast<const {node.name}&>(node));"
-            )
-    print("  }")
-    print("}\n")
-
-    print("}  // namespace Carbon\n")
-    cpp_file.close()
-
-
-if __name__ == "__main__":
-    main()

+ 4 - 5
explorer/interpreter/type_checker.cpp

@@ -2679,7 +2679,7 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
                                const ImplScope& impl_scope)
     -> ErrorOr<Success> {
   if (trace_stream_->is_enabled()) {
-    *trace_stream_ << "checking " << ExpressionKindName(e->kind()) << " " << *e;
+    *trace_stream_ << "checking " << e->kind() << " " << *e;
     *trace_stream_ << "\n";
   }
   if (e->is_type_checked()) {
@@ -4131,7 +4131,7 @@ auto TypeChecker::TypeCheckPattern(
     ImplScope& impl_scope, ValueCategory enclosing_value_category)
     -> ErrorOr<Success> {
   if (trace_stream_->is_enabled()) {
-    *trace_stream_ << "checking " << PatternKindName(p->kind()) << " " << *p;
+    *trace_stream_ << "checking " << p->kind() << " " << *p;
     if (expected) {
       *trace_stream_ << ", expecting " << **expected;
     }
@@ -4415,8 +4415,7 @@ auto TypeChecker::TypeCheckStmt(Nonnull<Statement*> s,
                                 const ImplScope& impl_scope)
     -> ErrorOr<Success> {
   if (trace_stream_->is_enabled()) {
-    *trace_stream_ << "checking " << StatementKindName(s->kind()) << " " << *s
-                   << "\n";
+    *trace_stream_ << "checking " << s->kind() << " " << *s << "\n";
   }
   switch (s->kind()) {
     case StatementKind::Match: {
@@ -5953,7 +5952,7 @@ auto TypeChecker::TypeCheckDeclaration(
     std::optional<Nonnull<const Declaration*>> enclosing_decl)
     -> ErrorOr<Success> {
   if (trace_stream_->is_enabled()) {
-    *trace_stream_ << "checking " << DeclarationKindName(d->kind()) << "\n";
+    *trace_stream_ << "checking " << d->kind() << "\n";
   }
   switch (d->kind()) {
     case DeclarationKind::NamespaceDeclaration: