From 0001f53ec1d5ebbdfbf024807f310009ebc1fa87 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Wed, 27 Mar 2024 16:38:10 -0700 Subject: [PATCH] Implement merging of conflicts found during import. (#3819) Note we only identify conflicts between libraries in the current package during import. This restructures the BUILD because of dependency cycles between cpp files... We're going to need context's name lookup to handle things such as merging, merging requires function logic, function logic requires context access. Per discussion, going with a single large cc_library for now rather than trying to split out small libraries. I'm envisioning the new merge.* as a hub for cross-declaration merge logic. Note function.cpp is already pretty sizable, and I think it may lean a little function-specific even if there are some utilities that could be split out. --- toolchain/check/BUILD | 38 +-- toolchain/check/function.cpp | 14 +- toolchain/check/function.h | 3 +- toolchain/check/import.cpp | 7 +- toolchain/check/merge.cpp | 81 +++++ toolchain/check/merge.h | 20 ++ .../function/declaration/extern.carbon | 4 +- .../function/declaration/import.carbon | 305 +++++++----------- ...conflict_in_imports_namespace_first.carbon | 4 +- .../packages/cross_package_import.carbon | 4 +- .../fail_conflict_no_namespaces.carbon | 6 +- toolchain/sem_ir/formatter.cpp | 8 +- 12 files changed, 245 insertions(+), 249 deletions(-) create mode 100644 toolchain/check/merge.cpp create mode 100644 toolchain/check/merge.h diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index bac9db6934f1..6643a9f2e152 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -54,10 +54,13 @@ cc_library( "convert.cpp", "decl_name_stack.cpp", "eval.cpp", + "function.cpp", "import_ref.cpp", "inst_block_stack.cpp", + "merge.cpp", "modifiers.cpp", "return.cpp", + "subst.cpp", ], hdrs = [ "context.h", @@ -66,12 +69,15 @@ cc_library( "decl_state.h", "diagnostic_helpers.h", "eval.h", + "function.h", "import_ref.h", "inst_block_stack.h", + "merge.h", "modifiers.h", "param_and_arg_refs_stack.h", "pending_block.h", "return.h", + "subst.h", ], deps = [ ":node_stack", @@ -104,7 +110,6 @@ cc_library( deps = [ ":call", ":context", - ":function", ":impl", ":import", ":interface", @@ -155,26 +160,12 @@ cc_library( ], ) -cc_library( - name = "function", - srcs = ["function.cpp"], - hdrs = ["function.h"], - deps = [ - ":context", - ":subst", - "//common:check", - "//toolchain/sem_ir:file", - ], -) - cc_library( name = "impl", srcs = ["impl.cpp"], hdrs = ["impl.h"], deps = [ ":context", - ":function", - ":subst", "//common:check", "//toolchain/diagnostics:diagnostic_emitter", "//toolchain/sem_ir:file", @@ -219,7 +210,6 @@ cc_library( hdrs = ["member_access.h"], deps = [ ":context", - ":subst", "//common:check", "//toolchain/diagnostics:diagnostic_emitter", "//toolchain/sem_ir:file", @@ -262,22 +252,6 @@ cc_library( ], ) -cc_library( - name = "subst", - srcs = ["subst.cpp"], - hdrs = ["subst.h"], - deps = [ - ":context", - "//common:check", - "//toolchain/diagnostics:diagnostic_emitter", - "//toolchain/sem_ir:file", - "//toolchain/sem_ir:ids", - "//toolchain/sem_ir:inst", - "//toolchain/sem_ir:inst_kind", - "@llvm-project//llvm:Support", - ], -) - glob_sh_run( args = [ "$(location //toolchain/driver:carbon)", diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index a6cdb14506a1..f37233623de0 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -193,7 +193,7 @@ auto CheckFunctionTypeMatches(Context& context, // Checks to see if a structurally valid redeclaration is allowed in context. // These all still merge. -static auto CheckIsAllowedRedecl(Context& context, Parse::NodeId node_id, +static auto CheckIsAllowedRedecl(Context& context, SemIR::LocationId loc_id, SemIR::Function& new_function, bool new_is_definition, SemIR::Function& prev_function, @@ -207,7 +207,7 @@ static auto CheckIsAllowedRedecl(Context& context, Parse::NodeId node_id, "Only one library can declare function {0} without `extern`.", SemIR::NameId); context.emitter() - .Build(node_id, FunctionNonExternRedecl, prev_function.name_id) + .Build(loc_id, FunctionNonExternRedecl, prev_function.name_id) .Note(prev_function.decl_id, FunctionPreviousDecl) .Emit(); return; @@ -218,7 +218,7 @@ static auto CheckIsAllowedRedecl(Context& context, Parse::NodeId node_id, "Redundant redeclaration of function {0}.", SemIR::NameId); context.emitter() - .Build(node_id, FunctionRedecl, prev_function.name_id) + .Build(loc_id, FunctionRedecl, prev_function.name_id) .Note(prev_function.decl_id, FunctionPreviousDecl) .Emit(); return; @@ -229,7 +229,7 @@ static auto CheckIsAllowedRedecl(Context& context, Parse::NodeId node_id, CARBON_DIAGNOSTIC(FunctionPreviousDefinition, Note, "Previously defined here."); context.emitter() - .Build(node_id, FunctionRedefinition, prev_function.name_id) + .Build(loc_id, FunctionRedefinition, prev_function.name_id) .Note(prev_function.definition_id, FunctionPreviousDefinition) .Emit(); return; @@ -243,7 +243,7 @@ static auto CheckIsAllowedRedecl(Context& context, Parse::NodeId node_id, CARBON_DIAGNOSTIC(FunctionPreviousExternDecl, Note, "Previously declared `extern` here."); context.emitter() - .Build(node_id, FunctionDefiningExtern, prev_function.name_id) + .Build(loc_id, FunctionDefiningExtern, prev_function.name_id) .Note(prev_function.decl_id, FunctionPreviousExternDecl) .Emit(); return; @@ -253,7 +253,7 @@ static auto CheckIsAllowedRedecl(Context& context, Parse::NodeId node_id, // TODO: Detect conflicting cross-file declarations, as well as uses of imported // declarations followed by a redeclaration. -auto MergeFunctionRedecl(Context& context, Parse::NodeId node_id, +auto MergeFunctionRedecl(Context& context, SemIR::LocationId loc_id, SemIR::Function& new_function, bool new_is_definition, SemIR::FunctionId prev_function_id, bool prev_is_import) -> bool { @@ -263,7 +263,7 @@ auto MergeFunctionRedecl(Context& context, Parse::NodeId node_id, return false; } - CheckIsAllowedRedecl(context, node_id, new_function, new_is_definition, + CheckIsAllowedRedecl(context, loc_id, new_function, new_is_definition, prev_function, prev_is_import); if (new_is_definition) { diff --git a/toolchain/check/function.h b/toolchain/check/function.h index 69f2cb0ea9ea..ceb6d351b1ff 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -8,6 +8,7 @@ #include "toolchain/check/context.h" #include "toolchain/check/subst.h" #include "toolchain/sem_ir/function.h" +#include "toolchain/sem_ir/ids.h" namespace Carbon::Check { @@ -27,7 +28,7 @@ auto CheckFunctionTypeMatches(Context& context, // // If merging is successful, updates the FunctionId on new_function and returns // true. Otherwise, returns false. Prints a diagnostic when appropriate. -auto MergeFunctionRedecl(Context& context, Parse::NodeId node_id, +auto MergeFunctionRedecl(Context& context, SemIR::LocationId loc_id, SemIR::Function& new_function, bool new_is_definition, SemIR::FunctionId prev_function_id, bool prev_is_imported) -> bool; diff --git a/toolchain/check/import.cpp b/toolchain/check/import.cpp index 8890a2959bf7..5b52eddb5366 100644 --- a/toolchain/check/import.cpp +++ b/toolchain/check/import.cpp @@ -6,6 +6,7 @@ #include "common/check.h" #include "toolchain/check/context.h" +#include "toolchain/check/merge.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/ids.h" @@ -242,15 +243,11 @@ auto ImportLibraryFromCurrentPackage(Context& context, } else { // Leave a placeholder that the inst comes from the other IR. auto target_id = context.AddImportRef(ir_id, import_inst_id); - // TODO: When importing from other packages, the scope's names should - // be changed to allow for ambiguous names. When importing from the - // current package, as is currently being done, we should issue a - // diagnostic on conflicts. auto [it, success] = context.name_scopes() .Get(enclosing_scope_id) .names.insert({name_id, target_id}); if (!success) { - context.DiagnoseDuplicateName(target_id, it->second); + MergeImportRef(context, target_id, it->second); } } } diff --git a/toolchain/check/merge.cpp b/toolchain/check/merge.cpp new file mode 100644 index 000000000000..9b7720bebc6d --- /dev/null +++ b/toolchain/check/merge.cpp @@ -0,0 +1,81 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "toolchain/check/merge.h" + +#include "toolchain/check/function.h" +#include "toolchain/check/import_ref.h" + +namespace Carbon::Check { + +// Returns the instruction to consider when merging the given inst_id. Returns +// nullopt if merging is infeasible and no diagnostic should be printed. +static auto ResolveMergeableInst(Context& context, SemIR::InstId inst_id) + -> std::optional { + auto inst = context.insts().Get(inst_id); + switch (inst.kind()) { + case SemIR::ImportRefUnused::Kind: + // Resolve before merging. + TryResolveImportRefUnused(context, inst_id); + break; + + case SemIR::ImportRefUsed::Kind: + // Already resolved. + break; + + case SemIR::Namespace::Kind: + // Return back the namespace directly. + return inst; + + default: + CARBON_FATAL() << "Unexpected inst kind passed to ResolveMergeableInst: " + << inst; + } + + auto const_id = context.constant_values().Get(inst_id); + // TODO: Function and type declarations are constant, but `var` declarations + // are non-constant and should still merge. + if (!const_id.is_constant()) { + return std::nullopt; + } + return context.insts().Get(const_id.inst_id()); +} + +auto MergeImportRef(Context& context, SemIR::InstId new_inst_id, + SemIR::InstId prev_inst_id) -> void { + auto new_inst = ResolveMergeableInst(context, new_inst_id); + auto prev_inst = ResolveMergeableInst(context, prev_inst_id); + if (!new_inst || !prev_inst) { + // TODO: Once `var` declarations get an associated instruction for handling, + // it might be more appropriate to return without diagnosing here, to handle + // invalid declarations. + context.DiagnoseDuplicateName(new_inst_id, prev_inst_id); + return; + } + + if (new_inst->kind() != prev_inst->kind()) { + context.DiagnoseDuplicateName(new_inst_id, prev_inst_id); + return; + } + + switch (new_inst->kind()) { + case SemIR::FunctionDecl::Kind: { + auto new_fn = context.functions().Get( + new_inst->As().function_id); + auto prev_fn_id = prev_inst->As().function_id; + // TODO: May need to "spoil" the new function to prevent it from being + // emitted, since it will already be added. + MergeFunctionRedecl(context, context.insts().GetLocationId(new_inst_id), + new_fn, + /*new_is_definition=*/false, prev_fn_id, + /*prev_is_imported=*/true); + return; + } + default: + context.TODO(new_inst_id, "Merging not yet supported."); + return; + } +} + +} // namespace Carbon::Check diff --git a/toolchain/check/merge.h b/toolchain/check/merge.h new file mode 100644 index 000000000000..ab5252577b5e --- /dev/null +++ b/toolchain/check/merge.h @@ -0,0 +1,20 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef CARBON_TOOLCHAIN_CHECK_MERGE_H_ +#define CARBON_TOOLCHAIN_CHECK_MERGE_H_ + +#include "toolchain/check/context.h" +#include "toolchain/sem_ir/ids.h" + +namespace Carbon::Check { + +// Merges an import ref at new_inst_id another at prev_inst_id. May print a +// diagnostic if merging is invalid. +auto MergeImportRef(Context& context, SemIR::InstId new_inst_id, + SemIR::InstId prev_inst_id) -> void; + +} // namespace Carbon::Check + +#endif // CARBON_TOOLCHAIN_CHECK_MERGE_H_ diff --git a/toolchain/check/testdata/function/declaration/extern.carbon b/toolchain/check/testdata/function/declaration/extern.carbon index 55db48ddc1b9..bdb1d304d193 100644 --- a/toolchain/check/testdata/function/declaration/extern.carbon +++ b/toolchain/check/testdata/function/declaration/extern.carbon @@ -61,7 +61,7 @@ class C { // CHECK:STDOUT: %F: = fn_decl @F [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redecl.carbon // CHECK:STDOUT: @@ -73,7 +73,7 @@ class C { // CHECK:STDOUT: %F.loc12: = fn_decl @F [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- fail_redecl_extern.carbon // CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/declaration/import.carbon b/toolchain/check/testdata/function/declaration/import.carbon index fbf60fed5bf1..6a6f413040fd 100644 --- a/toolchain/check/testdata/function/declaration/import.carbon +++ b/toolchain/check/testdata/function/declaration/import.carbon @@ -73,63 +73,11 @@ var b: i32 = B(1); var c: {.c: i32} = C((1,)); var d: () = D(); -// --- fail_todo_merge.carbon +// --- merge.carbon library "merge" api; import library "api"; -// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+52]]:1: In import. -// CHECK:STDERR: import library "extern_api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_api.carbon:4:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: extern fn A(); -// CHECK:STDERR: ^~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE-7]]:1: In import. -// CHECK:STDERR: import library "api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: api.carbon:4:1: Name is previously declared here. -// CHECK:STDERR: fn A(); -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+39]]:1: In import. -// CHECK:STDERR: import library "extern_api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_api.carbon:5:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: extern fn B(b: i32) -> i32; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE-20]]:1: In import. -// CHECK:STDERR: import library "api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: api.carbon:5:1: Name is previously declared here. -// CHECK:STDERR: fn B(b: i32) -> i32; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+26]]:1: In import. -// CHECK:STDERR: import library "extern_api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_api.carbon:6:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: extern fn C(c: (i32,)) -> {.c: i32}; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE-33]]:1: In import. -// CHECK:STDERR: import library "api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: api.carbon:6:1: Name is previously declared here. -// CHECK:STDERR: fn C(c: (i32,)) -> {.c: i32}; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE+13]]:1: In import. -// CHECK:STDERR: import library "extern_api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_api.carbon:7:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: extern fn D(); -// CHECK:STDERR: ^~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_merge.carbon:[[@LINE-46]]:1: In import. -// CHECK:STDERR: import library "api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: api.carbon:7:1: Name is previously declared here. -// CHECK:STDERR: extern fn D(); -// CHECK:STDERR: ^~~~~~~~~~~~~~ -// CHECK:STDERR: import library "extern_api"; var a: () = A(); @@ -137,62 +85,11 @@ var b: i32 = B(1); var c: {.c: i32} = C((1,)); var d: () = D(); -// --- fail_todo_merge_reverse.carbon +// --- merge_reverse.carbon library "merge_reverse" api; import library "extern_api"; -// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+51]]:1: In import. -// CHECK:STDERR: import library "api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: api.carbon:4:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: fn A(); -// CHECK:STDERR: ^~~~~~~ -// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE-7]]:1: In import. -// CHECK:STDERR: import library "extern_api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_api.carbon:4:1: Name is previously declared here. -// CHECK:STDERR: extern fn A(); -// CHECK:STDERR: ^~~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+38]]:1: In import. -// CHECK:STDERR: import library "api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: api.carbon:5:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: fn B(b: i32) -> i32; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE-20]]:1: In import. -// CHECK:STDERR: import library "extern_api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_api.carbon:5:1: Name is previously declared here. -// CHECK:STDERR: extern fn B(b: i32) -> i32; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+25]]:1: In import. -// CHECK:STDERR: import library "api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: api.carbon:6:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: fn C(c: (i32,)) -> {.c: i32}; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE-33]]:1: In import. -// CHECK:STDERR: import library "extern_api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_api.carbon:6:1: Name is previously declared here. -// CHECK:STDERR: extern fn C(c: (i32,)) -> {.c: i32}; -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE+12]]:1: In import. -// CHECK:STDERR: import library "api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: api.carbon:7:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: extern fn D(); -// CHECK:STDERR: ^~~~~~~~~~~~~~ -// CHECK:STDERR: fail_todo_merge_reverse.carbon:[[@LINE-46]]:1: In import. -// CHECK:STDERR: import library "extern_api"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: extern_api.carbon:7:1: Name is previously declared here. -// CHECK:STDERR: extern fn D(); -// CHECK:STDERR: ^~~~~~~~~~~~~~ import library "api"; var a: () = A(); @@ -238,7 +135,7 @@ var d: () = D(); // CHECK:STDOUT: // CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; // CHECK:STDOUT: -// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: extern fn @D(); // CHECK:STDOUT: // CHECK:STDOUT: --- extern_api.carbon // CHECK:STDOUT: @@ -272,13 +169,13 @@ var d: () = D(); // CHECK:STDOUT: %D: = fn_decl @D [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: extern fn @A(); // CHECK:STDOUT: -// CHECK:STDOUT: fn @B(%b: i32) -> i32; +// CHECK:STDOUT: extern fn @B(%b: i32) -> i32; // CHECK:STDOUT: -// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: extern fn @C(%c: (i32,)) -> {.c: i32}; // CHECK:STDOUT: -// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: extern fn @D(); // CHECK:STDOUT: // CHECK:STDOUT: --- basics.carbon // CHECK:STDOUT: @@ -326,7 +223,7 @@ var d: () = D(); // CHECK:STDOUT: // CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; // CHECK:STDOUT: -// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: extern fn @D(); // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: @@ -412,7 +309,7 @@ var d: () = D(); // CHECK:STDOUT: // CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; // CHECK:STDOUT: -// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: extern fn @D(); // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: @@ -492,13 +389,13 @@ var d: () = D(); // CHECK:STDOUT: %d: ref () = bind_name d, %d.var // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: extern fn @A(); // CHECK:STDOUT: -// CHECK:STDOUT: fn @B(%b: i32) -> i32; +// CHECK:STDOUT: extern fn @B(%b: i32) -> i32; // CHECK:STDOUT: -// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: extern fn @C(%c: (i32,)) -> {.c: i32}; // CHECK:STDOUT: -// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: extern fn @D(); // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: @@ -522,14 +419,14 @@ var d: () = D(); // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_merge.carbon +// CHECK:STDOUT: --- merge.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = tuple_type () [template] -// CHECK:STDOUT: %.2: i32 = int_literal 1 [template] -// CHECK:STDOUT: %.3: type = struct_type {.c: i32} [template] -// CHECK:STDOUT: %.4: type = tuple_type (i32) [template] -// CHECK:STDOUT: %.5: (i32,) = tuple_value (%.2) [template] +// CHECK:STDOUT: %.1: type = tuple_type (i32) [template] +// CHECK:STDOUT: %.2: type = struct_type {.c: i32} [template] +// CHECK:STDOUT: %.3: type = tuple_type () [template] +// CHECK:STDOUT: %.4: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.5: (i32,) = tuple_value (%.4) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -543,67 +440,75 @@ var d: () = D(); // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = imports.%A] -// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+5, used [template = imports.%B] -// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+17, used [template = imports.%C] -// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+18, used [template = imports.%D] -// CHECK:STDOUT: %import_ref.5 = import_ref ir2, inst+1, unused -// CHECK:STDOUT: %import_ref.6 = import_ref ir2, inst+5, unused -// CHECK:STDOUT: %import_ref.7 = import_ref ir2, inst+17, unused -// CHECK:STDOUT: %import_ref.8 = import_ref ir2, inst+18, unused -// CHECK:STDOUT: %.loc59_9.1: () = tuple_literal () -// CHECK:STDOUT: %.loc59_9.2: type = converted %.loc59_9.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = imports.%A.1] +// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+5, used [template = imports.%B.1] +// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+17, used [template = imports.%C.1] +// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+18, used [template = imports.%D.1] +// CHECK:STDOUT: %import_ref.5: = import_ref ir2, inst+1, used [template = imports.%A.2] +// CHECK:STDOUT: %import_ref.6: = import_ref ir2, inst+5, used [template = imports.%B.2] +// CHECK:STDOUT: %import_ref.7: = import_ref ir2, inst+17, used [template = imports.%C.2] +// CHECK:STDOUT: %import_ref.8: = import_ref ir2, inst+18, used [template = imports.%D.2] +// CHECK:STDOUT: %.loc7_9.1: () = tuple_literal () +// CHECK:STDOUT: %.loc7_9.2: type = converted %.loc7_9.1, constants.%.3 [template = constants.%.3] // CHECK:STDOUT: %a.var: ref () = var a // CHECK:STDOUT: %a: ref () = bind_name a, %a.var // CHECK:STDOUT: %b.var: ref i32 = var b // CHECK:STDOUT: %b: ref i32 = bind_name b, %b.var -// CHECK:STDOUT: %.loc61: type = struct_type {.c: i32} [template = constants.%.3] +// CHECK:STDOUT: %.loc9: type = struct_type {.c: i32} [template = constants.%.2] // CHECK:STDOUT: %c.var: ref {.c: i32} = var c // CHECK:STDOUT: %c: ref {.c: i32} = bind_name c, %c.var -// CHECK:STDOUT: %.loc62_9.1: () = tuple_literal () -// CHECK:STDOUT: %.loc62_9.2: type = converted %.loc62_9.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %.loc10_9.1: () = tuple_literal () +// CHECK:STDOUT: %.loc10_9.2: type = converted %.loc10_9.1, constants.%.3 [template = constants.%.3] // CHECK:STDOUT: %d.var: ref () = var d // CHECK:STDOUT: %d: ref () = bind_name d, %d.var // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: extern fn @A.1(); // CHECK:STDOUT: -// CHECK:STDOUT: fn @B(%b: i32) -> i32; +// CHECK:STDOUT: fn @A.2(); // CHECK:STDOUT: -// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: extern fn @B.1(%b: i32) -> i32; // CHECK:STDOUT: -// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: fn @B.2(%b: i32) -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: extern fn @C.1(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C.2(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: +// CHECK:STDOUT: extern fn @D.1(); +// CHECK:STDOUT: +// CHECK:STDOUT: extern fn @D.2(); // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.1 [template = imports.%A] -// CHECK:STDOUT: %.loc59: init () = call %A.ref() -// CHECK:STDOUT: assign file.%a.var, %.loc59 -// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = imports.%B] -// CHECK:STDOUT: %.loc60_16: i32 = int_literal 1 [template = constants.%.2] -// CHECK:STDOUT: %.loc60_15: init i32 = call %B.ref(%.loc60_16) -// CHECK:STDOUT: assign file.%b.var, %.loc60_15 -// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = imports.%C] -// CHECK:STDOUT: %.loc61_23: i32 = int_literal 1 [template = constants.%.2] -// CHECK:STDOUT: %.loc61_25.1: (i32,) = tuple_literal (%.loc61_23) -// CHECK:STDOUT: %.loc61_25.2: (i32,) = tuple_value (%.loc61_23) [template = constants.%.5] -// CHECK:STDOUT: %.loc61_25.3: (i32,) = converted %.loc61_25.1, %.loc61_25.2 [template = constants.%.5] -// CHECK:STDOUT: %.loc61_21: init {.c: i32} = call %C.ref(%.loc61_25.3) -// CHECK:STDOUT: assign file.%c.var, %.loc61_21 -// CHECK:STDOUT: %D.ref: = name_ref D, file.%import_ref.4 [template = imports.%D] -// CHECK:STDOUT: %.loc62: init () = call %D.ref() -// CHECK:STDOUT: assign file.%d.var, %.loc62 +// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.1 [template = imports.%A.1] +// CHECK:STDOUT: %.loc7: init () = call %A.ref() +// CHECK:STDOUT: assign file.%a.var, %.loc7 +// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = imports.%B.1] +// CHECK:STDOUT: %.loc8_16: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc8_15: init i32 = call %B.ref(%.loc8_16) +// CHECK:STDOUT: assign file.%b.var, %.loc8_15 +// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = imports.%C.1] +// CHECK:STDOUT: %.loc9_23: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc9_25.1: (i32,) = tuple_literal (%.loc9_23) +// CHECK:STDOUT: %.loc9_25.2: (i32,) = tuple_value (%.loc9_23) [template = constants.%.5] +// CHECK:STDOUT: %.loc9_25.3: (i32,) = converted %.loc9_25.1, %.loc9_25.2 [template = constants.%.5] +// CHECK:STDOUT: %.loc9_21: init {.c: i32} = call %C.ref(%.loc9_25.3) +// CHECK:STDOUT: assign file.%c.var, %.loc9_21 +// CHECK:STDOUT: %D.ref: = name_ref D, file.%import_ref.4 [template = imports.%D.1] +// CHECK:STDOUT: %.loc10: init () = call %D.ref() +// CHECK:STDOUT: assign file.%d.var, %.loc10 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_merge_reverse.carbon +// CHECK:STDOUT: --- merge_reverse.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { -// CHECK:STDOUT: %.1: type = tuple_type () [template] -// CHECK:STDOUT: %.2: i32 = int_literal 1 [template] -// CHECK:STDOUT: %.3: type = struct_type {.c: i32} [template] -// CHECK:STDOUT: %.4: type = tuple_type (i32) [template] -// CHECK:STDOUT: %.5: (i32,) = tuple_value (%.2) [template] +// CHECK:STDOUT: %.1: type = tuple_type (i32) [template] +// CHECK:STDOUT: %.2: type = struct_type {.c: i32} [template] +// CHECK:STDOUT: %.3: type = tuple_type () [template] +// CHECK:STDOUT: %.4: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.5: (i32,) = tuple_value (%.4) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { @@ -617,56 +522,64 @@ var d: () = D(); // CHECK:STDOUT: .c = %c // CHECK:STDOUT: .d = %d // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = imports.%A] -// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+5, used [template = imports.%B] -// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+17, used [template = imports.%C] -// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+18, used [template = imports.%D] -// CHECK:STDOUT: %import_ref.5 = import_ref ir2, inst+1, unused -// CHECK:STDOUT: %import_ref.6 = import_ref ir2, inst+5, unused -// CHECK:STDOUT: %import_ref.7 = import_ref ir2, inst+17, unused -// CHECK:STDOUT: %import_ref.8 = import_ref ir2, inst+18, unused -// CHECK:STDOUT: %.loc58_9.1: () = tuple_literal () -// CHECK:STDOUT: %.loc58_9.2: type = converted %.loc58_9.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = imports.%A.1] +// CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+5, used [template = imports.%B.1] +// CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+17, used [template = imports.%C.1] +// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+18, used [template = imports.%D.1] +// CHECK:STDOUT: %import_ref.5: = import_ref ir2, inst+1, used [template = imports.%A.2] +// CHECK:STDOUT: %import_ref.6: = import_ref ir2, inst+5, used [template = imports.%B.2] +// CHECK:STDOUT: %import_ref.7: = import_ref ir2, inst+17, used [template = imports.%C.2] +// CHECK:STDOUT: %import_ref.8: = import_ref ir2, inst+18, used [template = imports.%D.2] +// CHECK:STDOUT: %.loc7_9.1: () = tuple_literal () +// CHECK:STDOUT: %.loc7_9.2: type = converted %.loc7_9.1, constants.%.3 [template = constants.%.3] // CHECK:STDOUT: %a.var: ref () = var a // CHECK:STDOUT: %a: ref () = bind_name a, %a.var // CHECK:STDOUT: %b.var: ref i32 = var b // CHECK:STDOUT: %b: ref i32 = bind_name b, %b.var -// CHECK:STDOUT: %.loc60: type = struct_type {.c: i32} [template = constants.%.3] +// CHECK:STDOUT: %.loc9: type = struct_type {.c: i32} [template = constants.%.2] // CHECK:STDOUT: %c.var: ref {.c: i32} = var c // CHECK:STDOUT: %c: ref {.c: i32} = bind_name c, %c.var -// CHECK:STDOUT: %.loc61_9.1: () = tuple_literal () -// CHECK:STDOUT: %.loc61_9.2: type = converted %.loc61_9.1, constants.%.1 [template = constants.%.1] +// CHECK:STDOUT: %.loc10_9.1: () = tuple_literal () +// CHECK:STDOUT: %.loc10_9.2: type = converted %.loc10_9.1, constants.%.3 [template = constants.%.3] // CHECK:STDOUT: %d.var: ref () = var d // CHECK:STDOUT: %d: ref () = bind_name d, %d.var // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: fn @A.1(); // CHECK:STDOUT: -// CHECK:STDOUT: fn @B(%b: i32) -> i32; +// CHECK:STDOUT: fn @A.2(); // CHECK:STDOUT: -// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: fn @B.1(%b: i32) -> i32; // CHECK:STDOUT: -// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: fn @B.2(%b: i32) -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C.1(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C.2(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: +// CHECK:STDOUT: extern fn @D.1(); +// CHECK:STDOUT: +// CHECK:STDOUT: extern fn @D.2(); // CHECK:STDOUT: // CHECK:STDOUT: fn @__global_init() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.1 [template = imports.%A] -// CHECK:STDOUT: %.loc58: init () = call %A.ref() -// CHECK:STDOUT: assign file.%a.var, %.loc58 -// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = imports.%B] -// CHECK:STDOUT: %.loc59_16: i32 = int_literal 1 [template = constants.%.2] -// CHECK:STDOUT: %.loc59_15: init i32 = call %B.ref(%.loc59_16) -// CHECK:STDOUT: assign file.%b.var, %.loc59_15 -// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = imports.%C] -// CHECK:STDOUT: %.loc60_23: i32 = int_literal 1 [template = constants.%.2] -// CHECK:STDOUT: %.loc60_25.1: (i32,) = tuple_literal (%.loc60_23) -// CHECK:STDOUT: %.loc60_25.2: (i32,) = tuple_value (%.loc60_23) [template = constants.%.5] -// CHECK:STDOUT: %.loc60_25.3: (i32,) = converted %.loc60_25.1, %.loc60_25.2 [template = constants.%.5] -// CHECK:STDOUT: %.loc60_21: init {.c: i32} = call %C.ref(%.loc60_25.3) -// CHECK:STDOUT: assign file.%c.var, %.loc60_21 -// CHECK:STDOUT: %D.ref: = name_ref D, file.%import_ref.4 [template = imports.%D] -// CHECK:STDOUT: %.loc61: init () = call %D.ref() -// CHECK:STDOUT: assign file.%d.var, %.loc61 +// CHECK:STDOUT: %A.ref: = name_ref A, file.%import_ref.1 [template = imports.%A.1] +// CHECK:STDOUT: %.loc7: init () = call %A.ref() +// CHECK:STDOUT: assign file.%a.var, %.loc7 +// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = imports.%B.1] +// CHECK:STDOUT: %.loc8_16: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc8_15: init i32 = call %B.ref(%.loc8_16) +// CHECK:STDOUT: assign file.%b.var, %.loc8_15 +// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = imports.%C.1] +// CHECK:STDOUT: %.loc9_23: i32 = int_literal 1 [template = constants.%.4] +// CHECK:STDOUT: %.loc9_25.1: (i32,) = tuple_literal (%.loc9_23) +// CHECK:STDOUT: %.loc9_25.2: (i32,) = tuple_value (%.loc9_23) [template = constants.%.5] +// CHECK:STDOUT: %.loc9_25.3: (i32,) = converted %.loc9_25.1, %.loc9_25.2 [template = constants.%.5] +// CHECK:STDOUT: %.loc9_21: init {.c: i32} = call %C.ref(%.loc9_25.3) +// CHECK:STDOUT: assign file.%c.var, %.loc9_21 +// CHECK:STDOUT: %D.ref: = name_ref D, file.%import_ref.4 [template = imports.%D.1] +// CHECK:STDOUT: %.loc10: init () = call %D.ref() +// CHECK:STDOUT: assign file.%d.var, %.loc10 // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_first.carbon b/toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_first.carbon index 9913595042dc..20f2bc84f263 100644 --- a/toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_first.carbon +++ b/toolchain/check/testdata/namespace/fail_conflict_in_imports_namespace_first.carbon @@ -81,10 +81,12 @@ fn NS.Bar() {} // CHECK:STDOUT: .Bar = %Bar // CHECK:STDOUT: } // CHECK:STDOUT: %import_ref.2 = import_ref ir1, inst+2, unused -// CHECK:STDOUT: %import_ref.3 = import_ref ir2, inst+1, unused +// CHECK:STDOUT: %import_ref.3: = import_ref ir2, inst+1, used [template = imports.%NS] // CHECK:STDOUT: %Bar: = fn_decl @Bar [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @NS(); +// CHECK:STDOUT: // CHECK:STDOUT: fn @Bar() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return diff --git a/toolchain/check/testdata/packages/cross_package_import.carbon b/toolchain/check/testdata/packages/cross_package_import.carbon index 89f806f532f9..5ee5e12e2836 100644 --- a/toolchain/check/testdata/packages/cross_package_import.carbon +++ b/toolchain/check/testdata/packages/cross_package_import.carbon @@ -199,7 +199,7 @@ fn Other.G() {} // CHECK:STDOUT: %F: = fn_decl @F [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F(); +// CHECK:STDOUT: extern fn @F(); // CHECK:STDOUT: // CHECK:STDOUT: --- other_fn_conflict.carbon // CHECK:STDOUT: @@ -300,7 +300,7 @@ fn Other.G() {} // CHECK:STDOUT: // CHECK:STDOUT: fn @F.1(); // CHECK:STDOUT: -// CHECK:STDOUT: fn @F.2(); +// CHECK:STDOUT: extern fn @F.2(); // CHECK:STDOUT: // CHECK:STDOUT: --- main_unused_other_ambiguous.carbon // CHECK:STDOUT: diff --git a/toolchain/check/testdata/packages/fail_conflict_no_namespaces.carbon b/toolchain/check/testdata/packages/fail_conflict_no_namespaces.carbon index ad3927d9f082..8c8a7ae4f32a 100644 --- a/toolchain/check/testdata/packages/fail_conflict_no_namespaces.carbon +++ b/toolchain/check/testdata/packages/fail_conflict_no_namespaces.carbon @@ -62,7 +62,9 @@ import library "var"; // CHECK:STDOUT: package: = namespace [template] { // CHECK:STDOUT: .Foo = %import_ref.1 // CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref.1 = import_ref ir1, inst+1, unused -// CHECK:STDOUT: %import_ref.2 = import_ref ir2, inst+2, unused +// CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = imports.%Foo] +// CHECK:STDOUT: %import_ref.2: ref i32 = import_ref ir2, inst+2, used // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: fn @Foo(); +// CHECK:STDOUT: diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index a5f0cba34ff0..1836e5538508 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -771,7 +771,13 @@ class Formatter { auto FormatFunction(FunctionId id) -> void { const Function& fn = sem_ir_.functions().Get(id); - out_ << "\nfn "; + out_ << "\n"; + + if (fn.is_extern) { + out_ << "extern "; + } + + out_ << "fn "; FormatFunctionName(id); llvm::SaveAndRestore function_scope(scope_, inst_namer_.GetScopeFor(id));