mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 20:50:13 +01:00
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:
co-authored by
Dana Jansens
parent
4821eec2f8
commit
93dc369ebd
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user