recently i think it has become fashionable to jettison traditional allergies to swift existentials, which made me wonder if i could get away with this pattern:
public
func f1(items:[Int], b:Bool) -> Int
{
let foldable:any Foldable
if b
{
foldable = B.init(repeating: items)
}
else
{
foldable = A.init(items: items)
}
return foldable.folded()
}
(why do this in a real code base? because the .folded() call might be complex and i would rather not repeat it in every branch.)
i compared it against the “proper” factorization, which looks like:
public
func f2(items:[Int], b:Bool) -> Int
{
if b
{
let foldable:B = .init(repeating: items)
return foldable.folded()
}
else
{
let foldable:A = .init(items: items)
return foldable.folded()
}
}
according to godbolt, the spelling that uses the local existential is still going through all the existential boxing and witness lookups that a fully dynamic existential would go through.
lea rbx, [rip + (full type metadata for output.B)+16]
mov qword ptr [rsp + 24], rbx
lea r14, [rip + (protocol witness table for output.B : output.Foldable in output)]
mov qword ptr [rsp + 32], r14
mov qword ptr [rsp], rax
jmp .LBB6_3
.LBB6_2:
lea rbx, [rip + (full type metadata for output.A)+16]
mov qword ptr [rsp + 24], rbx
lea r14, [rip + (protocol witness table for output.A : output.Foldable in output)]
mov qword ptr [rsp + 32], r14
mov qword ptr [rsp], r15
mov rdi, r15
call swift_retain@PLT
.LBB6_3:
mov r15, rsp
mov rdi, r15
mov rsi, rbx
call __swift_project_boxed_opaque_existential_1
mov rdi, rbx
mov rsi, r14
mov r13, rax
call ((extension in output):output.Foldable.folded() -> Swift.Int)
mov rbx, rax
mov rdi, r15
call __swift_destroy_boxed_opaque_existential_1
mov rax, rbx
add rsp, 40
pop rbx
pop r13
pop r14
pop r15
ret
i checked it on 5.9 and also on main. any reason why the compiler can’t do this optimization?