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
+18 -24
View File
@@ -1195,15 +1195,14 @@ static auto CanRemoveQualifiers(SemIR::TypeQualifiers quals,
static auto DiagnoseConversionFailureToConstraintValue(
Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id,
SemIR::TypeId target_type_id) -> void {
CARBON_CHECK(context.types().IsFacetType(target_type_id));
CARBON_CHECK(context.types().Is<SemIR::FacetType>(target_type_id));
// If the source type is/has a facet value (converted with `as type` or
// otherwise), then we can include its `FacetType` in the diagnostic to help
// explain what interfaces the source type implements.
auto const_expr_id = GetCanonicalFacetOrTypeValue(context, expr_id);
// If the source is a facet with constraints (possibly converted to `type`),
// then we can include those constraints in the diagnostic.
auto const_expr_id = GetCanonicalFacet(context, expr_id);
auto const_expr_type_id = context.insts().Get(const_expr_id).type_id();
if (context.types().Is<SemIR::FacetType>(const_expr_type_id)) {
if (context.types().IsConstrainedFacetType(const_expr_type_id)) {
CARBON_DIAGNOSTIC(ConversionFailureFacetToFacet, Error,
"cannot convert type {0} that implements {1} into type "
"implementing {2}",
@@ -1556,7 +1555,7 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
}
}
if (sem_ir.types().IsFacetType(target.type_id)) {
if (sem_ir.types().Is<SemIR::FacetType>(target.type_id)) {
auto type_value_id = SemIR::TypeInstId::None;
// A tuple of types converts to type `type`.
@@ -1574,7 +1573,7 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
}
if (type_value_id != SemIR::InstId::None) {
if (sem_ir.types().Is<SemIR::FacetType>(target.type_id)) {
if (target.type_id != SemIR::TypeType::TypeId) {
// Use the converted `TypeType` value for converting to a facet.
value_id = type_value_id;
value_type_id = SemIR::TypeType::TypeId;
@@ -1585,21 +1584,18 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
}
}
// FacetType converts to Type by wrapping the facet value in
// FacetAccessType.
// All facets convert to `type` by wrapping the facet in FacetAccessType.
if (target.type_id == SemIR::TypeType::TypeId &&
sem_ir.types().Is<SemIR::FacetType>(value_type_id)) {
sem_ir.types().IsConstrainedFacetType(value_type_id)) {
return AddInst<SemIR::FacetAccessType>(
context, loc_id,
{.type_id = target.type_id, .facet_value_inst_id = value_id});
}
// Type values can convert to facet values, and facet values can convert to
// other facet values, as long as they satisfy the required interfaces of the
// target `FacetType`.
if (sem_ir.types().Is<SemIR::FacetType>(target.type_id) &&
sem_ir.types().IsOneOf<SemIR::TypeType, SemIR::FacetType>(
value_type_id)) {
// All facets (including types) can convert into other facets, as long as they
// satisfy the constraints of the target `FacetType`.
if (sem_ir.types().IsConstrainedFacetType(target.type_id) &&
sem_ir.types().Is<SemIR::FacetType>(value_type_id)) {
// TODO: Runtime facet values should be allowed to convert based on their
// FacetTypes, but we assume constant values for impl lookup at the moment.
if (!context.constant_values().Get(value_id).is_constant()) {
@@ -1608,9 +1604,9 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
}
// Get the canonical type for which we want to attach a new set of witnesses
// to match the requirements of the target FacetType.
// to match the requirements of the target `FacetType`.
auto type_inst_id = SemIR::TypeInstId::None;
if (sem_ir.types().Is<SemIR::FacetType>(value_type_id)) {
if (value_type_id != SemIR::TypeType::TypeId) {
type_inst_id = AddTypeInst<SemIR::FacetAccessType>(
context, loc_id,
{.type_id = SemIR::TypeType::TypeId,
@@ -1627,8 +1623,7 @@ static auto PerformBuiltinConversion(Context& context, SemIR::LocId loc_id,
// would evaluate back to the original SymbolicBinding as its canonical
// form. We can skip past the whole impl lookup step then and do that
// here.
auto facet_value_inst_id =
GetCanonicalFacetOrTypeValue(context, type_inst_id);
auto facet_value_inst_id = GetCanonicalFacet(context, type_inst_id);
if (sem_ir.insts().Get(facet_value_inst_id).type_id() == target.type_id) {
return facet_value_inst_id;
}
@@ -1742,8 +1737,7 @@ static auto PerformUserDefinedConversion(Context& context, SemIR::LocId loc_id,
target.kind == ConversionTarget::ExplicitAs ? 1
: target.kind == ConversionTarget::ExplicitUnsafeAs ? 2
: 0;
if (target.type_id == SemIR::TypeType::TypeId ||
context.types().Is<SemIR::FacetType>(target.type_id)) {
if (context.types().Is<SemIR::FacetType>(target.type_id)) {
CARBON_DIAGNOSTIC(
ConversionFailureNonTypeToFacet, Context,
"cannot{0:=0: implicitly|:} convert non-type value of type {1} "
@@ -2113,7 +2107,7 @@ static auto ConversionNeedsCompleteTarget(Context& context,
// We allow conversion to incomplete facet types, since their representation
// is fixed. This allows us to support using the `Self` of an interface inside
// its definition.
if (context.types().IsFacetType(target.type_id)) {
if (context.types().Is<SemIR::FacetType>(target.type_id)) {
return false;
}