Add an interface-with-self generic to each interface and same for constraints (#6667)

Currently each interface has a `Self` facet internally that becomes a
binding to every entity inside the interface: associated constants,
functions, and require decls. Each of these has to be independently
generic as a result. This makes is challenging in extended name lookup
to move into an extended scope of an interface, as we have a specific
for the interface, but the names within require a different specific
that includes a `Self` facet value.

We generalize this relationship by adding a second generic to Interface,
called `generic_with_self`. When we want to work with entities inside
the interface, we move from the interface-without-specific to the
interface-with-self specific by adding a Self to the specific. This is
done independently of any particular entity inside the Interface, as
those entities are now all members of the interface-with-self generic.

Associated constants no longer need a generic of their own, as they do
not have separate generic bindings. Functions retain a generic, but if
the function has no generic arguments, it will have no bindings of its
own now.

Require decls retain a generic so that their specific can be
instantiated separately from the interface. Requiring the interface to
be complete does not require the types in a require decl to be complete
unless it is modified by `extend`. So we allow them to be completed
later by keeping them in a separate generic.

Named constraints look like interfaces and gain the additional inner
generic-with-self, with the same relationship to require decls.

This removes the need for name lookup to perform Substitution of a Self
facet into the extended scope instruction. Instead, the
`SpecificConstant` instruction inserted by a `require` decl is part of
the interface-with-self generic. When looking through a FacetType for
extended scopes, for each interface, we push the scope with the specific
for the interface-with-self. Then the constant value of the
`SpecificConstant` is correctly modified by the provided self
automatically through applying that specific.
This commit is contained in:
Dana Jansens
2026-02-19 16:24:07 +00:00
committed by GitHub
parent e5b094fdad
commit 917a6ea971
419 changed files with 18175 additions and 11940 deletions
+140
View File
@@ -654,6 +654,8 @@ auto ResolveSpecificDecl(Context& context, SemIR::LocId loc_id,
// recursively resolve the same specific.
specific.decl_block_id = SemIR::InstBlockId::Empty;
// TODO: Store in the specific whether the declaration contains any
// ErrorInst values.
specific.decl_block_id =
TryEvalBlockForSpecific(context, loc_id, specific_id,
SemIR::GenericInstIndex::Region::Declaration);
@@ -720,6 +722,8 @@ auto ResolveSpecificDefinition(Context& context, SemIR::LocId loc_id,
// The generic is not defined yet.
return false;
}
// TODO: Store in the specific whether the definition contains any ErrorInst
// values.
specific.definition_block_id = TryEvalBlockForSpecific(
context, loc_id, specific_id, SemIR::GenericInstIndex::Definition);
}
@@ -739,4 +743,140 @@ auto DiagnoseIfGenericMissingExplicitParameters(
GenericMissingExplicitParameters);
}
static auto ValidateGenericWithoutAndWithSelfMatch(
Context& context, SemIR::GenericId generic_without_self_id,
SemIR::GenericId generic_with_self_id,
SemIR::SpecificId specific_without_self_id) -> void {
CARBON_CHECK(
generic_without_self_id.has_value() ==
specific_without_self_id.has_value(),
"Have a generic-without-self {0} but no specific-without-self {1} or "
"vice-versa",
generic_without_self_id, specific_without_self_id);
CARBON_CHECK(
generic_with_self_id.has_value(),
"Missing a generic ID for generic-with-self that should always exist.");
const auto& generic_with_self = context.generics().Get(generic_with_self_id);
auto generic_with_self_decl = context.insts().Get(generic_with_self.decl_id);
CARBON_CHECK(
(generic_with_self_decl.IsOneOf<SemIR::InterfaceWithSelfDecl,
SemIR::NamedConstraintWithSelfDecl>()),
"generic-with-self {0} should be a generic for an "
"InterfaceWithSelfDecl or NamedConstraintWithSelfDecl, found {1}",
generic_with_self, generic_with_self_decl);
if (!generic_without_self_id.has_value()) {
return;
}
const auto& generic_without_self =
context.generics().Get(generic_without_self_id);
const auto& specific_without_self =
context.specifics().Get(specific_without_self_id);
CARBON_CHECK(specific_without_self.generic_id == generic_without_self_id,
"specific-without-self {0} is not a specific for the "
"generic-without-self {1}",
specific_without_self, generic_without_self);
auto generic_without_self_decl =
context.insts().Get(generic_without_self.decl_id);
CARBON_KIND_SWITCH(generic_without_self_decl) {
case CARBON_KIND(SemIR::InterfaceDecl without_self_decl): {
auto with_self_decl =
generic_with_self_decl.As<SemIR::InterfaceWithSelfDecl>();
CARBON_CHECK(
without_self_decl.interface_id == with_self_decl.interface_id,
"Found generic-without-self for interface {0}, and generic-with-self "
"for interface {1}; expected the same interface for both",
context.interfaces().Get(without_self_decl.interface_id),
context.interfaces().Get(with_self_decl.interface_id));
break;
}
case CARBON_KIND(SemIR::NamedConstraintDecl without_self_decl): {
auto with_self_decl =
generic_with_self_decl.As<SemIR::NamedConstraintWithSelfDecl>();
CARBON_CHECK(
without_self_decl.named_constraint_id ==
with_self_decl.named_constraint_id,
"Found generic-without-self for constraint {0}, and "
"generic-with-self for named constraint {1}; expected the same named "
"constraint for both",
context.named_constraints().Get(
without_self_decl.named_constraint_id),
context.named_constraints().Get(with_self_decl.named_constraint_id));
break;
}
default:
CARBON_FATAL(
"generic-without-self {0} should be a generic for an InterfaceDecl "
"or NamedConstraintDecl, found {1}",
generic_without_self, generic_without_self_decl);
}
}
auto MakeSpecificWithInnerSelf(Context& context, SemIR::LocId loc_id,
SemIR::GenericId generic_without_self_id,
SemIR::GenericId generic_with_self_id,
SemIR::SpecificId specific_without_self_id,
SemIR::ConstantId self_facet)
-> SemIR::SpecificId {
ValidateGenericWithoutAndWithSelfMatch(context, generic_without_self_id,
generic_with_self_id,
specific_without_self_id);
auto outer_args_id =
context.specifics().GetArgsOrEmpty(specific_without_self_id);
auto outer_args = context.inst_blocks().Get(outer_args_id);
llvm::SmallVector<SemIR::InstId> args;
args.reserve(outer_args.size() + 1);
llvm::append_range(args, outer_args);
if (self_facet == SemIR::ErrorInst::ConstantId) {
args.push_back(SemIR::ErrorInst::InstId);
} else {
auto self_facet_inst_id = context.constant_values().GetInstId(self_facet);
CARBON_CHECK(context.types().Is<SemIR::FacetType>(
context.insts().Get(self_facet_inst_id).type_id()));
args.push_back(self_facet_inst_id);
}
auto specific_id = MakeSpecific(context, loc_id, generic_with_self_id, args);
ResolveSpecificDefinition(context, loc_id, specific_id);
return specific_id;
}
auto CopySpecificToGeneric(Context& context, SemIR::LocId loc_id,
SemIR::SpecificId specific_id,
SemIR::GenericId target_generic_id)
-> SemIR::SpecificId {
if (!specific_id.has_value()) {
const auto& target_generic = context.generics().Get(target_generic_id);
auto target_bindings =
context.inst_blocks().Get(target_generic.bindings_id);
CARBON_CHECK(target_bindings.empty());
return SemIR::SpecificId::None;
}
const auto& specific = context.specifics().Get(specific_id);
auto source_generic_id = specific.generic_id;
const auto& source_generic = context.generics().Get(source_generic_id);
const auto& target_generic = context.generics().Get(target_generic_id);
auto source_bindings = context.inst_blocks().Get(source_generic.bindings_id);
auto target_bindings = context.inst_blocks().Get(target_generic.bindings_id);
for (auto [source, target] :
llvm::zip_equal(source_bindings, target_bindings)) {
CARBON_CHECK(context.constant_values().Get(source) ==
context.constant_values().Get(target));
}
auto args_id = context.specifics().GetArgsOrEmpty(specific_id);
return MakeSpecific(context, loc_id, target_generic_id, args_id);
}
} // namespace Carbon::Check