Improve CanDestroyType to handle remaining cases (#6943)

This is only fixing the decision about *whether* to produce a witness.
Implementation of the witness is still a TODO, though where a body is
generated, it should also precisely reflect where one _needs_ to be
generated.

Note the tests:

- toolchain/lower/testdata/function/generic/import_core_witness.carbon
- toolchain/lower/testdata/function/generic/import_unused_def.carbon

These tests can probably be produced _without_ Core.Destroy, but I found
the essence of them while trying to build //examples with Core.Destroy
and a simpler minimization wasn't striking me.

Assisted-by: Google Antigravity with Gemini

---------

Co-authored-by: jonmeow <jperkins@google.com>
This commit is contained in:
Jon Ross-Perkins
2026-04-02 22:54:58 +00:00
committed by GitHub
co-authored by jonmeow
parent 1fa7a64cd4
commit 9266ced4e3
212 changed files with 11376 additions and 4978 deletions
+22 -12
View File
@@ -109,8 +109,8 @@ let (a: {}, b: {}) = ({}, {}, {});
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
// CHECK:STDOUT: %pattern_type.de4: type = pattern_type %tuple.type.b6b [concrete]
// CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete]
// CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete]
// CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc8_3.2 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @F(%v.param: %tuple.type.b6b) {
@@ -199,14 +199,19 @@ let (a: {}, b: {}) = ({}, {}, {});
// CHECK:STDOUT: %.loc8_33.3: type = converted %.loc8_33.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %d: ref %empty_struct_type = ref_binding d, %tuple.elem1.loc8_3
// CHECK:STDOUT: %Destroy.Op.bound.loc8: <bound method> = bound_method %.var.loc8, constants.%Destroy.Op
// CHECK:STDOUT: %Destroy.Op.bound.loc8: <bound method> = bound_method %.var.loc8, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.call.loc8: init %empty_tuple.type = call %Destroy.Op.bound.loc8(%.var.loc8)
// CHECK:STDOUT: %Destroy.Op.bound.loc7: <bound method> = bound_method %.var.loc7, constants.%Destroy.Op
// CHECK:STDOUT: %Destroy.Op.bound.loc7: <bound method> = bound_method %.var.loc7, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call %Destroy.Op.bound.loc7(%.var.loc7)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %tuple.type.b6b) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op.loc8_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc8_3.2(%self.param: ref %tuple.type.b6b) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- variable.carbon
// CHECK:STDOUT:
@@ -218,8 +223,8 @@ let (a: {}, b: {}) = ({}, {}, {});
// CHECK:STDOUT: %tuple: %tuple.type.b6b = tuple_value (%empty_struct, %empty_struct) [concrete]
// CHECK:STDOUT: %pattern_type.de4: type = pattern_type %tuple.type.b6b [concrete]
// CHECK:STDOUT: %pattern_type.a96: type = pattern_type %empty_struct_type [concrete]
// CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete]
// CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc7_3.2 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete]
// CHECK:STDOUT: %MakeTuple.type: type = fn_type @MakeTuple [concrete]
// CHECK:STDOUT: %MakeTuple: %MakeTuple.type = struct_value () [concrete]
// CHECK:STDOUT: }
@@ -283,14 +288,19 @@ let (a: {}, b: {}) = ({}, {}, {});
// CHECK:STDOUT: %.loc7_33.3: type = converted %.loc7_33.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %y: ref %empty_struct_type = ref_binding y, %tuple.elem1.loc7_3
// CHECK:STDOUT: %Destroy.Op.bound.loc7: <bound method> = bound_method %.var, constants.%Destroy.Op
// CHECK:STDOUT: %Destroy.Op.bound.loc7: <bound method> = bound_method %.var, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.call.loc7: init %empty_tuple.type = call %Destroy.Op.bound.loc7(%.var)
// CHECK:STDOUT: %Destroy.Op.bound.loc6: <bound method> = bound_method %tuple.var, constants.%Destroy.Op
// CHECK:STDOUT: %Destroy.Op.bound.loc6: <bound method> = bound_method %tuple.var, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.call.loc6: init %empty_tuple.type = call %Destroy.Op.bound.loc6(%tuple.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %tuple.type.b6b) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op.loc7_3.1(%self.param: ref %empty_struct_type) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc7_3.2(%self.param: ref %tuple.type.b6b) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @G() {
// CHECK:STDOUT: !entry:
@@ -346,7 +356,7 @@ let (a: {}, b: {}) = ({}, {}, {});
// CHECK:STDOUT: %.loc14_33.3: type = converted %.loc14_33.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %y: ref %empty_struct_type = ref_binding y, %tuple.elem1.loc14_3
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.var, constants.%Destroy.Op
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.var, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
@@ -376,7 +386,7 @@ let (a: {}, b: {}) = ({}, {}, {});
// CHECK:STDOUT: %.loc22_33.3: type = converted %.loc22_33.2, constants.%empty_struct_type [concrete = constants.%empty_struct_type]
// CHECK:STDOUT: }
// CHECK:STDOUT: %y: ref %empty_struct_type = ref_binding y, %tuple.elem1
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.var, constants.%Destroy.Op
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.var, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }