Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions common/template_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
23 changes: 16 additions & 7 deletions common/template_string_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down Expand Up @@ -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.
Expand All @@ -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));
Expand Down
1 change: 1 addition & 0 deletions toolchain/sem_ir/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
216 changes: 64 additions & 152 deletions toolchain/sem_ir/typed_insts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -571,18 +557,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 {
Expand Down Expand Up @@ -662,18 +637,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
Expand Down Expand Up @@ -1061,17 +1029,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.
Expand Down Expand Up @@ -1104,18 +1062,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
Expand Down Expand Up @@ -1244,18 +1195,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 {
Expand Down Expand Up @@ -1560,18 +1503,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.
Expand Down Expand Up @@ -1848,17 +1784,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 {
Expand Down Expand Up @@ -1995,17 +1921,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 {
Expand Down Expand Up @@ -2044,17 +1963,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.
Comment on lines +1967 to +1968
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've got a feeling at least half of these are just copy paste and don't actually make sense anymore, but I don't know what the comment means exactly so it's hard to say...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding what it means, I think zygoloid was thinking we'd want to construct them using more standard object representations, e.g. using TupleType, or perhaps just generating a bespoke type on demand, without a bespoke instruction.

I can't comment on whether TupleType is likely to happen, but I am mulling whether I can change these to eliminate the bespoke instructions, instead just having something like SingletonType that contains enough information to deduplicate itself.

using WitnessType = SingletonTypeInst<InstKind::WitnessType, "<witness>">;

// These concepts are an implementation detail of the library, not public API.
namespace Internal {
Expand Down
Loading