I meant consuming on arguments like func foo(_ x: consuming Player) but you're asking about consuming func foo().
So yes, kinda, you're definitely on the right track but it doesn't really remove self (except if a copy is impossible, see later) from the caller, it merely says 'I will consume ownership of self'. This may sound a little abstract, let's do examples. Let's start with a class C, note for classes copying means increasing the ref count.
Let's start with a program that doesn't use consuming:
class C { func bye() {} }
func HARNESS(_ c: C) -> C { c.bye(); return c; }
if we feed this through the compiler we get
$ echo 'class C { func bye() {} }; func HARNESS(_ c: C) -> C { c.bye(); return c; }' | swiftc -O -emit-assembly -module-name T - | swift demangle | grep -A20 ^T.HARNESS | grep -v .cfi_
T.HARNESS(T.C) -> T.C:
stp x20, x19, [sp, #-32]!
stp x29, x30, [sp, #16]
add x29, sp, #16
mov x20, x0
ldr x8, [x0]
ldr x8, [x8, #80]
blr x8 // <-- CALL to virtual function `bye`
mov x0, x20
ldp x29, x30, [sp, #16]
ldp x20, x19, [sp], #32
b _swift_retain // <-- a retain at the end before return
So when we're calling bye we don't need to do anything to the ref count because we borrowed (actually @guaranteed) that from the caller (standard for function parameters). And bye by default will also just borrowing (actually @guaranteed) self.
Before the return however we need to increase the reference count because the default for return values is @owned.
Now, if we change our little program and make it a consuming func bye() that means bye is no longer happy with a @guaranteed self, it now wants to consume it.
class C { consuming func bye() {} }
func HARNESS(_ c: C) -> C { c.bye(); return c; }
gives us
$ echo 'class C { consuming func bye() {} }; func HARNESS(_ c: C) -> C { c.bye(); return c; }' | swiftc -O -emit-assembly -module-name T - | swift demangle | grep -A20 ^T.HARNESS | grep -v .cfi_
T.HARNESS(T.C) -> T.C:
stp x20, x19, [sp, #-32]!
stp x29, x30, [sp, #16]
add x29, sp, #16
mov x20, x0
ldr x8, [x0]
ldr x19, [x8, #80]
bl _swift_retain // <--- retain before call to bye
blr x19 // <--- virtual bye call
mov x0, x20
ldp x29, x30, [sp, #16]
ldp x20, x19, [sp], #32
b _swift_retain // <--- another retain before release
So as you see, the caller doesn't strictly speaking lose access to c (aka self in bye) but it will need to increase the reference count an extra time.
Now, where this really becomes apparent is if we disallow the compiler from making copies/increasing the ref count. Let's consider this slightly modified program (note that C is now struct C: ~Copyable):
struct C: ~Copyable { @inline(never) func bye() {} }
func HARNESS(_ c: consuming C) -> C { c.bye(); return c; }
this gives us
$ echo 'struct C: ~Copyable { @inline(never) func bye() {} }; func HARNESS(_ c: consuming C) -> C { c.bye(); return c; }' | swiftc -O -emit-assembly -module-name T - | swift demangle | grep -A3 ^T.HARNESS
T.HARNESS(__owned T.C) -> T.C:
.cfi_startproc
b function signature specialization <Arg[0] = Dead> of T.C.bye() -> ()
.cfi_endproc
shiny! It compiles and the function does nothing but (tail) calling bye.
But if we now make bye a consuming func we'll get:
$ echo 'struct C: ~Copyable { @inline(never) consuming func bye() {} }; func HARNESS(_ c: consuming C) -> C { c.bye(); return c; }' | swiftc -O -emit-assembly -module-name T - | swift demangle | grep -A3 ^T.HARNESS
<stdin>:1:80: error: 'c' consumed more than once
1 | struct C: ~Copyable { @inline(never) consuming func bye() {} }; func HARNESS(_ c: consuming C) -> C { c.bye(); return c; }
| | | `- note: consumed again here
| | `- note: consumed here
| `- error: 'c' consumed more than once
2 |
because the compiler is now unable to add a defensive copy.
I know this is all quite something but I hope it helps.
FWIW, to figure out the calling conventions (@owned vs @guaranteed etc) that the compiler picked, I'd recommend looking at the SIL instead of the assembly. E.g.
regular func bye takes self (implicit first parameter) as @guaranteed C
$ echo 'class C { func bye() {} }; func HARNESS(_ c: C) -> C { c.bye(); return c; }' | swiftc -O -emit-sil -module-name T - | swift demangle | grep '^sil.*bye'
sil hidden @T.C.bye() -> () : $@convention(method) (@guaranteed C) -> () {
consuming func bye() takes self as @owned C
$ echo 'class C { consuming func bye() {} }; func HARNESS(_ c: C) -> C { c.bye(); return c; }' | swiftc -O -emit-sil -module-name T - | swift demangle | grep '^sil.*bye'
sil hidden @T.C.bye() -> () : $@convention(method) (@owned C) -> () {