Make Destroy.Op functions able to have a body (#6729)

This is iterating on how `Destroy.Op` generates, to start adding body
capabilities. This changes the way the signature is created, and adds a
`CoreWitness` function kind so that mangling can prevent name
collisions. The result is that what _was_ `DestroyOp` is now
`Core.Destroy.Op` or, as can be seen in
toolchain/lower/testdata/interop/cpp/nullptr.carbon,
`_COp.<hash>:core.Destroy.Core` where `:core` is indicating that it's a
core witness (taking a note from `:thunk`).

Assisted-by: Google Antigravity with Gemini 3 Flash
This commit is contained in:
Jon Ross-Perkins
2026-03-02 17:57:31 +00:00
committed by GitHub
parent 2389590230
commit b14015602b
163 changed files with 2472 additions and 2339 deletions
@@ -232,8 +232,8 @@ fn F() {
// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.baa: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%Optional.804, %ImplicitAs.facet.d14) [concrete]
// CHECK:STDOUT: %.73c: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.baa, %ImplicitAs.facet.d14 [concrete]
// CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %U.binding.as_type.as.ImplicitAs.impl.Convert.d74, @U.binding.as_type.as.ImplicitAs.impl.Convert.2(%OptionalStorage.facet.92c, %OptionalAs.facet) [concrete]
// CHECK:STDOUT: %DestroyOp.type.3e79c2.2: type = fn_type @DestroyOp.loc10 [concrete]
// CHECK:STDOUT: %DestroyOp.b0ebf8.2: %DestroyOp.type.3e79c2.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc10 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -281,12 +281,12 @@ fn F() {
// CHECK:STDOUT: %.loc10_11.3: ref %Optional.804 = temporary %.loc10_11.2, %.loc10_11.1
// CHECK:STDOUT: %.loc10_11.4: %Optional.804 = acquire_value %.loc10_11.3
// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call imports.%foo.decl(%.loc10_11.4)
// CHECK:STDOUT: %DestroyOp.bound: <bound method> = bound_method %.loc10_11.3, constants.%DestroyOp.b0ebf8.2
// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc10_11.3)
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.loc10_11.3, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc10_11.3)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @DestroyOp.loc10(%self.param: ref %Optional.804) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op.loc10(%self.param: ref %Optional.804) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: --- null_param.carbon
// CHECK:STDOUT:
@@ -318,8 +318,8 @@ fn F() {
// CHECK:STDOUT: %Optional.None.specific_fn: <specific function> = specific_function %Optional.None.a61, @Optional.None(%OptionalStorage.facet) [concrete]
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
// CHECK:STDOUT: %DestroyOp.type.3e79c2.2: type = fn_type @DestroyOp.loc10 [concrete]
// CHECK:STDOUT: %DestroyOp.b0ebf8.2: %DestroyOp.type.3e79c2.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc10 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -385,12 +385,12 @@ fn F() {
// CHECK:STDOUT: %.loc10_41.2: ref %Optional.ad7 = temporary %.loc10_41.1, %Optional.None.call
// CHECK:STDOUT: %.loc10_41.3: %Optional.ad7 = acquire_value %.loc10_41.2
// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call imports.%foo.decl(%.loc10_41.3)
// CHECK:STDOUT: %DestroyOp.bound: <bound method> = bound_method %.loc10_41.2, constants.%DestroyOp.b0ebf8.2
// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc10_41.2)
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.loc10_41.2, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc10_41.2)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @DestroyOp.loc10(%self.param: ref %Optional.ad7) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op.loc10(%self.param: ref %Optional.ad7) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: --- nullable_return_value.carbon
// CHECK:STDOUT:
@@ -415,8 +415,8 @@ fn F() {
// CHECK:STDOUT: %foo.cpp_overload_set.value: %foo.cpp_overload_set.type = cpp_overload_set_value @foo.cpp_overload_set [concrete]
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
// CHECK:STDOUT: %DestroyOp.type.3e79c2.2: type = fn_type @DestroyOp.loc10 [concrete]
// CHECK:STDOUT: %DestroyOp.b0ebf8.2: %DestroyOp.type.3e79c2.2 = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type.bae255.2: type = fn_type @Destroy.Op.loc10 [concrete]
// CHECK:STDOUT: %Destroy.Op.651ba6.2: %Destroy.Op.type.bae255.2 = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -480,12 +480,12 @@ fn F() {
// CHECK:STDOUT: %.loc10_57.2: ref %Optional.bb8 = temporary %.loc10_57.1, %foo.call
// CHECK:STDOUT: %.loc10_57.3: %Optional.bb8 = acquire_value %.loc10_57.2
// CHECK:STDOUT: %output: %Optional.bb8 = value_binding output, %.loc10_57.3
// CHECK:STDOUT: %DestroyOp.bound: <bound method> = bound_method %.loc10_57.2, constants.%DestroyOp.b0ebf8.2
// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc10_57.2)
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %.loc10_57.2, constants.%Destroy.Op.651ba6.2
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%.loc10_57.2)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @DestroyOp.loc10(%self.param: ref %Optional.bb8) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op.loc10(%self.param: ref %Optional.bb8) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: --- non_nullable_pointer.carbon
// CHECK:STDOUT:
@@ -503,8 +503,8 @@ fn F() {
// CHECK:STDOUT: %Invoke.cpp_overload_set.value: %Invoke.cpp_overload_set.type = cpp_overload_set_value @Invoke.cpp_overload_set [concrete]
// CHECK:STDOUT: %Invoke.type: type = fn_type @Invoke [concrete]
// CHECK:STDOUT: %Invoke: %Invoke.type = struct_value () [concrete]
// CHECK:STDOUT: %DestroyOp.type: type = fn_type @DestroyOp [concrete]
// CHECK:STDOUT: %DestroyOp: %DestroyOp.type = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete]
// CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -551,12 +551,12 @@ fn F() {
// CHECK:STDOUT: %non_nullable_pointer.ref: ref %ptr.e0b = name_ref non_nullable_pointer, %non_nullable_pointer
// CHECK:STDOUT: %.loc12: %ptr.e0b = acquire_value %non_nullable_pointer.ref
// CHECK:STDOUT: %Invoke.call: init %empty_tuple.type = call imports.%Invoke.decl(%.loc12)
// CHECK:STDOUT: %DestroyOp.bound: <bound method> = bound_method %non_nullable_pointer.var, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%non_nullable_pointer.var)
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %non_nullable_pointer.var, constants.%Destroy.Op
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%non_nullable_pointer.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @DestroyOp(%self.param: ref %ptr.e0b) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %ptr.e0b) = "no_op";
// CHECK:STDOUT:
// CHECK:STDOUT: --- non_nullable_const.carbon
// CHECK:STDOUT:
@@ -574,8 +574,8 @@ fn F() {
// CHECK:STDOUT: %Invoke.cpp_overload_set.value: %Invoke.cpp_overload_set.type = cpp_overload_set_value @Invoke.cpp_overload_set [concrete]
// CHECK:STDOUT: %Invoke.type: type = fn_type @Invoke [concrete]
// CHECK:STDOUT: %Invoke: %Invoke.type = struct_value () [concrete]
// CHECK:STDOUT: %DestroyOp.type: type = fn_type @DestroyOp [concrete]
// CHECK:STDOUT: %DestroyOp: %DestroyOp.type = struct_value () [concrete]
// CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete]
// CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
@@ -622,10 +622,10 @@ fn F() {
// CHECK:STDOUT: %const_void_pointer.ref: ref %ptr = name_ref const_void_pointer, %const_void_pointer
// CHECK:STDOUT: %.loc12: %ptr = acquire_value %const_void_pointer.ref
// CHECK:STDOUT: %Invoke.call: init %empty_tuple.type = call imports.%Invoke.decl(%.loc12)
// CHECK:STDOUT: %DestroyOp.bound: <bound method> = bound_method %const_void_pointer.var, constants.%DestroyOp
// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%const_void_pointer.var)
// CHECK:STDOUT: %Destroy.Op.bound: <bound method> = bound_method %const_void_pointer.var, constants.%Destroy.Op
// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%const_void_pointer.var)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: fn @DestroyOp(%self.param: ref %ptr) = "no_op";
// CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %ptr) = "no_op";
// CHECK:STDOUT: