mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 19:11:09 +01:00
Make EvalLookupSingleImplWitness shorter (#6517)
This reduces the function size from ~180 to 133. It removes early outs once we begin the process of doing an impl lookup so that we cache the result unconditionally at the end. It removes a second fallback call to look for a C++ witness by tracking additional information about the `Impl` that was found (if any) and just do the C++ lookup in one place afterward.
This commit is contained in:
@@ -233,9 +233,9 @@ class Context {
|
||||
}
|
||||
|
||||
// A map from a (self, interface) pair to a final witness.
|
||||
using ImplLookupCacheMap =
|
||||
Map<std::pair<SemIR::ConstantId, SemIR::SpecificInterfaceId>,
|
||||
SemIR::InstId>;
|
||||
using ImplLookupCacheKey =
|
||||
std::pair<SemIR::ConstantId, SemIR::SpecificInterfaceId>;
|
||||
using ImplLookupCacheMap = Map<ImplLookupCacheKey, SemIR::InstId>;
|
||||
auto impl_lookup_cache() -> ImplLookupCacheMap& { return impl_lookup_cache_; }
|
||||
|
||||
// An impl lookup query that resulted in a concrete witness from finding an
|
||||
|
||||
+96
-122
@@ -237,9 +237,8 @@ static auto GetWitnessIdForImpl(Context& context, SemIR::LocId loc_id,
|
||||
bool query_is_concrete,
|
||||
SemIR::ConstantId query_self_const_id,
|
||||
const SemIR::SpecificInterface& interface,
|
||||
SemIR::ImplId impl_id) -> EvalImplLookupResult {
|
||||
const SemIR::Impl& impl = context.impls().Get(impl_id);
|
||||
|
||||
const SemIR::Impl& impl)
|
||||
-> EvalImplLookupResult {
|
||||
// The impl may have generic arguments, in which case we need to deduce them
|
||||
// to find what they are given the specific type and interface query. We use
|
||||
// that specific to map values in the impl to the deduced values.
|
||||
@@ -796,8 +795,7 @@ class ImportImplFilter {
|
||||
} // namespace
|
||||
|
||||
struct CandidateImpl {
|
||||
SemIR::ImplId impl_id;
|
||||
SemIR::InstId loc_inst_id;
|
||||
const SemIR::Impl* impl;
|
||||
|
||||
// Used for sorting the candidates to find the most-specialized match.
|
||||
TypeStructure type_structure;
|
||||
@@ -881,8 +879,7 @@ static auto CollectCandidateImplsForQuery(
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.impls.push_back(
|
||||
{id, impl.definition_id, std::move(*type_structure)});
|
||||
candidates.impls.push_back({&impl, std::move(*type_structure)});
|
||||
}
|
||||
|
||||
auto compare = [](auto& lhs, auto& rhs) -> bool {
|
||||
@@ -918,6 +915,31 @@ static auto GetFacetAsType(Context& context, SemIR::LocId loc_id,
|
||||
return context.types().GetTypeIdForTypeInstId(facet_or_type_id);
|
||||
}
|
||||
|
||||
// Record the query which found a final impl witness. It's illegal to
|
||||
// write a final impl afterward that would match the same query.
|
||||
static auto PoisonImplLookupQuery(Context& context, SemIR::LocId loc_id,
|
||||
EvalImplLookupMode mode,
|
||||
SemIR::LookupImplWitness eval_query,
|
||||
const EvalImplLookupResult& result,
|
||||
const SemIR::Impl& impl) -> void {
|
||||
if (mode == EvalImplLookupMode::RecheckPoisonedLookup) {
|
||||
return;
|
||||
}
|
||||
if (!result.has_final_value()) {
|
||||
return;
|
||||
}
|
||||
// If the impl was effectively final, then we don't need to poison here. A
|
||||
// change of query result will already be diagnosed at the point where the
|
||||
// new impl decl was written that changes the result.
|
||||
if (IsImplEffectivelyFinal(context, impl)) {
|
||||
return;
|
||||
}
|
||||
context.poisoned_concrete_impl_lookup_queries().push_back(
|
||||
{.loc_id = loc_id,
|
||||
.query = eval_query,
|
||||
.impl_witness = result.final_witness()});
|
||||
}
|
||||
|
||||
auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::LookupImplWitness eval_query,
|
||||
SemIR::InstId self_facet_value_inst_id,
|
||||
@@ -926,52 +948,18 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
||||
auto query_specific_interface =
|
||||
context.specific_interfaces().Get(eval_query.query_specific_interface_id);
|
||||
|
||||
auto facet_lookup_result = LookupImplWitnessInSelfFacetValue(
|
||||
context, loc_id, self_facet_value_inst_id, query_specific_interface);
|
||||
if (facet_lookup_result.has_final_value()) {
|
||||
return facet_lookup_result;
|
||||
}
|
||||
|
||||
// Ensure specifics don't substitute in weird things for the query self.
|
||||
CARBON_CHECK(context.types().IsFacetType(
|
||||
context.insts().Get(eval_query.query_self_inst_id).type_id()));
|
||||
SemIR::ConstantId query_self_const_id =
|
||||
context.constant_values().Get(eval_query.query_self_inst_id);
|
||||
|
||||
// Check to see if this result is in the cache. But skip the cache if
|
||||
// //we're re-checking a poisoned result and need to redo the lookup.
|
||||
std::pair impl_lookup_cache_key = {query_self_const_id,
|
||||
eval_query.query_specific_interface_id};
|
||||
if (mode != EvalImplLookupMode::RecheckPoisonedLookup) {
|
||||
if (auto result =
|
||||
context.impl_lookup_cache().Lookup(impl_lookup_cache_key)) {
|
||||
return EvalImplLookupResult::MakeFinal(result.value());
|
||||
}
|
||||
auto facet_lookup_result = LookupImplWitnessInSelfFacetValue(
|
||||
context, loc_id, self_facet_value_inst_id, query_specific_interface);
|
||||
if (facet_lookup_result.has_final_value()) {
|
||||
return facet_lookup_result;
|
||||
}
|
||||
|
||||
// The kind of lookup we're performing, which determines what kind of result
|
||||
// we provide.
|
||||
enum LookupKind {
|
||||
// This is a concrete query, which should either provide a concrete witness
|
||||
// or fail.
|
||||
Concrete,
|
||||
// This query refers to an interface that can be found symbolically within
|
||||
// the facet type of the self value. The lookup will always succeed, but we
|
||||
// are still checking in case a more precise final impl supplies values of
|
||||
// associated constants.
|
||||
FoundInFacet,
|
||||
// This is an impl lookup with a symbolic query.
|
||||
Symbolic,
|
||||
};
|
||||
|
||||
LookupKind kind =
|
||||
QueryIsConcrete(context, query_self_const_id, query_specific_interface)
|
||||
? Concrete
|
||||
: facet_lookup_result.has_value() ? FoundInFacet
|
||||
: Symbolic;
|
||||
CARBON_CHECK(kind != Concrete || !facet_lookup_result.has_value(),
|
||||
"Non-concrete facet lookup value for concrete query");
|
||||
|
||||
// If the self type is a facet that provides a witness, then we are in an
|
||||
// `interface` or an `impl`. In both cases, we don't want to do any impl
|
||||
// lookups. The query will eventually resolve to a concrete witness when it
|
||||
@@ -984,13 +972,13 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
||||
// when the eval block is run, it finds the same `impl`, tries to build a
|
||||
// specific from it, which runs the eval block, creating a recursive loop that
|
||||
// crashes.
|
||||
if (kind == FoundInFacet) {
|
||||
if (facet_lookup_result.has_value()) {
|
||||
if (auto bind = context.insts().TryGetAs<SemIR::SymbolicBinding>(
|
||||
eval_query.query_self_inst_id)) {
|
||||
const auto& entity = context.entity_names().Get(bind->entity_name_id);
|
||||
if (entity.name_id == SemIR::NameId::PeriodSelf ||
|
||||
entity.name_id == SemIR::NameId::SelfType) {
|
||||
return EvalImplLookupResult::MakeNonFinal();
|
||||
return facet_lookup_result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1002,6 +990,17 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
||||
return EvalImplLookupResult::MakeNone();
|
||||
}
|
||||
|
||||
// Check to see if this result is in the cache. But skip the cache if we're
|
||||
// re-checking a poisoned result and need to redo the lookup.
|
||||
auto impl_lookup_cache_key = Context::ImplLookupCacheKey{
|
||||
query_self_const_id, eval_query.query_specific_interface_id};
|
||||
if (mode != EvalImplLookupMode::RecheckPoisonedLookup) {
|
||||
if (auto result =
|
||||
context.impl_lookup_cache().Lookup(impl_lookup_cache_key)) {
|
||||
return EvalImplLookupResult::MakeFinal(result.value());
|
||||
}
|
||||
}
|
||||
|
||||
// If the self value is a (symbolic) facet value that has a symbolic witness,
|
||||
// then we don't need to do impl lookup, except that we want to find any final
|
||||
// impls to return a concrete witness if possible. So we limit the query to
|
||||
@@ -1009,94 +1008,69 @@ auto EvalLookupSingleImplWitness(Context& context, SemIR::LocId loc_id,
|
||||
// not be concrete in this case, so only final impls can produce a concrete
|
||||
// witness for this query.
|
||||
auto candidates = CollectCandidateImplsForQuery(
|
||||
context, kind == FoundInFacet, query_self_const_id, *query_type_structure,
|
||||
query_specific_interface);
|
||||
context, facet_lookup_result.has_value(), query_self_const_id,
|
||||
*query_type_structure, query_specific_interface);
|
||||
|
||||
bool query_is_concrete =
|
||||
QueryIsConcrete(context, query_self_const_id, query_specific_interface);
|
||||
CARBON_CHECK(!query_is_concrete || !facet_lookup_result.has_value(),
|
||||
"Non-concrete facet lookup value for concrete query");
|
||||
|
||||
// Perform a lookup for an `impl` that matches the query. If we don't find a
|
||||
// final impl, the self value may still have been a facet that provides a
|
||||
// symbolic witness in the `facet_lookup_result`, which we want to fall back
|
||||
// to. It records that an `impl` will exist for the query, but is yet unknown.
|
||||
|
||||
struct LookupResult {
|
||||
EvalImplLookupResult result;
|
||||
const TypeStructure* impl_type_structure = nullptr;
|
||||
SemIR::LocId impl_loc_id = SemIR::LocId::None;
|
||||
};
|
||||
|
||||
LookupResult lookup_result = {.result = facet_lookup_result};
|
||||
|
||||
for (const auto& candidate : candidates.impls) {
|
||||
const auto& impl = *candidate.impl;
|
||||
|
||||
// In deferred lookup for a symbolic impl witness, while building a
|
||||
// specific, there may be no stack yet as this may be the first lookup. If
|
||||
// further lookups are started as a result in deduce, they will build the
|
||||
// stack.
|
||||
//
|
||||
// NOTE: Don't retain a reference into the stack, it may be invalidated if
|
||||
// we do further impl lookups when GetWitnessIdForImpl() does deduction.
|
||||
if (!context.impl_lookup_stack().empty()) {
|
||||
context.impl_lookup_stack().back().impl_loc = candidate.loc_inst_id;
|
||||
context.impl_lookup_stack().back().impl_loc = impl.definition_id;
|
||||
}
|
||||
|
||||
auto result = GetWitnessIdForImpl(
|
||||
context, loc_id, kind == Concrete, query_self_const_id,
|
||||
query_specific_interface, candidate.impl_id);
|
||||
auto result = GetWitnessIdForImpl(context, loc_id, query_is_concrete,
|
||||
query_self_const_id,
|
||||
query_specific_interface, impl);
|
||||
if (result.has_value()) {
|
||||
// Record the query which found a final impl witness. It's illegal to
|
||||
// write a final impl afterward that would match the same query.
|
||||
//
|
||||
// If the impl was effectively final, then we don't need to poison here. A
|
||||
// change of query result will already be diagnosed at the point where the
|
||||
// new impl decl was written that changes the result.
|
||||
if (mode != EvalImplLookupMode::RecheckPoisonedLookup &&
|
||||
result.has_final_value() &&
|
||||
!IsImplEffectivelyFinal(context,
|
||||
context.impls().Get(candidate.impl_id))) {
|
||||
context.poisoned_concrete_impl_lookup_queries().push_back(
|
||||
{.loc_id = loc_id,
|
||||
.query = eval_query,
|
||||
.impl_witness = result.final_witness()});
|
||||
}
|
||||
|
||||
if (kind == Concrete && candidates.consider_cpp_candidates) {
|
||||
// We found a Carbon impl. Also check for a C++ candidate that is a
|
||||
// better match than that impl.
|
||||
auto cpp_witness_id = LookupCppImpl(
|
||||
context, loc_id,
|
||||
GetFacetAsType(context, loc_id, query_self_const_id),
|
||||
query_specific_interface, &candidate.type_structure,
|
||||
SemIR::LocId(
|
||||
context.impls().Get(candidate.impl_id).first_owning_decl_id));
|
||||
if (cpp_witness_id.has_value()) {
|
||||
result = EvalImplLookupResult::MakeFinal(cpp_witness_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != EvalImplLookupMode::RecheckPoisonedLookup &&
|
||||
result.has_final_value()) {
|
||||
context.impl_lookup_cache().Insert(impl_lookup_cache_key,
|
||||
result.final_witness());
|
||||
}
|
||||
|
||||
return result;
|
||||
PoisonImplLookupQuery(context, loc_id, mode, eval_query, result, impl);
|
||||
lookup_result = {.result = result,
|
||||
.impl_type_structure = &candidate.type_structure,
|
||||
.impl_loc_id = SemIR::LocId(impl.definition_id)};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We didn't find a matching impl. Produce a suitable result.
|
||||
switch (kind) {
|
||||
case Concrete:
|
||||
if (candidates.consider_cpp_candidates) {
|
||||
// Look for a matching C++ result, with no Carbon candidate to compare
|
||||
// against.
|
||||
auto cpp_witness_id = LookupCppImpl(
|
||||
context, loc_id,
|
||||
GetFacetAsType(context, loc_id, query_self_const_id),
|
||||
query_specific_interface, nullptr, SemIR::LocId::None);
|
||||
if (cpp_witness_id.has_value()) {
|
||||
if (mode != EvalImplLookupMode::RecheckPoisonedLookup) {
|
||||
context.impl_lookup_cache().Insert(impl_lookup_cache_key,
|
||||
cpp_witness_id);
|
||||
}
|
||||
return EvalImplLookupResult::MakeFinal(cpp_witness_id);
|
||||
}
|
||||
}
|
||||
return EvalImplLookupResult::MakeNone();
|
||||
|
||||
case FoundInFacet:
|
||||
// We did not find a final impl, but the self value is a facet that
|
||||
// provides a symbolic witness. Record that an impl will exist for the
|
||||
// specific, but is yet unknown.
|
||||
return EvalImplLookupResult::MakeNonFinal();
|
||||
|
||||
case Symbolic:
|
||||
return EvalImplLookupResult::MakeNone();
|
||||
if (query_is_concrete && candidates.consider_cpp_candidates) {
|
||||
// Also check for a C++ candidate that is a better match than whatever
|
||||
// `impl` we may have found in Carbon.
|
||||
auto cpp_witness_id = LookupCppImpl(
|
||||
context, loc_id, GetFacetAsType(context, loc_id, query_self_const_id),
|
||||
query_specific_interface, lookup_result.impl_type_structure,
|
||||
lookup_result.impl_loc_id);
|
||||
if (cpp_witness_id.has_value()) {
|
||||
lookup_result = {.result =
|
||||
EvalImplLookupResult::MakeFinal(cpp_witness_id)};
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != EvalImplLookupMode::RecheckPoisonedLookup &&
|
||||
lookup_result.result.has_final_value()) {
|
||||
context.impl_lookup_cache().Insert(impl_lookup_cache_key,
|
||||
lookup_result.result.final_witness());
|
||||
}
|
||||
return lookup_result.result;
|
||||
}
|
||||
|
||||
auto LookupMatchesImpl(Context& context, SemIR::LocId loc_id,
|
||||
@@ -1108,7 +1082,7 @@ auto LookupMatchesImpl(Context& context, SemIR::LocId loc_id,
|
||||
}
|
||||
auto result = GetWitnessIdForImpl(
|
||||
context, loc_id, /*query_is_concrete=*/false, query_self_const_id,
|
||||
query_specific_interface, target_impl);
|
||||
query_specific_interface, context.impls().Get(target_impl));
|
||||
return result.has_value();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user