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>
This commit is contained in:
Jon Ross-Perkins
2025-10-29 16:52:46 +00:00
committed by GitHub
co-authored by Dana Jansens
parent 4821eec2f8
commit 93dc369ebd
4 changed files with 85 additions and 163 deletions
+4 -4
View File
@@ -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
View File
@@ -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(__builtin_strlen(CStrFromTemplate<"test">()) == 4,
static_assert(TemplateAsStringLiteral<"test">().size() == 4,
"Not usable in a `constexpr` context.");
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
View File
@@ -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
View File
@@ -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 {