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
+21 -22
View File
@@ -148,22 +148,21 @@ static auto ScopeNeedsImplLookup(Context& context,
SemIR::InstId inst_id =
context.constant_values().GetInstId(name_scope_const_id);
CARBON_CHECK(inst_id.has_value());
SemIR::Inst inst = context.insts().Get(inst_id);
if (inst.Is<SemIR::FacetType>()) {
// Don't perform impl lookup if an associated entity is named as a member of
// a facet type.
return false;
}
if (inst.Is<SemIR::Namespace>()) {
if (context.insts().Is<SemIR::Namespace>(inst_id)) {
// Don't perform impl lookup if an associated entity is named as a namespace
// member.
// TODO: This case is not yet listed in the design.
return false;
}
auto type_id = context.types().GetTypeIdForTypeInstId(inst_id);
// Don't perform impl lookup if an associated entity is named as a member of
// a constrained facet type.
//
// Any other kind of scope is assumed to be a type that implements the
// interface containing the associated entity, and impl lookup is performed.
return true;
return !context.types().IsConstrainedFacetType(type_id);
}
static auto PerformImplWitnessAccessAndSubstitute(
@@ -490,9 +489,8 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
SemIR::InstId base_id, SemIR::NameId name_id,
bool required) -> SemIR::InstId {
// Unwrap the facet value in `base_id` if possible.
if (auto facet_value = TryGetCanonicalFacetValue(context, base_id);
facet_value.has_value()) {
base_id = facet_value;
if (auto facet = TryGetCanonicalFacet(context, base_id); facet.has_value()) {
base_id = facet;
}
// If the base is a name scope, such as a class or namespace, perform lookup
@@ -515,7 +513,7 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
// `base_id` (as part the class case above), as the `base_id` facet should
// have member names that directly name members of the `impl`.
auto base_type_id = context.insts().Get(base_id).type_id();
if (context.types().Is<SemIR::FacetType>(base_type_id)) {
if (context.types().IsConstrainedFacetType(base_type_id)) {
// Name lookup into a facet requires the facet type to be complete, so
// that any names available through the facet type are known for the
// facet.
@@ -589,15 +587,15 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
auto lookup_const_id =
context.types().GetConstantId(unqualified_base_type_id);
// TODO: If the type is a facet, we look through it into the facet's type (a
// FacetType) for names. According to the design, we shouldn't need to do
// this, as the facet should have member names that directly name members of
// the `impl`.
auto base_type_as_facet = GetCanonicalFacetOrTypeValue(
context, context.types().GetTypeInstId(base_type_id));
// TODO: If the type is a constrained facet, we look through it into the
// facet's type (a FacetType) for names. According to the design, we shouldn't
// need to do this, as the facet should have member names that directly name
// members of the `impl`.
auto base_type_as_facet =
GetCanonicalFacet(context, context.types().GetTypeInstId(base_type_id));
auto base_type_facet_type_id =
context.insts().Get(base_type_as_facet).type_id();
if (context.types().Is<SemIR::FacetType>(base_type_facet_type_id)) {
if (context.types().IsConstrainedFacetType(base_type_facet_type_id)) {
lookup_const_id = context.types().GetConstantId(base_type_facet_type_id);
}
@@ -606,11 +604,12 @@ static auto PerformActionHelper(Context& context, SemIR::LocId loc_id,
if (AppendLookupScopesForConstant(
context, loc_id, lookup_const_id,
// The `self_type_const_id` should be the type of `base_id` even if
// it's a facet.
// it's a constrained facet.
//
// TODO: This can be replaced with `lookup_const_id` once we stop
// having to look through the facet at its type for the scope.
context.types().GetConstantId(base_type_id), /*extended_scope=*/false,
// having to look through the constrained facet at its type for the
// scope.
base_type_id.AsConstantId(), /*extended_scope=*/false,
&lookup_scopes)) {
auto member_id = LookupMemberNameInScope(
context, loc_id, base_id, name_id, lookup_const_id, lookup_scopes,