Fearlessly hold references into ValueStore again (#5589)

Undo changes that were meant to prevent use of a reference into
`ValueStore` after being invalidated. After #5576, the `ValueStore`
makes such references stable, so there's no need to worry about
invalidation.
This commit is contained in:
Dana Jansens
2025-06-03 18:07:23 +00:00
committed by GitHub
parent a85d292f8d
commit 493bea1647
14 changed files with 100 additions and 202 deletions
+28 -53
View File
@@ -22,15 +22,9 @@ namespace Carbon::Check {
namespace {
struct ImplAndInterface {
SemIR::ImplId impl_id;
SemIR::SpecificInterface interface;
};
// We avoid holding a reference into the ImplStore, as the act of verifying the
// diagnostic checks can invalidate such a reference. We collect what we need to
// know about the the `Impl` for validation into this struct.
// All information about a `SemIR::Impl` needed for validation.
struct ImplInfo {
SemIR::ImplId impl_id;
bool is_final;
SemIR::InstId witness_id;
SemIR::TypeInstId self_id;
@@ -56,7 +50,8 @@ static auto GetIRId(Context& context, SemIR::InstId owning_inst_id)
auto GetImplInfo(Context& context, SemIR::ImplId impl_id) -> ImplInfo {
const auto& impl = context.impls().Get(impl_id);
auto ir_id = GetIRId(context, impl.first_owning_decl_id);
return {.is_final = impl.is_final,
return {.impl_id = impl_id,
.is_final = impl.is_final,
.witness_id = impl.witness_id,
.self_id = impl.self_id,
.latest_decl_id = impl.latest_decl_id(),
@@ -146,15 +141,15 @@ static auto DiagnoseNonFinalImplsWithSameTypeStructure(Context& context,
// final impl.
//
// Returns true if an error was diagnosed.
static auto DiagnoseUnmatchableNonFinalImplWithFinalImpl(
Context& context, SemIR::ImplId impl_a_id, const ImplInfo& impl_a,
SemIR::ImplId impl_b_id, const ImplInfo& impl_b) -> bool {
static auto DiagnoseUnmatchableNonFinalImplWithFinalImpl(Context& context,
const ImplInfo& impl_a,
const ImplInfo& impl_b)
-> bool {
auto diagnose_unmatchable_impl = [&](const ImplInfo& query_impl,
SemIR::ImplId final_impl_id,
const ImplInfo& final_impl) -> bool {
if (LookupMatchesImpl(context, SemIR::LocId(query_impl.latest_decl_id),
context.constant_values().Get(query_impl.self_id),
query_impl.interface, final_impl_id)) {
query_impl.interface, final_impl.impl_id)) {
CARBON_DIAGNOSTIC(ImplFinalOverlapsNonFinal, Error,
"`impl` will never be used");
auto builder = context.emitter().Build(query_impl.latest_decl_id,
@@ -172,9 +167,9 @@ static auto DiagnoseUnmatchableNonFinalImplWithFinalImpl(
CARBON_CHECK(impl_a.is_final || impl_b.is_final);
if (impl_b.is_final) {
return diagnose_unmatchable_impl(impl_a, impl_b_id, impl_b);
return diagnose_unmatchable_impl(impl_a, impl_b);
} else {
return diagnose_unmatchable_impl(impl_b, impl_a_id, impl_a);
return diagnose_unmatchable_impl(impl_b, impl_a);
}
}
@@ -236,27 +231,17 @@ static auto DiagnoseFinalImplsOverlapOutsideMatchFirst(Context& context,
return false;
}
static auto ValidateImplsForInterface(
Context& context, llvm::ArrayRef<ImplAndInterface> impls_and_interface)
-> void {
// Range over `SemIR::ImplId` only. Caller has ensured all of these are
// for the same interface.
auto impl_ids = llvm::map_range(impls_and_interface,
[=](ImplAndInterface impl_and_interface) {
return impl_and_interface.impl_id;
});
static auto ValidateImplsForInterface(Context& context,
llvm::ArrayRef<ImplInfo> impls) -> void {
// All `impl`s we look at here have the same `InterfaceId` (though different
// `SpecificId`s in their `SpecificInterface`s). So we can grab the
// `ImportIRId` for the interface a single time up front.
auto interface_decl_id =
context.interfaces()
.Get(impls_and_interface[0].interface.interface_id)
.first_owning_decl_id;
auto interface_decl_id = context.interfaces()
.Get(impls[0].interface.interface_id)
.first_owning_decl_id;
auto interface_ir_id = GetIRId(context, interface_decl_id);
for (auto impl_id : impl_ids) {
auto impl = GetImplInfo(context, impl_id);
for (const auto& impl : impls) {
if (impl.is_final && impl.is_local) {
// =======================================================================
/// Rules for an individual final impl.
@@ -273,11 +258,8 @@ static auto ValidateImplsForInterface(
// For each impl, we compare it pair-wise which each impl found before it, so
// that diagnostics are attached to the later impl, as the earlier impl on its
// own does not generate a diagnostic.
size_t num_impl_ids = impls_and_interface.size();
for (auto [split_point, impl_b_id] :
llvm::drop_begin(llvm::enumerate(impl_ids))) {
auto impl_b = GetImplInfo(context, impl_b_id);
size_t num_impls = impls.size();
for (auto [split_point, impl_b] : llvm::drop_begin(llvm::enumerate(impls))) {
// Prevent diagnosing the same error multiple times for the same `impl_b`
// against different impls before it. But still ensure we do give one of
// each diagnostic when they are different errors.
@@ -286,10 +268,8 @@ static auto ValidateImplsForInterface(
bool did_diagnose_final_impls_overlap_in_different_files = false;
bool did_diagnose_final_impls_overlap_outside_match_first = false;
auto impls_before = llvm::drop_end(impl_ids, num_impl_ids - split_point);
for (auto impl_a_id : impls_before) {
auto impl_a = GetImplInfo(context, impl_a_id);
auto impls_before = llvm::drop_end(impls, num_impls - split_point);
for (const auto& impl_a : impls_before) {
// Only enforce rules when at least one of the impls was written in this
// file.
if (!impl_a.is_local && !impl_b.is_local) {
@@ -318,8 +298,8 @@ static auto ValidateImplsForInterface(
// Rules between final impl and non-final impl.
// =====================================================================
if (!did_diagnose_unmatchable_non_final_impl_with_final_impl) {
if (DiagnoseUnmatchableNonFinalImplWithFinalImpl(
context, impl_a_id, impl_a, impl_b_id, impl_b)) {
if (DiagnoseUnmatchableNonFinalImplWithFinalImpl(context, impl_a,
impl_b)) {
did_diagnose_unmatchable_non_final_impl_with_final_impl = true;
}
}
@@ -432,23 +412,18 @@ auto ValidateImplsInFile(Context& context) -> void {
// about them anyhow. We also verify the impl has an `InterfaceId` since it
// can be missing, in which case a diagnostic would have been generated
// already as well.
//
// Don't hold Impl pointers here because the process of looking for
// diagnostics may cause imports and may invalidate pointers into the
// ImplStore.
llvm::SmallVector<ImplAndInterface> impl_ids_by_interface(llvm::map_range(
llvm::SmallVector<ImplInfo> impl_ids_by_interface(llvm::map_range(
llvm::make_filter_range(
context.impls().enumerate(),
[](std::pair<SemIR::ImplId, const SemIR::Impl&> pair) {
return pair.second.witness_id != SemIR::ErrorInst::InstId &&
pair.second.interface.interface_id.has_value();
}),
[](std::pair<SemIR::ImplId, const SemIR::Impl&> pair) {
return ImplAndInterface{.impl_id = pair.first,
.interface = pair.second.interface};
[&](std::pair<SemIR::ImplId, const SemIR::Impl&> pair) {
return GetImplInfo(context, pair.first);
}));
llvm::stable_sort(impl_ids_by_interface, [](const ImplAndInterface& lhs,
const ImplAndInterface& rhs) {
llvm::stable_sort(impl_ids_by_interface, [](const ImplInfo& lhs,
const ImplInfo& rhs) {
return lhs.interface.interface_id.index < rhs.interface.interface_id.index;
});