Unify MergeRedecl functions (#7632)

As discussed in #7620.
This commit is contained in:
Özgür T. Önsoy
2026-08-25 18:43:52 +00:00
committed by GitHub
parent 1410795bf7
commit 2bdcb1fa62
17 changed files with 470 additions and 905 deletions
-163
View File
@@ -156,167 +156,4 @@ auto AddSelfSymbolicBindingToScope(Context& context,
return self_param_inst_id;
}
template <typename EntityT>
requires std::same_as<EntityT, SemIR::Interface>
static constexpr auto DeclTokenKind() -> Lex::TokenKind {
return Lex::TokenKind::Interface;
}
template <typename EntityT>
requires std::same_as<EntityT, SemIR::NamedConstraint>
static constexpr auto DeclTokenKind() -> Lex::TokenKind {
return Lex::TokenKind::Constraint;
}
template <typename EntityT>
requires SameAsOneOf<EntityT, SemIR::Interface, SemIR::NamedConstraint>
auto TryGetExistingDecl(Context& context, const NameComponent& name,
SemIR::ScopeLookupResult lookup_result,
const EntityT& entity, bool is_definition)
-> std::optional<SemIR::Inst> {
using EntityIdT =
std::conditional_t<std::is_same_v<EntityT, SemIR::Interface>,
SemIR::InterfaceId, SemIR::NamedConstraintId>;
constexpr bool IsInterface = std::is_same_v<EntityIdT, SemIR::InterfaceId>;
if (lookup_result.is_poisoned()) {
// This is a declaration of a poisoned name.
DiagnosePoisonedName(context, name.name_id,
lookup_result.poisoning_loc_id(), name.name_loc_id);
return std::nullopt;
}
if (!lookup_result.is_found()) {
return std::nullopt;
}
auto prev_id = lookup_result.target_inst_id();
auto prev = context.insts().Get(prev_id);
auto prev_entity_id = EntityIdT::None;
auto prev_import_ir_id = SemIR::ImportIRId::None;
auto existing_decl_id = SemIR::InstId::None;
CARBON_KIND_SWITCH(prev) {
case CARBON_KIND(SemIR::InterfaceDecl interface_decl): {
if constexpr (IsInterface) {
prev_entity_id = interface_decl.interface_id;
existing_decl_id = prev_id;
}
break;
}
case CARBON_KIND(SemIR::NamedConstraintDecl named_constraint_decl): {
if constexpr (!IsInterface) {
prev_entity_id = named_constraint_decl.named_constraint_id;
existing_decl_id = prev_id;
}
break;
}
case CARBON_KIND(SemIR::ImportRefLoaded import_ref): {
auto import_ir_inst =
context.import_ir_insts().Get(import_ref.import_ir_inst_id);
// Verify the decl so that things like aliases are name conflicts.
const auto* import_ir =
context.import_irs().Get(import_ir_inst.ir_id()).sem_ir;
if constexpr (IsInterface) {
if (!import_ir->insts().Is<SemIR::InterfaceDecl>(
import_ir_inst.inst_id())) {
break;
}
} else {
if (!import_ir->insts().Is<SemIR::NamedConstraintDecl>(
import_ir_inst.inst_id())) {
break;
}
}
// Use the constant value to get the ID.
auto decl_value = context.insts().Get(
context.constant_values().GetConstantInstId(prev_id));
if (auto facet_type = decl_value.TryAs<SemIR::FacetType>()) {
auto declared_facet_type = context.declared_facet_types().Get(
facet_type->declared_facet_type_id);
if constexpr (IsInterface) {
prev_entity_id =
declared_facet_type.extend_constraints[0].interface_id;
} else {
prev_entity_id = declared_facet_type.extend_named_constraints[0]
.named_constraint_id;
}
prev_import_ir_id = import_ir_inst.ir_id();
}
break;
}
default:
break;
}
if (!prev_entity_id.has_value()) {
// This is a redeclaration with a different entity kind.
DiagnoseDuplicateName(context, name.name_id, name.name_loc_id,
SemIR::LocId(prev_id));
return std::nullopt;
}
auto& prev_entity = [&]() -> EntityT& {
if constexpr (IsInterface) {
return context.interfaces().Get(prev_entity_id);
} else {
return context.named_constraints().Get(prev_entity_id);
}
}();
if (!CheckRedeclParamsMatch(
context,
DeclParams(SemIR::LocId(entity.latest_decl_id()),
name.first_param_node_id, name.last_param_node_id,
name.implicit_param_patterns_id, name.param_patterns_id),
DeclParams(prev_entity))) {
// Mismatch is diagnosed already if found.
return std::nullopt;
}
DiagnoseIfInvalidRedecl(
context, DeclTokenKind<EntityT>(), prev_entity.name_id,
RedeclInfo(entity, SemIR::LocId(entity.latest_decl_id()), is_definition),
RedeclInfo(prev_entity, SemIR::LocId(prev_entity.latest_decl_id()),
prev_entity.has_definition_started()),
prev_import_ir_id);
if (is_definition && prev_entity.has_definition_started()) {
// DiagnoseIfInvalidRedecl would diagnose an error in this case, since we'd
// have two definitions. Given the declaration parts of the definitions
// match, we would be able to use the prior declaration for error recovery,
// except that having two definitions causes larger problems for generics.
// All interfaces (and named constraints) are generic with an implicit Self
// compile time binding.
return std::nullopt;
}
if (is_definition) {
prev_entity.MergeDefinition(entity);
}
if (prev_import_ir_id.has_value()) {
prev_entity.first_owning_decl_id = entity.first_owning_decl_id;
ReplacePrevInstForMerge(context, entity.parent_scope_id,
prev_entity.name_id, entity.first_owning_decl_id);
}
// This is a matching redeclaration of an existing entity of the same type.
return existing_decl_id.has_value() ? std::optional<SemIR::Inst>(prev)
: std::nullopt;
}
template auto TryGetExistingDecl(Context& context, const NameComponent& name,
SemIR::ScopeLookupResult lookup_result,
const SemIR::Interface& entity,
bool is_definition)
-> std::optional<SemIR::Inst>;
template auto TryGetExistingDecl(Context& context, const NameComponent& name,
SemIR::ScopeLookupResult lookup_result,
const SemIR::NamedConstraint& entity,
bool is_definition)
-> std::optional<SemIR::Inst>;
} // namespace Carbon::Check