mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user