Add Copy interface and use it for making copies. (#6034)

Instead of hardcoding which types are copyable, add a `Core.Copy`
interface to perform copying. Move almost all the current copy support
to that interface. Some remaining pieces are still using builtin logic
after this PR:

* For tuples and structs, builtin logic is used to perform elementwise
copies. This also supports copying *adapters of* tuples and structs,
which seems like it may not be desirable, especially for non-extending
adapters. A `Copy` impl is provided for tuples of at most 2 elements, so
that `Core.Copy` constraints are satisfied, but we can't implement this
generally until we have variadics support, and don't yet have a
mechanism to generalize this to structs.
* For `enum` types imported from C++, builtin logic is used to perform a
copy. This is temporary until we have a mechanism to identify these
types from an impl in the prelude.

One lowering test in `toolchain/lower/testdata/class/generic.carbon` is
disabled for now, as it causes a crash in the lowering code due to an
ABI mismatch between the call signature in the lowered declaration of a
specific function and the call that is generated in the specific callee.
Fixing this is a little involved, and will be done in a separate PR.

---------

Co-authored-by: Geoff Romer <gromer@google.com>
This commit is contained in:
Richard Smith
2025-09-10 23:55:55 +00:00
committed by GitHub
co-authored by Geoff Romer
parent 91722ae21a
commit 1ec8ac7ef9
207 changed files with 6195 additions and 2566 deletions
@@ -255,6 +255,17 @@ fn F() {
// CHECK:STDOUT: %S.val: %S = struct_value () [concrete]
// CHECK:STDOUT: %ptr.5c7: type = ptr_type %S [concrete]
// CHECK:STDOUT: %pattern_type.259: type = pattern_type %ptr.5c7 [concrete]
// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete]
// CHECK:STDOUT: %Copy.Op.type: type = fn_type @Copy.Op [concrete]
// CHECK:STDOUT: %T.8b3: type = bind_symbolic_name T, 0 [symbolic]
// CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.f23: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%T.8b3) [symbolic]
// CHECK:STDOUT: %ptr.as.Copy.impl.Op.abf: %ptr.as.Copy.impl.Op.type.f23 = struct_value () [symbolic]
// CHECK:STDOUT: %Copy.impl_witness.dea: <witness> = impl_witness imports.%Copy.impl_witness_table.a71, @ptr.as.Copy.impl(%S) [concrete]
// CHECK:STDOUT: %ptr.as.Copy.impl.Op.type.7f7: type = fn_type @ptr.as.Copy.impl.Op, @ptr.as.Copy.impl(%S) [concrete]
// CHECK:STDOUT: %ptr.as.Copy.impl.Op.0c6: %ptr.as.Copy.impl.Op.type.7f7 = struct_value () [concrete]
// CHECK:STDOUT: %Copy.facet.334: %Copy.type = facet_value %ptr.5c7, (%Copy.impl_witness.dea) [concrete]
// CHECK:STDOUT: %.816: type = fn_type_with_self_type %Copy.Op.type, %Copy.facet.334 [concrete]
// CHECK:STDOUT: %ptr.as.Copy.impl.Op.specific_fn: <specific function> = specific_function %ptr.as.Copy.impl.Op.0c6, @ptr.as.Copy.impl.Op(%S) [concrete]
// CHECK:STDOUT: %ptr.dfe: type = ptr_type %ptr.5c7 [concrete]
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
@@ -271,6 +282,8 @@ fn F() {
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
// CHECK:STDOUT: %Core.import_ref.de9: @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op.type (%ptr.as.Copy.impl.Op.type.f23) = import_ref Core//prelude/parts/copy, loc32_31, loaded [symbolic = @ptr.as.Copy.impl.%ptr.as.Copy.impl.Op (constants.%ptr.as.Copy.impl.Op.abf)]
// CHECK:STDOUT: %Copy.impl_witness_table.a71 = impl_witness_table (%Core.import_ref.de9), @ptr.as.Copy.impl [concrete]
// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: } {
@@ -301,7 +314,12 @@ fn F() {
// CHECK:STDOUT: %p.var: ref %ptr.5c7 = var %p.var_patt
// CHECK:STDOUT: %s.ref: ref %S = name_ref s, %s
// CHECK:STDOUT: %addr.loc9_19: %ptr.5c7 = addr_of %s.ref
// CHECK:STDOUT: assign %p.var, %addr.loc9_19
// CHECK:STDOUT: %impl.elem0: %.816 = impl_witness_access constants.%Copy.impl_witness.dea, element0 [concrete = constants.%ptr.as.Copy.impl.Op.0c6]
// CHECK:STDOUT: %bound_method.loc9_19.1: <bound method> = bound_method %addr.loc9_19, %impl.elem0
// CHECK:STDOUT: %specific_fn: <specific function> = specific_function %impl.elem0, @ptr.as.Copy.impl.Op(constants.%S) [concrete = constants.%ptr.as.Copy.impl.Op.specific_fn]
// CHECK:STDOUT: %bound_method.loc9_19.2: <bound method> = bound_method %addr.loc9_19, %specific_fn
// CHECK:STDOUT: %ptr.as.Copy.impl.Op.call: init %ptr.5c7 = call %bound_method.loc9_19.2(%addr.loc9_19)
// CHECK:STDOUT: assign %p.var, %ptr.as.Copy.impl.Op.call
// CHECK:STDOUT: %.loc9: type = splice_block %ptr [concrete = constants.%ptr.5c7] {
// CHECK:STDOUT: %Cpp.ref.loc9: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %S.ref.loc9: type = name_ref S, imports.%S.decl [concrete = constants.%S]
@@ -315,9 +333,9 @@ fn F() {
// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref(%addr.loc10)
// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc9: <bound method> = bound_method %p.var, constants.%T.as.Destroy.impl.Op.014
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: %bound_method.loc9: <bound method> = bound_method %p.var, %T.as.Destroy.impl.Op.specific_fn.1
// CHECK:STDOUT: %bound_method.loc9_3: <bound method> = bound_method %p.var, %T.as.Destroy.impl.Op.specific_fn.1
// CHECK:STDOUT: %addr.loc9_3: %ptr.dfe = addr_of %p.var
// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%addr.loc9_3)
// CHECK:STDOUT: %T.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9_3(%addr.loc9_3)
// CHECK:STDOUT: %T.as.Destroy.impl.Op.bound.loc8: <bound method> = bound_method %s.var, constants.%T.as.Destroy.impl.Op.777
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: %bound_method.loc8: <bound method> = bound_method %s.var, %T.as.Destroy.impl.Op.specific_fn.2