From 807ef98758572b71b5eef97f408c768f5a5163e9 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 30 Mar 2023 14:16:44 -0700 Subject: [PATCH] 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 --- .clang-format | 1 + common/enum_base.h | 38 ++- explorer/BUILD | 5 - explorer/ast/BUILD | 56 ++--- explorer/ast/README.md | 8 +- explorer/ast/ast_kinds.h | 164 +++++++++++++ explorer/ast/ast_rtti.cpp | 20 ++ explorer/ast/ast_rtti.h | 99 ++++++++ explorer/ast/ast_rtti.txt | 92 ------- explorer/ast/clone_context.cpp | 16 +- explorer/ast/clone_context.h | 1 + explorer/gen_rtti.py | 340 -------------------------- explorer/interpreter/type_checker.cpp | 9 +- 13 files changed, 359 insertions(+), 490 deletions(-) create mode 100644 explorer/ast/ast_kinds.h create mode 100644 explorer/ast/ast_rtti.cpp create mode 100644 explorer/ast/ast_rtti.h delete mode 100644 explorer/ast/ast_rtti.txt delete mode 100755 explorer/gen_rtti.py diff --git a/.clang-format b/.clang-format index 32deb344b877..a97d54235429 100644 --- a/.clang-format +++ b/.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'] diff --git a/common/enum_base.h b/common/enum_base.h index e4dd1a829c86..3ffbaa5bfdc4 100644 --- a/common/enum_base.h +++ b/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(this)->name(); + out << static_cast(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 // 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::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::name() \ - const->llvm::StringRef { \ + CARBON_ENUM_NAME_FUNCTION(EnumClassName) { \ return names[static_cast(value_)]; \ } \ \ diff --git a/explorer/BUILD b/explorer/BUILD index f6ca898cbd2e..14416596b468 100644 --- a/explorer/BUILD +++ b/explorer/BUILD @@ -41,8 +41,3 @@ cc_binary( "@llvm-project//llvm:Support", ], ) - -py_binary( - name = "gen_rtti", - srcs = ["gen_rtti.py"], -) diff --git a/explorer/ast/BUILD b/explorer/ast/BUILD index 5c27faf06e41..6b9662ff4cce 100644 --- a/explorer/ast/BUILD +++ b/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", diff --git a/explorer/ast/README.md b/explorer/ast/README.md index dd65ac01c55a..6089a9dd3a03 100644 --- a/explorer/ast/README.md +++ b/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 diff --git a/explorer/ast/ast_kinds.h b/explorer/ast/ast_kinds.h new file mode 100644 index 000000000000..16180e231c9c --- /dev/null +++ b/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_ diff --git a/explorer/ast/ast_rtti.cpp b/explorer/ast/ast_rtti.cpp new file mode 100644 index 000000000000..6c718ac9b151 --- /dev/null +++ b/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(*this)).name(); \ + } +CARBON_AST_FOR_EACH_ABSTRACT_CLASS(DEFINE_NAME_FUNCTION) + +} // namespace Carbon diff --git a/explorer/ast/ast_rtti.h b/explorer/ast/ast_rtti.h new file mode 100644 index 000000000000..eb458d73a28e --- /dev/null +++ b/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 + /*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 \ + 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 { \ + using C##KindTemplate::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_ diff --git a/explorer/ast/ast_rtti.txt b/explorer/ast/ast_rtti.txt deleted file mode 100644 index a8d94f6e7eac..000000000000 --- a/explorer/ast/ast_rtti.txt +++ /dev/null @@ -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; diff --git a/explorer/ast/clone_context.cpp b/explorer/ast/clone_context.cpp index 5f2a33398c98..a7c86814cfb3 100644 --- a/explorer/ast/clone_context.cpp +++ b/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 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(Arena::WriteAddressTo{&it->second}, *this, \ + static_cast(*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]; diff --git a/explorer/ast/clone_context.h b/explorer/ast/clone_context.h index 8a6eb0fa1bff..a77158ce9ea5 100644 --- a/explorer/ast/clone_context.h +++ b/explorer/ast/clone_context.h @@ -17,6 +17,7 @@ namespace Carbon { +class AstNode; class Element; class Value; diff --git a/explorer/gen_rtti.py b/explorer/gen_rtti.py deleted file mode 100755 index 0574b1155516..000000000000 --- a/explorer/gen_rtti.py +++ /dev/null @@ -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 \w*) \s* - class \s+ - (?P \w+) - (?: \s*:\s* (?P \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 ") - 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(node));" - ) - print(" }") - print("}\n") - - print("} // namespace Carbon\n") - cpp_file.close() - - -if __name__ == "__main__": - main() diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 4b1da5a54487..eeba1a593071 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -2679,7 +2679,7 @@ auto TypeChecker::TypeCheckExp(Nonnull e, const ImplScope& impl_scope) -> ErrorOr { 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 { 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 s, const ImplScope& impl_scope) -> ErrorOr { 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> enclosing_decl) -> ErrorOr { if (trace_stream_->is_enabled()) { - *trace_stream_ << "checking " << DeclarationKindName(d->kind()) << "\n"; + *trace_stream_ << "checking " << d->kind() << "\n"; } switch (d->kind()) { case DeclarationKind::NamespaceDeclaration: