Sfoglia il codice sorgente

Use `inline constexpr` where appropriate. (#6374)

This fixes various violations of C++'s One Definition Rule, where we
accidentally gave the same static data member multiple definitions in
different translation units. Clang happens to emit such definitions with
weak linkage, which allows us to get away with this without link errors,
but it's still formally incorrect.

Also switch keyword order around for a handful of instances of
`constexpr inline`, per agreement in open discussion.

This happens to reduce the size of a `-c dbg` toolchain binary by 7.2
MiB, presumably by making more of our symbols and especially debug info
discardable.
Richard Smith 5 mesi fa
parent
commit
b300f36e6f

+ 1 - 1
common/enum_base.h

@@ -210,7 +210,7 @@ class EnumBase : public Printable<DerivedT> {
 // Use this immediately after the Carbon enum class body to define each named
 // constant.
 #define CARBON_ENUM_CONSTANT_DEFINITION(EnumClassName, Name) \
-  constexpr EnumClassName EnumClassName::Name =              \
+  inline constexpr EnumClassName EnumClassName::Name =       \
       EnumClassName::Make(RawEnumType::Name);
 
 // Use this in the `.cpp` file for an enum class to start the definition of the

+ 1 - 1
common/raw_hashtable.h

@@ -147,7 +147,7 @@ namespace Carbon::RawHashtable {
 
 // If allocating storage, allocate a minimum of one cacheline of group metadata
 // or a minimum of one group, whichever is larger.
-constexpr ssize_t MinAllocatedSize = std::max<ssize_t>(64, MaxGroupSize);
+inline constexpr ssize_t MinAllocatedSize = std::max<ssize_t>(64, MaxGroupSize);
 
 // An entry in the hashtable storage of a `KeyT` and `ValueT` object.
 //

+ 1 - 1
common/raw_hashtable_metadata_group.h

@@ -43,7 +43,7 @@ namespace Carbon::RawHashtable {
 // We define a constant max group size. The particular group size used in
 // practice may vary, but we want to have some upper bound used to ensure
 // memory allocation is done consistently across different architectures.
-constexpr ssize_t MaxGroupSize = 16;
+inline constexpr ssize_t MaxGroupSize = 16;
 
 // This takes a collection of bits representing the results of looking for a
 // particular tag in this metadata group and determines the first position with

+ 4 - 3
toolchain/base/int.h

@@ -206,12 +206,12 @@ class IntId : public Printable<IntId> {
   int32_t id_;
 };
 
-constexpr IntId IntId::None(IntId::NoneId);
+inline constexpr IntId IntId::None(IntId::NoneId);
 
 // Note that we initialize the `None` index in a constexpr context which
 // ensures there is no UB in forming it. This helps ensure all the ID -> index
 // conversions are correct because the `None` ID is at the limit of that range.
-constexpr int32_t IntId::NoneIndex = None.AsIndex();
+inline constexpr int32_t IntId::NoneIndex = None.AsIndex();
 
 // A canonicalizing value store with deep optimizations for integers.
 //
@@ -428,7 +428,8 @@ class IntStore {
   CanonicalValueStore<APIntId, llvm::APInt> values_;
 };
 
-constexpr IntStore::APIntId IntStore::APIntId::None(IntId::None.AsIndex());
+inline constexpr IntStore::APIntId IntStore::APIntId::None(
+    IntId::None.AsIndex());
 
 }  // namespace Carbon
 

+ 2 - 2
toolchain/base/llvm_tools.h

@@ -63,12 +63,12 @@ class LLVMTool : public CARBON_ENUM_BASE(LLVMTool) {
   CARBON_ENUM_CONSTANT_DEFINITION(LLVMTool, Identifier)
 #include "toolchain/base/llvm_tools.def"
 
-constexpr LLVMTool LLVMTool::ToolsStorage[] = {
+inline constexpr LLVMTool LLVMTool::ToolsStorage[] = {
 #define CARBON_LLVM_TOOL(Identifier, Name, BinName, MainFn) \
   LLVMTool::Identifier,
 #include "toolchain/base/llvm_tools.def"
 };
-constexpr llvm::ArrayRef<LLVMTool> LLVMTool::Tools = ToolsStorage;
+inline constexpr llvm::ArrayRef<LLVMTool> LLVMTool::Tools = ToolsStorage;
 
 }  // namespace Carbon
 

+ 11 - 11
toolchain/base/runtime_sources.bzl

@@ -54,34 +54,34 @@ _TEMPLATE = """
 
 namespace Carbon::RuntimeSources {{
 
-constexpr inline llvm::StringLiteral CrtBegin = {crtbegin_src};
-constexpr inline llvm::StringLiteral CrtEnd = {crtend_src};
+inline constexpr llvm::StringLiteral CrtBegin = {crtbegin_src};
+inline constexpr llvm::StringLiteral CrtEnd = {crtend_src};
 
-constexpr inline llvm::StringLiteral BuiltinsGenericSrcs[] = {{
+inline constexpr llvm::StringLiteral BuiltinsGenericSrcs[] = {{
 {generic_srcs}
 }};
-constexpr inline llvm::StringLiteral BuiltinsMacosSrcs[] = {{
+inline constexpr llvm::StringLiteral BuiltinsMacosSrcs[] = {{
 {macos_srcs}
 }};
-constexpr inline llvm::StringLiteral BuiltinsBf16Srcs[] = {{
+inline constexpr llvm::StringLiteral BuiltinsBf16Srcs[] = {{
 {bf16_srcs}
 }};
-constexpr inline llvm::StringLiteral BuiltinsTfSrcs[] = {{
+inline constexpr llvm::StringLiteral BuiltinsTfSrcs[] = {{
 {tf_srcs}
 }};
-constexpr inline llvm::StringLiteral BuiltinsX86ArchSrcs[] = {{
+inline constexpr llvm::StringLiteral BuiltinsX86ArchSrcs[] = {{
 {x86_arch_srcs}
 }};
-constexpr inline llvm::StringLiteral BuiltinsX86Fp80Srcs[] = {{
+inline constexpr llvm::StringLiteral BuiltinsX86Fp80Srcs[] = {{
 {x86_fp80_srcs}
 }};
-constexpr inline llvm::StringLiteral BuiltinsAarch64Srcs[] = {{
+inline constexpr llvm::StringLiteral BuiltinsAarch64Srcs[] = {{
 {aarch64_srcs}
 }};
-constexpr inline llvm::StringLiteral BuiltinsX86_64Srcs[] = {{
+inline constexpr llvm::StringLiteral BuiltinsX86_64Srcs[] = {{
 {x86_64_srcs}
 }};
-constexpr inline llvm::StringLiteral BuiltinsI386Srcs[] = {{
+inline constexpr llvm::StringLiteral BuiltinsI386Srcs[] = {{
 {i386_srcs}
 }};
 

+ 8 - 7
toolchain/base/value_ids.h

@@ -49,7 +49,7 @@ struct FloatId : public IdBase<FloatId> {
   static const FloatId None;
   using IdBase::IdBase;
 };
-constexpr FloatId FloatId::None(FloatId::NoneIndex);
+inline constexpr FloatId FloatId::None(FloatId::NoneIndex);
 
 // Corresponds to a Real value.
 struct RealId : public IdBase<RealId> {
@@ -62,7 +62,7 @@ struct RealId : public IdBase<RealId> {
   static const RealId None;
   using IdBase::IdBase;
 };
-constexpr RealId RealId::None(RealId::NoneIndex);
+inline constexpr RealId RealId::None(RealId::NoneIndex);
 
 // Corresponds to StringRefs for identifiers.
 //
@@ -73,7 +73,7 @@ struct IdentifierId : public IdBase<IdentifierId> {
   static const IdentifierId None;
   using IdBase::IdBase;
 };
-constexpr IdentifierId IdentifierId::None(IdentifierId::NoneIndex);
+inline constexpr IdentifierId IdentifierId::None(IdentifierId::NoneIndex);
 
 // The name of a package, which is either an identifier or the special `Core`
 // package name.
@@ -122,9 +122,10 @@ struct PackageNameId : public IdBase<PackageNameId> {
     }
   }
 };
-constexpr PackageNameId PackageNameId::None(PackageNameId::NoneIndex);
-constexpr PackageNameId PackageNameId::Core(PackageNameId::NoneIndex - 1);
-constexpr PackageNameId PackageNameId::Cpp(PackageNameId::NoneIndex - 2);
+inline constexpr PackageNameId PackageNameId::None(PackageNameId::NoneIndex);
+inline constexpr PackageNameId PackageNameId::Core(PackageNameId::NoneIndex -
+                                                   1);
+inline constexpr PackageNameId PackageNameId::Cpp(PackageNameId::NoneIndex - 2);
 
 // Corresponds to StringRefs for string literals.
 struct StringLiteralValueId : public IdBase<StringLiteralValueId> {
@@ -132,7 +133,7 @@ struct StringLiteralValueId : public IdBase<StringLiteralValueId> {
   static const StringLiteralValueId None;
   using IdBase::IdBase;
 };
-constexpr StringLiteralValueId StringLiteralValueId::None(
+inline constexpr StringLiteralValueId StringLiteralValueId::None(
     StringLiteralValueId::NoneIndex);
 
 }  // namespace Carbon

+ 1 - 1
toolchain/check/convert.h

@@ -149,7 +149,7 @@ struct TypeExpr {
   SemIR::TypeId type_id;
 };
 
-constexpr inline TypeExpr TypeExpr::None = {.inst_id = SemIR::TypeInstId::None,
+inline constexpr TypeExpr TypeExpr::None = {.inst_id = SemIR::TypeInstId::None,
                                             .type_id = SemIR::TypeId::None};
 
 // Converts an expression for use as a type.

+ 3 - 3
toolchain/check/eval_inst.h

@@ -86,13 +86,13 @@ class ConstantEvalResult {
   bool same_phase_as_inst_;
 };
 
-constexpr ConstantEvalResult ConstantEvalResult::Error =
+inline constexpr ConstantEvalResult ConstantEvalResult::Error =
     Existing(SemIR::ErrorInst::ConstantId);
 
-constexpr ConstantEvalResult ConstantEvalResult::NotConstant =
+inline constexpr ConstantEvalResult ConstantEvalResult::NotConstant =
     ConstantEvalResult(SemIR::ConstantId::NotConstant);
 
-constexpr ConstantEvalResult ConstantEvalResult::TODO = NotConstant;
+inline constexpr ConstantEvalResult ConstantEvalResult::TODO = NotConstant;
 
 // Implementation details to compute the type of the `EvalConstantInst`
 // functions.

+ 13 - 9
toolchain/check/keyword_modifier_set.h

@@ -112,15 +112,19 @@ class KeywordModifierSet : public CARBON_ENUM_MASK_BASE(KeywordModifierSet) {
 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 | Override |
-                                                        Virtual);
-constexpr KeywordModifierSet KeywordModifierSet::ImplDecl(Extend | Final);
-constexpr KeywordModifierSet KeywordModifierSet::Interface(Default | Final);
-constexpr KeywordModifierSet KeywordModifierSet::Decl(Class | Method | Impl |
-                                                      Interface | Export |
-                                                      Returned);
+inline constexpr KeywordModifierSet KeywordModifierSet::Access(Private |
+                                                               Protected);
+inline constexpr KeywordModifierSet KeywordModifierSet::Class(Abstract | Base);
+inline constexpr KeywordModifierSet KeywordModifierSet::Method(Abstract |
+                                                               Override |
+                                                               Virtual);
+inline constexpr KeywordModifierSet KeywordModifierSet::ImplDecl(Extend |
+                                                                 Final);
+inline constexpr KeywordModifierSet KeywordModifierSet::Interface(Default |
+                                                                  Final);
+inline constexpr KeywordModifierSet KeywordModifierSet::Decl(Class | Method |
+                                                             Impl | Interface |
+                                                             Export | Returned);
 
 static_assert(
     !KeywordModifierSet::Access.HasAnyOf(KeywordModifierSet::Extern) &&

+ 1 - 1
toolchain/check/node_stack.h

@@ -637,7 +637,7 @@ class NodeStack {
   llvm::SmallVector<Entry> stack_;
 };
 
-constexpr NodeStack::IdKindTableType NodeStack::IdKindTable =
+inline constexpr NodeStack::IdKindTableType NodeStack::IdKindTable =
     ComputeIdKindTable();
 
 inline auto NodeStack::PopExprWithNodeId()

+ 1 - 1
toolchain/check/scope_index.h

@@ -24,7 +24,7 @@ struct ScopeIndex : public IndexBase<ScopeIndex> {
   using IndexBase::IndexBase;
 };
 
-constexpr ScopeIndex ScopeIndex::Package = ScopeIndex(0);
+inline constexpr ScopeIndex ScopeIndex::Package = ScopeIndex(0);
 
 }  // namespace Carbon::Check
 

+ 2 - 2
toolchain/lex/token_index.h

@@ -40,8 +40,8 @@ struct TokenIndex : public IndexBase<TokenIndex> {
   using IndexBase::IndexBase;
 };
 
-constexpr TokenIndex TokenIndex::None(TokenIndex::NoneIndex);
-constexpr TokenIndex TokenIndex::FirstNonCommentToken(1);
+inline constexpr TokenIndex TokenIndex::None(TokenIndex::NoneIndex);
+inline constexpr TokenIndex TokenIndex::FirstNonCommentToken(1);
 
 // A lightweight handle to a lexed token in a `TokenizedBuffer` whose kind is
 // known to be `Kind`.

+ 2 - 2
toolchain/lex/token_kind.h

@@ -130,11 +130,11 @@ class TokenKind : public CARBON_ENUM_BASE(TokenKind) {
   CARBON_ENUM_CONSTANT_DEFINITION(TokenKind, TokenName)
 #include "toolchain/lex/token_kind.def"
 
-constexpr TokenKind TokenKind::KeywordTokensStorage[] = {
+inline constexpr TokenKind TokenKind::KeywordTokensStorage[] = {
 #define CARBON_KEYWORD_TOKEN(TokenName, Spelling) TokenKind::TokenName,
 #include "toolchain/lex/token_kind.def"
 };
-constexpr llvm::ArrayRef<TokenKind> TokenKind::KeywordTokens =
+inline constexpr llvm::ArrayRef<TokenKind> TokenKind::KeywordTokens =
     KeywordTokensStorage;
 
 }  // namespace Carbon::Lex

+ 2 - 2
toolchain/lex/tokenized_buffer.h

@@ -56,7 +56,7 @@ struct LineIndex : public IndexBase<LineIndex> {
   using IndexBase::IndexBase;
 };
 
-constexpr LineIndex LineIndex::None(NoneIndex);
+inline constexpr LineIndex LineIndex::None(NoneIndex);
 
 // A comment, which can be a block of lines. These are tracked separately from
 // tokens because they don't affect parse; if they were part of tokens, we'd
@@ -79,7 +79,7 @@ struct CommentIndex : public IndexBase<CommentIndex> {
   using IndexBase::IndexBase;
 };
 
-constexpr CommentIndex CommentIndex::None(NoneIndex);
+inline constexpr CommentIndex CommentIndex::None(NoneIndex);
 
 // Random-access iterator over comments within the buffer.
 using CommentIterator = IndexIterator<CommentIndex>;

+ 1 - 1
toolchain/parse/node_kind.h

@@ -80,7 +80,7 @@ class NodeKind : public CARBON_ENUM_BASE(NodeKind) {
   CARBON_ENUM_CONSTANT_DEFINITION(NodeKind, Name)
 #include "toolchain/parse/node_kind.def"
 
-constexpr int NodeKind::ValidCount = 0
+inline constexpr int NodeKind::ValidCount = 0
 #define CARBON_PARSE_NODE_KIND(Name) +1
 #include "toolchain/parse/node_kind.def"
     ;

+ 41 - 34
toolchain/sem_ir/ids.h

@@ -42,8 +42,9 @@ struct InstId : public IdBase<InstId> {
   auto Print(llvm::raw_ostream& out) const -> void;
 };
 
-constexpr InstId InstId::InitTombstone = InstId(NoneIndex - 1);
-constexpr InstId InstId::ImplWitnessTablePlaceholder = InstId(NoneIndex - 2);
+inline constexpr InstId InstId::InitTombstone = InstId(NoneIndex - 1);
+inline constexpr InstId InstId::ImplWitnessTablePlaceholder =
+    InstId(NoneIndex - 2);
 
 // An InstId whose value is a type. The fact it's a type must be validated
 // before construction, and this allows that validation to be represented in the
@@ -63,7 +64,8 @@ struct TypeInstId : public InstId {
       : InstId(id) {}
 };
 
-constexpr TypeInstId TypeInstId::None = TypeInstId::UnsafeMake(InstId::None);
+inline constexpr TypeInstId TypeInstId::None =
+    TypeInstId::UnsafeMake(InstId::None);
 
 // An InstId whose type is known to be T. The fact it's a type must be validated
 // before construction, and this allows that validation to be represented in the
@@ -89,7 +91,7 @@ struct KnownInstId : public InstId {
 };
 
 template <typename T>
-constexpr KnownInstId<T> KnownInstId<T>::None =
+inline constexpr KnownInstId<T> KnownInstId<T>::None =
     KnownInstId<T>::UnsafeMake(InstId::None);
 
 // An ID of an instruction that is referenced absolutely by another instruction.
@@ -249,7 +251,8 @@ struct ConstantId : public IdBase<ConstantId> {
   static constexpr int32_t FirstSymbolicId = NoneIndex - 2;
 };
 
-constexpr ConstantId ConstantId::NotConstant = ConstantId(NotConstantIndex);
+inline constexpr ConstantId ConstantId::NotConstant =
+    ConstantId(NotConstantIndex);
 
 // The ID of a `EntityName`.
 struct EntityNameId : public IdBase<EntityNameId> {
@@ -311,7 +314,7 @@ struct CheckIRId : public IdBase<CheckIRId> {
   auto Print(llvm::raw_ostream& out) const -> void;
 };
 
-constexpr CheckIRId CheckIRId::Cpp = CheckIRId(NoneIndex - 1);
+inline constexpr CheckIRId CheckIRId::Cpp = CheckIRId(NoneIndex - 1);
 
 // The ID of a `Class`.
 struct ClassId : public IdBase<ClassId> {
@@ -444,7 +447,7 @@ struct GenericInstIndex : public IndexBase<GenericInstIndex> {
   static constexpr int32_t FirstDefinitionIndex = NoneIndex - 1;
 };
 
-constexpr GenericInstIndex GenericInstIndex::None =
+inline constexpr GenericInstIndex GenericInstIndex::None =
     GenericInstIndex::MakeNone();
 
 // The ID of an `ImportIR` within the set of imported IRs, both direct and
@@ -465,8 +468,8 @@ struct ImportIRId : public IdBase<ImportIRId> {
   auto Print(llvm::raw_ostream& out) const -> void;
 };
 
-constexpr ImportIRId ImportIRId::ApiForImpl = ImportIRId(0);
-constexpr ImportIRId ImportIRId::Cpp = ImportIRId(ApiForImpl.index + 1);
+inline constexpr ImportIRId ImportIRId::ApiForImpl = ImportIRId(0);
+inline constexpr ImportIRId ImportIRId::Cpp = ImportIRId(ApiForImpl.index + 1);
 
 // A boolean value.
 struct BoolValue : public IdBase<BoolValue> {
@@ -490,8 +493,8 @@ struct BoolValue : public IdBase<BoolValue> {
   auto Print(llvm::raw_ostream& out) const -> void;
 };
 
-constexpr BoolValue BoolValue::False = BoolValue(0);
-constexpr BoolValue BoolValue::True = BoolValue(1);
+inline constexpr BoolValue BoolValue::False = BoolValue(0);
+inline constexpr BoolValue BoolValue::True = BoolValue(1);
 
 // A character literal value as a unicode codepoint.
 struct CharId : public IdBase<CharId> {
@@ -521,8 +524,8 @@ struct IntKind : public IdBase<IntKind> {
   auto Print(llvm::raw_ostream& out) const -> void;
 };
 
-constexpr IntKind IntKind::Unsigned = IntKind(0);
-constexpr IntKind IntKind::Signed = IntKind(1);
+inline constexpr IntKind IntKind::Unsigned = IntKind(0);
+inline constexpr IntKind IntKind::Signed = IntKind(1);
 
 // A float kind value. This describes the semantics of the floating-point type.
 // This represents very similar information to the bit-width, but is more
@@ -560,15 +563,15 @@ struct FloatKind : public IdBase<FloatKind> {
   auto Semantics() const -> const llvm::fltSemantics&;
 };
 
-constexpr FloatKind FloatKind::None = FloatKind(NoneIndex);
+inline constexpr FloatKind FloatKind::None = FloatKind(NoneIndex);
 
-constexpr FloatKind FloatKind::Binary16 = FloatKind(0);
-constexpr FloatKind FloatKind::Binary32 = FloatKind(1);
-constexpr FloatKind FloatKind::Binary64 = FloatKind(2);
-constexpr FloatKind FloatKind::Binary128 = FloatKind(3);
-constexpr FloatKind FloatKind::BFloat16 = FloatKind(4);
-constexpr FloatKind FloatKind::X87Float80 = FloatKind(5);
-constexpr FloatKind FloatKind::PPCFloat128 = FloatKind(6);
+inline constexpr FloatKind FloatKind::Binary16 = FloatKind(0);
+inline constexpr FloatKind FloatKind::Binary32 = FloatKind(1);
+inline constexpr FloatKind FloatKind::Binary64 = FloatKind(2);
+inline constexpr FloatKind FloatKind::Binary128 = FloatKind(3);
+inline constexpr FloatKind FloatKind::BFloat16 = FloatKind(4);
+inline constexpr FloatKind FloatKind::X87Float80 = FloatKind(5);
+inline constexpr FloatKind FloatKind::PPCFloat128 = FloatKind(6);
 
 // An X-macro for special names. Uses should look like:
 //
@@ -654,14 +657,14 @@ struct NameId : public IdBase<NameId> {
 
 // Define the special `static const NameId` values.
 #define CARBON_SPECIAL_NAME_ID_FOR_DEF(Name) \
-  constexpr NameId NameId::Name =            \
+  inline constexpr NameId NameId::Name =     \
       NameId(NoneIndex - 1 - static_cast<int>(NameId::SpecialNameId::Name));
 CARBON_SPECIAL_NAME_ID(CARBON_SPECIAL_NAME_ID_FOR_DEF)
 #undef CARBON_SPECIAL_NAME_ID_FOR_DEF
 
 // Count non-index values, including `None` and special names.
 #define CARBON_SPECIAL_NAME_ID_FOR_COUNT(...) +1
-constexpr int NameId::NonIndexValueCount =
+inline constexpr int NameId::NonIndexValueCount =
     1 CARBON_SPECIAL_NAME_ID(CARBON_SPECIAL_NAME_ID_FOR_COUNT);
 #undef CARBON_SPECIAL_NAME_ID_FOR_COUNT
 
@@ -675,7 +678,7 @@ struct NameScopeId : public IdBase<NameScopeId> {
   using IdBase::IdBase;
 };
 
-constexpr NameScopeId NameScopeId::Package = NameScopeId(0);
+inline constexpr NameScopeId NameScopeId::Package = NameScopeId(0);
 
 // The ID of an `InstId` block.
 struct InstBlockId : public IdBase<InstBlockId> {
@@ -705,11 +708,12 @@ struct InstBlockId : public IdBase<InstBlockId> {
   auto Print(llvm::raw_ostream& out) const -> void;
 };
 
-constexpr InstBlockId InstBlockId::Empty = InstBlockId(0);
-constexpr InstBlockId InstBlockId::Exports = InstBlockId(1);
-constexpr InstBlockId InstBlockId::Imports = InstBlockId(2);
-constexpr InstBlockId InstBlockId::GlobalInit = InstBlockId(3);
-constexpr InstBlockId InstBlockId::Unreachable = InstBlockId(NoneIndex - 1);
+inline constexpr InstBlockId InstBlockId::Empty = InstBlockId(0);
+inline constexpr InstBlockId InstBlockId::Exports = InstBlockId(1);
+inline constexpr InstBlockId InstBlockId::Imports = InstBlockId(2);
+inline constexpr InstBlockId InstBlockId::GlobalInit = InstBlockId(3);
+inline constexpr InstBlockId InstBlockId::Unreachable =
+    InstBlockId(NoneIndex - 1);
 
 // Contains either an `InstBlockId` value, an error value, or
 // `InstBlockId::None`.
@@ -817,7 +821,8 @@ struct StructTypeFieldsId : public IdBase<StructTypeFieldsId> {
   using IdBase::IdBase;
 };
 
-constexpr StructTypeFieldsId StructTypeFieldsId::Empty = StructTypeFieldsId(0);
+inline constexpr StructTypeFieldsId StructTypeFieldsId::Empty =
+    StructTypeFieldsId(0);
 
 // The ID of a `CustomLayout` block.
 struct CustomLayoutId : public IdBase<CustomLayoutId> {
@@ -837,7 +842,7 @@ struct CustomLayoutId : public IdBase<CustomLayoutId> {
   using IdBase::IdBase;
 };
 
-constexpr CustomLayoutId CustomLayoutId::Empty = CustomLayoutId(0);
+inline constexpr CustomLayoutId CustomLayoutId::Empty = CustomLayoutId(0);
 
 // The ID of a type.
 struct TypeId : public IdBase<TypeId> {
@@ -906,8 +911,10 @@ struct LibraryNameId : public IdBase<LibraryNameId> {
   auto Print(llvm::raw_ostream& out) const -> void;
 };
 
-constexpr LibraryNameId LibraryNameId::Default = LibraryNameId(NoneIndex - 1);
-constexpr LibraryNameId LibraryNameId::Error = LibraryNameId(NoneIndex - 2);
+inline constexpr LibraryNameId LibraryNameId::Default =
+    LibraryNameId(NoneIndex - 1);
+inline constexpr LibraryNameId LibraryNameId::Error =
+    LibraryNameId(NoneIndex - 2);
 
 // The ID of an `ImportIRInst`.
 struct ImportIRInstId : public IdBase<ImportIRInstId> {
@@ -940,7 +947,7 @@ struct RequireImplsBlockId : public IdBase<RequireImplsBlockId> {
   using IdBase::IdBase;
 };
 
-constexpr RequireImplsBlockId RequireImplsBlockId::Empty =
+inline constexpr RequireImplsBlockId RequireImplsBlockId::Empty =
     RequireImplsBlockId(0);
 
 // A SemIR location used as the location of instructions. This contains either a

+ 1 - 1
toolchain/sem_ir/impl.h

@@ -214,7 +214,7 @@ class ImplStore {
   llvm::SmallVector<llvm::SmallVector<ImplId, 2>> lookup_buckets_;
 };
 
-constexpr inline ImplStore::ImplOrLookupBucketId
+inline constexpr ImplStore::ImplOrLookupBucketId
     ImplStore::ImplOrLookupBucketId::None(NoneIndex);
 
 }  // namespace Carbon::SemIR