mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Avoid cyclic lookup of an impl inside its own definition (#6629)
An interface A requiring another interface B means that an impl of A must verify that the self-type also impls B. The instructions created from this can involved a lookup that the self-type impls A, which end up finding the impl being defined. This is not problematic of itself, but it is problematic if these lookup instructions become part of the impl's generic definition. When we find a specific of that `impl as A` during impl lookup of A, and we resolve the specific definition, those lookup instructions are replayed. Doing so does another lookup for `impl as A`, which creates an infinitely recursive loop. To break this loop we move the lookup instructions done to verify that the self-type impls B outside of the definition of `impl as A`. This prevents them from being specialized. But it doesn't prevent us from diagnosing monomorphization errors properly. They just get diagnosed at the use of that invalid specific, instead of inside the verification of `impl as B` in the definition of `impl as A`.
This commit is contained in:
@@ -663,12 +663,21 @@ auto FinishImplWitness(Context& context, const SemIR::Impl& impl) -> void {
|
||||
// TODO: Diagnose if any declarations in the impl are not in used_decl_ids.
|
||||
}
|
||||
|
||||
auto CheckRequireDeclsSatisfied(Context& context, SemIR::Impl& impl) -> void {
|
||||
auto CheckRequireDeclsSatisfied(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::Impl& impl) -> void {
|
||||
if (impl.witness_id == SemIR::ErrorInst::InstId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& interface = context.interfaces().Get(impl.interface.interface_id);
|
||||
if (!interface.is_complete()) {
|
||||
// This will be diagnosed later. We check for required decls before starting
|
||||
// the definition to avoid inserting these lookups into the definition, as
|
||||
// the lookups can end up looking for the impl being defined, which creates
|
||||
// a cycle.
|
||||
return;
|
||||
}
|
||||
|
||||
auto require_ids =
|
||||
context.require_impls_blocks().Get(interface.require_impls_block_id);
|
||||
if (require_ids.empty()) {
|
||||
@@ -690,8 +699,7 @@ auto CheckRequireDeclsSatisfied(Context& context, SemIR::Impl& impl) -> void {
|
||||
context, require_specific, require.facet_type_inst_id);
|
||||
|
||||
auto result =
|
||||
LookupImplWitness(context, SemIR::LocId(impl.latest_decl_id()),
|
||||
self_const_id, facet_type_const_id);
|
||||
LookupImplWitness(context, loc_id, self_const_id, facet_type_const_id);
|
||||
// TODO: If the facet type contains 2 interfaces, and one is not `impl`ed,
|
||||
// it would be nice to diagnose which one was not `impl`ed, but that
|
||||
// requires LookupImplWitness to return a partial result, or take a
|
||||
@@ -708,7 +716,7 @@ auto CheckRequireDeclsSatisfied(Context& context, SemIR::Impl& impl) -> void {
|
||||
SemIR::SpecificInterface, SemIR::TypeId,
|
||||
SemIR::FacetTypeId);
|
||||
context.emitter().Emit(
|
||||
impl.latest_decl_id(), RequireImplsNotImplemented, impl.interface,
|
||||
loc_id, RequireImplsNotImplemented, impl.interface,
|
||||
context.types().GetTypeIdForTypeConstantId(self_const_id),
|
||||
context.insts()
|
||||
.GetAs<SemIR::FacetType>(facet_type_inst_id)
|
||||
|
||||
Reference in New Issue
Block a user