diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index 84074099d9dd..8e2d1591c316 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -26,6 +26,7 @@ #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/emitter.h" #include "toolchain/parse/node_ids.h" +#include "toolchain/sem_ir/class.h" #include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/inst.h" @@ -48,128 +49,6 @@ auto HandleParseNode(Context& context, Parse::ClassIntroducerId node_id) return true; } -// Tries to merge new_class into prev_class_id. Since new_class won't have a -// definition even if one is upcoming, set is_definition to indicate the planned -// result. -// -// If merging is successful, returns true and may update the previous class. -// Otherwise, returns false. Prints a diagnostic when appropriate. -static auto MergeClassRedecl(Context& context, Parse::AnyClassDeclId node_id, - SemIR::Class& new_class, bool new_is_definition, - SemIR::ClassId prev_class_id, - SemIR::ImportIRId prev_import_ir_id) -> bool { - auto& prev_class = context.classes().Get(prev_class_id); - SemIR::LocId prev_loc_id(prev_class.latest_decl_id()); - - // Check the generic parameters match, if they were specified. - if (!CheckRedeclParamsMatch(context, DeclParams(new_class), - DeclParams(prev_class))) { - return false; - } - - DiagnoseIfInvalidRedecl( - context, Lex::TokenKind::Class, prev_class.name_id, - RedeclInfo(new_class, node_id, new_is_definition), - RedeclInfo(prev_class, prev_loc_id, prev_class.has_definition_started()), - prev_import_ir_id); - - if (new_is_definition && prev_class.has_definition_started()) { - // Don't attempt to merge multiple definitions. - return false; - } - - if (new_is_definition) { - prev_class.MergeDefinition(new_class); - } - - if (prev_import_ir_id.has_value() || - (prev_class.is_extern && !new_class.is_extern)) { - prev_class.first_owning_decl_id = new_class.first_owning_decl_id; - ReplacePrevInstForMerge(context, new_class.parent_scope_id, - prev_class.name_id, new_class.first_owning_decl_id); - } - return true; -} - -// Adds the name to name lookup. If there's a conflict, tries to merge. May -// update class_decl and class_info when merging. -static auto MergeOrAddName(Context& context, Parse::AnyClassDeclId node_id, - const DeclNameStack::NameContext& name_context, - SemIR::InstId class_decl_id, - SemIR::ClassDecl& class_decl, - SemIR::Class& class_info, bool is_definition, - SemIR::AccessKind access_kind) -> void { - SemIR::ScopeLookupResult lookup_result = - context.decl_name_stack().LookupOrAddName(name_context, class_decl_id, - access_kind); - if (lookup_result.is_poisoned()) { - // This is a declaration of a poisoned name. - DiagnosePoisonedName(context, name_context.name_id_for_new_inst(), - lookup_result.poisoning_loc_id(), name_context.loc_id); - return; - } - - if (!lookup_result.is_found()) { - return; - } - - SemIR::InstId prev_id = lookup_result.target_inst_id(); - - auto prev_class_id = SemIR::ClassId::None; - auto prev_import_ir_id = SemIR::ImportIRId::None; - auto prev = context.insts().Get(prev_id); - CARBON_KIND_SWITCH(prev) { - case CARBON_KIND(SemIR::ClassDecl class_decl): { - prev_class_id = class_decl.class_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 (!import_ir->insts().Is(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 class_type = decl_value.TryAs()) { - prev_class_id = class_type->class_id; - prev_import_ir_id = import_ir_inst.ir_id(); - } else if (auto generic_class_type = - context.types().TryGetAs( - decl_value.type_id())) { - prev_class_id = generic_class_type->class_id; - prev_import_ir_id = import_ir_inst.ir_id(); - } - break; - } - default: - break; - } - - if (!prev_class_id.has_value()) { - // This is a redeclaration of something other than a class. - DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id, - SemIR::LocId(prev_id)); - return; - } - - // TODO: Fix `extern` logic. It doesn't work correctly, but doesn't seem worth - // ripping out because existing code may incrementally help. - if (MergeClassRedecl(context, node_id, class_info, is_definition, - prev_class_id, prev_import_ir_id)) { - // When merging, use the existing entity rather than adding a new one. - class_decl.class_id = prev_class_id; - class_decl.type_id = prev.type_id(); - // TODO: Validate that the redeclaration doesn't set an access modifier. - } -} - static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id, bool is_definition) -> std::tuple { @@ -223,9 +102,13 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id, DiagnoseIfGenericMissingExplicitParameters(context, class_info); - MergeOrAddName(context, node_id, name_context, class_decl_id, class_decl, - class_info, is_definition, - introducer.modifier_set.GetAccessKind()); + SemIR::ScopeLookupResult lookup_result = + context.decl_name_stack().LookupOrAddName( + name_context, class_decl_id, introducer.modifier_set.GetAccessKind()); + TryMergeRedecl(context, name_context, lookup_result, + MergeRedeclEntityInfo{ + .new_entity_decl = class_decl, .new_entity = class_info}, + is_definition); // Create a new class if this isn't a valid redeclaration. bool is_new_class = !class_decl.class_id.has_value(); diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index edf7fe85d961..9c09438d7265 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -141,131 +141,6 @@ static auto GetInterfaceModifier(const KeywordModifierSet& modifier_set) .Default(None); } -// Tries to merge new_function into prev_function_id. Since new_function won't -// have a definition even if one is upcoming, set is_definition to indicate the -// planned result. -// -// If merging is successful, returns true and may update the previous function. -// Otherwise, returns false. Prints a diagnostic when appropriate. -static auto MergeFunctionRedecl(Context& context, - Parse::AnyFunctionDeclId node_id, - SemIR::Function& new_function, - bool new_is_definition, - SemIR::FunctionId prev_function_id, - SemIR::ImportIRId prev_import_ir_id) -> bool { - auto& prev_function = context.functions().Get(prev_function_id); - - if (!CheckFunctionTypeMatches(context, new_function, prev_function)) { - return false; - } - - DiagnoseIfInvalidRedecl( - context, Lex::TokenKind::Fn, prev_function.name_id, - RedeclInfo(new_function, node_id, new_is_definition), - RedeclInfo(prev_function, SemIR::LocId(prev_function.latest_decl_id()), - prev_function.has_definition_started()), - prev_import_ir_id); - if (new_is_definition && prev_function.has_definition_started()) { - return false; - } - - if (!prev_function.first_owning_decl_id.has_value()) { - prev_function.first_owning_decl_id = new_function.first_owning_decl_id; - } - if (new_is_definition) { - // Track the signature from the definition, so that IDs in the body - // match IDs in the signature. - prev_function.MergeDefinition(new_function); - } - if (prev_import_ir_id.has_value()) { - ReplacePrevInstForMerge(context, new_function.parent_scope_id, - prev_function.name_id, - new_function.first_owning_decl_id); - } - return true; -} - -// Check whether this is a redeclaration, merging if needed. -static auto TryMergeRedecl(Context& context, Parse::AnyFunctionDeclId node_id, - const DeclNameStack::NameContext& name_context, - SemIR::FunctionDecl& function_decl, - SemIR::Function& function_info, bool is_definition) - -> void { - // Diagnose if we are declaring a poisoned name. However, don't diagnose at - // impl scope: if the name was referenced before being declared, we will have - // produced an error already. - if (name_context.state == DeclNameStack::NameContext::State::Poisoned) { - if (!context.name_scopes().InstIs( - name_context.parent_scope_id)) { - DiagnosePoisonedName(context, name_context.name_id_for_new_inst(), - name_context.poisoning_loc_id, name_context.loc_id); - } - return; - } - - auto prev_id = name_context.prev_inst_id(); - if (!prev_id.has_value()) { - return; - } - - auto prev_function_id = SemIR::FunctionId::None; - auto prev_type_id = SemIR::TypeId::None; - auto prev_import_ir_id = SemIR::ImportIRId::None; - CARBON_KIND_SWITCH(context.insts().Get(prev_id)) { - case CARBON_KIND(SemIR::AssociatedEntity assoc_entity): { - // This is a function in an interface definition scope. - auto function_decl = - context.insts().GetAs(assoc_entity.decl_id); - prev_function_id = function_decl.function_id; - prev_type_id = function_decl.type_id; - break; - } - case CARBON_KIND(SemIR::FunctionDecl function_decl): { - prev_function_id = function_decl.function_id; - prev_type_id = function_decl.type_id; - break; - } - case SemIR::ImportRefLoaded::Kind: { - auto import_ir_inst = GetCanonicalImportIRInst(context, prev_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 (!import_ir->insts().Is( - import_ir_inst.inst_id())) { - break; - } - - // Use the type to get the ID. - if (auto struct_value = context.insts().TryGetAs( - context.constant_values().GetConstantInstId(prev_id))) { - if (auto function_type = context.types().TryGetAs( - struct_value->type_id)) { - prev_function_id = function_type->function_id; - prev_type_id = struct_value->type_id; - prev_import_ir_id = import_ir_inst.ir_id(); - } - } - break; - } - default: - break; - } - - if (!prev_function_id.has_value()) { - DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id, - SemIR::LocId(prev_id)); - return; - } - - if (MergeFunctionRedecl(context, node_id, function_info, is_definition, - prev_function_id, prev_import_ir_id)) { - // When merging, use the existing function rather than adding a new one. - function_decl.function_id = prev_function_id; - function_decl.type_id = prev_type_id; - } -} - // Adds the declaration to name lookup when appropriate. static auto MaybeAddToNameLookup(Context& context, const DeclNameStack::NameContext& name_context, @@ -580,8 +455,11 @@ static auto BuildFunctionDecl(Context& context, DiagnosePositionalParams(context, function_info); - TryMergeRedecl(context, node_id, name_context, function_decl, function_info, - is_definition); + TryMergeRedecl( + context, name_context, std::nullopt, + MergeRedeclEntityInfo{.new_entity_decl = function_decl, + .new_entity = function_info}, + is_definition); // Create a new function if this isn't a valid redeclaration. if (!function_decl.function_id.has_value()) { diff --git a/toolchain/check/handle_interface.cpp b/toolchain/check/handle_interface.cpp index 27f79c469d35..c0d598e2892a 100644 --- a/toolchain/check/handle_interface.cpp +++ b/toolchain/check/handle_interface.cpp @@ -73,12 +73,11 @@ static auto BuildInterfaceDecl(Context& context, SemIR::ScopeLookupResult lookup_result = context.decl_name_stack().LookupOrAddName( name_context, decl_inst_id, introducer.modifier_set.GetAccessKind()); - if (auto existing_decl = TryGetExistingDecl(context, name, lookup_result, - interface_info, is_definition)) { - auto existing_interface_decl = existing_decl->As(); - interface_decl.interface_id = existing_interface_decl.interface_id; - interface_decl.type_id = existing_interface_decl.type_id; - + if (TryMergeRedecl( + context, name_context, lookup_result, + MergeRedeclEntityInfo{ + .new_entity_decl = interface_decl, .new_entity = interface_info}, + is_definition)) { auto prev_decl_generic_id = context.interfaces().Get(interface_decl.interface_id).generic_id; FinishGenericRedecl(context, prev_decl_generic_id); diff --git a/toolchain/check/handle_named_constraint.cpp b/toolchain/check/handle_named_constraint.cpp index 1aa0c419dff8..02bba1a5dcbd 100644 --- a/toolchain/check/handle_named_constraint.cpp +++ b/toolchain/check/handle_named_constraint.cpp @@ -7,6 +7,7 @@ #include "toolchain/check/handle.h" #include "toolchain/check/inst.h" #include "toolchain/check/interface.h" +#include "toolchain/check/merge.h" #include "toolchain/check/modifiers.h" #include "toolchain/check/type.h" #include "toolchain/sem_ir/ids.h" @@ -69,17 +70,11 @@ static auto BuildNamedConstraintDecl(Context& context, SemIR::ScopeLookupResult lookup_result = context.decl_name_stack().LookupOrAddName( name_context, decl_inst_id, introducer.modifier_set.GetAccessKind()); - if (auto existing_decl = TryGetExistingDecl(context, name, lookup_result, - constraint_info, is_definition)) { - auto existing_constraint_decl = - existing_decl->As(); - constraint_decl.named_constraint_id = - existing_constraint_decl.named_constraint_id; - constraint_decl.type_id = existing_constraint_decl.type_id; - // TODO: If the new declaration is a definition, keep its parameter - // and implicit parameter lists rather than the ones from the - // previous declaration. - + if (TryMergeRedecl(context, name_context, lookup_result, + MergeRedeclEntityInfo{ + .new_entity_decl = constraint_decl, + .new_entity = constraint_info}, + is_definition)) { auto prev_decl_generic_id = context.named_constraints() .Get(constraint_decl.named_constraint_id) .generic_id; diff --git a/toolchain/check/interface.cpp b/toolchain/check/interface.cpp index ca153ee083dd..14f8739beff8 100644 --- a/toolchain/check/interface.cpp +++ b/toolchain/check/interface.cpp @@ -156,167 +156,4 @@ auto AddSelfSymbolicBindingToScope(Context& context, return self_param_inst_id; } -template - requires std::same_as -static constexpr auto DeclTokenKind() -> Lex::TokenKind { - return Lex::TokenKind::Interface; -} - -template - requires std::same_as -static constexpr auto DeclTokenKind() -> Lex::TokenKind { - return Lex::TokenKind::Constraint; -} - -template - requires SameAsOneOf -auto TryGetExistingDecl(Context& context, const NameComponent& name, - SemIR::ScopeLookupResult lookup_result, - const EntityT& entity, bool is_definition) - -> std::optional { - using EntityIdT = - std::conditional_t, - SemIR::InterfaceId, SemIR::NamedConstraintId>; - constexpr bool IsInterface = std::is_same_v; - - 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( - import_ir_inst.inst_id())) { - break; - } - } else { - if (!import_ir->insts().Is( - 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()) { - 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(), 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(prev) - : std::nullopt; -} - -template auto TryGetExistingDecl(Context& context, const NameComponent& name, - SemIR::ScopeLookupResult lookup_result, - const SemIR::Interface& entity, - bool is_definition) - -> std::optional; -template auto TryGetExistingDecl(Context& context, const NameComponent& name, - SemIR::ScopeLookupResult lookup_result, - const SemIR::NamedConstraint& entity, - bool is_definition) - -> std::optional; - } // namespace Carbon::Check diff --git a/toolchain/check/merge.cpp b/toolchain/check/merge.cpp index 8f0f4194b78e..885663bd3386 100644 --- a/toolchain/check/merge.cpp +++ b/toolchain/check/merge.cpp @@ -560,4 +560,329 @@ auto CheckRedeclParamsMatch(Context& context, const DeclParams& new_entity, return true; } +// Fills the previous class id, type id, and import ir id. +static auto FillPrevEntityInfo(Context& context, + const SemIR::ImportIRInst& import_ir_inst, + SemIR::Inst decl_val, + SemIR::ClassId& prev_entity_id, + SemIR::TypeId& prev_type_id, + SemIR::ImportIRId& prev_import_ir_id) -> void { + // 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 (!import_ir->insts().Is(import_ir_inst.inst_id())) { + return; + } + + if (auto class_type = decl_val.TryAs()) { + prev_entity_id = class_type->class_id; + prev_type_id = SemIR::TypeId::None; + prev_import_ir_id = import_ir_inst.ir_id(); + } else if (auto generic_class_type = + context.types().TryGetAs( + decl_val.type_id())) { + prev_entity_id = generic_class_type->class_id; + prev_type_id = SemIR::TypeId::None; + prev_import_ir_id = import_ir_inst.ir_id(); + } +} + +// Fills the previous function id, type id, and import ir id. +static auto FillPrevEntityInfo(Context& context, + const SemIR::ImportIRInst& import_ir_inst, + SemIR::Inst decl_val, + SemIR::FunctionId& prev_entity_id, + SemIR::TypeId& prev_type_id, + SemIR::ImportIRId& prev_import_ir_id) -> void { + // 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 (!import_ir->insts().Is(import_ir_inst.inst_id())) { + return; + } + + if (auto struct_value = decl_val.TryAs()) { + if (auto function_type = context.types().TryGetAs( + struct_value->type_id)) { + prev_entity_id = function_type->function_id; + prev_type_id = struct_value->type_id; + prev_import_ir_id = import_ir_inst.ir_id(); + } + } +} + +// Fills the previous interface id, type id, and import ir id. +static auto FillPrevEntityInfo(Context& context, + const SemIR::ImportIRInst& import_ir_inst, + SemIR::Inst decl_val, + SemIR::InterfaceId& prev_entity_id, + SemIR::TypeId& prev_type_id, + SemIR::ImportIRId& prev_import_ir_id) -> void { + // 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 (!import_ir->insts().Is(import_ir_inst.inst_id())) { + return; + } + + if (auto facet_type = decl_val.TryAs()) { + auto declared_facet_type = + context.declared_facet_types().Get(facet_type->declared_facet_type_id); + prev_entity_id = declared_facet_type.extend_constraints[0].interface_id; + prev_type_id = SemIR::TypeId::None; + prev_import_ir_id = import_ir_inst.ir_id(); + } +} + +// Fills the previous named constraint id, type id, and import ir id. +static auto FillPrevEntityInfo(Context& context, + const SemIR::ImportIRInst& import_ir_inst, + SemIR::Inst decl_val, + SemIR::NamedConstraintId& prev_entity_id, + SemIR::TypeId& prev_type_id, + SemIR::ImportIRId& prev_import_ir_id) -> void { + // 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 (!import_ir->insts().Is( + import_ir_inst.inst_id())) { + return; + } + + if (auto facet_type = decl_val.TryAs()) { + auto declared_facet_type = + context.declared_facet_types().Get(facet_type->declared_facet_type_id); + prev_entity_id = + declared_facet_type.extend_named_constraints[0].named_constraint_id; + prev_type_id = SemIR::TypeId::None; + prev_import_ir_id = import_ir_inst.ir_id(); + } +} + +template +auto TryMergeRedecl(Context& context, + const DeclNameStack::NameContext& name_context, + std::optional lookup_result, + MergeRedeclEntityInfo entity_info, + bool is_definition) -> bool { + constexpr bool IsClass = std::is_same_v; + constexpr bool IsFunction = std::is_same_v; + constexpr bool IsInterface = std::is_same_v; + constexpr bool IsNamedConstraint = + std::is_same_v; + + if constexpr (IsFunction) { + CARBON_CHECK(!lookup_result.has_value()); + // Diagnose if we are declaring a poisoned name. However, don't diagnose + // at impl scope: if the name was referenced before being declared, we + // will have produced an error already. + if (name_context.state == DeclNameStack::NameContext::State::Poisoned) { + if (!context.name_scopes().InstIs( + name_context.parent_scope_id)) { + DiagnosePoisonedName(context, name_context.name_id_for_new_inst(), + name_context.poisoning_loc_id, + name_context.loc_id); + } + return false; + } + } else if constexpr (IsClass || IsInterface || IsNamedConstraint) { + CARBON_CHECK(lookup_result.has_value()); + if (lookup_result->is_poisoned()) { + DiagnosePoisonedName(context, name_context.name_id_for_new_inst(), + lookup_result->poisoning_loc_id(), + name_context.loc_id); + return false; + } + + if (!lookup_result->is_found()) { + return false; + } + } else { + CARBON_FATAL("Unhandled entity type."); + } + + auto prev_id = lookup_result ? lookup_result->target_inst_id() + : name_context.prev_inst_id(); + if (!prev_id.has_value()) { + return false; + } + auto prev = context.insts().Get(prev_id); + + auto prev_entity_id = MergeRedeclEntityInfo::EntityIdT::None; + auto prev_type_id = SemIR::TypeId::None; + auto prev_import_ir_id = SemIR::ImportIRId::None; + CARBON_KIND_SWITCH(prev) { + case CARBON_KIND(SemIR::AssociatedEntity assoc_entity): { + if constexpr (IsFunction) { + // This is a function in an interface definition scope. + auto function_decl = + context.insts().GetAs(assoc_entity.decl_id); + prev_entity_id = function_decl.function_id; + prev_type_id = function_decl.type_id; + } + break; + } + case CARBON_KIND(SemIR::ClassDecl class_decl): { + if constexpr (IsClass) { + prev_entity_id = class_decl.class_id; + } + break; + } + case CARBON_KIND(SemIR::FunctionDecl function_decl): { + if constexpr (IsFunction) { + prev_entity_id = function_decl.function_id; + prev_type_id = function_decl.type_id; + } + break; + } + case CARBON_KIND(SemIR::InterfaceDecl interface_decl): { + if constexpr (IsInterface) { + prev_entity_id = interface_decl.interface_id; + } + break; + } + case CARBON_KIND(SemIR::NamedConstraintDecl named_constraint_decl): { + if constexpr (IsNamedConstraint) { + prev_entity_id = named_constraint_decl.named_constraint_id; + } + break; + } + case CARBON_KIND(SemIR::ImportRefLoaded import_ref): { + // TODO: Should we get canonical inst for all entity types? + auto import_ir_inst = [&]() -> SemIR::ImportIRInst { + if constexpr (IsClass || IsInterface || IsNamedConstraint) { + return context.import_ir_insts().Get(import_ref.import_ir_inst_id); + } else if constexpr (IsFunction) { + return GetCanonicalImportIRInst(context, prev_id); + } else { + CARBON_FATAL("Unhandled entity type."); + } + }(); + auto decl_val = context.insts().Get( + context.constant_values().GetConstantInstId(prev_id)); + FillPrevEntityInfo(context, import_ir_inst, decl_val, prev_entity_id, + prev_type_id, prev_import_ir_id); + break; + } + default: { + break; + } + } + + if (!prev_entity_id.has_value()) { + // This is a redeclaration with a different entity kind. + DiagnoseDuplicateName(context, name_context.name_id, name_context.loc_id, + SemIR::LocId(prev_id)); + return false; + } + + auto& prev_entity = [&]() -> EntityT& { + if constexpr (IsClass) { + return context.classes().Get(prev_entity_id); + } else if constexpr (IsFunction) { + return context.functions().Get(prev_entity_id); + } else if constexpr (IsInterface) { + return context.interfaces().Get(prev_entity_id); + } else if constexpr (IsNamedConstraint) { + return context.named_constraints().Get(prev_entity_id); + } else { + CARBON_FATAL("Unhandled entity type."); + } + }(); + + if constexpr (IsClass || IsInterface || IsNamedConstraint) { + if (!CheckRedeclParamsMatch(context, DeclParams(entity_info.new_entity), + DeclParams(prev_entity))) { + // Mismatch is diagnosed already if found. + return false; + } + } else if constexpr (IsFunction) { + if (!CheckFunctionTypeMatches(context, entity_info.new_entity, + prev_entity)) { + // Mismatch is diagnosed already if found. + return false; + } + } else { + CARBON_FATAL("Unhandled entity type."); + } + + DiagnoseIfInvalidRedecl( + context, MergeRedeclEntityInfo::DeclTokenKind, + prev_entity.name_id, + RedeclInfo(entity_info.new_entity, + SemIR::LocId(entity_info.new_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 false; + } + + if (!prev_entity.first_owning_decl_id.has_value()) { + prev_entity.first_owning_decl_id = + entity_info.new_entity.first_owning_decl_id; + } + + if (is_definition) { + prev_entity.MergeDefinition(entity_info.new_entity); + } + + auto replace_prev_inst = prev_import_ir_id.has_value(); + if constexpr (IsClass) { + replace_prev_inst |= + prev_entity.is_extern && !entity_info.new_entity.is_extern; + } + if (replace_prev_inst) { + ReplacePrevInstForMerge(context, entity_info.new_entity.parent_scope_id, + prev_entity.name_id, + entity_info.new_entity.first_owning_decl_id); + } + + // When merging, use the existing entity rather than adding a new one. + if constexpr (IsClass) { + // TODO: Fix `extern` logic. It doesn't work correctly, but doesn't seem + // worth ripping out because existing code may incrementally help. + entity_info.new_entity_decl.class_id = prev_entity_id; + entity_info.new_entity_decl.type_id = prev.type_id(); + // TODO: Validate that the redeclaration doesn't set an access modifier. + } else if constexpr (IsFunction) { + entity_info.new_entity_decl.function_id = prev_entity_id; + entity_info.new_entity_decl.type_id = prev_type_id; + } else if constexpr (IsInterface) { + entity_info.new_entity_decl.interface_id = prev_entity_id; + entity_info.new_entity_decl.type_id = prev.type_id(); + } else if constexpr (IsNamedConstraint) { + entity_info.new_entity_decl.named_constraint_id = prev_entity_id; + entity_info.new_entity_decl.type_id = prev.type_id(); + } else { + CARBON_FATAL("Unhandled entity type."); + } + + return true; +} + +template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&, + std::optional, + MergeRedeclEntityInfo, bool) -> bool; +template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&, + std::optional, + MergeRedeclEntityInfo, bool) + -> bool; +template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&, + std::optional, + MergeRedeclEntityInfo, bool) + -> bool; +template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&, + std::optional, + MergeRedeclEntityInfo, + bool) -> bool; + } // namespace Carbon::Check diff --git a/toolchain/check/merge.h b/toolchain/check/merge.h index 5b4a6254f809..d89379a33836 100644 --- a/toolchain/check/merge.h +++ b/toolchain/check/merge.h @@ -5,9 +5,26 @@ #ifndef CARBON_TOOLCHAIN_CHECK_MERGE_H_ #define CARBON_TOOLCHAIN_CHECK_MERGE_H_ +#include +#include + +#include "common/check.h" +#include "common/concepts.h" +#include "toolchain/base/kind_switch.h" #include "toolchain/check/context.h" +#include "toolchain/check/decl_name_stack.h" +#include "toolchain/check/function.h" +#include "toolchain/check/import_ref.h" +#include "toolchain/check/name_lookup.h" #include "toolchain/check/subst.h" +#include "toolchain/lex/token_kind.h" +#include "toolchain/sem_ir/class.h" +#include "toolchain/sem_ir/function.h" #include "toolchain/sem_ir/ids.h" +#include "toolchain/sem_ir/import_ir.h" +#include "toolchain/sem_ir/interface.h" +#include "toolchain/sem_ir/named_constraint.h" +#include "toolchain/sem_ir/typed_insts.h" namespace Carbon::Check { @@ -109,6 +126,92 @@ inline auto CheckRedeclParamsMatch(Context& context, /*check_syntax=*/true); } +// Provides type traits and data for merging redeclarations. +template +struct MergeRedeclEntityInfo; + +// Information for merging redeclarations of classes. +template <> +struct MergeRedeclEntityInfo { + using EntityIdT = SemIR::ClassId; + using EntityT = SemIR::Class; + using EntityDeclT = SemIR::ClassDecl; + + static constexpr auto DeclTokenKind = Lex::TokenKind::Class; + + EntityDeclT& new_entity_decl; + const EntityT& new_entity; +}; + +// Information for merging redeclarations of functions. +template <> +struct MergeRedeclEntityInfo { + using EntityIdT = SemIR::FunctionId; + using EntityT = SemIR::Function; + using EntityDeclT = SemIR::FunctionDecl; + + static constexpr auto DeclTokenKind = Lex::TokenKind::Fn; + + EntityDeclT& new_entity_decl; + const EntityT& new_entity; +}; + +// Information for merging redeclarations of interfaces. +template <> +struct MergeRedeclEntityInfo { + using EntityIdT = SemIR::InterfaceId; + using EntityT = SemIR::Interface; + using EntityDeclT = SemIR::InterfaceDecl; + + static constexpr auto DeclTokenKind = Lex::TokenKind::Interface; + + EntityDeclT& new_entity_decl; + const EntityT& new_entity; +}; + +// Information for merging redeclarations of named constraints. +template <> +struct MergeRedeclEntityInfo { + using EntityIdT = SemIR::NamedConstraintId; + using EntityT = SemIR::NamedConstraint; + using EntityDeclT = SemIR::NamedConstraintDecl; + + static constexpr auto DeclTokenKind = Lex::TokenKind::Constraint; + + EntityDeclT& new_entity_decl; + const EntityT& new_entity; +}; + +// Tries to merge new_entity into prev_entity_id. Since new_entity won't have a +// definition even if one is upcoming, set is_definition to indicate the planned +// result. +// +// If merging is successful, returns the previous declaration. +// Otherwise, returns nullopt. Prints a diagnostic when appropriate. +template +auto TryMergeRedecl(Context& context, + const DeclNameStack::NameContext& name_context, + std::optional lookup_result, + MergeRedeclEntityInfo entity_info, + bool is_definition) -> bool; + +extern template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&, + std::optional, + MergeRedeclEntityInfo, bool) + -> bool; +extern template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&, + std::optional, + MergeRedeclEntityInfo, + bool) -> bool; +extern template auto TryMergeRedecl(Context&, const DeclNameStack::NameContext&, + std::optional, + MergeRedeclEntityInfo, + bool) -> bool; +extern template auto TryMergeRedecl( + Context&, const DeclNameStack::NameContext&, + std::optional, + MergeRedeclEntityInfo, bool) -> bool; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_MERGE_H_ diff --git a/toolchain/check/testdata/class/access/import_access.carbon b/toolchain/check/testdata/class/access/import_access.carbon index 5c1f1aa984ae..2aae8db1a043 100644 --- a/toolchain/check/testdata/class/access/import_access.carbon +++ b/toolchain/check/testdata/class/access/import_access.carbon @@ -479,7 +479,7 @@ private class Redecl {} // CHECK:STDOUT: %Forward.decl: type = class_decl @Forward [concrete = constants.%Forward] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @Forward { +// CHECK:STDOUT: class @Forward [from "forward.carbon"] { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/extern.carbon b/toolchain/check/testdata/class/extern.carbon index cc27bb601990..98603c6afc7b 100644 --- a/toolchain/check/testdata/class/extern.carbon +++ b/toolchain/check/testdata/class/extern.carbon @@ -576,7 +576,7 @@ extern class C; // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C; +// CHECK:STDOUT: class @C [from "extern_decl.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_decl_after_import_extern_decl.carbon // CHECK:STDOUT: @@ -592,7 +592,7 @@ extern class C; // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C; +// CHECK:STDOUT: class @C [from "decl.carbon"]; // CHECK:STDOUT: // CHECK:STDOUT: --- fail_def_after_import_extern_decl.carbon // CHECK:STDOUT: @@ -615,7 +615,7 @@ extern class C; // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C { +// CHECK:STDOUT: class @C [from "def.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: @@ -643,7 +643,7 @@ extern class C; // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C { +// CHECK:STDOUT: class @C [from "def.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: diff --git a/toolchain/check/testdata/class/generic/import.carbon b/toolchain/check/testdata/class/generic/import.carbon index 6bf52fdad1f1..9992e291cc9b 100644 --- a/toolchain/check/testdata/class/generic/import.carbon +++ b/toolchain/check/testdata/class/generic/import.carbon @@ -385,7 +385,7 @@ class Class(U: type) { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: generic class @Class(imports.%Main.import_ref.b3bc94.1: type) { +// CHECK:STDOUT: generic class @Class(imports.%Main.import_ref.b3bc94.1: type) [from "foo.carbon"] { // CHECK:STDOUT: %T.patt.1: %pattern_type.98f = symbolic_binding_pattern T, 0 [symbolic = %T.patt.1 (constants.%T.patt)] // CHECK:STDOUT: %T.1: type = symbolic_binding T, 0 [symbolic = %T.1 (constants.%T)] // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/implicit_import.carbon b/toolchain/check/testdata/class/implicit_import.carbon index 56825afab651..568dba064417 100644 --- a/toolchain/check/testdata/class/implicit_import.carbon +++ b/toolchain/check/testdata/class/implicit_import.carbon @@ -117,7 +117,7 @@ class B {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C { +// CHECK:STDOUT: class @C [from "basic.carbon"] { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: @@ -170,7 +170,7 @@ class B {} // CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @C { +// CHECK:STDOUT: class @C [from "redecl_after_def.carbon"] { // CHECK:STDOUT: complete_type_witness = imports.%Main.import_ref.8f2 // CHECK:STDOUT: // CHECK:STDOUT: !members: diff --git a/toolchain/check/testdata/class/import_forward_decl.carbon b/toolchain/check/testdata/class/import_forward_decl.carbon index 34677b6a0f24..5c91e01d8573 100644 --- a/toolchain/check/testdata/class/import_forward_decl.carbon +++ b/toolchain/check/testdata/class/import_forward_decl.carbon @@ -75,7 +75,7 @@ class ForwardDecl { // CHECK:STDOUT: %ForwardDecl.decl: type = class_decl @ForwardDecl [concrete = constants.%ForwardDecl] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: class @ForwardDecl { +// CHECK:STDOUT: class @ForwardDecl [from "a.carbon"] { // CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] // CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/no_definition_in_impl_file.carbon b/toolchain/check/testdata/class/no_definition_in_impl_file.carbon index ba7d2a13aa4c..f80fd7e5e00c 100644 --- a/toolchain/check/testdata/class/no_definition_in_impl_file.carbon +++ b/toolchain/check/testdata/class/no_definition_in_impl_file.carbon @@ -3,8 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon -// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". -// EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: @@ -76,149 +74,3 @@ impl library "[[@TEST_NAME]]"; // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: class D; - -// CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A: type = class_type @A [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: type = class_decl @A [concrete = constants.%A] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @A; -// CHECK:STDOUT: -// CHECK:STDOUT: --- todo_fail_decl_in_api_definition_in_impl.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A: type = class_type @A [concrete] -// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] -// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl.loc5 -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_46.1 = import -// CHECK:STDOUT: %default.import.loc2_46.2 = import -// CHECK:STDOUT: %A.decl.loc5: type = class_decl @A [concrete = constants.%A] {} {} -// CHECK:STDOUT: %A.decl.loc7: type = class_decl @A [concrete = constants.%A] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @A { -// CHECK:STDOUT: %complete_type: = complete_type_witness constants.%empty_struct_type [concrete = constants.%complete_type] -// CHECK:STDOUT: complete_type_witness = %complete_type -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = constants.%A -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- use_decl_in_api.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- use_decl_in_api.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Main.A = import_ref Main//decl_in_api_definition_in_impl, A, unloaded -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = imports.%Main.A -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_31.1 = import -// CHECK:STDOUT: %default.import.loc2_31.2 = import -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_only_in_api.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %B: type = class_type @B [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .B = %B.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %B.decl: type = class_decl @B [concrete = constants.%B] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @B; -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_only_in_api.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Main.B = import_ref Main//decl_only_in_api, B, unloaded -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .B = imports.%Main.B -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_32.1 = import -// CHECK:STDOUT: %default.import.loc2_32.2 = import -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C: type = class_type @C [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_40.1 = import -// CHECK:STDOUT: %default.import.loc2_40.2 = import -// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @C; -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_only_in_impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %D: type = class_type @D [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .D = %D.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_33.1 = import -// CHECK:STDOUT: %default.import.loc2_33.2 = import -// CHECK:STDOUT: %D.decl: type = class_decl @D [concrete = constants.%D] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: class @D; -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/class/syntactic_merge.carbon b/toolchain/check/testdata/class/syntactic_merge.carbon index 1196e30e5e55..413afeb2af45 100644 --- a/toolchain/check/testdata/class/syntactic_merge.carbon +++ b/toolchain/check/testdata/class/syntactic_merge.carbon @@ -687,7 +687,7 @@ fn Base.F(ref self: Base) { // CHECK:STDOUT: .Self = imports.%Main.import_ref.e47 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.99696d.1: %C) { +// CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.99696d.1: %C) [from "two_file.carbon"] { // CHECK:STDOUT: %a.patt.1: %pattern_type = symbolic_binding_pattern a, 0 [symbolic = %a.patt.1 (constants.%a.patt)] // CHECK:STDOUT: %a.1: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)] // CHECK:STDOUT: @@ -702,7 +702,7 @@ fn Base.F(ref self: Base) { // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: generic class @Bar(imports.%Main.import_ref.99696d.2: %C) { +// CHECK:STDOUT: generic class @Bar(imports.%Main.import_ref.99696d.2: %C) [from "two_file.carbon"] { // CHECK:STDOUT: %a.patt.1: %pattern_type = symbolic_binding_pattern a, 0 [symbolic = %a.patt.1 (constants.%a.patt)] // CHECK:STDOUT: %a.1: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)] // CHECK:STDOUT: @@ -1093,7 +1093,7 @@ fn Base.F(ref self: Base) { // CHECK:STDOUT: .Self = imports.%Main.import_ref.e47 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.996: %C) { +// CHECK:STDOUT: generic class @Foo(imports.%Main.import_ref.996: %C) [from "alias_two_file.carbon"] { // CHECK:STDOUT: %a.patt.1: %pattern_type = symbolic_binding_pattern a, 0 [symbolic = %a.patt.1 (constants.%a.patt)] // CHECK:STDOUT: %a.1: %C = symbolic_binding a, 0 [symbolic = %a.1 (constants.%a)] // CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/declaration/no_definition_in_impl_file.carbon b/toolchain/check/testdata/function/declaration/no_definition_in_impl_file.carbon index 83f36903551e..96e03af73c05 100644 --- a/toolchain/check/testdata/function/declaration/no_definition_in_impl_file.carbon +++ b/toolchain/check/testdata/function/declaration/no_definition_in_impl_file.carbon @@ -3,8 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon -// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". -// EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: @@ -76,150 +74,3 @@ impl library "[[@TEST_NAME]]"; // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fn D(); - -// CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = fn_type @A [concrete] -// CHECK:STDOUT: %A: %A.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @A(); -// CHECK:STDOUT: -// CHECK:STDOUT: --- todo_fail_decl_in_api_definition_in_impl.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = fn_type @A [concrete] -// CHECK:STDOUT: %A: %A.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl.loc5 -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_46.1 = import -// CHECK:STDOUT: %default.import.loc2_46.2 = import -// CHECK:STDOUT: %A.decl.loc5: %A.type = fn_decl @A [concrete = constants.%A] {} {} -// CHECK:STDOUT: %A.decl.loc7: %A.type = fn_decl @A [concrete = constants.%A] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @A() [from "decl_in_api_definition_in_impl.carbon"] { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- use_decl_in_api.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- use_decl_in_api.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Main.A = import_ref Main//decl_in_api_definition_in_impl, A, unloaded -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = imports.%Main.A -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_31.1 = import -// CHECK:STDOUT: %default.import.loc2_31.2 = import -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_only_in_api.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %B.type: type = fn_type @B [concrete] -// CHECK:STDOUT: %B: %B.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .B = %B.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %B.decl: %B.type = fn_decl @B [concrete = constants.%B] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @B(); -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_only_in_api.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Main.B = import_ref Main//decl_only_in_api, B, unloaded -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .B = imports.%Main.B -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_32.1 = import -// CHECK:STDOUT: %default.import.loc2_32.2 = import -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C.type: type = fn_type @C [concrete] -// CHECK:STDOUT: %C: %C.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @C(); -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C.type: type = fn_type @C [concrete] -// CHECK:STDOUT: %C: %C.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_40.1 = import -// CHECK:STDOUT: %default.import.loc2_40.2 = import -// CHECK:STDOUT: %C.decl: %C.type = fn_decl @C [concrete = constants.%C] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @C [from "decl_in_api_decl_in_impl.carbon"]; -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_only_in_impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %D.type: type = fn_type @D [concrete] -// CHECK:STDOUT: %D: %D.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .D = %D.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_33.1 = import -// CHECK:STDOUT: %default.import.loc2_33.2 = import -// CHECK:STDOUT: %D.decl: %D.type = fn_decl @D [concrete = constants.%D] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @D(); -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interface/fail_definition_imported.carbon b/toolchain/check/testdata/interface/fail_definition_imported.carbon index 2bcffd10ce66..124a360084fa 100644 --- a/toolchain/check/testdata/interface/fail_definition_imported.carbon +++ b/toolchain/check/testdata/interface/fail_definition_imported.carbon @@ -52,8 +52,8 @@ interface I {} // CHECK:STDOUT: --- fail_b.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %I.type.8e70aa.2: type = facet_type <@I.loc14_13.2> [concrete] -// CHECK:STDOUT: %Self: %I.type.8e70aa.2 = symbolic_binding Self, 0 [symbolic] +// CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] +// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -61,14 +61,12 @@ interface I {} // CHECK:STDOUT: .I = %I.decl // CHECK:STDOUT: } // CHECK:STDOUT: %default.import = import -// CHECK:STDOUT: %I.decl: type = interface_decl @I.loc14_13.2 [concrete = constants.%I.type.8e70aa.2] {} {} +// CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: interface @I.loc14_13.1; -// CHECK:STDOUT: -// CHECK:STDOUT: interface @I.loc14_13.2 { -// CHECK:STDOUT: %Self: %I.type.8e70aa.2 = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I.loc14_13.2 [concrete] +// CHECK:STDOUT: interface @I [from "a.carbon"] { +// CHECK:STDOUT: %Self: %I.type = symbolic_binding Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %I.WithSelf.decl = interface_with_self_decl @I [concrete] // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = %Self diff --git a/toolchain/check/testdata/interface/no_definition_in_impl_file.carbon b/toolchain/check/testdata/interface/no_definition_in_impl_file.carbon index 4eccc577277b..bd3d551f7b36 100644 --- a/toolchain/check/testdata/interface/no_definition_in_impl_file.carbon +++ b/toolchain/check/testdata/interface/no_definition_in_impl_file.carbon @@ -3,8 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon -// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". -// EXTRA-ARGS: --dump-sem-ir-ranges=if-present // // AUTOUPDATE // TIP: To test this file alone, run: @@ -76,157 +74,3 @@ impl library "[[@TEST_NAME]]"; // CHECK:STDERR: ^~~~~~~~~~~~ // CHECK:STDERR: interface D; - -// CHECK:STDOUT: --- decl_in_api_definition_in_impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type: type = facet_type <@A> [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: type = interface_decl @A [concrete = constants.%A.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @A; -// CHECK:STDOUT: -// CHECK:STDOUT: --- todo_fail_decl_in_api_definition_in_impl.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %A.type.3138d6.2: type = facet_type <@A.loc7> [concrete] -// CHECK:STDOUT: %Self: %A.type.3138d6.2 = symbolic_binding Self, 0 [symbolic] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl.loc5 -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_46.1 = import -// CHECK:STDOUT: %default.import.loc2_46.2 = import -// CHECK:STDOUT: %A.decl.loc5: type = interface_decl @A.loc7 [concrete = constants.%A.type.3138d6.2] {} {} -// CHECK:STDOUT: %A.decl.loc7: type = interface_decl @A.loc7 [concrete = constants.%A.type.3138d6.2] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @A.loc5; -// CHECK:STDOUT: -// CHECK:STDOUT: interface @A.loc7 { -// CHECK:STDOUT: %Self: %A.type.3138d6.2 = symbolic_binding Self, 0 [symbolic = constants.%Self] -// CHECK:STDOUT: %A.WithSelf.decl = interface_with_self_decl @A.loc7 [concrete] -// CHECK:STDOUT: -// CHECK:STDOUT: !members: -// CHECK:STDOUT: .Self = %Self -// CHECK:STDOUT: witness = () -// CHECK:STDOUT: -// CHECK:STDOUT: !requires: -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: specific @A.WithSelf(constants.%Self) {} -// CHECK:STDOUT: -// CHECK:STDOUT: --- use_decl_in_api.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- use_decl_in_api.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Main.A = import_ref Main//decl_in_api_definition_in_impl, A, unloaded -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = imports.%Main.A -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_31.1 = import -// CHECK:STDOUT: %default.import.loc2_31.2 = import -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_only_in_api.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %B.type: type = facet_type <@B> [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .B = %B.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %B.decl: type = interface_decl @B [concrete = constants.%B.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @B; -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_only_in_api.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: imports { -// CHECK:STDOUT: %Main.B = import_ref Main//decl_only_in_api, B, unloaded -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .B = imports.%Main.B -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_32.1 = import -// CHECK:STDOUT: %default.import.loc2_32.2 = import -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_in_api_decl_in_impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C.type: type = facet_type <@C> [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %C.decl: type = interface_decl @C [concrete = constants.%C.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @C; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_decl_in_api_decl_in_impl.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %C.type.709119.2: type = facet_type <@C.loc8_12.2> [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .C = %C.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_40.1 = import -// CHECK:STDOUT: %default.import.loc2_40.2 = import -// CHECK:STDOUT: %C.decl: type = interface_decl @C.loc8_12.2 [concrete = constants.%C.type.709119.2] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @C.loc8_12.1; -// CHECK:STDOUT: -// CHECK:STDOUT: interface @C.loc8_12.2; -// CHECK:STDOUT: -// CHECK:STDOUT: --- decl_only_in_impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_decl_only_in_impl.impl.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: %D.type: type = facet_type <@D> [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .D = %D.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %default.import.loc2_33.1 = import -// CHECK:STDOUT: %default.import.loc2_33.2 = import -// CHECK:STDOUT: %D.decl: type = interface_decl @D [concrete = constants.%D.type] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: interface @D; -// CHECK:STDOUT: