From a155c1e3d535bd1e731bb2ab1f591c47388f3fa1 Mon Sep 17 00:00:00 2001 From: jonmeow Date: Tue, 28 Oct 2025 15:43:01 -0700 Subject: [PATCH 1/2] Add a base struct for singleton type insts --- common/template_string.h | 8 +- common/template_string_test.cpp | 23 +++- toolchain/sem_ir/BUILD | 1 + toolchain/sem_ir/typed_insts.h | 219 ++++++++++---------------------- 4 files changed, 88 insertions(+), 163 deletions(-) diff --git a/common/template_string.h b/common/template_string.h index a4a1801e8f43c..cee90a55bd079 100644 --- a/common/template_string.h +++ b/common/template_string.h @@ -52,12 +52,12 @@ struct TemplateString { __builtin_memcpy(storage_, str, N + 1); } - // This type is designed to act as a `StringRef` implicitly while having the - // storage necessary to be used as a template parameter. + // This type is designed to act as a `StringLiteral` implicitly while having + // the storage necessary to be used as a template parameter. // // NOLINTNEXTLINE(google-explicit-constructor) - explicit(false) constexpr operator llvm::StringRef() const { - return llvm::StringRef(storage_, N); + explicit(false) constexpr operator llvm::StringLiteral() const { + return llvm::StringLiteral::withInnerNUL(storage_); } // Accesses the string data directly as a compile-time C string. diff --git a/common/template_string_test.cpp b/common/template_string_test.cpp index 0c51507fb9bf5..7703f72cd8150 100644 --- a/common/template_string_test.cpp +++ b/common/template_string_test.cpp @@ -15,12 +15,17 @@ namespace { using ::testing::StrEq; template -constexpr auto FromTemplate() -> llvm::StringRef { +constexpr auto TemplateAsStringRef() -> llvm::StringRef { return S; } template -constexpr auto CStrFromTemplate() -> const char* { +constexpr auto TemplateAsStringLiteral() -> llvm::StringLiteral { + return S; +} + +template +constexpr auto TemplateAsCStr() -> const char* { return S.c_str(); } @@ -48,9 +53,11 @@ constexpr auto IsValidTemplateString(...) -> std::false_type { } // Compile time tests with `static_assert` -static_assert(FromTemplate<"test">().size() == 4, +static_assert(TemplateAsStringRef<"test">().size() == 4, + "Not usable in a `constexpr` context."); +static_assert(TemplateAsStringLiteral<"test">().size() == 4, "Not usable in a `constexpr` context."); -static_assert(__builtin_strlen(CStrFromTemplate<"test">()) == 4, +static_assert(__builtin_strlen(TemplateAsCStr<"test">()) == 4, "Not usable in a `constexpr` context."); // The string must not contain embedded nulls. @@ -63,12 +70,14 @@ static_assert(IsValidTemplateString(0)); static_assert(!IsValidTemplateString(0)); TEST(TemplateStringTest, Test) { - EXPECT_THAT(FromTemplate<"test">(), StrEq("test")); - EXPECT_THAT(CStrFromTemplate<"test">(), StrEq("test")); + EXPECT_THAT(TemplateAsStringRef<"test">(), StrEq("test")); + EXPECT_THAT(TemplateAsStringLiteral<"test">(), StrEq("test")); + EXPECT_THAT(TemplateAsCStr<"test">(), StrEq("test")); constexpr char GoodStr[5] = {'t', 'e', 's', 't', '\0'}; static_assert(IsValidTemplateString(0)); - EXPECT_THAT(FromTemplate(), StrEq("test")); + EXPECT_THAT(TemplateAsStringRef(), StrEq("test")); + EXPECT_THAT(TemplateAsStringLiteral(), StrEq("test")); constexpr char BadStr[4] = {'t', 'e', 's', 't'}; static_assert(!IsValidTemplateString(0)); diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 3faec327c58ba..3c37707410db2 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -26,6 +26,7 @@ cc_library( "//common:check", "//common:enum_base", "//common:ostream", + "//common:template_string", "//common:type_enum", "//toolchain/base:canonical_value_store", "//toolchain/base:index_base", diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 2de03bfe56db3..d41c8391bab09 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -5,6 +5,7 @@ #ifndef CARBON_TOOLCHAIN_SEM_IR_TYPED_INSTS_H_ #define CARBON_TOOLCHAIN_SEM_IR_TYPED_INSTS_H_ +#include "common/template_string.h" #include "toolchain/base/int.h" #include "toolchain/parse/node_ids.h" #include "toolchain/parse/typed_nodes.h" @@ -51,6 +52,24 @@ namespace Carbon::SemIR { +// A singleton type. +template +struct SingletonTypeInst final { + static constexpr auto Kind = InstKind::Make(KindT).Define( + InstKind::DefinitionInfo{.ir_name = IrName, + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::Always}); + static constexpr auto TypeInstId = MakeSingletonTypeInstId(); + static constexpr SemIR::InstId InstId = TypeInstId; + static constexpr auto ConstantId = ConstantId::ForConcreteConstant(InstId); + static constexpr auto TypeId = + TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId)); + + // Singleton types have a type of `TypeType`, except for `ErrorInst` which + // uses itself. + SemIR::TypeId type_id; +}; + // An action that performs simple member access, `base.name`. struct AccessMemberAction { static constexpr auto Kind = @@ -247,17 +266,7 @@ struct AssociatedEntityType { }; // Used for the type of patterns that do not match a fixed type. -struct AutoType { - static constexpr auto Kind = InstKind::AutoType.Define( - {.ir_name = "auto", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - static constexpr auto TypeId = - TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId)); - - SemIR::TypeId type_id; -}; +using AutoType = SingletonTypeInst; // A base in a class, of the form `base: base_type;`. A base class is an // element of the derived class, and the type of the `BaseDecl` instruction is @@ -292,17 +301,10 @@ struct BoolLiteral { }; // The type of bool literals and branch conditions, bool. -struct BoolType { - static constexpr auto Kind = InstKind::BoolType.Define( - {.ir_name = "bool", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - - TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using BoolType = SingletonTypeInst; // For member access such as `object.MethodName`, combines a member function // with the value to use for `self`. This is a callable structure; `Call` will @@ -322,18 +324,11 @@ struct BoundMethod { }; // The type of bound method values. -struct BoundMethodType { - static constexpr auto Kind = - InstKind::BoundMethodType.Define( - {.ir_name = "", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - - TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using BoundMethodType = + SingletonTypeInst">; // Control flow to branch to the target block. struct Branch { @@ -394,20 +389,11 @@ struct Call { // A unicode code point character literal. This type only provides compile-time // operations, and is represented as an empty type at runtime. -struct CharLiteralType { - static constexpr auto Kind = - InstKind::CharLiteralType.Define( - {.ir_name = "Core.CharLiteral", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - static constexpr auto TypeId = - TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId)); - - SemIR::TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using CharLiteralType = + SingletonTypeInst; // A unicode code point character value, whose type is `CharLiteralType`. struct CharLiteralValue { @@ -571,18 +557,10 @@ struct Deref { // required. For example, when there is a type checking issue, this will be used // in the type_id. It's typically used as a cue that semantic checking doesn't // need to issue further diagnostics. -struct ErrorInst { - static constexpr auto Kind = InstKind::ErrorInst.Define( - {.ir_name = "", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - static constexpr SemIR::InstId InstId = TypeInstId; - static constexpr auto ConstantId = ConstantId::ForConcreteConstant(InstId); - static constexpr auto TypeId = TypeId::ForTypeConstant(ConstantId); - - SemIR::TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using ErrorInst = SingletonTypeInst">; // An `export bind_name` declaration. struct ExportDecl { @@ -662,18 +640,11 @@ struct FieldDecl { // The float literal type. // TODO: Replace this with a rational number type, following the design. -struct FloatLiteralType { - static constexpr auto Kind = - InstKind::FloatLiteralType.Define( - {.ir_name = "Core.FloatLiteral", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - - TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using FloatLiteralType = + SingletonTypeInst; // A floating point literal value. // TODO: Eventually this should be represented as a rational number, and should @@ -1061,17 +1032,7 @@ struct InitializeFrom { }; // Used as the type of template actions that produce instructions. -struct InstType { - static constexpr auto Kind = InstKind::InstType.Define( - {.ir_name = "", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - static constexpr auto TypeId = - TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId)); - - SemIR::TypeId type_id; -}; +using InstType = SingletonTypeInst">; // A value of type `InstType` that refers to an instruction. This is used to // represent an instruction as a value for use as a result of a template action. @@ -1104,18 +1065,11 @@ struct InterfaceDecl { // literals and as the parameter type of `Core.Int` and `Core.Float`. This type // only provides compile-time operations, and is represented as an empty type at // runtime. -struct IntLiteralType { - static constexpr auto Kind = - InstKind::IntLiteralType.Define( - {.ir_name = "Core.IntLiteral", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - - TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using IntLiteralType = + SingletonTypeInst; // A primitive integer type whose representation and operations are defined by // the toolchain. The `Core.Int` and `Core.UInt` classes are defined as adapters @@ -1244,18 +1198,10 @@ struct Namespace { }; // The type of namespace and imported package names. -struct NamespaceType { - static constexpr auto Kind = - InstKind::NamespaceType.Define( - {.ir_name = "", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - - TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using NamespaceType = SingletonTypeInst">; // An output `Call` parameter. See AnyParam for member documentation. struct OutParam { @@ -1560,18 +1506,11 @@ struct SpecificFunction { }; // The type of specific functions. -struct SpecificFunctionType { - static constexpr auto Kind = - InstKind::SpecificFunctionType.Define( - {.ir_name = "", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - - TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using SpecificFunctionType = + SingletonTypeInst">; // A specific instance of a function from an impl, named as the function from // the interface. @@ -1848,17 +1787,7 @@ struct TypeOfInst { // Tracks expressions which are valid as types. This has a deliberately // self-referential type. -struct TypeType { - static constexpr auto Kind = InstKind::TypeType.Define( - {.ir_name = "type", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - static constexpr auto TypeId = - TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId)); - - SemIR::TypeId type_id; -}; +using TypeType = SingletonTypeInst; // The `not` operator, such as `not operand`. struct UnaryOperatorNot { @@ -1995,17 +1924,10 @@ struct VarStorage { }; // The type of virtual function tables. -struct VtableType { - static constexpr auto Kind = InstKind::VtableType.Define( - {.ir_name = "", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - - TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using VtableType = SingletonTypeInst">; // Initializer for virtual function table pointers in object initialization. struct VtablePtr { @@ -2044,17 +1966,10 @@ struct WhereExpr { // their types should not change in the process. // // Also the type of `RequireCompleteType` instructions. -struct WitnessType { - static constexpr auto Kind = InstKind::WitnessType.Define( - {.ir_name = "", - .is_type = InstIsType::Always, - .constant_kind = InstConstantKind::Always}); - // This is a singleton instruction. However, it may still evolve into a more - // standard type and be removed. - static constexpr auto TypeInstId = MakeSingletonTypeInstId(); - - TypeId type_id; -}; +// +// This is a singleton instruction. However, it may still evolve into a more +// standard type and be removed. +using WitnessType = SingletonTypeInst">; // These concepts are an implementation detail of the library, not public API. namespace Internal { From a422232ada508433492d420fdfb72cc86ccdcb7c Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Wed, 29 Oct 2025 09:03:38 -0700 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Dana Jansens --- toolchain/sem_ir/typed_insts.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index d41c8391bab09..b914e2e7afc84 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -52,7 +52,7 @@ namespace Carbon::SemIR { -// A singleton type. +// A template for singleton types. template struct SingletonTypeInst final { static constexpr auto Kind = InstKind::Make(KindT).Define( @@ -557,9 +557,6 @@ struct Deref { // required. For example, when there is a type checking issue, this will be used // in the type_id. It's typically used as a cue that semantic checking doesn't // need to issue further diagnostics. -// -// This is a singleton instruction. However, it may still evolve into a more -// standard type and be removed. using ErrorInst = SingletonTypeInst">; // An `export bind_name` declaration.