diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index c7ce61cc3c0b1..a6cdb14506a1f 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -191,60 +191,93 @@ auto CheckFunctionTypeMatches(Context& context, context.functions().Get(prev_function_id), substitutions); } +// 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, + SemIR::Function& new_function, + bool new_is_definition, + SemIR::Function& prev_function, + bool prev_is_import) -> void { + CARBON_DIAGNOSTIC(FunctionPreviousDecl, Note, "Previously declared here."); + if (prev_is_import) { + // TODO: Allow non-extern declarations in the same library. + if (!new_function.is_extern && !prev_function.is_extern) { + CARBON_DIAGNOSTIC( + FunctionNonExternRedecl, Error, + "Only one library can declare function {0} without `extern`.", + SemIR::NameId); + context.emitter() + .Build(node_id, FunctionNonExternRedecl, prev_function.name_id) + .Note(prev_function.decl_id, FunctionPreviousDecl) + .Emit(); + return; + } + } else { + if (!new_is_definition) { + CARBON_DIAGNOSTIC(FunctionRedecl, Error, + "Redundant redeclaration of function {0}.", + SemIR::NameId); + context.emitter() + .Build(node_id, FunctionRedecl, prev_function.name_id) + .Note(prev_function.decl_id, FunctionPreviousDecl) + .Emit(); + return; + } + if (prev_function.definition_id.is_valid()) { + CARBON_DIAGNOSTIC(FunctionRedefinition, Error, + "Redefinition of function {0}.", SemIR::NameId); + CARBON_DIAGNOSTIC(FunctionPreviousDefinition, Note, + "Previously defined here."); + context.emitter() + .Build(node_id, FunctionRedefinition, prev_function.name_id) + .Note(prev_function.definition_id, FunctionPreviousDefinition) + .Emit(); + return; + } + // `extern` definitions are prevented in handle_function.cpp; this is only + // checking for a non-`extern` definition after an `extern` declaration. + if (prev_function.is_extern) { + CARBON_DIAGNOSTIC(FunctionDefiningExtern, Error, + "Redeclaring `extern` function `{0}` as non-`extern`.", + SemIR::NameId); + CARBON_DIAGNOSTIC(FunctionPreviousExternDecl, Note, + "Previously declared `extern` here."); + context.emitter() + .Build(node_id, FunctionDefiningExtern, prev_function.name_id) + .Note(prev_function.decl_id, FunctionPreviousExternDecl) + .Emit(); + return; + } + } +} + // 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, - SemIR::Function& new_function, - SemIR::FunctionId prev_function_id, bool is_definition) - -> bool { + SemIR::Function& new_function, bool new_is_definition, + SemIR::FunctionId prev_function_id, + bool prev_is_import) -> bool { auto& prev_function = context.functions().Get(prev_function_id); if (!CheckRedecl(context, new_function, prev_function, {})) { return false; } - if (!is_definition) { - CARBON_DIAGNOSTIC(FunctionRedecl, Error, - "Redundant redeclaration of function {0}.", - SemIR::NameId); - CARBON_DIAGNOSTIC(FunctionPreviousDecl, Note, "Previously declared here."); - context.emitter() - .Build(node_id, FunctionRedecl, prev_function.name_id) - .Note(prev_function.decl_id, FunctionPreviousDecl) - .Emit(); - // The diagnostic doesn't prevent a merge. - return true; - } else if (prev_function.definition_id.is_valid()) { - CARBON_DIAGNOSTIC(FunctionRedefinition, Error, - "Redefinition of function {0}.", SemIR::NameId); - CARBON_DIAGNOSTIC(FunctionPreviousDefinition, Note, - "Previously defined here."); - context.emitter() - .Build(node_id, FunctionRedefinition, prev_function.name_id) - .Note(prev_function.definition_id, FunctionPreviousDefinition) - .Emit(); - // The second definition will be unused as a consequence of the error. - return true; - } else if (prev_function.is_extern) { - CARBON_DIAGNOSTIC(FunctionDefiningExtern, Error, - "Cannot define `extern` function `{0}`.", SemIR::NameId); - CARBON_DIAGNOSTIC(FunctionPreviousExternDecl, Note, - "Previously declared `extern` here."); - context.emitter() - .Build(node_id, FunctionDefiningExtern, prev_function.name_id) - .Note(prev_function.decl_id, FunctionPreviousExternDecl) - .Emit(); - // The diagnostic doesn't prevent a merge. - return true; - } + CheckIsAllowedRedecl(context, node_id, new_function, new_is_definition, + prev_function, prev_is_import); - // Track the signature from the definition, so that IDs in the body - // match IDs in the signature. - prev_function.definition_id = new_function.definition_id; - prev_function.implicit_param_refs_id = new_function.implicit_param_refs_id; - prev_function.param_refs_id = new_function.param_refs_id; - prev_function.return_type_id = new_function.return_type_id; - prev_function.return_slot_id = new_function.return_slot_id; + if (new_is_definition) { + // Track the signature from the definition, so that IDs in the body + // match IDs in the signature. + prev_function.definition_id = new_function.definition_id; + prev_function.implicit_param_refs_id = new_function.implicit_param_refs_id; + prev_function.param_refs_id = new_function.param_refs_id; + prev_function.return_type_id = new_function.return_type_id; + prev_function.return_slot_id = new_function.return_slot_id; + } + if (!new_function.is_extern) { + prev_function.is_extern = false; + } return true; } diff --git a/toolchain/check/function.h b/toolchain/check/function.h index f6add8cd21208..69f2cb0ea9ea1 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -28,9 +28,9 @@ 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, - SemIR::Function& new_function, - SemIR::FunctionId prev_function_id, bool is_definition) - -> bool; + SemIR::Function& new_function, bool new_is_definition, + SemIR::FunctionId prev_function_id, + bool prev_is_imported) -> bool; } // namespace Carbon::Check diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 309b72c1dbfac..01027804244a4 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -165,14 +165,22 @@ static auto BuildFunctionDecl(Context& context, } // Check whether this is a redeclaration. - auto existing_id = + auto prev_id = context.decl_name_stack().LookupOrAddName(name_context, lookup_result_id); - if (existing_id.is_valid()) { - if (auto existing_function_decl = - context.insts().Get(existing_id).TryAs()) { - if (MergeFunctionRedecl(context, node_id, function_info, + if (prev_id.is_valid()) { + auto prev_inst = context.insts().Get(prev_id); + bool prev_is_import = false; + + if (prev_inst.Is()) { + prev_inst = + context.insts().Get(context.constant_values().Get(prev_id).inst_id()); + prev_is_import = true; + } + + if (auto existing_function_decl = prev_inst.TryAs()) { + if (MergeFunctionRedecl(context, node_id, function_info, is_definition, existing_function_decl->function_id, - is_definition)) { + prev_is_import)) { // When merging, use the existing function rather than adding a new one. function_decl.function_id = existing_function_decl->function_id; } @@ -180,7 +188,7 @@ static auto BuildFunctionDecl(Context& context, // This is a redeclaration of something other than a function. This // includes the case where an associated function redeclares another // associated function. - context.DiagnoseDuplicateName(function_info.decl_id, existing_id); + context.DiagnoseDuplicateName(function_info.decl_id, prev_id); } } diff --git a/toolchain/check/import_ref.cpp b/toolchain/check/import_ref.cpp index 492bdff7f5262..5377b37c5e1c6 100644 --- a/toolchain/check/import_ref.cpp +++ b/toolchain/check/import_ref.cpp @@ -707,8 +707,11 @@ class ImportRefResolver { GetLocalParamRefsId(function.param_refs_id, param_const_ids), .return_type_id = new_return_type_id, .return_slot_id = new_return_slot, + .is_extern = function.is_extern, .builtin_kind = function.builtin_kind}); // Write the function ID into the FunctionDecl. + // TODO: The Invalid NodeId means it's hard to associate diagnostics. We + // should replace this. context_.ReplaceInstBeforeConstantUse( function_decl_id, {Parse::NodeId::Invalid, function_decl}); return {context_.constant_values().Get(function_decl_id)}; diff --git a/toolchain/check/testdata/function/builtin/fail_redefined.carbon b/toolchain/check/testdata/function/builtin/fail_redefined.carbon index 7dc8e380f36cc..4a5bfd73f09e5 100644 --- a/toolchain/check/testdata/function/builtin/fail_redefined.carbon +++ b/toolchain/check/testdata/function/builtin/fail_redefined.carbon @@ -43,57 +43,57 @@ fn C(n: i32, m: i32) -> i32 = "int.add"; // CHECK:STDOUT: } // CHECK:STDOUT: %A.loc7: = fn_decl @A [template] { // CHECK:STDOUT: %n.loc7_6.1: i32 = param n -// CHECK:STDOUT: @A.%n: i32 = bind_name n, %n.loc7_6.1 +// CHECK:STDOUT: %n.loc7_6.2: i32 = bind_name n, %n.loc7_6.1 // CHECK:STDOUT: %m.loc7_14.1: i32 = param m -// CHECK:STDOUT: @A.%m: i32 = bind_name m, %m.loc7_14.1 +// CHECK:STDOUT: %m.loc7_14.2: i32 = bind_name m, %m.loc7_14.1 // CHECK:STDOUT: %return.var.loc7: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: %A.loc15: = fn_decl @A [template] { // CHECK:STDOUT: %n.loc15_6.1: i32 = param n -// CHECK:STDOUT: %n.loc15_6.2: i32 = bind_name n, %n.loc15_6.1 +// CHECK:STDOUT: @A.%n: i32 = bind_name n, %n.loc15_6.1 // CHECK:STDOUT: %m.loc15_14.1: i32 = param m -// CHECK:STDOUT: %m.loc15_14.2: i32 = bind_name m, %m.loc15_14.1 +// CHECK:STDOUT: @A.%m: i32 = bind_name m, %m.loc15_14.1 // CHECK:STDOUT: %return.var.loc15: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: %B.loc17: = fn_decl @B [template] { // CHECK:STDOUT: %n.loc17_6.1: i32 = param n -// CHECK:STDOUT: @B.%n: i32 = bind_name n, %n.loc17_6.1 +// CHECK:STDOUT: %n.loc17_6.2: i32 = bind_name n, %n.loc17_6.1 // CHECK:STDOUT: %m.loc17_14.1: i32 = param m -// CHECK:STDOUT: @B.%m: i32 = bind_name m, %m.loc17_14.1 +// CHECK:STDOUT: %m.loc17_14.2: i32 = bind_name m, %m.loc17_14.1 // CHECK:STDOUT: %return.var.loc17: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: %B.loc25: = fn_decl @B [template] { // CHECK:STDOUT: %n.loc25_6.1: i32 = param n -// CHECK:STDOUT: %n.loc25_6.2: i32 = bind_name n, %n.loc25_6.1 +// CHECK:STDOUT: @B.%n: i32 = bind_name n, %n.loc25_6.1 // CHECK:STDOUT: %m.loc25_14.1: i32 = param m -// CHECK:STDOUT: %m.loc25_14.2: i32 = bind_name m, %m.loc25_14.1 +// CHECK:STDOUT: @B.%m: i32 = bind_name m, %m.loc25_14.1 // CHECK:STDOUT: %return.var.loc25: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: %C.loc27: = fn_decl @C [template] { // CHECK:STDOUT: %n.loc27_6.1: i32 = param n -// CHECK:STDOUT: @C.%n: i32 = bind_name n, %n.loc27_6.1 +// CHECK:STDOUT: %n.loc27_6.2: i32 = bind_name n, %n.loc27_6.1 // CHECK:STDOUT: %m.loc27_14.1: i32 = param m -// CHECK:STDOUT: @C.%m: i32 = bind_name m, %m.loc27_14.1 +// CHECK:STDOUT: %m.loc27_14.2: i32 = bind_name m, %m.loc27_14.1 // CHECK:STDOUT: %return.var.loc27: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: %C.loc34: = fn_decl @C [template] { // CHECK:STDOUT: %n.loc34_6.1: i32 = param n -// CHECK:STDOUT: %n.loc34_6.2: i32 = bind_name n, %n.loc34_6.1 +// CHECK:STDOUT: @C.%n: i32 = bind_name n, %n.loc34_6.1 // CHECK:STDOUT: %m.loc34_14.1: i32 = param m -// CHECK:STDOUT: %m.loc34_14.2: i32 = bind_name m, %m.loc34_14.1 +// CHECK:STDOUT: @C.%m: i32 = bind_name m, %m.loc34_14.1 // CHECK:STDOUT: %return.var.loc34: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(%n: i32, %m: i32) -> i32 = "int.add" { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %n.ref: i32 = name_ref n, file.%n.loc15_6.2 +// CHECK:STDOUT: %n.ref: i32 = name_ref n, %n // CHECK:STDOUT: return %n.ref // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @B(%n: i32, %m: i32) -> i32 = "int.add" { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %n.ref: i32 = name_ref n, %n +// CHECK:STDOUT: %n.ref: i32 = name_ref n, file.%n.loc17_6.2 // CHECK:STDOUT: return %n.ref // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/declaration/import.carbon b/toolchain/check/testdata/function/declaration/import.carbon index 233daf6e9569f..fbf60fed5bf12 100644 --- a/toolchain/check/testdata/function/declaration/import.carbon +++ b/toolchain/check/testdata/function/declaration/import.carbon @@ -17,6 +17,15 @@ fn B(b: i32) -> i32; fn C(c: (i32,)) -> {.c: i32}; extern fn D(); +// --- extern_api.carbon + +library "extern_api" api; + +extern fn A(); +extern fn B(b: i32) -> i32; +extern fn C(c: (i32,)) -> {.c: i32}; +extern fn D(); + // ============================================================================ // Test files // ============================================================================ @@ -32,15 +41,165 @@ var b: i32 = B(1); var c: {.c: i32} = C((1,)); var d: () = D(); -// --- redecl_extern.carbon +// --- redecl_api.carbon -library "redecl_extern" api; +library "redecl_api" api; + +import library "api"; extern fn A(); extern fn B(b: i32) -> i32; extern fn C(c: (i32,)) -> {.c: i32}; extern fn D(); +var a: () = A(); +var b: i32 = B(1); +var c: {.c: i32} = C((1,)); +var d: () = D(); + +// --- redecl_extern_api.carbon + +library "redecl_extern_api" api; + +import library "extern_api"; + +extern fn A(); +extern fn B(b: i32) -> i32; +extern fn C(c: (i32,)) -> {.c: i32}; +extern fn D(); + +var a: () = A(); +var b: i32 = B(1); +var c: {.c: i32} = C((1,)); +var d: () = D(); + +// --- fail_todo_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(); +var b: i32 = B(1); +var c: {.c: i32} = C((1,)); +var d: () = D(); + +// --- fail_todo_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(); +var b: i32 = B(1); +var c: {.c: i32} = C((1,)); +var d: () = D(); + // CHECK:STDOUT: --- api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -81,6 +240,46 @@ extern fn D(); // CHECK:STDOUT: // CHECK:STDOUT: fn @D(); // CHECK:STDOUT: +// CHECK:STDOUT: --- extern_api.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type (type) [template] +// CHECK:STDOUT: %.2: type = tuple_type (i32) [template] +// CHECK:STDOUT: %.3: type = struct_type {.c: i32} [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %A +// CHECK:STDOUT: .B = %B +// CHECK:STDOUT: .C = %C +// CHECK:STDOUT: .D = %D +// CHECK:STDOUT: } +// CHECK:STDOUT: %A: = fn_decl @A [template] {} +// CHECK:STDOUT: %B: = fn_decl @B [template] { +// CHECK:STDOUT: %b.loc5_13.1: i32 = param b +// CHECK:STDOUT: @B.%b: i32 = bind_name b, %b.loc5_13.1 +// CHECK:STDOUT: %return.var.loc5: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %C: = fn_decl @C [template] { +// CHECK:STDOUT: %.loc6_21.1: (type,) = tuple_literal (i32) +// CHECK:STDOUT: %.loc6_21.2: type = converted %.loc6_21.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: %c.loc6_13.1: (i32,) = param c +// CHECK:STDOUT: @C.%c: (i32,) = bind_name c, %c.loc6_13.1 +// CHECK:STDOUT: %.loc6_35: type = struct_type {.c: i32} [template = constants.%.3] +// CHECK:STDOUT: %return.var.loc6: ref {.c: i32} = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %D: = fn_decl @D [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B(%b: i32) -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: // CHECK:STDOUT: --- basics.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -151,36 +350,60 @@ extern fn D(); // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- redecl_extern.carbon +// CHECK:STDOUT: --- redecl_api.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = tuple_type (type) [template] // CHECK:STDOUT: %.2: type = tuple_type (i32) [template] // CHECK:STDOUT: %.3: type = struct_type {.c: i32} [template] +// CHECK:STDOUT: %.4: type = tuple_type () [template] +// CHECK:STDOUT: %.5: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.6: (i32,) = tuple_value (%.5) [template] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .A = %A -// CHECK:STDOUT: .B = %B -// CHECK:STDOUT: .C = %C -// CHECK:STDOUT: .D = %D +// CHECK:STDOUT: .A = %import_ref.1 +// CHECK:STDOUT: .B = %import_ref.2 +// CHECK:STDOUT: .C = %import_ref.3 +// CHECK:STDOUT: .D = %import_ref.4 +// CHECK:STDOUT: .a = %a +// CHECK:STDOUT: .b = %b.loc12 +// CHECK:STDOUT: .c = %c.loc13 +// 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: %A: = fn_decl @A [template] {} // CHECK:STDOUT: %B: = fn_decl @B [template] { -// CHECK:STDOUT: %b.loc5_13.1: i32 = param b -// CHECK:STDOUT: @B.%b: i32 = bind_name b, %b.loc5_13.1 -// CHECK:STDOUT: %return.var.loc5: ref i32 = var +// CHECK:STDOUT: %b.loc7_13.1: i32 = param b +// CHECK:STDOUT: %b.loc7_13.2: i32 = bind_name b, %b.loc7_13.1 +// CHECK:STDOUT: %return.var.loc7: ref i32 = var // CHECK:STDOUT: } // CHECK:STDOUT: %C: = fn_decl @C [template] { -// CHECK:STDOUT: %.loc6_21.1: (type,) = tuple_literal (i32) -// CHECK:STDOUT: %.loc6_21.2: type = converted %.loc6_21.1, constants.%.2 [template = constants.%.2] -// CHECK:STDOUT: %c.loc6_13.1: (i32,) = param c -// CHECK:STDOUT: @C.%c: (i32,) = bind_name c, %c.loc6_13.1 -// CHECK:STDOUT: %.loc6_35: type = struct_type {.c: i32} [template = constants.%.3] -// CHECK:STDOUT: %return.var.loc6: ref {.c: i32} = var +// CHECK:STDOUT: %.loc8_21.1: (type,) = tuple_literal (i32) +// CHECK:STDOUT: %.loc8_21.2: type = converted %.loc8_21.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: %c.loc8_13.1: (i32,) = param c +// CHECK:STDOUT: %c.loc8_13.2: (i32,) = bind_name c, %c.loc8_13.1 +// CHECK:STDOUT: %.loc8_35: type = struct_type {.c: i32} [template = constants.%.3] +// CHECK:STDOUT: %return.var.loc8: ref {.c: i32} = var // CHECK:STDOUT: } // CHECK:STDOUT: %D: = fn_decl @D [template] {} +// CHECK:STDOUT: %.loc11_9.1: () = tuple_literal () +// CHECK:STDOUT: %.loc11_9.2: type = converted %.loc11_9.1, constants.%.4 [template = constants.%.4] +// 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.loc12: ref i32 = bind_name b, %b.var +// CHECK:STDOUT: %.loc13: type = struct_type {.c: i32} [template = constants.%.3] +// CHECK:STDOUT: %c.var: ref {.c: i32} = var c +// CHECK:STDOUT: %c.loc13: ref {.c: i32} = bind_name c, %c.var +// CHECK:STDOUT: %.loc14_9.1: () = tuple_literal () +// CHECK:STDOUT: %.loc14_9.2: type = converted %.loc14_9.1, constants.%.4 [template = constants.%.4] +// CHECK:STDOUT: %d.var: ref () = var d +// CHECK:STDOUT: %d: ref () = bind_name d, %d.var // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A(); @@ -191,3 +414,259 @@ extern fn D(); // CHECK:STDOUT: // CHECK:STDOUT: fn @D(); // 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: %.loc11: init () = call %A.ref() +// CHECK:STDOUT: assign file.%a.var, %.loc11 +// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = imports.%B] +// CHECK:STDOUT: %.loc12_16: i32 = int_literal 1 [template = constants.%.5] +// CHECK:STDOUT: %.loc12_15: init i32 = call %B.ref(%.loc12_16) +// CHECK:STDOUT: assign file.%b.var, %.loc12_15 +// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = imports.%C] +// CHECK:STDOUT: %.loc13_23: i32 = int_literal 1 [template = constants.%.5] +// CHECK:STDOUT: %.loc13_25.1: (i32,) = tuple_literal (%.loc13_23) +// CHECK:STDOUT: %.loc13_25.2: (i32,) = tuple_value (%.loc13_23) [template = constants.%.6] +// CHECK:STDOUT: %.loc13_25.3: (i32,) = converted %.loc13_25.1, %.loc13_25.2 [template = constants.%.6] +// CHECK:STDOUT: %.loc13_21: init {.c: i32} = call %C.ref(%.loc13_25.3) +// CHECK:STDOUT: assign file.%c.var, %.loc13_21 +// CHECK:STDOUT: %D.ref: = name_ref D, file.%import_ref.4 [template = imports.%D] +// CHECK:STDOUT: %.loc14: init () = call %D.ref() +// CHECK:STDOUT: assign file.%d.var, %.loc14 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- redecl_extern_api.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %.1: type = tuple_type (type) [template] +// CHECK:STDOUT: %.2: type = tuple_type (i32) [template] +// CHECK:STDOUT: %.3: type = struct_type {.c: i32} [template] +// CHECK:STDOUT: %.4: type = tuple_type () [template] +// CHECK:STDOUT: %.5: i32 = int_literal 1 [template] +// CHECK:STDOUT: %.6: (i32,) = tuple_value (%.5) [template] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %import_ref.1 +// CHECK:STDOUT: .B = %import_ref.2 +// CHECK:STDOUT: .C = %import_ref.3 +// CHECK:STDOUT: .D = %import_ref.4 +// CHECK:STDOUT: .a = %a +// CHECK:STDOUT: .b = %b.loc12 +// CHECK:STDOUT: .c = %c.loc13 +// 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: %A: = fn_decl @A [template] {} +// CHECK:STDOUT: %B: = fn_decl @B [template] { +// CHECK:STDOUT: %b.loc7_13.1: i32 = param b +// CHECK:STDOUT: %b.loc7_13.2: i32 = bind_name b, %b.loc7_13.1 +// CHECK:STDOUT: %return.var.loc7: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %C: = fn_decl @C [template] { +// CHECK:STDOUT: %.loc8_21.1: (type,) = tuple_literal (i32) +// CHECK:STDOUT: %.loc8_21.2: type = converted %.loc8_21.1, constants.%.2 [template = constants.%.2] +// CHECK:STDOUT: %c.loc8_13.1: (i32,) = param c +// CHECK:STDOUT: %c.loc8_13.2: (i32,) = bind_name c, %c.loc8_13.1 +// CHECK:STDOUT: %.loc8_35: type = struct_type {.c: i32} [template = constants.%.3] +// CHECK:STDOUT: %return.var.loc8: ref {.c: i32} = var +// CHECK:STDOUT: } +// CHECK:STDOUT: %D: = fn_decl @D [template] {} +// CHECK:STDOUT: %.loc11_9.1: () = tuple_literal () +// CHECK:STDOUT: %.loc11_9.2: type = converted %.loc11_9.1, constants.%.4 [template = constants.%.4] +// 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.loc12: ref i32 = bind_name b, %b.var +// CHECK:STDOUT: %.loc13: type = struct_type {.c: i32} [template = constants.%.3] +// CHECK:STDOUT: %c.var: ref {.c: i32} = var c +// CHECK:STDOUT: %c.loc13: ref {.c: i32} = bind_name c, %c.var +// CHECK:STDOUT: %.loc14_9.1: () = tuple_literal () +// CHECK:STDOUT: %.loc14_9.2: type = converted %.loc14_9.1, constants.%.4 [template = constants.%.4] +// 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: +// CHECK:STDOUT: fn @B(%b: i32) -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @D(); +// 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: %.loc11: init () = call %A.ref() +// CHECK:STDOUT: assign file.%a.var, %.loc11 +// CHECK:STDOUT: %B.ref: = name_ref B, file.%import_ref.2 [template = imports.%B] +// CHECK:STDOUT: %.loc12_16: i32 = int_literal 1 [template = constants.%.5] +// CHECK:STDOUT: %.loc12_15: init i32 = call %B.ref(%.loc12_16) +// CHECK:STDOUT: assign file.%b.var, %.loc12_15 +// CHECK:STDOUT: %C.ref: = name_ref C, file.%import_ref.3 [template = imports.%C] +// CHECK:STDOUT: %.loc13_23: i32 = int_literal 1 [template = constants.%.5] +// CHECK:STDOUT: %.loc13_25.1: (i32,) = tuple_literal (%.loc13_23) +// CHECK:STDOUT: %.loc13_25.2: (i32,) = tuple_value (%.loc13_23) [template = constants.%.6] +// CHECK:STDOUT: %.loc13_25.3: (i32,) = converted %.loc13_25.1, %.loc13_25.2 [template = constants.%.6] +// CHECK:STDOUT: %.loc13_21: init {.c: i32} = call %C.ref(%.loc13_25.3) +// CHECK:STDOUT: assign file.%c.var, %.loc13_21 +// CHECK:STDOUT: %D.ref: = name_ref D, file.%import_ref.4 [template = imports.%D] +// CHECK:STDOUT: %.loc14: init () = call %D.ref() +// CHECK:STDOUT: assign file.%d.var, %.loc14 +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_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: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %import_ref.1 +// CHECK:STDOUT: .B = %import_ref.2 +// CHECK:STDOUT: .C = %import_ref.3 +// CHECK:STDOUT: .D = %import_ref.4 +// CHECK:STDOUT: .a = %a +// CHECK:STDOUT: .b = %b +// 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: %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: %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: %d.var: ref () = var d +// CHECK:STDOUT: %d: ref () = bind_name d, %d.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B(%b: i32) -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @D(); +// 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: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_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: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %import_ref.1 +// CHECK:STDOUT: .B = %import_ref.2 +// CHECK:STDOUT: .C = %import_ref.3 +// CHECK:STDOUT: .D = %import_ref.4 +// CHECK:STDOUT: .a = %a +// CHECK:STDOUT: .b = %b +// 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: %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: %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: %d.var: ref () = var d +// CHECK:STDOUT: %d: ref () = bind_name d, %d.var +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A(); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B(%b: i32) -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @C(%c: (i32,)) -> {.c: i32}; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @D(); +// 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: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/definition/extern.carbon b/toolchain/check/testdata/function/definition/extern.carbon index 421d9d1790daf..4f740c6bf8fa9 100644 --- a/toolchain/check/testdata/function/definition/extern.carbon +++ b/toolchain/check/testdata/function/definition/extern.carbon @@ -19,7 +19,7 @@ extern fn F() {} library "def_for_extern_decl" api; extern fn F(); -// CHECK:STDERR: fail_def_for_extern_decl.carbon:[[@LINE+7]]:1: ERROR: Cannot define `extern` function `F`. +// CHECK:STDERR: fail_def_for_extern_decl.carbon:[[@LINE+7]]:1: ERROR: Redeclaring `extern` function `F` as non-`extern`. // CHECK:STDERR: fn F() {} // CHECK:STDERR: ^~~~~~~~ // CHECK:STDERR: fail_def_for_extern_decl.carbon:[[@LINE-4]]:1: Previously declared `extern` here. @@ -28,6 +28,21 @@ extern fn F(); // CHECK:STDERR: fn F() {} +// --- fail_extern_diag_suppressed.carbon + +library "extern_diag_suppressed" api; + +extern fn F(); +// CHECK:STDERR: fail_extern_diag_suppressed.carbon:[[@LINE+7]]:1: ERROR: Redundant redeclaration of function F. +// CHECK:STDERR: fn F(); +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_extern_diag_suppressed.carbon:[[@LINE-4]]:1: Previously declared here. +// CHECK:STDERR: extern fn F(); +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F(); +fn F() {} + // --- fail_extern_decl_after_def.carbon library "extern_decl_after_def" api; @@ -70,6 +85,22 @@ extern fn F(); // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_extern_diag_suppressed.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .F = %F.loc4 +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.loc4: = fn_decl @F [template] {} +// CHECK:STDOUT: %F.loc12: = fn_decl @F [template] {} +// CHECK:STDOUT: %F.loc13: = fn_decl @F [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- fail_extern_decl_after_def.carbon // CHECK:STDOUT: // CHECK:STDOUT: file { diff --git a/toolchain/check/testdata/function/definition/fail_todo_import_forward_decl.carbon b/toolchain/check/testdata/function/definition/fail_todo_import_forward_decl.carbon deleted file mode 100644 index 06008179cc449..0000000000000 --- a/toolchain/check/testdata/function/definition/fail_todo_import_forward_decl.carbon +++ /dev/null @@ -1,59 +0,0 @@ -// 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 -// -// AUTOUPDATE - -// --- a.carbon - -library "a" api; - -fn Foo(); - -// --- fail_b.carbon - -library "b" api; - -import library "a"; - -// TODO: This should be an error until we can be clear whether `a` or `b` should -// own the definition, as there might be a separate definition in `a`'s impl. -// CHECK:STDERR: fail_b.carbon:[[@LINE+9]]:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: fn Foo() {} -// CHECK:STDERR: ^~~~~~~~~~ -// CHECK:STDERR: fail_b.carbon:[[@LINE-7]]:1: In import. -// CHECK:STDERR: import library "a"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: a.carbon:4:1: Name is previously declared here. -// CHECK:STDERR: fn Foo(); -// CHECK:STDERR: ^~~~~~~~~ -fn Foo() {} - -// CHECK:STDOUT: --- a.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .Foo = %Foo -// CHECK:STDOUT: } -// CHECK:STDOUT: %Foo: = fn_decl @Foo [template] {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @Foo(); -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_b.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [template] { -// CHECK:STDOUT: .Foo = %import_ref -// CHECK:STDOUT: } -// CHECK:STDOUT: %import_ref: = import_ref ir1, inst+1, used [template = imports.%Foo] -// CHECK:STDOUT: %.loc17: = fn_decl @.1 [template] {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @Foo(); -// CHECK:STDOUT: -// CHECK:STDOUT: fn @.1() { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/definition/import.carbon b/toolchain/check/testdata/function/definition/import.carbon index cb0c7606750f1..28f4261d9c694 100644 --- a/toolchain/check/testdata/function/definition/import.carbon +++ b/toolchain/check/testdata/function/definition/import.carbon @@ -4,6 +4,10 @@ // // AUTOUPDATE +// ============================================================================ +// Setup files +// ============================================================================ + // --- a.carbon library "a" api; @@ -11,10 +15,15 @@ library "a" api; fn A() {} fn B(b: i32) -> i32 { return b; } fn C(c: (i32,)) -> {.c: i32} { return {.c = c[0]}; } +fn D(); + +// ============================================================================ +// Test files +// ============================================================================ -// --- b.carbon +// --- basics.carbon -library "b" api; +library "basics" api; import library "a"; @@ -22,6 +31,38 @@ var a: () = A(); var b: i32 = B(1); var c: {.c: i32} = C((1,)); +// --- fail_def_ownership.carbon + +library "def_ownership" api; + +import library "a"; + +// CHECK:STDERR: fail_def_ownership.carbon:[[@LINE+5]]:1: ERROR: Only one library can declare function A without `extern`. +// CHECK:STDERR: fn A() {}; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: fail_def_ownership.carbon: Previously declared here. +// CHECK:STDERR: +fn A() {}; +// CHECK:STDERR: fail_def_ownership.carbon:[[@LINE+5]]:1: ERROR: Only one library can declare function B without `extern`. +// CHECK:STDERR: fn B(b: i32) -> i32; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_def_ownership.carbon: Previously declared here. +// CHECK:STDERR: +fn B(b: i32) -> i32; + +// --- fail_mix_extern_decl.carbon + +library "mix_extern_decl" api; + +import library "a"; + +extern fn D(); +// CHECK:STDERR: fail_mix_extern_decl.carbon:[[@LINE+4]]:1: ERROR: Only one library can declare function D without `extern`. +// CHECK:STDERR: fn D() {} +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: fail_mix_extern_decl.carbon: Previously declared here. +fn D() {} + // CHECK:STDOUT: --- a.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -36,6 +77,7 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: .A = %A // CHECK:STDOUT: .B = %B // CHECK:STDOUT: .C = %C +// CHECK:STDOUT: .D = %D // CHECK:STDOUT: } // CHECK:STDOUT: %A: = fn_decl @A [template] {} // CHECK:STDOUT: %B: = fn_decl @B [template] { @@ -51,6 +93,7 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: %.loc6_28: type = struct_type {.c: i32} [template = constants.%.3] // CHECK:STDOUT: %return.var.loc6: ref {.c: i32} = var // CHECK:STDOUT: } +// CHECK:STDOUT: %D: = fn_decl @D [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @A() { @@ -75,7 +118,9 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: return %.loc6_49.3 // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- b.carbon +// CHECK:STDOUT: fn @D(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- basics.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %.1: type = tuple_type () [template] @@ -90,6 +135,7 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: .A = %import_ref.1 // CHECK:STDOUT: .B = %import_ref.2 // CHECK:STDOUT: .C = %import_ref.3 +// CHECK:STDOUT: .D = %import_ref.4 // CHECK:STDOUT: .a = %a // CHECK:STDOUT: .b = %b // CHECK:STDOUT: .c = %c @@ -97,6 +143,7 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used [template = imports.%A] // CHECK:STDOUT: %import_ref.2: = import_ref ir1, inst+6, used [template = imports.%B] // CHECK:STDOUT: %import_ref.3: = import_ref ir1, inst+20, used [template = imports.%C] +// CHECK:STDOUT: %import_ref.4 = import_ref ir1, inst+30, unused // CHECK:STDOUT: %.loc6_9.1: () = tuple_literal () // CHECK:STDOUT: %.loc6_9.2: type = converted %.loc6_9.1, constants.%.1 [template = constants.%.1] // CHECK:STDOUT: %a.var: ref () = var a @@ -133,3 +180,53 @@ var c: {.c: i32} = C((1,)); // CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_def_ownership.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %import_ref.1 +// CHECK:STDOUT: .B = %import_ref.2 +// CHECK:STDOUT: .C = %import_ref.3 +// CHECK:STDOUT: .D = %import_ref.4 +// 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+6, used [template = imports.%B] +// CHECK:STDOUT: %import_ref.3 = import_ref ir1, inst+20, unused +// CHECK:STDOUT: %import_ref.4 = import_ref ir1, inst+30, unused +// CHECK:STDOUT: %A: = fn_decl @A [template] {} +// CHECK:STDOUT: %B: = fn_decl @B [template] { +// CHECK:STDOUT: %b.loc17_6.1: i32 = param b +// CHECK:STDOUT: %b.loc17_6.2: i32 = bind_name b, %b.loc17_6.1 +// CHECK:STDOUT: %return.var: ref i32 = var +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B(%b: i32) -> i32; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_mix_extern_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [template] { +// CHECK:STDOUT: .A = %import_ref.1 +// CHECK:STDOUT: .B = %import_ref.2 +// CHECK:STDOUT: .C = %import_ref.3 +// CHECK:STDOUT: .D = %import_ref.4 +// CHECK:STDOUT: } +// CHECK:STDOUT: %import_ref.1 = import_ref ir1, inst+1, unused +// CHECK:STDOUT: %import_ref.2 = import_ref ir1, inst+6, unused +// CHECK:STDOUT: %import_ref.3 = import_ref ir1, inst+20, unused +// CHECK:STDOUT: %import_ref.4: = import_ref ir1, inst+30, used [template = imports.%D] +// CHECK:STDOUT: %D.loc6: = fn_decl @D [template] {} +// CHECK:STDOUT: %D.loc11: = fn_decl @D [template] {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @D() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/packages/cross_package_import.carbon b/toolchain/check/testdata/packages/cross_package_import.carbon index 1d9e28be561a9..f0ff127d0f7c0 100644 --- a/toolchain/check/testdata/packages/cross_package_import.carbon +++ b/toolchain/check/testdata/packages/cross_package_import.carbon @@ -18,8 +18,7 @@ fn F() {} package Other library "fn_extern" api; -// TODO: Mark extern -fn F(); +extern fn F(); // --- other_fn_conflict.carbon @@ -63,9 +62,9 @@ import Other library "fn"; // CHECK:STDERR: fail_main_use_other_extern.carbon:[[@LINE+12]]:1: In import. // CHECK:STDERR: import Other library "fn_extern"; // CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: other_fn_extern.carbon:5:1: ERROR: Duplicate name being declared in the same scope. -// CHECK:STDERR: fn F(); -// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: other_fn_extern.carbon:4:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: extern fn F(); +// CHECK:STDERR: ^~~~~~~~~~~~~~ // CHECK:STDERR: fail_main_use_other_extern.carbon:[[@LINE-7]]:1: In import. // CHECK:STDERR: import Other library "fn"; // CHECK:STDERR: ^~~~~~ @@ -133,15 +132,10 @@ import library "other_ns"; // CHECK:STDERR: import Other library "fn"; -// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE+10]]:1: ERROR: Duplicate name being declared in the same scope. +// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE+5]]:1: ERROR: Only one library can declare function F without `extern`. // CHECK:STDERR: fn Other.F() {} // CHECK:STDERR: ^~~~~~~~~~~~~~ -// CHECK:STDERR: fail_main_namespace_conflict.carbon:[[@LINE-5]]:1: In import. -// CHECK:STDERR: import Other library "fn"; -// CHECK:STDERR: ^~~~~~ -// CHECK:STDERR: other_fn.carbon:4:1: Name is previously declared here. -// CHECK:STDERR: fn F() {} -// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: fail_main_namespace_conflict.carbon: Previously declared here. // CHECK:STDERR: fn Other.F() {} @@ -350,12 +344,10 @@ fn Other.G() {} // CHECK:STDOUT: %import_ref.1: = import_ref ir1, inst+1, used // CHECK:STDOUT: %Other: = namespace %import_ref.1, [template] {} // CHECK:STDOUT: %import_ref.2: = import_ref ir2, inst+1, used [template = imports.%F] -// CHECK:STDOUT: %.loc27: = fn_decl @.1 [template] {} +// CHECK:STDOUT: %F: = fn_decl @F [template] {} // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: fn @F(); -// CHECK:STDOUT: -// CHECK:STDOUT: fn @.1() { +// CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 728a77d7f8d4a..8b5fa9d1fca5c 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -160,6 +160,7 @@ CARBON_DIAGNOSTIC_KIND(MissingObjectInMethodCall) // Function declaration checking. CARBON_DIAGNOSTIC_KIND(FunctionPreviousDecl) CARBON_DIAGNOSTIC_KIND(FunctionRedecl) +CARBON_DIAGNOSTIC_KIND(FunctionNonExternRedecl) CARBON_DIAGNOSTIC_KIND(FunctionPreviousDefinition) CARBON_DIAGNOSTIC_KIND(FunctionRedefinition) CARBON_DIAGNOSTIC_KIND(FunctionDefiningExtern)