diff --git a/toolchain/check/testdata/basics/builtin_insts.carbon b/toolchain/check/testdata/basics/builtin_insts.carbon index f5e51deca940..923caf812914 100644 --- a/toolchain/check/testdata/basics/builtin_insts.carbon +++ b/toolchain/check/testdata/basics/builtin_insts.carbon @@ -33,31 +33,31 @@ // CHECK:STDOUT: type_block0: {} // CHECK:STDOUT: insts: // CHECK:STDOUT: instTypeType: {kind: TypeType, type: typeTypeType} -// CHECK:STDOUT: instErrorInst: {kind: ErrorInst, type: typeError} // CHECK:STDOUT: instAutoType: {kind: AutoType, type: typeTypeType} // CHECK:STDOUT: instBoolType: {kind: BoolType, type: typeTypeType} +// CHECK:STDOUT: instBoundMethodType: {kind: BoundMethodType, type: typeTypeType} +// CHECK:STDOUT: instErrorInst: {kind: ErrorInst, type: typeError} // CHECK:STDOUT: instIntLiteralType: {kind: IntLiteralType, type: typeTypeType} // CHECK:STDOUT: instLegacyFloatType: {kind: LegacyFloatType, type: typeTypeType} -// CHECK:STDOUT: instStringType: {kind: StringType, type: typeTypeType} -// CHECK:STDOUT: instBoundMethodType: {kind: BoundMethodType, type: typeTypeType} -// CHECK:STDOUT: instSpecificFunctionType: {kind: SpecificFunctionType, type: typeTypeType} // CHECK:STDOUT: instNamespaceType: {kind: NamespaceType, type: typeTypeType} -// CHECK:STDOUT: instWitnessType: {kind: WitnessType, type: typeTypeType} +// CHECK:STDOUT: instSpecificFunctionType: {kind: SpecificFunctionType, type: typeTypeType} +// CHECK:STDOUT: instStringType: {kind: StringType, type: typeTypeType} // CHECK:STDOUT: instVtableType: {kind: VtableType, type: typeTypeType} +// CHECK:STDOUT: instWitnessType: {kind: WitnessType, type: typeTypeType} // CHECK:STDOUT: 'inst+0': {kind: Namespace, arg0: name_scope0, arg1: inst, type: type(instNamespaceType)} // CHECK:STDOUT: constant_values: // CHECK:STDOUT: instTypeType: templateConstant(instTypeType) -// CHECK:STDOUT: instErrorInst: templateConstant(instErrorInst) // CHECK:STDOUT: instAutoType: templateConstant(instAutoType) // CHECK:STDOUT: instBoolType: templateConstant(instBoolType) +// CHECK:STDOUT: instBoundMethodType: templateConstant(instBoundMethodType) +// CHECK:STDOUT: instErrorInst: templateConstant(instErrorInst) // CHECK:STDOUT: instIntLiteralType: templateConstant(instIntLiteralType) // CHECK:STDOUT: instLegacyFloatType: templateConstant(instLegacyFloatType) -// CHECK:STDOUT: instStringType: templateConstant(instStringType) -// CHECK:STDOUT: instBoundMethodType: templateConstant(instBoundMethodType) -// CHECK:STDOUT: instSpecificFunctionType: templateConstant(instSpecificFunctionType) // CHECK:STDOUT: instNamespaceType: templateConstant(instNamespaceType) -// CHECK:STDOUT: instWitnessType: templateConstant(instWitnessType) +// CHECK:STDOUT: instSpecificFunctionType: templateConstant(instSpecificFunctionType) +// CHECK:STDOUT: instStringType: templateConstant(instStringType) // CHECK:STDOUT: instVtableType: templateConstant(instVtableType) +// CHECK:STDOUT: instWitnessType: templateConstant(instWitnessType) // CHECK:STDOUT: 'inst+0': templateConstant(inst+0) // CHECK:STDOUT: symbolic_constants: {} // CHECK:STDOUT: inst_blocks: diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 6a6635dd35d4..e64d5e4f984e 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -22,6 +22,7 @@ cc_library( name = "typed_insts", srcs = [ "builtin_inst_kind.cpp", + "ids.cpp", "inst_kind.cpp", ], hdrs = [ @@ -29,6 +30,7 @@ cc_library( "id_kind.h", "ids.h", "inst_kind.h", + "singleton_insts.h", "typed_insts.h", ], textual_hdrs = ["inst_kind.def"], diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 0cb334482b26..d9987d161b11 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -11,7 +11,6 @@ #include "toolchain/base/shared_value_stores.h" #include "toolchain/base/yaml.h" #include "toolchain/parse/node_ids.h" -#include "toolchain/sem_ir/builtin_inst_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" #include "toolchain/sem_ir/inst_kind.h" @@ -42,23 +41,12 @@ File::File(CheckIRId check_ir_id, types_.SetValueRepr(TypeId::Error, {.kind = ValueRepr::Copy, .type_id = TypeId::Error}); - insts_.Reserve(BuiltinInstKind::ValidCount); -// Error uses a self-referential type so that it's not accidentally treated as -// a normal type. Every other builtin is a type, including the -// self-referential TypeType. -#define CARBON_SEM_IR_BUILTIN_INST_KIND(Name) \ - insts_.AddInNoBlock(LocIdAndInst::NoLoc( \ - {.type_id = BuiltinInstKind::Name == BuiltinInstKind::ErrorInst \ - ? TypeId::Error \ - : TypeId::TypeType})); -#include "toolchain/sem_ir/inst_kind.def" - CARBON_CHECK(insts_.size() == BuiltinInstKind::ValidCount, - "Builtins should produce {0} insts, actual: {1}", - BuiltinInstKind::ValidCount, insts_.size()); - for (auto i : llvm::seq(BuiltinInstKind::ValidCount)) { - auto builtin_id = SemIR::InstId(i); - constant_values_.Set(builtin_id, - SemIR::ConstantId::ForTemplateConstant(builtin_id)); + insts_.Reserve(SingletonInstKinds.size()); + for (auto kind : SingletonInstKinds) { + auto inst_id = + insts_.AddInNoBlock(LocIdAndInst::NoLoc(Inst::MakeSingleton(kind))); + constant_values_.Set(inst_id, + SemIR::ConstantId::ForTemplateConstant(inst_id)); } } @@ -99,9 +87,9 @@ auto File::Verify() const -> ErrorOr { return Success(); } -auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping { - return Yaml::OutputMapping([this, - include_builtins](Yaml::OutputMapping::Map map) { +auto File::OutputYaml(bool include_singletons) const -> Yaml::OutputMapping { + return Yaml::OutputMapping([this, include_singletons]( + Yaml::OutputMapping::Map map) { map.Add("filename", filename_); map.Add( "sem_ir", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { @@ -118,7 +106,7 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping { map.Add("type_blocks", type_blocks_.OutputYaml()); map.Add( "insts", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { - int start = include_builtins ? 0 : BuiltinInstKind::ValidCount; + int start = include_singletons ? 0 : SingletonInstKinds.size(); for (int i : llvm::seq(start, insts_.size())) { auto id = InstId(i); map.Add(PrintToString(id), @@ -128,7 +116,7 @@ auto File::OutputYaml(bool include_builtins) const -> Yaml::OutputMapping { map.Add("constant_values", Yaml::OutputMapping([&](Yaml::OutputMapping::Map map) { int start = - include_builtins ? 0 : BuiltinInstKind::ValidCount; + include_singletons ? 0 : SingletonInstKinds.size(); for (int i : llvm::seq(start, insts_.size())) { auto id = InstId(i); auto value = constant_values_.Get(id); diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index b9ac4afe9d70..528ff15e1eb9 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -28,6 +28,7 @@ #include "toolchain/sem_ir/interface.h" #include "toolchain/sem_ir/name.h" #include "toolchain/sem_ir/name_scope.h" +#include "toolchain/sem_ir/singleton_insts.h" #include "toolchain/sem_ir/struct_type_field.h" #include "toolchain/sem_ir/type.h" #include "toolchain/sem_ir/type_info.h" @@ -48,16 +49,13 @@ class File : public Printable { // Verifies that invariants of the semantics IR hold. auto Verify() const -> ErrorOr; - // Prints the full IR. Allow omitting builtins so that unrelated changes are - // less likely to alter test golden files. - // TODO: In the future, the things to print may change, for example by adding - // preludes. We may then want the ability to omit other things similar to - // builtins. - auto Print(llvm::raw_ostream& out, bool include_builtins = false) const + // Prints the full IR. Allow omitting singletons so that changes to the list + // of singletons won't churn golden test file content. + auto Print(llvm::raw_ostream& out, bool include_singletons = false) const -> void { - Yaml::Print(out, OutputYaml(include_builtins)); + Yaml::Print(out, OutputYaml(include_singletons)); } - auto OutputYaml(bool include_builtins) const -> Yaml::OutputMapping; + auto OutputYaml(bool include_singletons) const -> Yaml::OutputMapping; // Collects memory usage of members. auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const @@ -241,8 +239,8 @@ class File : public Printable { // the data is provided by allocator_. BlockValueStore type_blocks_; - // All instructions. The first entries will always be BuiltinInsts, at - // indices matching BuiltinInstKind ordering. + // All instructions. The first entries will always be the singleton + // instructions. InstStore insts_; // Storage for name scopes. diff --git a/toolchain/sem_ir/ids.cpp b/toolchain/sem_ir/ids.cpp new file mode 100644 index 000000000000..6acce679e720 --- /dev/null +++ b/toolchain/sem_ir/ids.cpp @@ -0,0 +1,174 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "toolchain/sem_ir/ids.h" + +#include "toolchain/sem_ir/singleton_insts.h" + +namespace Carbon::SemIR { + +auto InstId::Print(llvm::raw_ostream& out) const -> void { + out << "inst"; + if (!is_valid()) { + IdBase::Print(out); + } else if (is_builtin()) { + out << builtin_inst_kind(); + } else { + // Use the `+` as a small reminder that this is a delta, rather than an + // absolute index. + out << "+" << index - SingletonInstKinds.size(); + } +} + +auto ConstantId::Print(llvm::raw_ostream& out, bool disambiguate) const + -> void { + if (!is_valid()) { + IdBase::Print(out); + } else if (is_template()) { + if (disambiguate) { + out << "templateConstant("; + } + out << template_inst_id(); + if (disambiguate) { + out << ")"; + } + } else if (is_symbolic()) { + out << "symbolicConstant" << symbolic_index(); + } else { + out << "runtime"; + } +} + +auto RuntimeParamIndex::Print(llvm::raw_ostream& out) const -> void { + out << "runtime_param"; + if (*this == Unknown) { + out << ""; + } else { + IndexBase::Print(out); + } +} + +auto GenericInstIndex::Print(llvm::raw_ostream& out) const -> void { + out << "genericInst"; + if (is_valid()) { + out << (region() == Declaration ? "InDecl" : "InDef") << index(); + } else { + out << ""; + } +} + +auto BoolValue::Print(llvm::raw_ostream& out) const -> void { + if (*this == False) { + out << "false"; + } else if (*this == True) { + out << "true"; + } else { + CARBON_FATAL("Invalid bool value {0}", index); + } +} + +auto IntKind::Print(llvm::raw_ostream& out) const -> void { + if (*this == Unsigned) { + out << "unsigned"; + } else if (*this == Signed) { + out << "signed"; + } else { + CARBON_FATAL("Invalid int kind value {0}", index); + } +} + +auto NameId::ForIdentifier(IdentifierId id) -> NameId { + if (id.index >= 0) { + return NameId(id.index); + } else if (!id.is_valid()) { + return NameId::Invalid; + } else { + CARBON_FATAL("Unexpected identifier ID {0}", id); + } +} + +auto NameId::Print(llvm::raw_ostream& out) const -> void { + out << "name"; + if (*this == SelfValue) { + out << "SelfValue"; + } else if (*this == SelfType) { + out << "SelfType"; + } else if (*this == PeriodSelf) { + out << "PeriodSelf"; + } else if (*this == ReturnSlot) { + out << "ReturnSlot"; + } else if (*this == PackageNamespace) { + out << "PackageNamespace"; + } else if (*this == Base) { + out << "Base"; + } else { + CARBON_CHECK(!is_valid() || index >= 0, "Unknown index {0}", index); + IdBase::Print(out); + } +} + +auto InstBlockId::Print(llvm::raw_ostream& out) const -> void { + if (*this == Unreachable) { + out << "unreachable"; + } else if (*this == Empty) { + out << "empty"; + } else if (*this == Exports) { + out << "exports"; + } else if (*this == ImportRefs) { + out << "import_refs"; + } else if (*this == GlobalInit) { + out << "global_init"; + } else { + out << "block"; + IdBase::Print(out); + } +} + +auto TypeId::Print(llvm::raw_ostream& out) const -> void { + out << "type"; + if (*this == TypeType) { + out << "TypeType"; + } else if (*this == AutoType) { + out << "AutoType"; + } else if (*this == Error) { + out << "Error"; + } else { + out << "("; + AsConstantId().Print(out, /*disambiguate=*/false); + out << ")"; + } +} + +auto LibraryNameId::ForStringLiteralValueId(StringLiteralValueId id) + -> LibraryNameId { + CARBON_CHECK(id.index >= InvalidIndex, "Unexpected library name ID {0}", id); + if (id == StringLiteralValueId::Invalid) { + // Prior to SemIR, we use invalid to indicate `default`. + return LibraryNameId::Default; + } else { + return LibraryNameId(id.index); + } +} + +auto LibraryNameId::Print(llvm::raw_ostream& out) const -> void { + out << "libraryName"; + if (*this == Default) { + out << "Default"; + } else if (*this == Error) { + out << ""; + } else { + IdBase::Print(out); + } +} + +auto LocId::Print(llvm::raw_ostream& out) const -> void { + out << "loc_"; + if (is_node_id() || !is_valid()) { + out << node_id(); + } else { + out << import_ir_inst_id(); + } +} + +} // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index c0e25d15ae2c..08f3a4c30800 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -66,18 +66,7 @@ struct InstId : public IdBase, public Printable { return BuiltinInstKind::FromInt(index); } - auto Print(llvm::raw_ostream& out) const -> void { - out << "inst"; - if (!is_valid()) { - IdBase::Print(out); - } else if (is_builtin()) { - out << builtin_inst_kind(); - } else { - // Use the `+` as a small reminder that this is a delta, rather than an - // absolute index. - out << "+" << index - BuiltinInstKind::ValidCount; - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr InstId InstId::Invalid = InstId(InvalidIndex); @@ -165,23 +154,7 @@ struct ConstantId : public IdBase, public Printable { // template constants should be wrapped with "templateConstant(...)" so that // they aren't printed the same as an InstId. This can be set to false if // there is no risk of ambiguity. - auto Print(llvm::raw_ostream& out, bool disambiguate = true) const -> void { - if (!is_valid()) { - IdBase::Print(out); - } else if (is_template()) { - if (disambiguate) { - out << "templateConstant("; - } - out << template_inst_id(); - if (disambiguate) { - out << ")"; - } - } else if (is_symbolic()) { - out << "symbolicConstant" << symbolic_index(); - } else { - out << "runtime"; - } - } + auto Print(llvm::raw_ostream& out, bool disambiguate = true) const -> void; private: friend class ConstantValueStore; @@ -266,14 +239,7 @@ struct RuntimeParamIndex : public IndexBase, using IndexBase::IndexBase; - auto Print(llvm::raw_ostream& out) const -> void { - out << "runtime_param"; - if (*this == Unknown) { - out << ""; - } else { - IndexBase::Print(out); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr RuntimeParamIndex RuntimeParamIndex::Invalid = @@ -441,14 +407,7 @@ struct GenericInstIndex : public IndexBase, public Printable { return IndexBase::index >= 0 ? Declaration : Definition; } - auto Print(llvm::raw_ostream& out) const -> void { - out << "genericInst"; - if (is_valid()) { - out << (region() == Declaration ? "InDecl" : "InDef") << index(); - } else { - out << ""; - } - } + auto Print(llvm::raw_ostream& out) const -> void; private: static constexpr auto MakeInvalid() -> GenericInstIndex { @@ -501,15 +460,7 @@ struct BoolValue : public IdBase, public Printable { } using IdBase::IdBase; - auto Print(llvm::raw_ostream& out) const -> void { - if (*this == False) { - out << "false"; - } else if (*this == True) { - out << "true"; - } else { - CARBON_FATAL("Invalid bool value {0}", index); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr BoolValue BoolValue::False = BoolValue(0); @@ -528,15 +479,7 @@ struct IntKind : public IdBase, public Printable { // Returns whether this type is signed. constexpr auto is_signed() -> bool { return *this == Signed; } - auto Print(llvm::raw_ostream& out) const -> void { - if (*this == Unsigned) { - out << "unsigned"; - } else if (*this == Signed) { - out << "signed"; - } else { - CARBON_FATAL("Invalid int kind value {0}", index); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr IntKind IntKind::Unsigned = IntKind(0); @@ -577,15 +520,7 @@ struct NameId : public IdBase, public Printable { static const int NonIndexValueCount; // Returns the NameId corresponding to a particular IdentifierId. - static auto ForIdentifier(IdentifierId id) -> NameId { - if (id.index >= 0) { - return NameId(id.index); - } else if (!id.is_valid()) { - return NameId::Invalid; - } else { - CARBON_FATAL("Unexpected identifier ID {0}", id); - } - } + static auto ForIdentifier(IdentifierId id) -> NameId; using IdBase::IdBase; @@ -595,25 +530,7 @@ struct NameId : public IdBase, public Printable { return index >= 0 ? IdentifierId(index) : IdentifierId::Invalid; } - auto Print(llvm::raw_ostream& out) const -> void { - out << "name"; - if (*this == SelfValue) { - out << "SelfValue"; - } else if (*this == SelfType) { - out << "SelfType"; - } else if (*this == PeriodSelf) { - out << "PeriodSelf"; - } else if (*this == ReturnSlot) { - out << "ReturnSlot"; - } else if (*this == PackageNamespace) { - out << "PackageNamespace"; - } else if (*this == Base) { - out << "Base"; - } else { - CARBON_CHECK(!is_valid() || index >= 0, "Unknown index {0}", index); - IdBase::Print(out); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr NameId NameId::Invalid = NameId(InvalidIndex); @@ -676,22 +593,7 @@ struct InstBlockId : public IdBase, public Printable { static const InstBlockId Unreachable; using IdBase::IdBase; - auto Print(llvm::raw_ostream& out) const -> void { - if (*this == Unreachable) { - out << "unreachable"; - } else if (*this == Empty) { - out << "empty"; - } else if (*this == Exports) { - out << "exports"; - } else if (*this == ImportRefs) { - out << "import_refs"; - } else if (*this == GlobalInit) { - out << "global_init"; - } else { - out << "block"; - IdBase::Print(out); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr InstBlockId InstBlockId::Empty = InstBlockId(0); @@ -756,20 +658,7 @@ struct TypeId : public IdBase, public Printable { // Returns the constant ID that defines the type. auto AsConstantId() const -> ConstantId { return ConstantId(index); } - auto Print(llvm::raw_ostream& out) const -> void { - out << "type"; - if (*this == TypeType) { - out << "TypeType"; - } else if (*this == AutoType) { - out << "AutoType"; - } else if (*this == Error) { - out << "Error"; - } else { - out << "("; - AsConstantId().Print(out, /*disambiguate=*/false); - out << ")"; - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr TypeId TypeId::TypeType = TypeId::ForTypeConstant( @@ -829,17 +718,7 @@ struct LibraryNameId : public IdBase, public Printable { static const LibraryNameId Error; // Returns the LibraryNameId for a library name as a string literal. - static auto ForStringLiteralValueId(StringLiteralValueId id) - -> LibraryNameId { - CARBON_CHECK(id.index >= InvalidIndex, "Unexpected library name ID {0}", - id); - if (id == StringLiteralValueId::Invalid) { - // Prior to SemIR, we use invalid to indicate `default`. - return LibraryNameId::Default; - } else { - return LibraryNameId(id.index); - } - } + static auto ForStringLiteralValueId(StringLiteralValueId id) -> LibraryNameId; using IdBase::IdBase; @@ -849,16 +728,7 @@ struct LibraryNameId : public IdBase, public Printable { return StringLiteralValueId(index); } - auto Print(llvm::raw_ostream& out) const -> void { - out << "libraryName"; - if (*this == Default) { - out << "Default"; - } else if (*this == Error) { - out << ""; - } else { - IdBase::Print(out); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr LibraryNameId LibraryNameId::Invalid = LibraryNameId(InvalidIndex); @@ -945,14 +815,7 @@ struct LocId : public IdBase, public Printable { return ImportIRInstId(InvalidIndex + ImportIRInstId::InvalidIndex - index); } - auto Print(llvm::raw_ostream& out) const -> void { - out << "loc_"; - if (is_node_id() || !is_valid()) { - out << node_id(); - } else { - out << import_ir_inst_id(); - } - } + auto Print(llvm::raw_ostream& out) const -> void; }; constexpr LocId LocId::Invalid = LocId(Parse::NodeId::Invalid); diff --git a/toolchain/sem_ir/inst.h b/toolchain/sem_ir/inst.h index ae43afc861af..ba02d016e8ce 100644 --- a/toolchain/sem_ir/inst.h +++ b/toolchain/sem_ir/inst.h @@ -19,6 +19,7 @@ #include "toolchain/sem_ir/builtin_inst_kind.h" #include "toolchain/sem_ir/id_kind.h" #include "toolchain/sem_ir/inst_kind.h" +#include "toolchain/sem_ir/singleton_insts.h" #include "toolchain/sem_ir/typed_insts.h" namespace Carbon::SemIR { @@ -127,6 +128,18 @@ concept InstLikeType = requires { sizeof(InstLikeTypeInfo); }; // data where the instruction's kind is not known. class Inst : public Printable { public: + // Makes an instruction for a singleton. This exists to support simple + // construction of all singletons by File. + static auto MakeSingleton(InstKind kind) -> Inst { + CARBON_CHECK(IsSingletonInstKind(kind)); + // Error uses a self-referential type so that it's not accidentally treated + // as a normal type. Every other builtin is a type, including the + // self-referential TypeType. + auto type_id = + kind == InstKind::ErrorInst ? TypeId::Error : TypeId::TypeType; + return Inst(kind, type_id, InstId::InvalidIndex, InstId::InvalidIndex); + } + template requires Internal::InstLikeType // NOLINTNEXTLINE(google-explicit-constructor) diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 141f0b2ed698..28b9ddf3965d 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -37,17 +37,17 @@ #endif CARBON_SEM_IR_BUILTIN_INST_KIND(TypeType) -CARBON_SEM_IR_BUILTIN_INST_KIND(ErrorInst) CARBON_SEM_IR_BUILTIN_INST_KIND(AutoType) CARBON_SEM_IR_BUILTIN_INST_KIND(BoolType) +CARBON_SEM_IR_BUILTIN_INST_KIND(BoundMethodType) +CARBON_SEM_IR_BUILTIN_INST_KIND(ErrorInst) CARBON_SEM_IR_BUILTIN_INST_KIND(IntLiteralType) CARBON_SEM_IR_BUILTIN_INST_KIND(LegacyFloatType) -CARBON_SEM_IR_BUILTIN_INST_KIND(StringType) -CARBON_SEM_IR_BUILTIN_INST_KIND(BoundMethodType) -CARBON_SEM_IR_BUILTIN_INST_KIND(SpecificFunctionType) CARBON_SEM_IR_BUILTIN_INST_KIND(NamespaceType) -CARBON_SEM_IR_BUILTIN_INST_KIND(WitnessType) +CARBON_SEM_IR_BUILTIN_INST_KIND(SpecificFunctionType) +CARBON_SEM_IR_BUILTIN_INST_KIND(StringType) CARBON_SEM_IR_BUILTIN_INST_KIND(VtableType) +CARBON_SEM_IR_BUILTIN_INST_KIND(WitnessType) // For each instruction kind declared here there is a matching definition in // `typed_insts.h`. diff --git a/toolchain/sem_ir/inst_kind.h b/toolchain/sem_ir/inst_kind.h index 671c90cc0ac9..eb603e377c80 100644 --- a/toolchain/sem_ir/inst_kind.h +++ b/toolchain/sem_ir/inst_kind.h @@ -104,6 +104,7 @@ class InstKind : public CARBON_ENUM_BASE(InstKind) { using EnumBase::AsInt; using EnumBase::FromInt; + using EnumBase::Make; // Returns true if the kind matches any of the provided instructions' kinds. template diff --git a/toolchain/sem_ir/singleton_insts.h b/toolchain/sem_ir/singleton_insts.h new file mode 100644 index 000000000000..043c507fb8b0 --- /dev/null +++ b/toolchain/sem_ir/singleton_insts.h @@ -0,0 +1,76 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef CARBON_TOOLCHAIN_SEM_IR_SINGLETON_INSTS_H_ +#define CARBON_TOOLCHAIN_SEM_IR_SINGLETON_INSTS_H_ + +#include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/inst_kind.h" + +namespace Carbon::SemIR { + +// The canonical list of singleton kinds. The order of `TypeType` is +// significant because other singletons use it as a type. +static constexpr std::array SingletonInstKinds = { + InstKind::TypeType, + InstKind::AutoType, + InstKind::BoolType, + InstKind::BoundMethodType, + InstKind::ErrorInst, + InstKind::IntLiteralType, + InstKind::LegacyFloatType, + InstKind::NamespaceType, + InstKind::SpecificFunctionType, + InstKind::StringType, + InstKind::VtableType, + InstKind::WitnessType, +}; + +// Returns true if the InstKind is a singleton. +inline constexpr auto IsSingletonInstKind(InstKind kind) -> bool; + +// Provides the InstId for singleton instructions. These are exposed as +// `InstT::SingletonInstId` in `typed_insts.h`. +template + requires(IsSingletonInstKind(InstKind::Make(Kind))) +inline constexpr auto MakeSingletonInstId() -> InstId; + +// Only implementation details are below. + +namespace Internal { + +// Returns the index for a singleton instruction, or -1 if it's not a singleton. +inline constexpr auto GetSingletonInstIndex(InstKind kind) -> int32_t { + for (int32_t i = 0; i < static_cast(SingletonInstKinds.size()); + ++i) { + if (SingletonInstKinds[i] == kind) { + return i; + } + } + return -1; +} + +} // namespace Internal + +inline constexpr auto IsSingletonInstKind(InstKind kind) -> bool { + return Internal::GetSingletonInstIndex(kind) >= 0; +} + +template + requires(IsSingletonInstKind(InstKind::Make(Kind))) +inline constexpr auto MakeSingletonInstId() -> InstId { + auto index = Internal::GetSingletonInstIndex(InstKind::Make(Kind)); + return InstId(index); +} + +// TODO: This verifies values match while working on removing +// `CARBON_SEM_IR_BUILTIN_INST_KIND`. +#define CARBON_SEM_IR_BUILTIN_INST_KIND(Name) \ + static_assert(InstId::Builtin##Name == \ + MakeSingletonInstId()); +#include "toolchain/sem_ir/inst_kind.def" + +} // namespace Carbon::SemIR + +#endif // CARBON_TOOLCHAIN_SEM_IR_SINGLETON_INSTS_H_ diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 73469dd6e034..c0bb7c61414e 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -10,6 +10,7 @@ #include "toolchain/sem_ir/builtin_inst_kind.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst_kind.h" +#include "toolchain/sem_ir/singleton_insts.h" // Representations for specific kinds of instructions. // @@ -17,6 +18,7 @@ // // - Either a `Kind` constant, or a `Kinds` constant and an `InstKind kind;` // member. These are described below. +// - Optionally, a `SingletonInstId` if it is a singleton instruction. // - Optionally, a `TypeId type_id;` member, for instructions that produce a // value. This includes instructions that produce an abstract value, such as a // `Namespace`, for which a placeholder type should be used. @@ -49,28 +51,25 @@ namespace Carbon::SemIR { // Used for the type of patterns that do not match a fixed type. -// -// TODO: Annotate as a builtin. struct AutoType { static constexpr auto Kind = InstKind::AutoType.Define( {.ir_name = "auto", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; // The type of bool literals and branch conditions, bool. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. -// -// TODO: Annotate as a builtin. 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 SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -401,15 +400,15 @@ struct BoundMethod { }; // The type of bound method values. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. 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 SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -584,13 +583,12 @@ 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. -// -// TODO: Annotate as a builtin. struct ErrorInst { static constexpr auto Kind = InstKind::ErrorInst.Define( {.ir_name = "", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -703,15 +701,15 @@ struct FloatType { // The legacy float type. This is currently used for real literals, and is // treated as f64. It's separate from `FloatType`, and should change to mirror // integers, likely replacing this with a `FloatLiteralType`. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct LegacyFloatType { static constexpr auto Kind = InstKind::LegacyFloatType.Define( {.ir_name = "f64", .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 SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -899,15 +897,15 @@ struct IntValue { // 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. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. 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 SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -953,15 +951,15 @@ struct Namespace { }; // The type of namespace and imported package names. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. 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 SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1185,15 +1183,15 @@ struct SpecificFunction { }; // The type of specific functions. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. 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 SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1223,15 +1221,15 @@ struct StringLiteral { }; // The type of string values and String literals. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. struct StringType { static constexpr auto Kind = InstKind::StringType.Define( {.ir_name = "String", .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 SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1370,13 +1368,12 @@ struct TupleValue { // Tracks expressions which are valid as types. This has a deliberately // self-referential type. -// -// TODO: Annotate as a builtin. struct TypeType { static constexpr auto Kind = InstKind::TypeType.Define( {.ir_name = "type", .is_type = InstIsType::Always, .constant_kind = InstConstantKind::Always}); + static constexpr auto SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1444,15 +1441,15 @@ struct VarStorage { }; // The type of virtual function tables. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. 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 SingletonInstId = MakeSingletonInstId(); TypeId type_id; }; @@ -1472,15 +1469,15 @@ struct WhereExpr { }; // The type of witnesses. -// -// Although this is a builtin, it may still evolve to a more standard type and -// be removed. 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 SingletonInstId = MakeSingletonInstId(); TypeId type_id; };