Repository: carbon-language/carbon-lang
File: toolchain/lower/file_context.cpp (C++)

Code before:
  equivalent_specifics_.Set(specific_id2, canon_id1);
  equivalent_specifics_.Set(canon_id1, canon_id1);
  equivalent_specifics_.Set(canon_id2, canon_id1);
}

auto FileContext::UpdateEquivalentSpecific(SemIR::SpecificId specific_id)
    -> void {
  if (!equivalent_specifics_.Get(specific_id).has_value()) {
    return;
  }

  llvm::SmallVector<SemIR::SpecificId> stack;
  SemIR::SpecificId specific_to_update = specific_id;
  while (equivalent_specifics_.Get(
             equivalent_specifics_.Get(specific_to_update)) !=
         equivalent_specifics_.Get(specific_to_update)) {
    stack.push_back(specific_to_update);
    specific_to_update = equivalent_specifics_.Get(specific_to_update);
  }
  for (auto specific : llvm::reverse(stack)) {
    equivalent_specifics_.Set(
        specific,
        equivalent_specifics_.Get(equivalent_specifics_.Get(specific)));
  }
}

auto FileContext::UpdateAndDeleteLLVMFunction(SemIR::SpecificId specific_id)
    -> void {
  specific_functions_.Get(specific_id)
      ->replaceAllUsesWith(
          specific_functions_.Get(equivalent_specifics_.Get(specific_id)));
  specific_functions_.Get(specific_id)->eraseFromParent();
  specific_functions_.Set(
      specific_id,
      specific_functions_.Get(equivalent_specifics_.Get(specific_id)));
}

auto FileContext::IsKnownEquivalence(SemIR::SpecificId specific_id1,
                                     SemIR::SpecificId specific_id2) -> bool {
  if (!equivalent_specifics_.Get(specific_id1).has_value() ||
      !equivalent_specifics_.Get(specific_id2).has_value()) {
    return false;
  }

  UpdateEquivalentSpecific(specific_id1);
  UpdateEquivalentSpecific(specific_id2);

  return equivalent_specifics_.Get(specific_id1) ==
         equivalent_specifics_.Get(specific_id2);
}


Review comment (bug):
Had you considered just combining this into `UpdateEquivalentSpecific`? Just asking since it's closely related.

Note too the `if (!equivalent_specifics_.Get(specific_id).has_value())` check in `UpdateEquivalentSpecific`, but not here. Is it possible that's hitting an edge case that's not well tested?

Code after:
  equivalent_specifics_.Set(canon_id2, canon_id1);
}

auto FileContext::UpdateEquivalentSpecific(SemIR::SpecificId specific_id)
    -> void {
  if (!equivalent_specifics_.Get(specific_id).has_value()) {
    return;
  }

  llvm::SmallVector<SemIR::SpecificId> stack;
  SemIR::SpecificId specific_to_update = specific_id;
  SemIR::SpecificId equivalent = equivalent_specifics_.Get(specific_to_update);
  SemIR::SpecificId equivalent_next = equivalent_specifics_.Get(equivalent);
  while (equivalent != equivalent_next) {
    stack.push_back(specific_to_update);
    specific_to_update = equivalent;
    equivalent = equivalent_next;
    equivalent_next = equivalent_specifics_.Get(equivalent_next);
  }

  for (auto specific : stack) {
    equivalent_specifics_.Set(specific, equivalent);
  }
}

auto FileContext::UpdateAndDeleteLLVMFunction(SemIR::SpecificId specific_id)
    -> void {
  UpdateEquivalentSpecific(specific_id);
  auto* old_function = specific_functions_.Get(specific_id);
  auto* new_function =
      specific_functions_.Get(equivalent_specifics_.Get(specific_id));
  old_function->replaceAllUsesWith(new_function);
  old_function->eraseFromParent();
  specific_functions_.Set(specific_id, new_function);
}

auto FileContext::IsKnownEquivalence(SemIR::SpecificId specific_id1,
                                     SemIR::SpecificId specific_id2) -> bool {
  if (!equivalent_specifics_.Get(specific_id1).has_value() ||
      !equivalent_specifics_.Get(specific_id2).has_value()) {
    return false;
  }

  UpdateEquivalentSpecific(specific_id1);
  UpdateEquivalentSpecific(specific_id2);

  return equivalent_specifics_.Get(specific_id1) ==
         equivalent_specifics_.Get(specific_id2);
}

auto FileContext::AreFunctionTypesEquivalent(SemIR::SpecificId specific_id1,