From 90b6f5a22c0359852ef05d5ecf2c2618becad9dc Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Wed, 26 Feb 2025 17:00:10 -0800 Subject: [PATCH] Refactor NodeCategory for X-macros (#5029) Taking an approach similar to NameId in #5018 --- toolchain/parse/BUILD | 11 ++++ toolchain/parse/node_category.cpp | 37 +++++++++++ toolchain/parse/node_category.h | 91 ++++++++++++++++++++++++++++ toolchain/parse/node_kind.cpp | 31 ---------- toolchain/parse/node_kind.h | 54 +---------------- toolchain/parse/typed_nodes_test.cpp | 2 +- 6 files changed, 141 insertions(+), 85 deletions(-) create mode 100644 toolchain/parse/node_category.cpp create mode 100644 toolchain/parse/node_category.h diff --git a/toolchain/parse/BUILD b/toolchain/parse/BUILD index 8a8cb9dcf3a66..6f6e1cd3a1006 100644 --- a/toolchain/parse/BUILD +++ b/toolchain/parse/BUILD @@ -18,6 +18,16 @@ manifest( srcs = [":testdata"], ) +cc_library( + name = "node_category", + srcs = ["node_category.cpp"], + hdrs = ["node_category.h"], + deps = [ + "//common:ostream", + "@llvm-project//llvm:Support", + ], +) + cc_library( name = "node_kind", srcs = ["node_kind.cpp"], @@ -28,6 +38,7 @@ cc_library( ], textual_hdrs = ["node_kind.def"], deps = [ + ":node_category", "//common:check", "//common:enum_base", "//common:ostream", diff --git a/toolchain/parse/node_category.cpp b/toolchain/parse/node_category.cpp new file mode 100644 index 0000000000000..ecf0c833f731f --- /dev/null +++ b/toolchain/parse/node_category.cpp @@ -0,0 +1,37 @@ +// 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 "toolchain/parse/node_category.h" + +#include "llvm/ADT/StringExtras.h" + +namespace Carbon::Parse { + +// Returns a string form of the node category for printing. +static auto NodeCategoryToString(NodeCategory::RawEnumType category) + -> llvm::StringLiteral { +#define CARBON_NODE_CATEGORY_TO_STRING(Name) \ + case NodeCategory::Name: \ + return #Name; + + switch (category) { + CARBON_NODE_CATEGORY(CARBON_NODE_CATEGORY_TO_STRING) + CARBON_NODE_CATEGORY_TO_STRING(None) + } + +#undef CARBON_NODE_CATEGORY_TO_STRING +} + +auto NodeCategory::Print(llvm::raw_ostream& out) const -> void { + llvm::ListSeparator sep("|"); + auto value = value_; + do { + // The lowest set bit in the value, or 0 (`None`) if no bits are set. + auto lowest_bit = static_cast(value & -value); + out << sep << NodeCategoryToString(lowest_bit); + value &= ~lowest_bit; + } while (value); +} + +} // namespace Carbon::Parse diff --git a/toolchain/parse/node_category.h b/toolchain/parse/node_category.h new file mode 100644 index 0000000000000..fb67a4691cefd --- /dev/null +++ b/toolchain/parse/node_category.h @@ -0,0 +1,91 @@ +// 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_TOOLCHAIN_PARSE_NODE_CATEGORY_H_ +#define CARBON_TOOLCHAIN_PARSE_NODE_CATEGORY_H_ + +#include "common/ostream.h" +#include "llvm/ADT/BitmaskEnum.h" + +namespace Carbon::Parse { + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +// An X-macro for node categories. Uses should look like: +// +// #define CARBON_NODE_CATEGORY_FOR_XYZ(Name) ... +// CARBON_NODE_CATEGORY(CARBON_NODE_CATEGORY_FOR_XYZ) +// #undef CARBON_NODE_CATEGORY_FOR_XYZ +#define CARBON_NODE_CATEGORY(X) \ + X(Decl) \ + X(Expr) \ + X(ImplAs) \ + X(IntConst) \ + X(MemberExpr) \ + X(MemberName) \ + X(Modifier) \ + X(NonExprIdentifierName) \ + X(PackageName) \ + X(Pattern) \ + X(Requirement) \ + X(Statement) + +// Represents a set of keyword modifiers, using a separate bit per modifier. +class NodeCategory : public Printable { + private: + // Use an enum to get incremental bit shifts. + enum class BitShift : uint8_t { +#define CARBON_NODE_CATEGORY_FOR_BIT_SHIFT(Name) Name, + CARBON_NODE_CATEGORY(CARBON_NODE_CATEGORY_FOR_BIT_SHIFT) +#undef CARBON_NODE_CATEGORY_FOR_BIT_SHIFT + + // For `LLVM_MARK_AS_BITMASK_ENUM`. + LargestValueMarker, + }; + + public: + // Provide values as an enum. This doesn't expose these as NodeCategory + // instances just due to the duplication of declarations that would cause. + // + // We expect this to grow, so are using a bigger size than needed. + // NOLINTNEXTLINE(performance-enum-size) + enum RawEnumType : uint32_t { +#define CARBON_NODE_CATEGORY_FOR_BIT_MASK(Name) \ + Name = 1 << static_cast(BitShift::Name), + CARBON_NODE_CATEGORY(CARBON_NODE_CATEGORY_FOR_BIT_MASK) +#undef CARBON_NODE_CATEGORY_FOR_BIT_MASK + // If you add a new category here, also add it to the Print function. + None = 0, + + LLVM_MARK_AS_BITMASK_ENUM( + /*LargestValue=*/1 + << (static_cast(BitShift::LargestValueMarker) - 1)) + }; + + // Support implicit conversion so that the difference with the member enum is + // opaque. + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr NodeCategory(RawEnumType value) : value_(value) {} + + // Returns true if there's a non-empty set intersection. + constexpr auto HasAnyOf(NodeCategory other) const -> bool { + return value_ & other.value_; + } + + // Returns the set inverse. + constexpr auto operator~() const -> NodeCategory { return ~value_; } + + friend auto operator==(NodeCategory lhs, NodeCategory rhs) -> bool { + return lhs.value_ == rhs.value_; + } + + auto Print(llvm::raw_ostream& out) const -> void; + + private: + RawEnumType value_; +}; + +} // namespace Carbon::Parse + +#endif // CARBON_TOOLCHAIN_PARSE_NODE_CATEGORY_H_ diff --git a/toolchain/parse/node_kind.cpp b/toolchain/parse/node_kind.cpp index 9dd8e037dcd20..e60a28aef80ac 100644 --- a/toolchain/parse/node_kind.cpp +++ b/toolchain/parse/node_kind.cpp @@ -9,37 +9,6 @@ namespace Carbon::Parse { -auto NodeCategory::Print(llvm::raw_ostream& out) const -> void { - llvm::ListSeparator sep("|"); - auto value = value_; - do { - // The lowest set bit in the value, or 0 (`None`) if no bits are set. - auto lowest_bit = static_cast(value & -value); - switch (lowest_bit) { -#define CARBON_NODE_CATEGORY(Name) \ - case NodeCategory::Name: { \ - out << sep << #Name; \ - break; \ - } - CARBON_NODE_CATEGORY(Decl); - CARBON_NODE_CATEGORY(Expr); - CARBON_NODE_CATEGORY(ImplAs); - CARBON_NODE_CATEGORY(MemberExpr); - CARBON_NODE_CATEGORY(MemberName); - CARBON_NODE_CATEGORY(Modifier); - CARBON_NODE_CATEGORY(Pattern); - CARBON_NODE_CATEGORY(Statement); - CARBON_NODE_CATEGORY(IntConst); - CARBON_NODE_CATEGORY(Requirement); - CARBON_NODE_CATEGORY(NonExprIdentifierName); - CARBON_NODE_CATEGORY(PackageName); - CARBON_NODE_CATEGORY(None); -#undef CARBON_NODE_CATEGORY - } - value &= ~lowest_bit; - } while (value); -} - CARBON_DEFINE_ENUM_CLASS_NAMES(NodeKind) = { #define CARBON_PARSE_NODE_KIND(Name) CARBON_ENUM_CLASS_NAME_STRING(Name) #include "toolchain/parse/node_kind.def" diff --git a/toolchain/parse/node_kind.h b/toolchain/parse/node_kind.h index 1ad2abbe056d5..cee4259f98916 100644 --- a/toolchain/parse/node_kind.h +++ b/toolchain/parse/node_kind.h @@ -9,63 +9,11 @@ #include "common/enum_base.h" #include "common/ostream.h" -#include "llvm/ADT/BitmaskEnum.h" #include "toolchain/lex/token_kind.h" +#include "toolchain/parse/node_category.h" namespace Carbon::Parse { -LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); - -// Represents a set of keyword modifiers, using a separate bit per modifier. -class NodeCategory : public Printable { - public: - // Provide values as an enum. This doesn't expose these as NodeCategory - // instances just due to the duplication of declarations that would cause. - // - // We expect this to grow, so are using a bigger size than needed. - // NOLINTNEXTLINE(performance-enum-size) - enum RawEnumType : uint32_t { - Decl = 1 << 0, - Expr = 1 << 1, - ImplAs = 1 << 2, - MemberExpr = 1 << 3, - MemberName = 1 << 4, - Modifier = 1 << 5, - Pattern = 1 << 6, - Statement = 1 << 7, - IntConst = 1 << 8, - Requirement = 1 << 9, - NonExprIdentifierName = 1 << 10, - PackageName = 1 << 11, - // If you add a new category here, also add it to the Print function. - None = 0, - - LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/PackageName) - }; - - // Support implicit conversion so that the difference with the member enum is - // opaque. - // NOLINTNEXTLINE(google-explicit-constructor) - constexpr NodeCategory(RawEnumType value) : value_(value) {} - - // Returns true if there's a non-empty set intersection. - constexpr auto HasAnyOf(NodeCategory other) const -> bool { - return value_ & other.value_; - } - - // Returns the set inverse. - constexpr auto operator~() const -> NodeCategory { return ~value_; } - - friend auto operator==(NodeCategory lhs, NodeCategory rhs) -> bool { - return lhs.value_ == rhs.value_; - } - - auto Print(llvm::raw_ostream& out) const -> void; - - private: - RawEnumType value_; -}; - CARBON_DEFINE_RAW_ENUM_CLASS(NodeKind, uint8_t) { #define CARBON_PARSE_NODE_KIND(Name) CARBON_RAW_ENUM_ENUMERATOR(Name) #include "toolchain/parse/node_kind.def" diff --git a/toolchain/parse/typed_nodes_test.cpp b/toolchain/parse/typed_nodes_test.cpp index 06a9401b336da..daf7570401cb6 100644 --- a/toolchain/parse/typed_nodes_test.cpp +++ b/toolchain/parse/typed_nodes_test.cpp @@ -262,7 +262,7 @@ Aggregate [^:]*: success // Use Regex matching to avoid hard-coding the result of `typeinfo(T).name()`. EXPECT_THAT(err2.message(), testing::MatchesRegex( R"Trace(Aggregate [^:]*: begin -NodeIdInCategory MemberExpr\|MemberName\|IntConst: kind IdentifierNameNotBeforeParams consumed +NodeIdInCategory IntConst\|MemberExpr\|MemberName: kind IdentifierNameNotBeforeParams consumed NodeIdInCategory Expr: kind PointerMemberAccessExpr consumed Aggregate [^:]*: success )Trace"));