diff --git a/.clang-format b/.clang-format index edc1fccff31b..a1cb60a4e88a 100644 --- a/.clang-format +++ b/.clang-format @@ -14,5 +14,10 @@ InsertBraces: 'true' PointerAlignment: Left # We abuse control macros for formatting other kinds of macros. SpaceBeforeParens: ControlStatementsExceptControlMacros -IfMacros: ['CARBON_DEFINE_RAW_ENUM_CLASS', 'CARBON_KIND_SWITCH'] +IfMacros: + [ + 'CARBON_DEFINE_RAW_ENUM_CLASS', + 'CARBON_DEFINE_RAW_ENUM_MASK', + 'CARBON_KIND_SWITCH', + ] StatementMacros: ['ABSTRACT'] diff --git a/common/BUILD b/common/BUILD index 45849a014d59..98457b685416 100644 --- a/common/BUILD +++ b/common/BUILD @@ -159,6 +159,15 @@ cc_test( ], ) +cc_library( + name = "enum_mask_base", + hdrs = ["enum_mask_base.h"], + deps = [ + ":enum_base", + "@llvm-project//llvm:Support", + ], +) + cc_library( name = "error", hdrs = ["error.h"], diff --git a/common/enum_base.h b/common/enum_base.h index 1fdc87f7b190..d4053ec2f8d2 100644 --- a/common/enum_base.h +++ b/common/enum_base.h @@ -165,6 +165,10 @@ class EnumBase : public Printable { } private: + template + friend class EnumMaskBase; + RawEnumType value_; }; @@ -175,21 +179,24 @@ class EnumBase : public Printable { // raw enum class. #define CARBON_DEFINE_RAW_ENUM_CLASS(EnumClassName, UnderlyingType) \ namespace Internal { \ - extern const llvm::StringLiteral EnumClassName##Names[]; \ - enum class EnumClassName##RawEnum : UnderlyingType; \ + struct EnumClassName##Data { \ + static const llvm::StringLiteral Names[]; \ + enum class RawEnum : UnderlyingType; \ + }; \ } \ - enum class Internal::EnumClassName##RawEnum : UnderlyingType + enum class Internal::EnumClassName##Data::RawEnum : UnderlyingType -// In CARBON_DEFINE_RAW_ENUM_CLASS block, use this to generate each enumerator. +// In the `CARBON_DEFINE_RAW_ENUM_CLASS` block, use this to generate each +// enumerator. #define CARBON_RAW_ENUM_ENUMERATOR(Name) Name, // 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 +#define CARBON_ENUM_BASE(EnumClassName) \ + ::Carbon::Internal::EnumBase // Use this within the Carbon enum class body to generate named constant // declarations for each value. @@ -208,7 +215,7 @@ class EnumBase : public Printable { // `clang-format` has a bug with spacing around `->` returns in macros. See // https://bugs.llvm.org/show_bug.cgi?id=48320 for details. #define CARBON_DEFINE_ENUM_CLASS_NAMES(EnumClassName) \ - constexpr llvm::StringLiteral Internal::EnumClassName##Names[] + constexpr llvm::StringLiteral Internal::EnumClassName##Data::Names[] // Use this within the names array initializer to generate a string for each // name. diff --git a/common/enum_mask_base.h b/common/enum_mask_base.h new file mode 100644 index 000000000000..bc5279cf0a29 --- /dev/null +++ b/common/enum_mask_base.h @@ -0,0 +1,158 @@ +// 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_COMMON_ENUM_MASK_BASE_H_ +#define CARBON_COMMON_ENUM_MASK_BASE_H_ + +#include + +#include "common/enum_base.h" +#include "llvm/ADT/StringExtras.h" + +namespace Carbon::Internal { + +// CRTP-style base class similar to `EnumBase`, but supporting mask enums. +// +// Users must be in the `Carbon` namespace and should look like the following. +// +// In `my_kind.h`: +// ``` +// #define CARBON_MY_KIND(X) \ +// X(Enumerator1) \ +// X(Enumerator2) \ +// X(Enumerator3) \ +// ... +// +// CARBON_DEFINE_RAW_ENUM_MASK(MyKind) { +// CARBON_MY_KIND(CARBON_RAW_ENUM_MASK_ENUMERATOR) +// }; +// +// class MyKind : public CARBON_ENUM_MASK_BASE(MyKind) { +// public: +// CARBON_MY_KIND(CARBON_ENUM_MASK_CONSTANT_DECL) +// +// // Plus, anything else you wish to include. +// }; +// +// #define CARBON_MY_KIND_WITH_TYPE(X) \ +// CARBON_ENUM_MASK_CONSTANT_DEFINITION(MyKind, X) +// CARBON_MY_KIND(CARBON_MY_KIND_WITH_TYPE) +// #undef CARBON_MY_KIND_WITH_TYPE +// ``` +// +// In `my_kind.cpp`: +// ``` +// CARBON_DEFINE_ENUM_MASK_NAMES(MyKind) = { +// CARBON_MY_KIND(CARBON_ENUM_MASK_NAME_STRING)}; +// ``` +template +class EnumMaskBase : public EnumBase { + public: + // Provide a standard `None`. + // + // This uses a `&` to trigger slightly different instantiation behaviors in + // Clang. For context on why this is needed, see http://wg21.link/CWG2800. + // NOLINTNEXTLINE(readability-identifier-naming) + static const DerivedT& None; + + // Returns true if there's a non-empty set intersection. + constexpr auto HasAnyOf(DerivedT other) const -> bool { + return !(*this & other).empty(); + } + + // Adds entries to the mask. + auto Add(DerivedT other) -> void { *this = *this | other; } + + // Removes entries from the mask. + auto Remove(DerivedT other) -> void { *this = *this & ~other; } + + constexpr auto operator|(DerivedT other) const -> DerivedT { + return DerivedT::FromInt(this->AsInt() | other.AsInt()); + } + + constexpr auto operator&(DerivedT other) const -> DerivedT { + return DerivedT::FromInt(this->AsInt() & other.AsInt()); + } + + constexpr auto operator~() const -> DerivedT { + return DerivedT::FromInt(~this->AsInt()); + } + + constexpr auto empty() const -> bool { return this->AsInt() == 0; } + + // Returns the name of this value. Requires it to be a single value, not + // combined. + // + // This method will be automatically defined using the static `names` string + // table in the base class, which is in turn will be populated for each + // derived type using the macro helpers in this file. + // + // This shadows EnumBase::name. + auto name() const -> llvm::StringRef { + CARBON_CHECK(std::has_single_bit(this->AsInt()), "Not a single bit: {0}", + this->AsInt()); + return Names[std::bit_width(this->AsInt())]; + } + + // Prints this value as a `|`-separated list of mask entries, or `None`. + // + // This shadows EnumBase::Print. + auto Print(llvm::raw_ostream& out) const -> void { + int value = this->AsInt(); + if (value == 0) { + out << "None"; + return; + } + llvm::ListSeparator sep("|"); + for (int bit = 0; value != 0; value >>= 1, ++bit) { + if (value & 1) { + out << sep << Names[bit]; + } + } + } +}; + +template +constexpr const DerivedT& EnumMaskBase::None = + DerivedT::FromInt(0); + +} // namespace Carbon::Internal + +// Use this before defining a class that derives from `EnumBase` to begin the +// definition of the raw `enum class`. It should be followed by the body of that +// raw enum class. +#define CARBON_DEFINE_RAW_ENUM_MASK(EnumMaskName, UnderlyingType) \ + namespace Internal { \ + struct EnumMaskName##Data { \ + static const llvm::StringLiteral Names[]; \ + /* For bit shifts, track the initial counter value. This will increment on \ + * each enum entry. */ \ + static constexpr uint64_t BitShiftCounter = __COUNTER__ + 1; \ + enum class RawEnum : UnderlyingType; \ + }; \ + } \ + enum class Internal::EnumMaskName##Data::RawEnum : UnderlyingType + +// In the `CARBON_DEFINE_RAW_ENUM_MASK` block, use this to generate each +// enumerator. +#define CARBON_RAW_ENUM_MASK_ENUMERATOR(Name) \ + Name = 1 << (__COUNTER__ - BitShiftCounter), + +// Use this to compute the `Internal::EnumMaskBase` specialization for a Carbon +// enum mask. It both computes the name of the raw enum and ensures all the +// namespaces are correct. +#define CARBON_ENUM_MASK_BASE(EnumMaskName) \ + ::Carbon::Internal::EnumMaskBase + +// Constants and names are declared equivalently as to `EnumBase`. +#define CARBON_ENUM_MASK_CONSTANT_DECL(Name) CARBON_ENUM_CONSTANT_DECL(Name) +#define CARBON_ENUM_MASK_CONSTANT_DEFINITION(EnumMaskName, Name) \ + CARBON_ENUM_CONSTANT_DEFINITION(EnumMaskName, Name) +#define CARBON_DEFINE_ENUM_MASK_NAMES(EnumMaskName) \ + CARBON_DEFINE_ENUM_CLASS_NAMES(EnumMaskName) +#define CARBON_ENUM_MASK_NAME_STRING(Name) CARBON_ENUM_CLASS_NAME_STRING(Name) + +#endif // CARBON_COMMON_ENUM_MASK_BASE_H_ diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 4b036566a070..5c7ea336c981 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -42,6 +42,7 @@ cc_library( "inst.cpp", "inst_block_stack.cpp", "interface.cpp", + "keyword_modifier_set.cpp", "literal.cpp", "member_access.cpp", "merge.cpp", @@ -118,6 +119,7 @@ cc_library( "//common:check", "//common:concepts", "//common:emplace_by_calling", + "//common:enum_mask_base", "//common:find", "//common:growing_range", "//common:map", diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index e7a0e5dae4e3..85ff7d28aec4 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -773,7 +773,7 @@ static auto CanUseValueOfInitializer(const SemIR::File& sem_ir, // of an expression of the given category. static auto CanAddQualifiers(SemIR::TypeQualifiers quals, SemIR::ExprCategory cat) -> bool { - if (HasTypeQualifier(quals, SemIR::TypeQualifiers::MaybeUnformed) && + if (quals.HasAnyOf(SemIR::TypeQualifiers::MaybeUnformed) && !SemIR::IsRefCategory(cat)) { // `MaybeUnformed(T)` may have a different value representation or // initializing representation from `T`, so only allow it to be added for a @@ -794,13 +794,13 @@ static auto CanAddQualifiers(SemIR::TypeQualifiers quals, static auto CanRemoveQualifiers(SemIR::TypeQualifiers quals, SemIR::ExprCategory cat, bool allow_unsafe) -> bool { - if (HasTypeQualifier(quals, SemIR::TypeQualifiers::Const) && !allow_unsafe && + if (quals.HasAnyOf(SemIR::TypeQualifiers::Const) && !allow_unsafe && SemIR::IsRefCategory(cat)) { // Removing `const` is an unsafe conversion for a reference expression. return false; } - if (HasTypeQualifier(quals, SemIR::TypeQualifiers::Partial) && + if (quals.HasAnyOf(SemIR::TypeQualifiers::Partial) && (!allow_unsafe || cat == SemIR::ExprCategory::Initializing)) { // TODO: Allow removing `partial` for initializing expressions as a safe // conversion. `PerformBuiltinConversion` will need to initialize the vptr @@ -808,7 +808,7 @@ static auto CanRemoveQualifiers(SemIR::TypeQualifiers quals, return false; } - if (HasTypeQualifier(quals, SemIR::TypeQualifiers::MaybeUnformed) && + if (quals.HasAnyOf(SemIR::TypeQualifiers::MaybeUnformed) && (!allow_unsafe || cat == SemIR::ExprCategory::Initializing)) { // As an unsafe conversion, `MaybeUnformed` can be removed from a value or // reference expression. diff --git a/toolchain/check/generic.cpp b/toolchain/check/generic.cpp index 2c704e2f7721..9769e4ca4068 100644 --- a/toolchain/check/generic.cpp +++ b/toolchain/check/generic.cpp @@ -23,6 +23,9 @@ namespace Carbon::Check { +CARBON_DEFINE_ENUM_MASK_NAMES(DependentInstKind) = { + CARBON_DEPENDENT_INST_KIND(CARBON_ENUM_MASK_NAME_STRING)}; + static auto MakeSelfSpecificId(Context& context, SemIR::GenericId generic_id) -> SemIR::SpecificId; @@ -302,14 +305,14 @@ auto AttachDependentInstToCurrentGeneric(Context& context, if (context.generic_region_stack().Empty()) { // This should only happen for `*Decl` instructions, never for template // actions. - CARBON_CHECK((dep_kind & DependentInst::Template) == DependentInst::None); + CARBON_CHECK(!dep_kind.HasAnyOf(DependentInstKind::Template)); return; } context.generic_region_stack().AddDependentInst(dependent_inst.inst_id); // If the type is symbolic, replace it with a type specific to this generic. - if ((dep_kind & DependentInst::SymbolicType) != DependentInst::None) { + if (dep_kind.HasAnyOf(DependentInstKind::SymbolicType)) { auto inst = context.insts().Get(inst_id); auto type_id = AddGenericTypeToEvalBlock(context, SemIR::LocId(inst_id), inst.type_id()); @@ -327,7 +330,7 @@ auto AttachDependentInstToCurrentGeneric(Context& context, // we'll need to evaluate this instruction when forming the specific. Update // the constant value of the instruction to refer to the result of that // eventual evaluation. - if ((dep_kind & DependentInst::SymbolicConstant) != DependentInst::None) { + if (dep_kind.HasAnyOf(DependentInstKind::SymbolicConstant)) { // Update the constant value to refer to this generic. context.constant_values().Set( inst_id, AddGenericConstantToEvalBlock(context, inst_id)); @@ -335,7 +338,7 @@ auto AttachDependentInstToCurrentGeneric(Context& context, // If the instruction is a template action, add it directly to this position // in the eval block. - if ((dep_kind & DependentInst::Template) != DependentInst::None) { + if (dep_kind.HasAnyOf(DependentInstKind::Template)) { AddTemplateActionToEvalBlock(context, inst_id); } } diff --git a/toolchain/check/generic.h b/toolchain/check/generic.h index feaa6245b3fa..cb86a3def31d 100644 --- a/toolchain/check/generic.h +++ b/toolchain/check/generic.h @@ -5,15 +5,13 @@ #ifndef CARBON_TOOLCHAIN_CHECK_GENERIC_H_ #define CARBON_TOOLCHAIN_CHECK_GENERIC_H_ -#include "llvm/ADT/BitmaskEnum.h" +#include "common/enum_mask_base.h" #include "toolchain/check/context.h" #include "toolchain/sem_ir/entity_with_params_base.h" #include "toolchain/sem_ir/ids.h" namespace Carbon::Check { -LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); - // Start processing a declaration or definition that might be a generic entity. auto StartGenericDecl(Context& context) -> void; @@ -21,22 +19,33 @@ auto StartGenericDecl(Context& context) -> void; auto StartGenericDefinition(Context& context, SemIR::GenericId generic_id) -> void; +#define CARBON_DEPENDENT_INST_KIND(X) \ + /* The type of the instruction depends on a checked generic parameter. */ \ + X(SymbolicType) \ + /* The constant value of the instruction depends on a checked generic \ + * parameter. */ \ + X(SymbolicConstant) \ + X(Template) + +CARBON_DEFINE_RAW_ENUM_MASK(DependentInstKind, uint8_t) { + CARBON_DEPENDENT_INST_KIND(CARBON_RAW_ENUM_MASK_ENUMERATOR) +}; + +// Represents a set of keyword modifiers, using a separate bit per modifier. +class DependentInstKind : public CARBON_ENUM_MASK_BASE(DependentInstKind) { + public: + CARBON_DEPENDENT_INST_KIND(CARBON_ENUM_MASK_CONSTANT_DECL) +}; + +#define CARBON_DEPENDENT_INST_KIND_WITH_TYPE(X) \ + CARBON_ENUM_MASK_CONSTANT_DEFINITION(DependentInstKind, X) +CARBON_DEPENDENT_INST_KIND(CARBON_DEPENDENT_INST_KIND_WITH_TYPE) +#undef CARBON_DEPENDENT_INST_KIND_WITH_TYPE + // An instruction that depends on a generic parameter in some way. struct DependentInst { - // Ways in which an instruction can depend on a generic parameter. - enum Kind : int8_t { - None = 0x0, - // The type of the instruction depends on a checked generic parameter. - SymbolicType = 0x1, - // The constant value of the instruction depends on a checked generic - // parameter. - SymbolicConstant = 0x2, - Template = 0x4, - LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/Template) - }; - SemIR::InstId inst_id; - Kind kind; + DependentInstKind kind; }; // Attach a dependent instruction to the current generic, updating its type and diff --git a/toolchain/check/inst.cpp b/toolchain/check/inst.cpp index fdb2dd3b88ce..6c874553fb67 100644 --- a/toolchain/check/inst.cpp +++ b/toolchain/check/inst.cpp @@ -18,13 +18,13 @@ namespace Carbon::Check { // any applicable instruction lists. static auto FinishInst(Context& context, SemIR::InstId inst_id, SemIR::Inst inst) -> void { - DependentInst::Kind dep_kind = DependentInst::None; + DependentInstKind dep_kind = DependentInstKind::None; // If the instruction has a symbolic constant type, track that we need to // substitute into it. if (context.constant_values().DependsOnGenericParameter( context.types().GetConstantId(inst.type_id()))) { - dep_kind |= DependentInst::SymbolicType; + dep_kind.Add(DependentInstKind::SymbolicType); } // If the instruction has a constant value, compute it. @@ -37,7 +37,7 @@ static auto FinishInst(Context& context, SemIR::InstId inst_id, // If the constant value is symbolic, track that we need to substitute into // it. if (context.constant_values().DependsOnGenericParameter(const_id)) { - dep_kind |= DependentInst::SymbolicConstant; + dep_kind.Add(DependentInstKind::SymbolicConstant); } } @@ -48,7 +48,7 @@ static auto FinishInst(Context& context, SemIR::InstId inst_id, "Use AddDependentActionInst to add an action instruction"); // Keep track of dependent instructions. - if (dep_kind != DependentInst::None) { + if (!dep_kind.empty()) { AttachDependentInstToCurrentGeneric(context, {.inst_id = inst_id, .kind = dep_kind}); } @@ -86,7 +86,7 @@ auto AddDependentActionInst(Context& context, // Register the instruction to be added to the eval block. AttachDependentInstToCurrentGeneric( - context, {.inst_id = inst_id, .kind = DependentInst::Template}); + context, {.inst_id = inst_id, .kind = DependentInstKind::Template}); return inst_id; } diff --git a/toolchain/check/keyword_modifier_set.cpp b/toolchain/check/keyword_modifier_set.cpp new file mode 100644 index 000000000000..091ba97ee822 --- /dev/null +++ b/toolchain/check/keyword_modifier_set.cpp @@ -0,0 +1,12 @@ +// 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/check/keyword_modifier_set.h" + +namespace Carbon::Check { + +CARBON_DEFINE_ENUM_MASK_NAMES(KeywordModifierSet) = { + CARBON_KEYWORD_MODIFIER_SET(CARBON_ENUM_MASK_NAME_STRING)}; + +} // namespace Carbon::Check diff --git a/toolchain/check/keyword_modifier_set.h b/toolchain/check/keyword_modifier_set.h index c476a6b0aa93..bfabe5625f79 100644 --- a/toolchain/check/keyword_modifier_set.h +++ b/toolchain/check/keyword_modifier_set.h @@ -7,75 +7,57 @@ #include -#include "llvm/ADT/BitmaskEnum.h" +#include "common/enum_mask_base.h" #include "toolchain/sem_ir/name_scope.h" namespace Carbon::Check { -LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); - // The order of modifiers. Each of these corresponds to a group on // KeywordModifierSet, and can be used as an array index. enum class ModifierOrder : int8_t { Access, Extern, Extend, Decl, Last = Decl }; +// A single X-macro to cover modifier groups. These are split out to make groups +// clearer. +#define CARBON_KEYWORD_MODIFIER_SET(X) \ + /* At most one of these access modifiers allowed for a given declaration, \ + * and if present it must be first. */ \ + X(Private) \ + X(Protected) \ + \ + /* Extern is standalone. */ \ + X(Extern) \ + \ + /* Extend can be combined with Final, but no others in the group below. */ \ + X(Extend) \ + \ + /* At most one of these declaration modifiers allowed for a given \ + * declaration. */ \ + X(Abstract) \ + X(Base) \ + X(Default) \ + X(Export) \ + X(Final) \ + X(Impl) \ + X(Returned) \ + X(Virtual) + +// We expect this to grow, so are using a bigger size than needed. +CARBON_DEFINE_RAW_ENUM_MASK(KeywordModifierSet, uint32_t) { + CARBON_KEYWORD_MODIFIER_SET(CARBON_RAW_ENUM_MASK_ENUMERATOR) +}; + // Represents a set of keyword modifiers, using a separate bit per modifier. -class KeywordModifierSet { +class KeywordModifierSet : public CARBON_ENUM_MASK_BASE(KeywordModifierSet) { public: - // Provide values as an enum. This doesn't expose these as KeywordModifierSet - // instances just due to the duplication of declarations that would cause. - // - // We expect this to grow, so are using a bigger size than needed. - enum RawEnumType : uint32_t { - // At most one of these access modifiers allowed for a given declaration, - // and if present it must be first: - Private = 1 << 0, - Protected = 1 << 1, + CARBON_KEYWORD_MODIFIER_SET(CARBON_ENUM_MASK_CONSTANT_DECL) - // Extern is standalone. - Extern = 1 << 2, - - // Extend can be combined with Final, but no others in the group below. - Extend = 1 << 3, - - // At most one of these declaration modifiers allowed for a given - // declaration: - Abstract = 1 << 4, - Base = 1 << 5, - Default = 1 << 6, - Export = 1 << 7, - Final = 1 << 8, - Impl = 1 << 9, - Virtual = 1 << 10, - Returned = 1 << 11, - - // Sets of modifiers: - Access = Private | Protected, - Class = Abstract | Base, - Method = Abstract | Impl | Virtual, - ImplDecl = Extend | Final, - Interface = Default | Final, - Decl = Class | Method | Interface | Export | Returned, - None = 0, - - LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/Returned) - }; - - // Default construct to empty. - explicit KeywordModifierSet() : set_(None) {} - - // Support implicit conversion so that the difference with the member enum is - // opaque. - explicit(false) constexpr KeywordModifierSet(RawEnumType set) : set_(set) {} - - // Adds entries to the set. - auto Add(KeywordModifierSet set) -> void { set_ |= set.set_; } - // Removes entries from the set. - auto Remove(KeywordModifierSet set) -> void { set_ &= ~set.set_; } - - // Returns true if there's a non-empty set intersection. - constexpr auto HasAnyOf(KeywordModifierSet other) const -> bool { - return set_ & other.set_; - } + // Sets of modifiers. + static const KeywordModifierSet Access; + static const KeywordModifierSet Class; + static const KeywordModifierSet Method; + static const KeywordModifierSet ImplDecl; + static const KeywordModifierSet Interface; + static const KeywordModifierSet Decl; // Return a builder that returns the new enumeration type once a series of // mapping `Case`s and a final `Default` are provided. For example: @@ -91,8 +73,8 @@ class KeywordModifierSet { public: explicit Converter(const KeywordModifierSet& set) : set_(set) {} - auto Case(RawEnumType raw_enumerator, T result) -> Converter& { - if (set_.HasAnyOf(raw_enumerator)) { + auto Case(KeywordModifierSet other, T result) -> Converter& { + if (set_.HasAnyOf(other)) { result_ = result; } return *this; @@ -122,34 +104,37 @@ class KeywordModifierSet { } return SemIR::AccessKind::Public; } - - // Returns true if empty. - constexpr auto empty() const -> bool { return !set_; } - - // Returns the set intersection. - constexpr auto operator&(KeywordModifierSet other) const - -> KeywordModifierSet { - return set_ & other.set_; - } - - // Returns the set inverse. - auto operator~() const -> KeywordModifierSet { return ~set_; } - - private: - RawEnumType set_; }; -static_assert(!KeywordModifierSet(KeywordModifierSet::Access) - .HasAnyOf(KeywordModifierSet::Extern) && - !KeywordModifierSet(KeywordModifierSet::Access | - KeywordModifierSet::Extern | - KeywordModifierSet::Extend) - .HasAnyOf(KeywordModifierSet::Decl), - "Order-related sets must not overlap"); -static_assert(~KeywordModifierSet::None == - (KeywordModifierSet::Access | KeywordModifierSet::Extern | - KeywordModifierSet::Extend | KeywordModifierSet::Decl), - "Modifier missing from all modifier sets"); +#define CARBON_KEYWORD_MODIFIER_SET_WITH_TYPE(X) \ + CARBON_ENUM_MASK_CONSTANT_DEFINITION(KeywordModifierSet, X) +CARBON_KEYWORD_MODIFIER_SET(CARBON_KEYWORD_MODIFIER_SET_WITH_TYPE) +#undef CARBON_KEYWORD_MODIFIER_SET_WITH_TYPE + +constexpr KeywordModifierSet KeywordModifierSet::Access(Private | Protected); +constexpr KeywordModifierSet KeywordModifierSet::Class(Abstract | Base); +constexpr KeywordModifierSet KeywordModifierSet::Method(Abstract | Impl | + Virtual); +constexpr KeywordModifierSet KeywordModifierSet::ImplDecl(Extend | Final); +constexpr KeywordModifierSet KeywordModifierSet::Interface(Default | Final); +constexpr KeywordModifierSet KeywordModifierSet::Decl(Class | Method | + Interface | Export | + Returned); + +static_assert( + !KeywordModifierSet::Access.HasAnyOf(KeywordModifierSet::Extern) && + !(KeywordModifierSet::Access | KeywordModifierSet::Extern | + KeywordModifierSet::Extend) + .HasAnyOf(KeywordModifierSet::Decl), + "Order-related sets must not overlap"); + +#define CARBON_KEYWORD_MODIFIER_SET_IN_GROUP(Modifier) \ + static_assert((KeywordModifierSet::Access | KeywordModifierSet::Extern | \ + KeywordModifierSet::Extend | KeywordModifierSet::Decl) \ + .HasAnyOf(KeywordModifierSet::Modifier), \ + "Modifier missing from all modifier sets: " #Modifier); +CARBON_KEYWORD_MODIFIER_SET(CARBON_KEYWORD_MODIFIER_SET_IN_GROUP) +#undef CARBON_KEYWORD_MODIFIER_SET_IN_GROUP } // namespace Carbon::Check diff --git a/toolchain/check/type.cpp b/toolchain/check/type.cpp index 703cf7a35c63..1f3f380ffabd 100644 --- a/toolchain/check/type.cpp +++ b/toolchain/check/type.cpp @@ -138,19 +138,19 @@ auto GetConstType(Context& context, SemIR::TypeInstId inner_type_id) auto GetQualifiedType(Context& context, SemIR::TypeId type_id, SemIR::TypeQualifiers quals) -> SemIR::TypeId { - if (HasTypeQualifier(quals, SemIR::TypeQualifiers::Const)) { + if (quals.HasAnyOf(SemIR::TypeQualifiers::Const)) { type_id = GetConstType(context, context.types().GetInstId(type_id)); - quals &= ~SemIR::TypeQualifiers::Const; + quals.Remove(SemIR::TypeQualifiers::Const); } - if (HasTypeQualifier(quals, SemIR::TypeQualifiers::MaybeUnformed)) { + if (quals.HasAnyOf(SemIR::TypeQualifiers::MaybeUnformed)) { type_id = GetTypeImpl( context, context.types().GetInstId(type_id)); - quals &= ~SemIR::TypeQualifiers::MaybeUnformed; + quals.Remove(SemIR::TypeQualifiers::MaybeUnformed); } - if (HasTypeQualifier(quals, SemIR::TypeQualifiers::Partial)) { + if (quals.HasAnyOf(SemIR::TypeQualifiers::Partial)) { type_id = GetTypeImpl( context, context.types().GetInstId(type_id)); - quals &= ~SemIR::TypeQualifiers::Partial; + quals.Remove(SemIR::TypeQualifiers::Partial); } CARBON_CHECK(quals == SemIR::TypeQualifiers::None); return type_id; diff --git a/toolchain/parse/BUILD b/toolchain/parse/BUILD index 8e09b1d9a3ea..846d1bf8038e 100644 --- a/toolchain/parse/BUILD +++ b/toolchain/parse/BUILD @@ -23,6 +23,7 @@ cc_library( srcs = ["node_category.cpp"], hdrs = ["node_category.h"], deps = [ + "//common:enum_mask_base", "//common:ostream", "@llvm-project//llvm:Support", ], diff --git a/toolchain/parse/node_category.cpp b/toolchain/parse/node_category.cpp index ecf0c833f731..49f236ddd061 100644 --- a/toolchain/parse/node_category.cpp +++ b/toolchain/parse/node_category.cpp @@ -8,30 +8,7 @@ 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); -} +CARBON_DEFINE_ENUM_MASK_NAMES(NodeCategory) = { + CARBON_NODE_CATEGORY(CARBON_ENUM_MASK_NAME_STRING)}; } // namespace Carbon::Parse diff --git a/toolchain/parse/node_category.h b/toolchain/parse/node_category.h index cc2b3f2ac021..1408a79eb0be 100644 --- a/toolchain/parse/node_category.h +++ b/toolchain/parse/node_category.h @@ -5,13 +5,11 @@ #ifndef CARBON_TOOLCHAIN_PARSE_NODE_CATEGORY_H_ #define CARBON_TOOLCHAIN_PARSE_NODE_CATEGORY_H_ +#include "common/enum_mask_base.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) ... @@ -31,59 +29,28 @@ LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); 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. - 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. - explicit(false) 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_; +// We expect this to grow, so are using a bigger size than needed. +CARBON_DEFINE_RAW_ENUM_MASK(NodeCategory, uint32_t) { + CARBON_NODE_CATEGORY(CARBON_RAW_ENUM_MASK_ENUMERATOR) }; +// Represents a set of keyword modifiers, using a separate bit per modifier. +class NodeCategory : public CARBON_ENUM_MASK_BASE(NodeCategory) { + public: + CARBON_NODE_CATEGORY(CARBON_ENUM_MASK_CONSTANT_DECL) + + using EnumMaskBase::EnumMaskBase; + // Provide implicit conversion because the raw enum is used in templates. + explicit(false) constexpr NodeCategory(RawEnumType value) { + *this = EnumBase::Make(value); + } +}; + +#define CARBON_NODE_CATEGORY_WITH_TYPE(X) \ + CARBON_ENUM_MASK_CONSTANT_DEFINITION(NodeCategory, X) +CARBON_NODE_CATEGORY(CARBON_NODE_CATEGORY_WITH_TYPE) +#undef CARBON_NODE_CATEGORY_WITH_TYPE + } // namespace Carbon::Parse #endif // CARBON_TOOLCHAIN_PARSE_NODE_CATEGORY_H_ diff --git a/toolchain/sem_ir/type.cpp b/toolchain/sem_ir/type.cpp index ea731f6190e4..61030fc54ac8 100644 --- a/toolchain/sem_ir/type.cpp +++ b/toolchain/sem_ir/type.cpp @@ -10,6 +10,9 @@ namespace Carbon::SemIR { +CARBON_DEFINE_ENUM_MASK_NAMES(TypeQualifiers) = { + CARBON_TYPE_QUALIFIERS(CARBON_ENUM_MASK_NAME_STRING)}; + // Verify that the constant value's type is `TypeType` (or an error). static void CheckTypeOfConstantIsTypeType(File& file, ConstantId constant_id) { CARBON_CHECK(constant_id.is_constant(), @@ -101,13 +104,13 @@ auto TypeStore::GetUnqualifiedTypeAndQualifiers(TypeId type_id) const type_id = file_->types().GetTypeIdForTypeInstId(qualified_type->inner_id); switch (qualified_type->kind) { case ConstType::Kind: - quals |= TypeQualifiers::Const; + quals.Add(TypeQualifiers::Const); break; case MaybeUnformedType::Kind: - quals |= TypeQualifiers::MaybeUnformed; + quals.Add(TypeQualifiers::MaybeUnformed); break; case PartialType::Kind: - quals |= TypeQualifiers::Partial; + quals.Add(TypeQualifiers::Partial); break; default: CARBON_FATAL("Unknown type qualifier {0}", qualified_type->kind); @@ -129,7 +132,7 @@ auto TypeStore::GetTransitiveUnqualifiedAdaptedType(TypeId type_id) const return {type_id, quals}; } type_id = unqual_type_id; - quals |= inner_quals; + quals.Add(inner_quals); } } diff --git a/toolchain/sem_ir/type.h b/toolchain/sem_ir/type.h index c76c84113424..3381b25b622d 100644 --- a/toolchain/sem_ir/type.h +++ b/toolchain/sem_ir/type.h @@ -5,7 +5,6 @@ #ifndef CARBON_TOOLCHAIN_SEM_IR_TYPE_H_ #define CARBON_TOOLCHAIN_SEM_IR_TYPE_H_ -#include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/STLExtras.h" #include "toolchain/base/shared_value_stores.h" #include "toolchain/sem_ir/constant.h" @@ -15,23 +14,25 @@ namespace Carbon::SemIR { -LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); +#define CARBON_TYPE_QUALIFIERS(X) \ + X(Const) \ + X(MaybeUnformed) \ + X(Partial) -// A bitmask of type qualifiers. -enum class TypeQualifiers { - None = 0, - Const = 1 << 0, - MaybeUnformed = 1 << 1, - Partial = 1 << 2, - - LLVM_MARK_AS_BITMASK_ENUM(Partial) +CARBON_DEFINE_RAW_ENUM_MASK(TypeQualifiers, uint8_t) { + CARBON_TYPE_QUALIFIERS(CARBON_RAW_ENUM_MASK_ENUMERATOR) }; -// Returns whether the type qualifier set `quals` contains `qual`. -inline auto HasTypeQualifier(TypeQualifiers quals, TypeQualifiers qual) - -> bool { - return (quals & qual) != TypeQualifiers::None; -} +// Represents a set of keyword modifiers, using a separate bit per modifier. +class TypeQualifiers : public CARBON_ENUM_MASK_BASE(TypeQualifiers) { + public: + CARBON_TYPE_QUALIFIERS(CARBON_ENUM_MASK_CONSTANT_DECL) +}; + +#define CARBON_TYPE_QUALIFIERS_WITH_TYPE(X) \ + CARBON_ENUM_MASK_CONSTANT_DEFINITION(TypeQualifiers, X) +CARBON_TYPE_QUALIFIERS(CARBON_TYPE_QUALIFIERS_WITH_TYPE) +#undef CARBON_TYPE_QUALIFIERS_WITH_TYPE // Provides a ValueStore wrapper with an API specific to types. class TypeStore : public Yaml::Printable {