mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
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:
@@ -17,6 +17,7 @@
|
||||
#include "toolchain/diagnostics/diagnostic.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
#include "toolchain/sem_ir/name_scope.h"
|
||||
#include "toolchain/sem_ir/typed_insts.h"
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
@@ -204,15 +205,47 @@ auto DeclNameStack::LookupOrAddName(NameContext name_context,
|
||||
return SemIR::ScopeLookupResult::MakeNotFound();
|
||||
}
|
||||
|
||||
// Get the name scope and generic to use for associated entities in `scope`.
|
||||
// Typically this is None, in which case the input scope should be used, but
|
||||
// some entities have a separate generic and inner scope used for associated
|
||||
// entities.
|
||||
static auto GetAssociatedEntityScope(Context& context,
|
||||
const SemIR::NameScope& scope)
|
||||
-> std::pair<SemIR::NameScopeId, SemIR::GenericId> {
|
||||
auto scope_inst = context.insts().Get(scope.inst_id());
|
||||
CARBON_KIND_SWITCH(scope_inst) {
|
||||
case CARBON_KIND(SemIR::InterfaceDecl interface_decl): {
|
||||
const auto& interface =
|
||||
context.interfaces().Get(interface_decl.interface_id);
|
||||
return {interface.scope_with_self_id, interface.generic_with_self_id};
|
||||
}
|
||||
case CARBON_KIND(SemIR::InterfaceWithSelfDecl _): {
|
||||
CARBON_FATAL("Expected InterfaceDecl as qualifier scope");
|
||||
}
|
||||
case CARBON_KIND(SemIR::NamedConstraintDecl _): {
|
||||
// ResolveAsScope() does not allow named constraints as a scope qualifier.
|
||||
CARBON_FATAL(
|
||||
"Did not expect to find named constraint as scope qualifier");
|
||||
}
|
||||
case CARBON_KIND(SemIR::NamedConstraintWithSelfDecl _): {
|
||||
CARBON_FATAL("Expected NamedConstraintDecl as qualifier scope");
|
||||
}
|
||||
default:
|
||||
return {SemIR::NameScopeId::None, SemIR::GenericId::None};
|
||||
}
|
||||
}
|
||||
|
||||
// Push a scope corresponding to a name qualifier. For example, for
|
||||
// `fn Class(T:! type).F(n: i32)` we will push the scope for `Class(T:! type)`
|
||||
// between the scope containing the declaration of `T` and the scope
|
||||
// containing the declaration of `n`.
|
||||
//
|
||||
// Returns the NameScopeId to use as the parent scope of the next name.
|
||||
static auto PushNameQualifierScope(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::InstId scope_inst_id,
|
||||
SemIR::NameScopeId scope_id,
|
||||
SemIR::GenericId generic_id,
|
||||
bool has_error = false) -> void {
|
||||
bool has_error = false)
|
||||
-> SemIR::NameScopeId {
|
||||
// If the qualifier has no parameters, we don't need to keep around a
|
||||
// parameter scope.
|
||||
context.scope_stack().PopIfEmpty();
|
||||
@@ -233,20 +266,35 @@ static auto PushNameQualifierScope(Context& context, SemIR::LocId loc_id,
|
||||
// providing the definition.
|
||||
StartGenericDecl(context);
|
||||
|
||||
context.scope_stack().PushForEntity(scope_inst_id, scope_id, self_specific_id,
|
||||
has_error);
|
||||
const auto& scope = context.name_scopes().Get(scope_id);
|
||||
context.scope_stack().PushForEntity(scope.inst_id(), scope_id,
|
||||
self_specific_id, has_error);
|
||||
|
||||
// An interface also introduces its 'Self' parameter into scope, despite it
|
||||
// not being redeclared as part of the qualifier.
|
||||
if (auto interface_decl =
|
||||
context.insts().TryGetAs<SemIR::InterfaceDecl>(scope_inst_id)) {
|
||||
auto& interface = context.interfaces().Get(interface_decl->interface_id);
|
||||
auto [assoc_entity_scope_id, assoc_entity_generic_id] =
|
||||
GetAssociatedEntityScope(context, scope);
|
||||
|
||||
if (assoc_entity_scope_id.has_value()) {
|
||||
const auto& assoc_entity_scope =
|
||||
context.name_scopes().Get(assoc_entity_scope_id);
|
||||
// InterfaceDecl is the only inst that can be a scope qualifier and that has
|
||||
// an associated entity scope, the InterfaceWithSelfDecl.
|
||||
auto interface_decl = context.insts().GetAs<SemIR::InterfaceWithSelfDecl>(
|
||||
assoc_entity_scope.inst_id());
|
||||
auto& interface = context.interfaces().Get(interface_decl.interface_id);
|
||||
// An interface also introduces its 'Self' parameter into the associated
|
||||
// entity scope, despite it not being redeclared as part of the qualifier.
|
||||
context.scope_stack().AddCompileTimeBinding();
|
||||
context.scope_stack().PushCompileTimeBinding(interface.self_param_id);
|
||||
// Move into the interface-with-self scope.
|
||||
context.scope_stack().PushForEntity(
|
||||
assoc_entity_scope.inst_id(), assoc_entity_scope_id,
|
||||
context.generics().GetSelfSpecific(assoc_entity_generic_id), has_error);
|
||||
}
|
||||
|
||||
// Enter a parameter scope in case the qualified name itself has parameters.
|
||||
context.scope_stack().PushForSameRegion();
|
||||
|
||||
return assoc_entity_scope_id.has_value() ? assoc_entity_scope_id : scope_id;
|
||||
}
|
||||
|
||||
auto DeclNameStack::ApplyNameQualifier(const NameComponent& name) -> void {
|
||||
@@ -257,10 +305,9 @@ auto DeclNameStack::ApplyNameQualifier(const NameComponent& name) -> void {
|
||||
// Resolve the qualifier as a scope and enter the new scope.
|
||||
auto [scope_id, generic_id] = ResolveAsScope(name_context, name);
|
||||
if (scope_id.has_value()) {
|
||||
PushNameQualifierScope(*context_, name_context.loc_id,
|
||||
name_context.resolved_inst_id, scope_id, generic_id,
|
||||
context_->name_scopes().Get(scope_id).has_error());
|
||||
name_context.parent_scope_id = scope_id;
|
||||
name_context.parent_scope_id = PushNameQualifierScope(
|
||||
*context_, name_context.loc_id, scope_id, generic_id,
|
||||
context_->name_scopes().Get(scope_id).has_error());
|
||||
} else {
|
||||
name_context.state = NameContext::State::Error;
|
||||
}
|
||||
@@ -435,7 +482,11 @@ auto DeclNameStack::ResolveAsScope(const NameContext& name_context,
|
||||
name_context.resolved_inst_id);
|
||||
return InvalidResult;
|
||||
}
|
||||
return {interface_info.scope_id, interface_info.generic_id};
|
||||
// The scope and generic of an `I(T:! type)` is the outer
|
||||
// interface-without-self. That is the generic where parameters appear.
|
||||
// However when moving to the next qualifier, we need to move to the
|
||||
// interface-with-self for the associated entity name.
|
||||
return {interface_info.scope_without_self_id, interface_info.generic_id};
|
||||
}
|
||||
case CARBON_KIND(SemIR::Namespace resolved_inst): {
|
||||
auto scope_id = resolved_inst.name_scope_id;
|
||||
|
||||
Reference in New Issue
Block a user