mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Impls in final match_first block aren't always final (#7512)
If an earlier impl may match a more specific query, then later impls can not be treated as final for the given query. See https://github.com/carbon-language/carbon-lang/blob/de8b03faa3178ae683d8e7124fbcba81eb88e00c/proposals/p005337-interface-extension-and-final-impl-update.md#using-associated-constants-from-impls-in-a-final-match_first
This commit is contained in:
@@ -702,6 +702,12 @@ static auto CollectCandidateImplsForQuery(
|
||||
}
|
||||
}
|
||||
|
||||
// For each `match_first` block, track the position of the first impl that is
|
||||
// more specific than the query, and could thus match the query once it is
|
||||
// made more specific. Any impls with a later position in the same
|
||||
// `match_first` block cannot be treated as final.
|
||||
Map<SemIR::InstId, int, 16> first_more_specific_impl_in_match_first;
|
||||
|
||||
for (auto [id, impl] : context.impls().enumerate()) {
|
||||
CARBON_CHECK(impl.witness_id.has_value());
|
||||
|
||||
@@ -711,16 +717,9 @@ static auto CollectCandidateImplsForQuery(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (final_only) {
|
||||
if (!TreatImplAsFinal(context, impl) &&
|
||||
// TODO: For `match_first_is_final`, the impl can only be treated as
|
||||
// final if there's no `impl` above it in the match_first block that
|
||||
// overlaps with the query (such that a more specific query might
|
||||
// choose it). See
|
||||
// https://github.com/carbon-language/carbon-lang/blob/de8b03faa3178ae683d8e7124fbcba81eb88e00c/proposals/p005337-interface-extension-and-final-impl-update.md#using-associated-constants-from-impls-in-a-final-match_first
|
||||
!impl.match_first_is_final) {
|
||||
continue;
|
||||
}
|
||||
if (final_only && !TreatImplAsFinal(context, impl) &&
|
||||
!impl.match_first_is_final) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (llvm::is_contained(context.forbidden_impls(), id)) {
|
||||
@@ -747,12 +746,30 @@ static auto CollectCandidateImplsForQuery(
|
||||
if (!type_structure) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: We can skip the comparison here if the `impl_interface_const_id` is
|
||||
// not symbolic, since when the interface and specific ids match, and they
|
||||
// aren't symbolic, the structure will be identical.
|
||||
if (!query_type_structure.CompareStructure(
|
||||
TypeStructure::CompareTest::IsEqualToOrMoreSpecificThan,
|
||||
*type_structure)) {
|
||||
// If the query does not match this impl, but the impl is part of a final
|
||||
// `match_first` block, then we also check to see if the impl is _more
|
||||
// specific_ than the query. Meaning that the query, once specialized,
|
||||
// could match the impl. In that case, impls that come after can not be
|
||||
// treated as final.
|
||||
if (final_only && impl.match_first_is_final) {
|
||||
if (type_structure->CompareStructure(
|
||||
TypeStructure::CompareTest::IsEqualToOrMoreSpecificThan,
|
||||
query_type_structure)) {
|
||||
auto result = first_more_specific_impl_in_match_first.Insert(
|
||||
impl.match_first_id, impl.match_first_position);
|
||||
if (!result.is_inserted()) {
|
||||
result.value() =
|
||||
std::min(result.value(), impl.match_first_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -761,6 +778,25 @@ static auto CollectCandidateImplsForQuery(
|
||||
impl.match_first_position});
|
||||
}
|
||||
|
||||
if (final_only) {
|
||||
// When searching for `final_only`: Remove candidates if they are in a final
|
||||
// `match_first` block, and there is a more specific impl in an earlier
|
||||
// position in that block. That prevents the later impl from being
|
||||
// considered final.
|
||||
llvm::erase_if(candidates.impls, [&](auto& candidate) {
|
||||
if (!candidate.match_first_block.has_value()) {
|
||||
return false;
|
||||
}
|
||||
auto result = first_more_specific_impl_in_match_first.Lookup(
|
||||
candidate.match_first_block);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
int first_more_specific_position = result.value();
|
||||
return candidate.match_first_position > first_more_specific_position;
|
||||
});
|
||||
}
|
||||
|
||||
auto compare = [](auto& lhs, auto& rhs) -> bool {
|
||||
// If they are in the same block, then order wins. Final impls will always
|
||||
// be in the same block if they overlap, as will impls that have the same
|
||||
|
||||
Reference in New Issue
Block a user