From fc5dcfe95772d12fafa73dd7bc273427abc3cd9c Mon Sep 17 00:00:00 2001 From: Boaz Brickner Date: Fri, 28 Feb 2025 22:10:37 +0100 Subject: [PATCH] Add a test for the case that `impl` function is poisoned (#4950) This adds missing coverage. Part of #4622. --- .../no_prelude/name_poisoning.carbon | 3 +- .../impl/no_prelude/name_poisoning.carbon | 148 ++++++++++++++++++ 2 files changed, 150 insertions(+), 1 deletion(-) diff --git a/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon b/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon index 4962c6109d4e..efd997ce12cf 100644 --- a/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon +++ b/toolchain/check/testdata/function/declaration/no_prelude/name_poisoning.carbon @@ -94,6 +94,7 @@ alias N.F2 = F1; fn N.F1(); // Failure: `N.F1` used after declaration failed. +// TODO: #4622 - Allow defining a poisoned name so it would be found if used after it's declared. // CHECK:STDERR: fail_use_declaration_after_poison.carbon:[[@LINE+4]]:14: error: member name `F1` not found in `N` [MemberNameNotFoundInScope] // CHECK:STDERR: alias N.F3 = N.F1; // CHECK:STDERR: ^~~~ @@ -414,7 +415,7 @@ fn F1() { // CHECK:STDOUT: %F2: %F1.type.75d = bind_alias F2, %F1.decl.loc4 [concrete = constants.%F1.afe] // CHECK:STDOUT: %F1.decl.loc18: %F1.type.fe6 = fn_decl @F1.2 [concrete = constants.%F1.cc1] {} {} // CHECK:STDOUT: %N.ref: = name_ref N, %N [concrete = %N] -// CHECK:STDOUT: %F1.ref.loc25: = name_ref F1, [concrete = ] +// CHECK:STDOUT: %F1.ref.loc26: = name_ref F1, [concrete = ] // CHECK:STDOUT: %F3: = bind_alias F3, [concrete = ] // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/no_prelude/name_poisoning.carbon b/toolchain/check/testdata/impl/no_prelude/name_poisoning.carbon index 31bdb33974c6..32d1b3f61b85 100644 --- a/toolchain/check/testdata/impl/no_prelude/name_poisoning.carbon +++ b/toolchain/check/testdata/impl/no_prelude/name_poisoning.carbon @@ -23,6 +23,37 @@ class N.C { } } +// --- fail_impl_function_poisoned.carbon + +library "[[@TEST_NAME]]"; + +interface I { + fn A(x: Self); + fn B(); +} + +class B { + impl as I { + // CHECK:STDERR: fail_impl_function_poisoned.carbon:[[@LINE+3]]:13: error: name `B` used before it was declared [NameUseBeforeDecl] + // CHECK:STDERR: fn A(x: B); + // CHECK:STDERR: ^ + fn A(x: B); + // TODO: Avoid ImplMissingFunction for functions that were declared after they were poisoned. + // CHECK:STDERR: fail_impl_function_poisoned.carbon:[[@LINE+11]]:8: note: declared here [NameUseBeforeDeclNote] + // CHECK:STDERR: fn B(); + // CHECK:STDERR: ^ + // CHECK:STDERR: + // CHECK:STDERR: fail_impl_function_poisoned.carbon:[[@LINE-10]]:3: error: missing implementation of B in impl of interface I [ImplMissingFunction] + // CHECK:STDERR: impl as I { + // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: fail_impl_function_poisoned.carbon:[[@LINE-17]]:3: note: associated function B declared here [AssociatedFunctionHere] + // CHECK:STDERR: fn B(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + fn B(); + } +} + // CHECK:STDOUT: --- using_poisoned_name_in_impl.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -100,3 +131,120 @@ class N.C { // CHECK:STDOUT: %x.patt.loc8_9.2 => constants.%x // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- fail_impl_function_poisoned.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %I.type: type = facet_type <@I> [concrete] +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic] +// CHECK:STDOUT: %Self.as_type: type = facet_access_type %Self [symbolic] +// CHECK:STDOUT: %A.type.edf: type = fn_type @A.1 [concrete] +// CHECK:STDOUT: %A.ab0: %A.type.edf = struct_value () [concrete] +// CHECK:STDOUT: %I.assoc_type: type = assoc_entity_type %I.type [concrete] +// CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, @I.%A.decl [concrete] +// CHECK:STDOUT: %B.type.785: type = fn_type @B.1 [concrete] +// CHECK:STDOUT: %B.6d4: %B.type.785 = struct_value () [concrete] +// CHECK:STDOUT: %assoc1: %I.assoc_type = assoc_entity element1, @I.%B.decl [concrete] +// CHECK:STDOUT: %B.fa3: type = class_type @B.3 [concrete] +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.%A.decl, ) [concrete] +// CHECK:STDOUT: %A.type.9b9: type = fn_type @A.2 [concrete] +// CHECK:STDOUT: %A.71f: %A.type.9b9 = struct_value () [concrete] +// CHECK:STDOUT: %B.type.52d: type = fn_type @B.2 [concrete] +// CHECK:STDOUT: %B.30a: %B.type.52d = struct_value () [concrete] +// CHECK:STDOUT: %I.facet: %I.type = facet_value %B.fa3, %impl_witness [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: .I = %I.decl +// CHECK:STDOUT: .B = %B.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %I.decl: type = interface_decl @I [concrete = constants.%I.type] {} {} +// CHECK:STDOUT: %B.decl: type = class_decl @B.3 [concrete = constants.%B.fa3] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: interface @I { +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = constants.%Self] +// CHECK:STDOUT: %A.decl: %A.type.edf = fn_decl @A.1 [concrete = constants.%A.ab0] { +// CHECK:STDOUT: %x.patt: @A.1.%Self.as_type.loc5_11.1 (%Self.as_type) = binding_pattern x +// CHECK:STDOUT: %x.param_patt: @A.1.%Self.as_type.loc5_11.1 (%Self.as_type) = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: @A.1.%Self.as_type.loc5_11.1 (%Self.as_type) = value_param runtime_param0 +// CHECK:STDOUT: %.loc5_11.1: type = splice_block %.loc5_11.2 [symbolic = %Self.as_type.loc5_11.1 (constants.%Self.as_type)] { +// CHECK:STDOUT: %Self.ref: %I.type = name_ref Self, @I.%Self [symbolic = %Self (constants.%Self)] +// CHECK:STDOUT: %Self.as_type.loc5_11.2: type = facet_access_type %Self.ref [symbolic = %Self.as_type.loc5_11.1 (constants.%Self.as_type)] +// CHECK:STDOUT: %.loc5_11.2: type = converted %Self.ref, %Self.as_type.loc5_11.2 [symbolic = %Self.as_type.loc5_11.1 (constants.%Self.as_type)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: @A.1.%Self.as_type.loc5_11.1 (%Self.as_type) = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %assoc0: %I.assoc_type = assoc_entity element0, %A.decl [concrete = constants.%assoc0] +// CHECK:STDOUT: %B.decl: %B.type.785 = fn_decl @B.1 [concrete = constants.%B.6d4] {} {} +// CHECK:STDOUT: %assoc1: %I.assoc_type = assoc_entity element1, %B.decl [concrete = constants.%assoc1] +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = %Self +// CHECK:STDOUT: .A = %assoc0 +// CHECK:STDOUT: .B = %assoc1 +// CHECK:STDOUT: witness = (%A.decl, %B.decl) +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: impl @impl: %Self.ref as %I.ref { +// CHECK:STDOUT: %A.decl: %A.type.9b9 = fn_decl @A.2 [concrete = constants.%A.71f] { +// CHECK:STDOUT: %x.patt: %B.fa3 = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %B.fa3 = value_param_pattern %x.patt, runtime_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: %B.fa3 = value_param runtime_param0 +// CHECK:STDOUT: %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B.fa3] +// CHECK:STDOUT: %x: %B.fa3 = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %B.decl: %B.type.52d = fn_decl @B.2 [concrete = constants.%B.30a] {} {} +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .B = +// CHECK:STDOUT: .A = %A.decl +// CHECK:STDOUT: witness = @B.3.%impl_witness +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @B.3 { +// CHECK:STDOUT: impl_decl @impl [concrete] {} { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%B.fa3 [concrete = constants.%B.fa3] +// CHECK:STDOUT: %I.ref: type = name_ref I, file.%I.decl [concrete = constants.%I.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %impl_witness: = impl_witness (@impl.%A.decl, ) [concrete = constants.%impl_witness] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%B.fa3 +// CHECK:STDOUT: .I = +// CHECK:STDOUT: .B = +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @A.1(@I.%Self: %I.type) { +// CHECK:STDOUT: %Self: %I.type = bind_symbolic_name Self, 0 [symbolic = %Self (constants.%Self)] +// CHECK:STDOUT: %Self.as_type.loc5_11.1: type = facet_access_type %Self [symbolic = %Self.as_type.loc5_11.1 (constants.%Self.as_type)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn(%x.param_patt: @A.1.%Self.as_type.loc5_11.1 (%Self.as_type)); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @B.1(@I.%Self: %I.type) { +// CHECK:STDOUT: fn(); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A.2(%x.param_patt: %B.fa3); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @B.2(); +// CHECK:STDOUT: +// CHECK:STDOUT: specific @A.1(constants.%Self) { +// CHECK:STDOUT: %Self => constants.%Self +// CHECK:STDOUT: %Self.as_type.loc5_11.1 => constants.%Self.as_type +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @B.1(constants.%Self) {} +// CHECK:STDOUT: +// CHECK:STDOUT: specific @A.1(constants.%I.facet) { +// CHECK:STDOUT: %Self => constants.%I.facet +// CHECK:STDOUT: %Self.as_type.loc5_11.1 => constants.%B.fa3 +// CHECK:STDOUT: } +// CHECK:STDOUT: