Просмотр исходного кода

Add a base struct for singleton type insts (#6289)

This is just reducing boilerplate in `typed_insts.h` because we have a
number of singleton types, and keep adding more.

The changes to `TemplateString` allow `TemplateString IrName` to be used
as a `StringLiteral`.

---------

Co-authored-by: Dana Jansens <danakj@orodu.net>
Jon Ross-Perkins 6 месяцев назад
Родитель
Сommit
93dc369ebd
4 измененных файлов с 85 добавлено и 163 удалено
  1. 4 4
      common/template_string.h
  2. 16 7
      common/template_string_test.cpp
  3. 1 0
      toolchain/sem_ir/BUILD
  4. 64 152
      toolchain/sem_ir/typed_insts.h

+ 4 - 4
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.

+ 16 - 7
common/template_string_test.cpp

@@ -15,12 +15,17 @@ namespace {
 using ::testing::StrEq;
 
 template <TemplateString S>
-constexpr auto FromTemplate() -> llvm::StringRef {
+constexpr auto TemplateAsStringRef() -> llvm::StringRef {
   return S;
 }
 
 template <TemplateString S>
-constexpr auto CStrFromTemplate() -> const char* {
+constexpr auto TemplateAsStringLiteral() -> llvm::StringLiteral {
+  return S;
+}
+
+template <TemplateString S>
+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<FourChars{'t', 'e', 's', 0}>(0));
 static_assert(!IsValidTemplateString<FourChars{'t', 'e', 's', 't'}>(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<GoodStr>(0));
-  EXPECT_THAT(FromTemplate<GoodStr>(), StrEq("test"));
+  EXPECT_THAT(TemplateAsStringRef<GoodStr>(), StrEq("test"));
+  EXPECT_THAT(TemplateAsStringLiteral<GoodStr>(), StrEq("test"));
 
   constexpr char BadStr[4] = {'t', 'e', 's', 't'};
   static_assert(!IsValidTemplateString<BadStr>(0));

+ 1 - 0
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",

+ 64 - 152
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 template for singleton types.
+template <InstKind::RawEnumType KindT, TemplateString IrName>
+struct SingletonTypeInst final {
+  static constexpr auto Kind = InstKind::Make(KindT).Define<Parse::NoneNodeId>(
+      InstKind::DefinitionInfo{.ir_name = IrName,
+                               .is_type = InstIsType::Always,
+                               .constant_kind = InstConstantKind::Always});
+  static constexpr auto TypeInstId = MakeSingletonTypeInstId<Kind>();
+  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<Parse::NoneNodeId>(
-      {.ir_name = "auto",
-       .is_type = InstIsType::Always,
-       .constant_kind = InstConstantKind::Always});
-  static constexpr auto TypeInstId = MakeSingletonTypeInstId<Kind>();
-  static constexpr auto TypeId =
-      TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId));
-
-  SemIR::TypeId type_id;
-};
+using AutoType = SingletonTypeInst<InstKind::AutoType, "auto">;
 
 // 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<Parse::NoneNodeId>(
-      {.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<Kind>();
-
-  TypeId type_id;
-};
+//
+// This is a singleton instruction. However, it may still evolve into a more
+// standard type and be removed.
+using BoolType = SingletonTypeInst<InstKind::BoolType, "bool">;
 
 // 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<Parse::NoneNodeId>(
-          {.ir_name = "<bound method>",
-           .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<Kind>();
-
-  TypeId type_id;
-};
+//
+// This is a singleton instruction. However, it may still evolve into a more
+// standard type and be removed.
+using BoundMethodType =
+    SingletonTypeInst<InstKind::BoundMethodType, "<bound method>">;
 
 // 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<Parse::NoneNodeId>(
-          {.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<Kind>();
-  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<InstKind::CharLiteralType, "Core.CharLiteral">;
 
 // A unicode code point character value, whose type is `CharLiteralType`.
 struct CharLiteralValue {
@@ -586,18 +572,7 @@ 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<Parse::NoneNodeId>(
-      {.ir_name = "<error>",
-       .is_type = InstIsType::Always,
-       .constant_kind = InstConstantKind::Always});
-  static constexpr auto TypeInstId = MakeSingletonTypeInstId<Kind>();
-  static constexpr SemIR::InstId InstId = TypeInstId;
-  static constexpr auto ConstantId = ConstantId::ForConcreteConstant(InstId);
-  static constexpr auto TypeId = TypeId::ForTypeConstant(ConstantId);
-
-  SemIR::TypeId type_id;
-};
+using ErrorInst = SingletonTypeInst<InstKind::ErrorInst, "<error>">;
 
 // An `export bind_name` declaration.
 struct ExportDecl {
@@ -677,18 +652,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<Parse::NoneNodeId>(
-          {.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<Kind>();
-
-  TypeId type_id;
-};
+//
+// This is a singleton instruction. However, it may still evolve into a more
+// standard type and be removed.
+using FloatLiteralType =
+    SingletonTypeInst<InstKind::FloatLiteralType, "Core.FloatLiteral">;
 
 // A floating point literal value.
 // TODO: Eventually this should be represented as a rational number, and should
@@ -1076,17 +1044,7 @@ struct InitializeFrom {
 };
 
 // Used as the type of template actions that produce instructions.
-struct InstType {
-  static constexpr auto Kind = InstKind::InstType.Define<Parse::NoneNodeId>(
-      {.ir_name = "<instruction>",
-       .is_type = InstIsType::Always,
-       .constant_kind = InstConstantKind::Always});
-  static constexpr auto TypeInstId = MakeSingletonTypeInstId<Kind>();
-  static constexpr auto TypeId =
-      TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId));
-
-  SemIR::TypeId type_id;
-};
+using InstType = SingletonTypeInst<InstKind::InstType, "<instruction>">;
 
 // 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.
@@ -1119,18 +1077,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<Parse::NoneNodeId>(
-          {.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<Kind>();
-
-  TypeId type_id;
-};
+//
+// This is a singleton instruction. However, it may still evolve into a more
+// standard type and be removed.
+using IntLiteralType =
+    SingletonTypeInst<InstKind::IntLiteralType, "Core.IntLiteral">;
 
 // A primitive integer type whose representation and operations are defined by
 // the toolchain. The `Core.Int` and `Core.UInt` classes are defined as adapters
@@ -1259,18 +1210,10 @@ struct Namespace {
 };
 
 // The type of namespace and imported package names.
-struct NamespaceType {
-  static constexpr auto Kind =
-      InstKind::NamespaceType.Define<Parse::NoneNodeId>(
-          {.ir_name = "<namespace>",
-           .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<Kind>();
-
-  TypeId type_id;
-};
+//
+// This is a singleton instruction. However, it may still evolve into a more
+// standard type and be removed.
+using NamespaceType = SingletonTypeInst<InstKind::NamespaceType, "<namespace>">;
 
 // An output `Call` parameter. See AnyParam for member documentation.
 struct OutParam {
@@ -1575,18 +1518,11 @@ struct SpecificFunction {
 };
 
 // The type of specific functions.
-struct SpecificFunctionType {
-  static constexpr auto Kind =
-      InstKind::SpecificFunctionType.Define<Parse::NoneNodeId>(
-          {.ir_name = "<specific function>",
-           .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<Kind>();
-
-  TypeId type_id;
-};
+//
+// This is a singleton instruction. However, it may still evolve into a more
+// standard type and be removed.
+using SpecificFunctionType =
+    SingletonTypeInst<InstKind::SpecificFunctionType, "<specific function>">;
 
 // A specific instance of a function from an impl, named as the function from
 // the interface.
@@ -1863,17 +1799,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<Parse::NoneNodeId>(
-      {.ir_name = "type",
-       .is_type = InstIsType::Always,
-       .constant_kind = InstConstantKind::Always});
-  static constexpr auto TypeInstId = MakeSingletonTypeInstId<Kind>();
-  static constexpr auto TypeId =
-      TypeId::ForTypeConstant(ConstantId::ForConcreteConstant(TypeInstId));
-
-  SemIR::TypeId type_id;
-};
+using TypeType = SingletonTypeInst<InstKind::TypeType, "type">;
 
 // The `not` operator, such as `not operand`.
 struct UnaryOperatorNot {
@@ -2010,17 +1936,10 @@ struct VarStorage {
 };
 
 // The type of virtual function tables.
-struct VtableType {
-  static constexpr auto Kind = InstKind::VtableType.Define<Parse::NoneNodeId>(
-      {.ir_name = "<vtable>",
-       .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<Kind>();
-
-  TypeId type_id;
-};
+//
+// This is a singleton instruction. However, it may still evolve into a more
+// standard type and be removed.
+using VtableType = SingletonTypeInst<InstKind::VtableType, "<vtable>">;
 
 // Initializer for virtual function table pointers in object initialization.
 struct VtablePtr {
@@ -2059,17 +1978,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<Parse::NoneNodeId>(
-      {.ir_name = "<witness>",
-       .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<Kind>();
-
-  TypeId type_id;
-};
+//
+// This is a singleton instruction. However, it may still evolve into a more
+// standard type and be removed.
+using WitnessType = SingletonTypeInst<InstKind::WitnessType, "<witness>">;
 
 // These concepts are an implementation detail of the library, not public API.
 namespace Internal {