From f0403dadab0348dd2c82ddbceee2bc6dfa992e9f Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Sat, 1 Mar 2025 00:00:17 -0800 Subject: [PATCH] Move None to IdBase (#5030) Use CRTP to eliminate per-type declarations of `None`. Note this adds `None` to a few that may not need it, but eliminates a lot off boilerplate. Note this leaves `GenericInstIndex::None` because it has a more complex construction. Also fix `InstId::InitTombstone` to be `NoneIndex - 1` --- toolchain/base/index_base.h | 10 +++ toolchain/check/scope_index.h | 2 - toolchain/parse/tree.h | 5 -- toolchain/sem_ir/ids.h | 132 +--------------------------------- 4 files changed, 11 insertions(+), 138 deletions(-) diff --git a/toolchain/base/index_base.h b/toolchain/base/index_base.h index c1e805135842..46ba96a7f018 100644 --- a/toolchain/base/index_base.h +++ b/toolchain/base/index_base.h @@ -48,6 +48,13 @@ template struct IdBase : public AnyIdBase, public Printable { using AnyIdBase::AnyIdBase; + // Provide a standard `None` mapping to `NoneIndex`. + // + // 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 IdT& None; + auto Print(llvm::raw_ostream& out) const -> void { out << IdT::Label; if (has_value()) { @@ -63,6 +70,9 @@ struct IdBase : public AnyIdBase, public Printable { } }; +template +constexpr const IdT& IdBase::None = IdT(NoneIndex); + // A lightweight handle to an item that behaves like an index. // // Unlike IdBase, classes derived from IndexBase are not completely opaque, and diff --git a/toolchain/check/scope_index.h b/toolchain/check/scope_index.h index 0d8a99fd83cf..d8d71b5cd370 100644 --- a/toolchain/check/scope_index.h +++ b/toolchain/check/scope_index.h @@ -20,13 +20,11 @@ namespace Carbon::Check { struct ScopeIndex : public IndexBase { static constexpr llvm::StringLiteral Label = "scope"; static const ScopeIndex Package; - static const ScopeIndex None; using IndexBase::IndexBase; }; constexpr ScopeIndex ScopeIndex::Package = ScopeIndex(0); -constexpr ScopeIndex ScopeIndex::None = ScopeIndex(NoneIndex); } // namespace Carbon::Check diff --git a/toolchain/parse/tree.h b/toolchain/parse/tree.h index dfc94fc606db..6221561686fb 100644 --- a/toolchain/parse/tree.h +++ b/toolchain/parse/tree.h @@ -29,14 +29,9 @@ struct DeferredDefinitionIndex : public IndexBase { static constexpr llvm::StringLiteral Label = "deferred_def"; using ValueType = DeferredDefinition; - static const DeferredDefinitionIndex None; - using IndexBase::IndexBase; }; -constexpr DeferredDefinitionIndex DeferredDefinitionIndex::None = - DeferredDefinitionIndex(NoneIndex); - // A function whose definition is deferred because it is defined inline in a // class or similar scope. // diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index b1a949d2c8b6..3c820c5ecf70 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -40,9 +40,6 @@ struct InstId : public IdBase { static constexpr llvm::StringLiteral Label = "inst"; using ValueType = Inst; - // An ID with no value. - static const InstId None; - // Represents the result of a name lookup that is temporarily disallowed // because the name is currently being initialized. static const InstId InitTombstone; @@ -52,8 +49,7 @@ struct InstId : public IdBase { auto Print(llvm::raw_ostream& out) const -> void; }; -constexpr InstId InstId::None = InstId(NoneIndex); -constexpr InstId InstId::InitTombstone = InstId(NoneIndex - 2); +constexpr InstId InstId::InitTombstone = InstId(NoneIndex - 1); // An ID of an instruction that is referenced absolutely by another instruction. // This should only be used as the type of a field within a typed instruction @@ -113,8 +109,6 @@ struct ConstantId : public IdBase { // An ID for an expression that is not constant. static const ConstantId NotConstant; - // An ID with no value. - static const ConstantId None; // Returns the constant ID corresponding to a concrete constant, which should // either be in the `constants` block in the file or should be known to be @@ -180,36 +174,24 @@ struct ConstantId : public IdBase { }; constexpr ConstantId ConstantId::NotConstant = ConstantId(NotConstantIndex); -constexpr ConstantId ConstantId::None = ConstantId(NoneIndex); // The ID of a EntityName. struct EntityNameId : public IdBase { static constexpr llvm::StringLiteral Label = "entity_name"; using ValueType = EntityName; - // An ID with no value. - static const EntityNameId None; - using IdBase::IdBase; }; -constexpr EntityNameId EntityNameId::None = EntityNameId(NoneIndex); - // The index of a compile-time binding. This is the de Bruijn level for the // binding -- that is, this is the number of other compile time bindings whose // scope encloses this binding. struct CompileTimeBindIndex : public IndexBase { static constexpr llvm::StringLiteral Label = "comp_time_bind"; - // An index with no value. - static const CompileTimeBindIndex None; - using IndexBase::IndexBase; }; -constexpr CompileTimeBindIndex CompileTimeBindIndex::None = - CompileTimeBindIndex(NoneIndex); - // The index of a runtime parameter in a function. These are allocated // sequentially, left-to-right, to the function parameters that will have // arguments passed to them at runtime. In a `call` instruction, a runtime @@ -220,9 +202,6 @@ constexpr CompileTimeBindIndex CompileTimeBindIndex::None = struct RuntimeParamIndex : public IndexBase { static constexpr llvm::StringLiteral Label = "runtime_param"; - // An index with no value. - static const RuntimeParamIndex None; - // An placeholder for index whose value is not yet known. static const RuntimeParamIndex Unknown; @@ -231,9 +210,6 @@ struct RuntimeParamIndex : public IndexBase { auto Print(llvm::raw_ostream& out) const -> void; }; -constexpr RuntimeParamIndex RuntimeParamIndex::None = - RuntimeParamIndex(NoneIndex); - constexpr RuntimeParamIndex RuntimeParamIndex::Unknown = RuntimeParamIndex(NoneIndex - 1); @@ -242,14 +218,9 @@ struct FunctionId : public IdBase { static constexpr llvm::StringLiteral Label = "function"; using ValueType = Function; - // An ID with no value. - static const FunctionId None; - using IdBase::IdBase; }; -constexpr FunctionId FunctionId::None = FunctionId(NoneIndex); - // The ID of an IR within the set of all IRs being evaluated in the current // check execution. struct CheckIRId : public IdBase { @@ -262,109 +233,66 @@ struct ClassId : public IdBase { static constexpr llvm::StringLiteral Label = "class"; using ValueType = Class; - // An ID with no value. - static const ClassId None; - using IdBase::IdBase; }; -constexpr ClassId ClassId::None = ClassId(NoneIndex); - // The ID of an interface. struct InterfaceId : public IdBase { static constexpr llvm::StringLiteral Label = "interface"; using ValueType = Interface; - // An ID with no value. - static const InterfaceId None; - using IdBase::IdBase; }; -constexpr InterfaceId InterfaceId::None = InterfaceId(NoneIndex); - // The ID of an associated constant. struct AssociatedConstantId : public IdBase { static constexpr llvm::StringLiteral Label = "assoc_const"; using ValueType = AssociatedConstant; - // An ID with no value. - static const AssociatedConstantId None; - using IdBase::IdBase; }; -constexpr AssociatedConstantId AssociatedConstantId::None = - AssociatedConstantId(NoneIndex); - // The ID of an facet type value. struct FacetTypeId : public IdBase { static constexpr llvm::StringLiteral Label = "facet_type"; using ValueType = FacetTypeInfo; - // An ID with no value. - static const FacetTypeId None; - using IdBase::IdBase; }; -constexpr FacetTypeId FacetTypeId::None = FacetTypeId(NoneIndex); - // The ID of an resolved facet type value. struct CompleteFacetTypeId : public IdBase { static constexpr llvm::StringLiteral Label = "complete_facet_type"; using ValueType = CompleteFacetType; - // An ID with no value. - static const CompleteFacetTypeId None; - using IdBase::IdBase; }; -constexpr CompleteFacetTypeId CompleteFacetTypeId::None = - CompleteFacetTypeId(NoneIndex); - // The ID of an impl. struct ImplId : public IdBase { static constexpr llvm::StringLiteral Label = "impl"; using ValueType = Impl; - // An ID with no value. - static const ImplId None; - using IdBase::IdBase; }; -constexpr ImplId ImplId::None = ImplId(NoneIndex); - // The ID of a generic. struct GenericId : public IdBase { static constexpr llvm::StringLiteral Label = "generic"; using ValueType = Generic; - // An ID with no value. - static const GenericId None; - using IdBase::IdBase; }; -constexpr GenericId GenericId::None = GenericId(NoneIndex); - // The ID of a specific, which is the result of specifying the generic arguments // for a generic. struct SpecificId : public IdBase { static constexpr llvm::StringLiteral Label = "specific"; using ValueType = Specific; - // An ID with no value. This is typically used to represent a non-generic - // entity. - static const SpecificId None; - using IdBase::IdBase; }; -constexpr SpecificId SpecificId::None = SpecificId(NoneIndex); - // The index of an instruction that depends on generic parameters within a // region of a generic. A corresponding specific version of the instruction can // be found in each specific corresponding to that generic. This is a pair of a @@ -419,22 +347,14 @@ struct ImportCppId : public IdBase { static constexpr llvm::StringLiteral Label = "import_cpp"; using ValueType = ImportCpp; - // An ID with no value. - static const ImportCppId None; - using IdBase::IdBase; }; -constexpr ImportCppId ImportCppId::None = ImportCppId(NoneIndex); - // The ID of an IR within the set of imported IRs, both direct and indirect. struct ImportIRId : public IdBase { static constexpr llvm::StringLiteral Label = "ir"; using ValueType = ImportIR; - // An ID with no value. - static const ImportIRId None; - // The implicit `api` import, for an `impl` file. A null entry is added if // there is none, as in an `api`, in which case this ID should not show up in // instructions. @@ -443,7 +363,6 @@ struct ImportIRId : public IdBase { using IdBase::IdBase; }; -constexpr ImportIRId ImportIRId::None = ImportIRId(NoneIndex); constexpr ImportIRId ImportIRId::ApiForImpl = ImportIRId(0); // A boolean value. @@ -536,9 +455,6 @@ struct NameId : public IdBase { // names().GetFormatted() is used for diagnostics. using DiagnosticType = DiagnosticTypeInfo; - // An ID with no value. - static const NameId None; - // An enum of special names. enum class SpecialNameId : uint8_t { #define CARBON_SPECIAL_NAME_ID_FOR_ENUM(Name) Name, @@ -582,8 +498,6 @@ struct NameId : public IdBase { auto Print(llvm::raw_ostream& out) const -> void; }; -constexpr NameId NameId::None = NameId(NoneIndex); - // Define the special `static const NameId` values. #define CARBON_SPECIAL_NAME_ID_FOR_DEF(Name) \ constexpr NameId NameId::Name = \ @@ -602,15 +516,12 @@ struct NameScopeId : public IdBase { static constexpr llvm::StringLiteral Label = "name_scope"; using ValueType = NameScope; - // An ID with no value. - static const NameScopeId None; // The package (or file) name scope, guaranteed to be the first added. static const NameScopeId Package; using IdBase::IdBase; }; -constexpr NameScopeId NameScopeId::None = NameScopeId(NoneIndex); constexpr NameScopeId NameScopeId::Package = NameScopeId(0); // The ID of an instruction block. @@ -637,9 +548,6 @@ struct InstBlockId : public IdBase { // be inserted into it. static const InstBlockId GlobalInit; - // An ID with no value. - static const InstBlockId None; - // An ID for unreachable code. static const InstBlockId Unreachable; @@ -651,7 +559,6 @@ constexpr InstBlockId InstBlockId::Empty = InstBlockId(0); constexpr InstBlockId InstBlockId::Exports = InstBlockId(1); constexpr InstBlockId InstBlockId::ImportRefs = InstBlockId(2); constexpr InstBlockId InstBlockId::GlobalInit = InstBlockId(3); -constexpr InstBlockId InstBlockId::None = InstBlockId(NoneIndex); constexpr InstBlockId InstBlockId::Unreachable = InstBlockId(NoneIndex - 1); // An ID of an instruction block that is referenced absolutely by an @@ -704,14 +611,9 @@ struct ExprRegionId : public IdBase { static constexpr llvm::StringLiteral Label = "region"; using ValueType = ExprRegion; - // An ID with no value. - static const ExprRegionId None; - using IdBase::IdBase; }; -constexpr ExprRegionId ExprRegionId::None = ExprRegionId(NoneIndex); - // The ID of a struct type field block. struct StructTypeFieldsId : public IdBase { static constexpr llvm::StringLiteral Label = "struct_type_fields"; @@ -719,9 +621,6 @@ struct StructTypeFieldsId : public IdBase { using ElementType = StructTypeField; using ValueType = llvm::MutableArrayRef; - // An ID with no value. - static const StructTypeFieldsId None; - // The canonical empty block, reused to avoid allocating empty vectors. Always // the 0-index block. static const StructTypeFieldsId Empty; @@ -729,8 +628,6 @@ struct StructTypeFieldsId : public IdBase { using IdBase::IdBase; }; -constexpr StructTypeFieldsId StructTypeFieldsId::None = - StructTypeFieldsId(NoneIndex); constexpr StructTypeFieldsId StructTypeFieldsId::Empty = StructTypeFieldsId(0); // The ID of a type. @@ -742,9 +639,6 @@ struct TypeId : public IdBase { // `InstIdAsType` or `TypeOfInstId` as the diagnostic argument type. using DiagnosticType = DiagnosticTypeInfo; - // An ID with no value. - static const TypeId None; - using IdBase::IdBase; // Returns the ID of the type corresponding to the constant `const_id`, which @@ -765,8 +659,6 @@ struct TypeId : public IdBase { auto Print(llvm::raw_ostream& out) const -> void; }; -constexpr TypeId TypeId::None = TypeId(NoneIndex); - // The ID of a type block. struct TypeBlockId : public IdBase { static constexpr llvm::StringLiteral Label = "type_block"; @@ -774,9 +666,6 @@ struct TypeBlockId : public IdBase { using ElementType = TypeId; using ValueType = llvm::MutableArrayRef; - // An ID with no value. - static const TypeBlockId None; - // The canonical empty block, reused to avoid allocating empty vectors. Always // the 0-index block. static const TypeBlockId Empty; @@ -784,27 +673,19 @@ struct TypeBlockId : public IdBase { using IdBase::IdBase; }; -constexpr TypeBlockId TypeBlockId::None = TypeBlockId(NoneIndex); constexpr TypeBlockId TypeBlockId::Empty = TypeBlockId(0); // An index for element access, for structs, tuples, and classes. struct ElementIndex : public IndexBase { static constexpr llvm::StringLiteral Label = "element"; using IndexBase::IndexBase; - - // An ID with no value. - static const ElementIndex None; }; -constexpr ElementIndex ElementIndex::None = ElementIndex(NoneIndex); - // The ID of a library name. This is either a string literal or `default`. struct LibraryNameId : public IdBase { static constexpr llvm::StringLiteral Label = "library_name"; using DiagnosticType = DiagnosticTypeInfo; - // An ID with no value. - static const LibraryNameId None; // The name of `default`. static const LibraryNameId Default; // Track cases where the library name was set, but has been diagnosed and @@ -825,7 +706,6 @@ struct LibraryNameId : public IdBase { auto Print(llvm::raw_ostream& out) const -> void; }; -constexpr LibraryNameId LibraryNameId::None = LibraryNameId(NoneIndex); constexpr LibraryNameId LibraryNameId::Default = LibraryNameId(NoneIndex - 1); constexpr LibraryNameId LibraryNameId::Error = LibraryNameId(NoneIndex - 2); @@ -834,14 +714,9 @@ struct ImportIRInstId : public IdBase { static constexpr llvm::StringLiteral Label = "import_ir_inst"; using ValueType = ImportIRInst; - // An ID with no value. - static const ImportIRInstId None; - using IdBase::IdBase; }; -constexpr ImportIRInstId ImportIRInstId::None = ImportIRInstId(NoneIndex); - // A SemIR location used as the location of instructions. // // Contents: @@ -855,9 +730,6 @@ struct LocId : public IdBase { // operations performed implicitly. static const int32_t ImplicitBit = 1 << 30; - // An ID with no value. - static const LocId None; - using IdBase::IdBase; // NOLINTNEXTLINE(google-explicit-constructor) @@ -909,8 +781,6 @@ struct LocId : public IdBase { auto Print(llvm::raw_ostream& out) const -> void; }; -constexpr LocId LocId::None = LocId(Parse::NodeId::None); - // Polymorphic id for fields in `Any[...]` typed instruction category. Used for // fields where the specific instruction structs have different field types in // that position or do not have a field in that position at all. Allows