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
@@ -83,6 +83,7 @@ fn F() {
// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete]
// CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete]
// CHECK:STDOUT: %array_type.643: type = array_type %int_42, %i32 [concrete]
// CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete]
// CHECK:STDOUT: %pattern_type.b6e: type = pattern_type %array_type.643 [concrete]
// CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete]
// CHECK:STDOUT: %UnformedInit.type: type = facet_type <@UnformedInit> [concrete]
@@ -125,6 +126,11 @@ fn F() {
// CHECK:STDOUT: %ptr.as.OptionalStorage.impl.Some.38a: %ptr.as.OptionalStorage.impl.Some.type.c7a = struct_value () [symbolic]
// CHECK:STDOUT: %ptr.as.OptionalStorage.impl.None.type.03c: type = fn_type @ptr.as.OptionalStorage.impl.None, @ptr.as.OptionalStorage.impl(%T.67d) [symbolic]
// CHECK:STDOUT: %ptr.as.OptionalStorage.impl.None.f37: %ptr.as.OptionalStorage.impl.None.type.03c = struct_value () [symbolic]
// CHECK:STDOUT: %Destroy.Op.type.bae255.1: type = fn_type @Destroy.Op.1 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.1: %Destroy.Op.type.bae255.1 = struct_value () [concrete]
// CHECK:STDOUT: %custom_witness.8d7fae.1: <witness> = custom_witness (%Destroy.Op.651ba6.1), @Destroy [concrete]
// CHECK:STDOUT: %Destroy.facet.252: %Destroy.type = facet_value %ptr.235, (%custom_witness.8d7fae.1) [concrete]
// CHECK:STDOUT: %MaybeUnformed.859: type = class_type @MaybeUnformed, @MaybeUnformed(%Destroy.facet.252) [concrete]
// CHECK:STDOUT: %OptionalStorage.impl_witness.e42: <witness> = impl_witness imports.%OptionalStorage.impl_witness_table.851, @ptr.as.OptionalStorage.impl(%i32) [concrete]
// CHECK:STDOUT: %OptionalStorage.facet.6bd: %OptionalStorage.type = facet_value %ptr.235, (%OptionalStorage.impl_witness.e42) [concrete]
// CHECK:STDOUT: %Optional.75d: type = class_type @Optional, @Optional(%OptionalStorage.facet.6bd) [concrete]
@@ -160,10 +166,10 @@ fn F() {
// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %uninit, %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.a00 [concrete]
// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.a00, @Cpp.nullptr_t.as.ImplicitAs.impl.Convert(%i32) [concrete]
// CHECK:STDOUT: %bound_method.3ee: <bound method> = bound_method %uninit, %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.specific_fn [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.3: type = fn_type @Destroy.Op.loc13 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.3: %Destroy.Op.type.bae255.3 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.4: type = fn_type @Destroy.Op.loc10 [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.4: type = fn_type @Destroy.Op.loc13_21.2 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.4: %Destroy.Op.type.bae255.4 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.7: type = fn_type @Destroy.Op.loc10_3.3 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.7: %Destroy.Op.type.bae255.7 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -264,18 +270,36 @@ fn F() {
// CHECK:STDOUT: %.loc13_21.3: ref %Optional.75d = temporary %.loc13_21.2, %.loc13_21.1
// CHECK:STDOUT: %.loc13_21.4: %Optional.75d = acquire_value %.loc13_21.3
// CHECK:STDOUT: %TakesArray.call.loc13: init %empty_tuple.type = call imports.%TakesArray.decl(%.loc13_21.4)
// CHECK:STDOUT: %Destroy.Op.bound.loc13: <bound method> = bound_method %.loc13_21.3, constants.%Destroy.Op.651ba6.3
// CHECK:STDOUT: %Destroy.Op.bound.loc13: <bound method> = bound_method %.loc13_21.3, constants.%Destroy.Op.651ba6.4
// CHECK:STDOUT: %Destroy.Op.call.loc13: init %empty_tuple.type = call %Destroy.Op.bound.loc13(%.loc13_21.3)
// CHECK:STDOUT: %Destroy.Op.bound.loc11: <bound method> = bound_method %.loc11_18.3, constants.%Destroy.Op.651ba6.3
// CHECK:STDOUT: %Destroy.Op.bound.loc11: <bound method> = bound_method %.loc11_18.3, constants.%Destroy.Op.651ba6.4
// CHECK:STDOUT: %Destroy.Op.call.loc11: init %empty_tuple.type = call %Destroy.Op.bound.loc11(%.loc11_18.3)
// CHECK:STDOUT: %Destroy.Op.bound.loc10: <bound method> = bound_method %n.var, constants.%Destroy.Op.651ba6.4
// CHECK:STDOUT: %Destroy.Op.bound.loc10: <bound method> = bound_method %n.var, constants.%Destroy.Op.651ba6.7
// CHECK:STDOUT: %Destroy.Op.call.loc10: init %empty_tuple.type = call %Destroy.Op.bound.loc10(%n.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc13(%self.param: ref %Optional.75d) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op.loc13_21.1(%self.param: ref %MaybeUnformed.859) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc10(%self.param: ref %array_type.643) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op.loc13_21.2(%self.param: ref %Optional.75d) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc10_3.1(%self.param: ref %i32.builtin) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc10_3.2(%self.param: ref %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc10_3.3(%self.param: ref %array_type.643) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_call_params_2.carbon
// CHECK:STDOUT:
@@ -291,6 +315,7 @@ fn F() {
// CHECK:STDOUT: %Function.cpp_overload_set.value: %Function.cpp_overload_set.type = cpp_overload_set_value @Function.cpp_overload_set [concrete]
// CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete]
// CHECK:STDOUT: %array_type.643: type = array_type %int_42, %i32 [concrete]
// CHECK:STDOUT: %i32.builtin: type = int_type signed, %int_32 [concrete]
// CHECK:STDOUT: %pattern_type.b6e: type = pattern_type %array_type.643 [concrete]
// CHECK:STDOUT: %DefaultOrUnformed.type: type = facet_type <@DefaultOrUnformed> [concrete]
// CHECK:STDOUT: %UnformedInit.type: type = facet_type <@UnformedInit> [concrete]
@@ -321,8 +346,8 @@ fn F() {
// CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete]
// CHECK:STDOUT: %Cpp.nullptr_t: type = class_type @NullptrT [concrete]
// CHECK:STDOUT: %uninit: %Cpp.nullptr_t = uninitialized_value [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc22 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.4: type = fn_type @Destroy.Op.loc22_3.3 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.4: %Destroy.Op.type.bae255.4 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -388,10 +413,20 @@ fn F() {
// CHECK:STDOUT: %Cpp.ref.loc37_21: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: %nullptr.ref: %Cpp.nullptr_t = name_ref nullptr, %uninit [concrete = constants.%uninit]
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %n.var, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %n.var, constants.%Destroy.Op.651ba6.4
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%n.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc22(%self.param: ref %array_type.643) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op.loc22_3.1(%self.param: ref %i32.builtin) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc22_3.2(%self.param: ref %i32) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @Destroy.Op.loc22_3.3(%self.param: ref %array_type.643) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT: