1212#include " toolchain/check/diagnostic_helpers.h"
1313#include " toolchain/check/eval.h"
1414#include " toolchain/check/generic.h"
15+ #include " toolchain/check/impl.h"
1516#include " toolchain/check/import_ref.h"
1617#include " toolchain/check/inst.h"
1718#include " toolchain/check/type.h"
@@ -220,6 +221,7 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
220221 // DeduceImplArguments can import new impls which can invalidate any
221222 // pointers into `context.impls()`.
222223 const SemIR::Impl& impl = context.impls ().Get (impl_id);
224+
223225 if (impl.generic_id .has_value ()) {
224226 specific_id =
225227 DeduceImplArguments (context, loc_id,
@@ -290,11 +292,12 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
290292 ResolveSpecificDefinition (context, loc_id, specific_id);
291293 }
292294
293- bool impl_is_effectively_final =
294- // TODO: impl.is_final ||
295- (context.constant_values ().Get (impl.self_id ).is_concrete () &&
296- context.constant_values ().Get (impl.constraint_id ).is_concrete ());
297- if (query_is_concrete || impl_is_effectively_final) {
295+ if (query_is_concrete || IsImplEffectivelyFinal (context, impl)) {
296+ // TODO: These final results should be cached somehow. Positive (non-None)
297+ // results could be cached globally, as they can not change. But
298+ // negative results can change after a final impl is written, so
299+ // they can only be cached in a limited way, or the cache needs to
300+ // be invalidated by writing a final impl that would match.
298301 return EvalImplLookupResult::MakeFinal (
299302 context.constant_values ().GetInstId (SemIR::GetConstantValueInSpecific (
300303 context.sem_ir (), specific_id, impl.witness_id )));
@@ -340,9 +343,9 @@ static auto FindWitnessInFacet(
340343// if not, it will evaluate to itself as a symbolic witness to be further
341344// evaluated with a more specific query when building a specific for the generic
342345// context the query came from.
343- static auto FindWitnessInImpls (Context& context, SemIR::LocId loc_id,
344- SemIR::ConstantId query_self_const_id,
345- SemIR::SpecificInterface interface)
346+ static auto GetOrAddLookupImplWitness (Context& context, SemIR::LocId loc_id,
347+ SemIR::ConstantId query_self_const_id,
348+ SemIR::SpecificInterface interface)
346349 -> SemIR::InstId {
347350 auto witness_const_id = EvalOrAddInst (
348351 context, loc_id.ToImplicit (),
@@ -434,9 +437,12 @@ auto LookupImplWitness(Context& context, SemIR::LocId loc_id,
434437 // do an O(N+M) merge instead of O(N*M) nested loops.
435438 auto result_witness_id =
436439 FindWitnessInFacet (context, loc_id, query_self_const_id, interface);
440+ // TODO: If the impl lookup finds a final impl, it should take precedence
441+ // over the witness from the facet value. See the test:
442+ // fail_todo_final_impl_precidence_over_facet_value.carbon.
437443 if (!result_witness_id.has_value ()) {
438- result_witness_id =
439- FindWitnessInImpls ( context, loc_id, query_self_const_id, interface);
444+ result_witness_id = GetOrAddLookupImplWitness (
445+ context, loc_id, query_self_const_id, interface);
440446 }
441447 if (result_witness_id.has_value ()) {
442448 result_witness_ids.push_back (result_witness_id);
@@ -491,11 +497,16 @@ struct CandidateImpl {
491497
492498// Returns the list of candidates impls for lookup to select from.
493499static auto CollectCandidateImplsForQuery (
494- Context& context, const TypeStructure& query_type_structure,
500+ Context& context, bool final_only,
501+ const TypeStructure& query_type_structure,
495502 SemIR::SpecificInterface& query_specific_interface)
496503 -> llvm::SmallVector<CandidateImpl> {
497504 llvm::SmallVector<CandidateImpl> candidate_impls;
498505 for (auto [id, impl] : context.impls ().enumerate()) {
506+ if (final_only && !IsImplEffectivelyFinal (context, impl)) {
507+ continue ;
508+ }
509+
499510 // If the impl's interface_id differs from the query, then this impl can
500511 // not possibly provide the queried interface.
501512 if (impl.interface .interface_id != query_specific_interface.interface_id ) {
@@ -593,7 +604,8 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
593604 QueryIsConcrete (context, query_self_const_id, query_specific_interface);
594605
595606 auto candidate_impls = CollectCandidateImplsForQuery (
596- context, query_type_structure, query_specific_interface);
607+ context, /* final_only=*/ false , query_type_structure,
608+ query_specific_interface);
597609
598610 for (const auto & candidate : candidate_impls) {
599611 // In deferred lookup for a symbolic impl witness, while building a
@@ -619,4 +631,48 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
619631 return EvalImplLookupResult::MakeNone ();
620632}
621633
634+ auto LookupFinalImplWitnessForSpecificInterface (
635+ Context& context, SemIR::LocId loc_id,
636+ SemIR::ConstantId query_self_const_id,
637+ SemIR::SpecificInterface query_specific_interface) -> SemIR::InstId {
638+ // This would mean we need to UnwrapFacetAccessType(query_self_const_id), but
639+ // it's already done by member access, which is the one use of this function.
640+ CARBON_DCHECK (!context.insts ().Is <SemIR::FacetAccessType>(
641+ context.constant_values ().GetInstId (query_self_const_id)));
642+
643+ auto query_type_structure = BuildTypeStructure (
644+ context, context.constant_values ().GetInstId (query_self_const_id),
645+ query_specific_interface);
646+ bool query_is_concrete =
647+ QueryIsConcrete (context, query_self_const_id, query_specific_interface);
648+
649+ auto candidate_impls = CollectCandidateImplsForQuery (
650+ context, /* final_only=*/ true , query_type_structure,
651+ query_specific_interface);
652+
653+ for (const auto & candidate : candidate_impls) {
654+ // In deferred lookup for a symbolic impl witness, while building a
655+ // specific, there may be no stack yet as this may be the first lookup. If
656+ // further lookups are started as a result in deduce, they will build the
657+ // stack.
658+ //
659+ // NOTE: Don't retain a reference into the stack, it may be invalidated if
660+ // we do further impl lookups when GetWitnessIdForImpl() does deduction.
661+ if (!context.impl_lookup_stack ().empty ()) {
662+ context.impl_lookup_stack ().back ().impl_loc = candidate.loc_inst_id ;
663+ }
664+
665+ // NOTE: GetWitnessIdForImpl() does deduction, which can cause new impls
666+ // to be imported, invalidating any pointer into `context.impls()`.
667+ auto result = GetWitnessIdForImpl (
668+ context, loc_id, query_is_concrete, query_self_const_id,
669+ query_specific_interface, candidate.impl_id );
670+ if (result.has_value ()) {
671+ CARBON_CHECK (result.has_concrete_value ());
672+ return result.concrete_witness ();
673+ }
674+ }
675+ return SemIR::InstId::None;
676+ }
677+
622678} // namespace Carbon::Check
0 commit comments