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
+70 -19
View File
@@ -117,35 +117,77 @@ auto HandleParseNode(Context& context,
CARBON_CHECK(!interface_info.has_definition_started(),
"Can't merge with defined interfaces.");
interface_info.definition_id = decl_inst_id;
interface_info.scope_id = context.name_scopes().Add(
interface_info.scope_without_self_id = context.name_scopes().Add(
decl_inst_id, SemIR::NameId::None, interface_info.parent_scope_id);
// Start the definition of interface-without-self.
StartGenericDefinition(context, interface_info.generic_id);
context.inst_block_stack().Push();
// Enter the interface-without-self scope, which is used for the Self
// instruction, since it needs to reference the interface (without-self)
// generic. Self can't reference the interface-with-self generic since it's a
// parameter to the generic.
context.scope_stack().PushForEntity(
decl_inst_id, interface_info.scope_without_self_id,
context.generics().GetSelfSpecific(interface_info.generic_id));
// Declare and introduce `Self`. We model `Self` as a symbolic binding whose
// type is the interface, excluding any other interfaces mentioned by
// `require` declarations.
//
// This is an instruction in the interface-without-self so that we can apply a
// SpecificInterface to it, and get the inner `Self` as modified by any
// enclosing specific.
SemIR::TypeId self_type_id = GetInterfaceType(
context, interface_id,
context.generics().GetSelfSpecific(interface_info.generic_id));
interface_info.self_param_id = AddSelfSymbolicBindingToScope(
context, node_id, self_type_id, interface_info.scope_without_self_id,
/*is_template=*/false);
// Start the declaration of interface-with-self.
StartGenericDecl(context);
// Push `Self` as a parameter of the interface-with-self.
context.scope_stack().PushCompileTimeBinding(interface_info.self_param_id);
// Add the interface-with-self declaration and build the generic for it. This
// captures the `interface_info.self_param_id` as a parameter of the generic.
auto interface_with_self_decl =
SemIR::InterfaceWithSelfDecl{.interface_id = interface_id};
auto decl_with_self_inst_id =
AddPlaceholderInst(context, node_id, interface_with_self_decl);
auto generic_with_self_id = BuildGenericDecl(context, decl_with_self_inst_id);
interface_info.generic_with_self_id = generic_with_self_id;
ReplaceInstBeforeConstantUse(context, decl_with_self_inst_id,
interface_with_self_decl);
interface_info.scope_with_self_id =
context.name_scopes().Add(decl_with_self_inst_id, SemIR::NameId::None,
interface_info.scope_without_self_id);
// Set on the name scope that `M` is replaced by `Self.M`.
context.name_scopes()
.Get(interface_info.scope_id)
.Get(interface_info.scope_with_self_id)
.set_is_interface_definition();
auto self_specific_id =
context.generics().GetSelfSpecific(interface_info.generic_id);
// Start the definition of interface-with-self.
StartGenericDefinition(context, interface_info.generic_with_self_id);
StartGenericDefinition(context, interface_info.generic_id);
// Enter a scope for the interace-with-self.
context.scope_stack().PushForEntity(
decl_with_self_inst_id, interface_info.scope_with_self_id,
context.generics().GetSelfSpecific(interface_info.generic_with_self_id));
interface_info.body_block_without_self_id =
context.inst_block_stack().PeekOrAdd();
context.inst_block_stack().Push();
context.require_impls_stack().PushArray();
// We use the arg stack to build the witness table type.
context.args_type_info_stack().Push();
// Declare and introduce `Self`. We model `Self` as a symbolic binding whose
// type is the interface, excluding any other interfaces mentioned by
// `require` declarations.
SemIR::TypeId self_type_id =
GetInterfaceType(context, interface_id, self_specific_id);
interface_info.self_param_id =
AddSelfGenericParameter(context, node_id, self_type_id,
interface_info.scope_id, /*is_template=*/false);
// Enter the interface scope.
context.scope_stack().PushForEntity(decl_inst_id, interface_info.scope_id,
self_specific_id);
// TODO: Handle the case where there's control flow in the interface body. For
// example:
//
@@ -155,7 +197,8 @@ auto HandleParseNode(Context& context,
//
// We may need to track a list of instruction blocks here, as we do for a
// function.
interface_info.body_block_id = context.inst_block_stack().PeekOrAdd();
interface_info.body_block_with_self_id =
context.inst_block_stack().PeekOrAdd();
context.node_stack().Push(node_id, interface_id);
return true;
@@ -165,6 +208,7 @@ auto HandleParseNode(Context& context, Parse::InterfaceDefinitionId /*node_id*/)
-> bool {
auto interface_id =
context.node_stack().Pop<Parse::NodeKind::InterfaceDefinitionStart>();
// Pop the body_block_with_self.
context.inst_block_stack().Pop();
auto associated_entities_id = context.args_type_info_stack().Pop();
@@ -179,6 +223,13 @@ auto HandleParseNode(Context& context, Parse::InterfaceDefinitionId /*node_id*/)
interface_info.associated_entities_id = associated_entities_id;
}
// Finish the definition of interface-with-self.
FinishGenericDefinition(context, interface_info.generic_with_self_id);
// Pop the body_block_without_self.
context.inst_block_stack().Pop();
// Finish the definition of interface-without-self.
FinishGenericDefinition(context, interface_info.generic_id);
// The decl_name_stack and scopes are popped by `ProcessNodeIds`.