Make SemIR::TypeType be an empty FacetType instruction (#7813)

The type `type` is now a `FacetType` inst with no constraints. This
brings the model implemented in the toolchain into better alignment with
the language design. The `SemIR::TypeType` struct remains as a scope for
holding the `TypeInstId`, `ConstantId`, and `TypeId` constants, but is
not an `InstKind` anymore.

The `TypeType` inst looks a lot like singletons, but there are many
`FacetType` insts so it doesn't quite fit that model. So we put it
alongside singletons with a fixed inst id but refer to it as a more
general "builtin" inst that is not a singleton.
`Namespace::PackageInstId` is similar, and we group it with `TypeType`
conceptually as another builtin instruction with a fixed id.

No conversion is needed anymore to use a `type` as a facet, since types
also have a `FacetType` type. This simplifies and removes a number of
helpers and branches throughout the code.

The `TypeType` inst is now part of the constant store, so we end up
printing it in the constants block in every test. But it's also named
`type` rather than `%type` to preserve the majority of existing
formatting behaviour, though this does look different from other
constants.

Assisted-by: Opus 5 was used to generate a first draft and validate the
refactoring. Though nearly everything non-trivial the tool wrote has
been modified or rewritten.
This commit is contained in:
Dana Jansens
2026-09-22 15:04:40 +00:00
committed by GitHub
parent 83ca5b71e3
commit fcae9610bd
795 changed files with 9505 additions and 8048 deletions
+20 -6
View File
@@ -20,6 +20,7 @@
#include "toolchain/base/value_store_impl.h"
#include "toolchain/base/yaml.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/sem_ir/constant.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/inst.h"
#include "toolchain/sem_ir/inst_kind.h"
@@ -56,7 +57,8 @@ File::File(const Parse::Tree* parse_tree, CheckIRId check_ir_id,
// 1 reserved id for `ObserveBlockId::Empty`.
observe_blocks_(allocator_, check_ir_id, 1),
associated_constants_(check_ir_id),
declared_facet_types_(check_ir_id),
// 1 reserved id for `DeclaredFacetTypeId::Empty`.
declared_facet_types_(check_ir_id, 1),
identified_facet_types_(check_ir_id),
impls_(*this),
specific_interfaces_(check_ir_id),
@@ -67,10 +69,9 @@ File::File(const Parse::Tree* parse_tree, CheckIRId check_ir_id,
import_irs_(check_ir_id, 2),
clang_decls_(check_ir_id),
clang_decl_signatures_(check_ir_id),
// The `+1` prevents adding a tag to the global `NameSpace::PackageInstId`
// instruction. It's not a "singleton" instruction, but it's a unique
// instruction id that comes right after the singletons.
insts_(this, SingletonInstKinds.size() + 1),
// We have some builtin instructions that have untagged fixed indices, and
// `NumBuiltinInsts` tracks how many.
insts_(this, NumBuiltinInsts),
vtables_(check_ir_id),
constant_values_(ConstantId::NotConstant, &insts_),
inst_blocks_(allocator_, check_ir_id),
@@ -103,7 +104,20 @@ File::File(const Parse::Tree* parse_tree, CheckIRId check_ir_id,
{.value_repr = {.kind = ValueRepr::Copy, .type_id = InstType::TypeId},
.object_layout = SemIR::ObjectLayout::Empty()});
insts_.Reserve(SingletonInstKinds.size());
auto empty_facet_type_id = declared_facet_types_.Add({});
CARBON_CHECK(empty_facet_type_id == DeclaredFacetTypeId::Empty);
insts_.Reserve(NumBuiltinInsts);
// Construct the `TypeType:TypeInstId` inst first, as it has InstId of 0. It
// goes in the `constants_` store so that all empty facet types dedupe to the
// singleton's constant value.
auto type_type_const_id = constants_.GetOrAdd(
FacetType{.type_id = TypeType::TypeId,
.declared_facet_type_id = DeclaredFacetTypeId::Empty},
ConstantDependence::None);
CARBON_CHECK(type_type_const_id == TypeType::ConstantId);
CARBON_CHECK(constant_values_.GetInstId(type_type_const_id) ==
TypeType::TypeInstId);
for (auto kind : SingletonInstKinds) {
auto inst_id =
insts_.AddInNoBlock(LocIdAndInst::NoLoc(Inst::MakeSingleton(kind)));