Canonicalize generated functions for Core witnesses (#7729)

Use a single `SemIR::Function` per `Core` interface method, whether it's
generated locally or imported. This prevents generating duplicate
functions, which lead to different types when the witness appears in a
`FacetValue` as part of a specific for a class.

We use a `CanonicalValueStore` of `GeneratedFunction` objects that allow
finding an existing FunctionId for a `Generated` special function before
(re-)generating it. Mangling for `Generated` functions is also moved to
use the values from the `GeneratedFunction`'s canonicalization key, so
that we have a consistent source of truth for the unique ID of a
`Generated` function across all files.

New tests are in
`toolchain/check/testdata/impl/custom_witness/destroy.carbon`.
This commit is contained in:
Dana Jansens
2026-09-15 16:02:13 +00:00
committed by GitHub
parent db2e26ba86
commit 4416f3525b
278 changed files with 5941 additions and 5506 deletions
@@ -211,18 +211,18 @@ void CallUnsupportedF() {
// CHECK:STDOUT: %self.patt.b4c: %pattern_type.b3a = wrapper_binding_pattern self, %self.param_patt.58e [symbolic]
// CHECK:STDOUT: %specific_impl_fn: <specific function> = specific_impl_function %impl.elem0, @I.WithSelf.Doit(%T) [symbolic]
// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete]
// CHECK:STDOUT: %Destroy.Op.type.1d8f74.2: type = fn_type @Destroy.Op.loc16_9.2 [concrete]
// CHECK:STDOUT: %Destroy.Op.1a2547.2: %Destroy.Op.type.1d8f74.2 = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.df9cc1.2: <witness> = custom_witness (%Destroy.Op.1a2547.2), @Destroy [concrete]
// CHECK:STDOUT: %Destroy.facet.f70: %Destroy.type = facet_value %B, (%custom_witness.df9cc1.2) [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.cda: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.f70) [concrete]
// CHECK:STDOUT: %.3d9: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.cda, %Destroy.facet.f70 [concrete]
// CHECK:STDOUT: %Destroy.Op.type.1d8f74.3: type = fn_type @Destroy.Op.loc9 [concrete]
// CHECK:STDOUT: %Destroy.Op.1a2547.3: %Destroy.Op.type.1d8f74.3 = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.df9cc1.3: <witness> = custom_witness (%Destroy.Op.1a2547.3), @Destroy [concrete]
// CHECK:STDOUT: %Destroy.facet.6e5: %Destroy.type = facet_value %A, (%custom_witness.df9cc1.3) [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.09e: type = fn_type @Destroy.WithSelf.Op, @Destroy.WithSelf(%Destroy.facet.6e5) [concrete]
// CHECK:STDOUT: %.dca: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.09e, %Destroy.facet.6e5 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc16_9.2 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.6c4ec3.2: <witness> = custom_witness (%Destroy.WithSelf.Op.403171.2), @Destroy [concrete]
// CHECK:STDOUT: %Destroy.facet.5dd: %Destroy.type = facet_value %B, (%custom_witness.6c4ec3.2) [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.bf9: type = fn_type @Destroy.WithSelf.Op.1, @Destroy.WithSelf(%Destroy.facet.5dd) [concrete]
// CHECK:STDOUT: %.9b8: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.bf9, %Destroy.facet.5dd [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.3: type = fn_type @Destroy.WithSelf.Op.loc9 [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.3: %Destroy.WithSelf.Op.type.ef016f.3 = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.6c4ec3.3: <witness> = custom_witness (%Destroy.WithSelf.Op.403171.3), @Destroy [concrete]
// CHECK:STDOUT: %Destroy.facet.288: %Destroy.type = facet_value %A, (%custom_witness.6c4ec3.3) [concrete]
// CHECK:STDOUT: %Destroy.WithSelf.Op.type.c82: type = fn_type @Destroy.WithSelf.Op.1, @Destroy.WithSelf(%Destroy.facet.288) [concrete]
// CHECK:STDOUT: %.528: type = fn_type_with_self_type %Destroy.WithSelf.Op.type.c82, %Destroy.facet.288 [concrete]
// CHECK:STDOUT: %t.param_patt.169: %pattern_type.9ef = value_param_pattern [concrete]
// CHECK:STDOUT: %t.patt.490: %pattern_type.9ef = wrapper_binding_pattern t, %t.param_patt.169 [concrete]
// CHECK:STDOUT: %F.specific_fn.9d8: <specific function> = specific_function %F, @F(%I.facet.f31) [concrete]
@@ -456,28 +456,28 @@ void CallUnsupportedF() {
// CHECK:STDOUT:
// CHECK:STDOUT: fn @B.__destroy_thunk(%self.param: ref %B) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %impl.elem0: %.3d9 = impl_witness_access constants.%custom_witness.df9cc1.2, element0 [concrete = constants.%Destroy.Op.1a2547.2]
// CHECK:STDOUT: %impl.elem0: %.9b8 = impl_witness_access constants.%custom_witness.6c4ec3.2, element0 [concrete = constants.%Destroy.WithSelf.Op.403171.2]
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %self.param, %impl.elem0
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %bound_method(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc16_9.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc16_9.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc16_9.2(%self.param: ref %B) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc16_9.2(%self.param: ref %B) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @A.__destroy_thunk(%self.param: ref %A) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %impl.elem0: %.dca = impl_witness_access constants.%custom_witness.df9cc1.3, element0 [concrete = constants.%Destroy.Op.1a2547.3]
// CHECK:STDOUT: %impl.elem0: %.528 = impl_witness_access constants.%custom_witness.6c4ec3.3, element0 [concrete = constants.%Destroy.WithSelf.Op.403171.3]
// CHECK:STDOUT: %bound_method: <bound method> = bound_method %self.param, %impl.elem0
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %bound_method(%self.param)
// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call %bound_method(%self.param)
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc9(%self.param: ref %A) {
// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc9(%self.param: ref %A) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }