@Douglas_Gregor, for your consideration sometime after the WWDC crush ends:
Regarding conformance of a type to a progressively refined protocol hierarchy, it appears that the Protocol Witness Table Pattern for a conditional conformance to a "child" protocol disregards the default implementations declared by that protocol.
For the sake of clarity, here is a minimum example:
protocol P { var id: Int { get } }
extension P { var id: Int { 1 } }
protocol Q: P {}
extension Q { var id: Int { 2 } }
struct X<T>: P {}
extension X: Q where T: Equatable {}
func callId<T: Q>(_ t: T) -> Int { t.id } // dispatches to `P.id` even thought T: Q
print( callId( X<Int>() ) ) // "1", not "2"
In the resultant binary, the Protocol Witness Table Pattern for X: Q
does not point to Q.id
as the witness for the id
requirement. AFAICS, the table pattern for X: Q
solely points to the protocol witness table for X: P
.
excerpts from binary
_$s4main1XVyxGAA1QAASQRzlWp:
// protocol witness table pattern
// for <A where A: Swift.Equatable> main.X<A> : main.Q in main
0000000100002030 db 0xe4 ; '.' << first word points to Q protocol descriptor
0000000100002031 db 0x1e ; '.'
0000000100002032 db 0x00 ; '.'
0000000100002033 db 0x00 ; '.'
0000000100002034 db 0x01 ; '.'
0000000100002035 db 0x00 ; '.'
0000000100002036 db 0x00 ; '.'
0000000100002037 db 0x00 ; '.'
0000000100002038 db 0x20 ; ' ' << second word points to PWT for X: P
0000000100002039 db 0x20 ; ' '
000000010000203a db 0x00 ; '.'
000000010000203b db 0x00 ; '.'
000000010000203c db 0x01 ; '.'
000000010000203d db 0x00 ; '.'
000000010000203e db 0x00 ; '.'
000000010000203f db 0x00 ; '.'
_$s4main1XVyxGAA1PAAWP:
// protocol witness table for main.X<A> : main.P in main
0000000100002020 db 0x18 ; '.' << points to protocol conf descriptor for X: P
0000000100002021 db 0x1e ; '.'
0000000100002022 db 0x00 ; '.'
0000000100002023 db 0x00 ; '.'
0000000100002024 db 0x01 ; '.'
0000000100002025 db 0x00 ; '.'
0000000100002026 db 0x00 ; '.'
0000000100002027 db 0x00 ; '.'
0000000100002028 db 0x60 ; '`' << points to P.id.getter witness
0000000100002029 db 0x19 ; '.'
000000010000202a db 0x00 ; '.'
000000010000202b db 0x00 ; '.'
000000010000202c db 0x01 ; '.'
000000010000202d db 0x00 ; '.'
000000010000202e db 0x00 ; '.'
000000010000202f db 0x00 ; '.'
When calling into the Q
-constrained generic context of callId<T: Q>(_ t: T) -> Int
, the assembly code correctly selects and passes an instantiated X: Q
Protocol Witness Table. But, since the table doesn't point at the Q.id
witness, the call to id
is dispatched to the P.id
witness.
excerpt from assembly code
_main:
push rbp
mov rbp, rsp
sub rsp, 0x20
mov rax, qword [_$sSiN_100002008] ; _$sSiN_100002008
mov dword [rbp+var_4], edi
mov rdi, rax ; argument #1 for method X.init
mov qword [rbp+var_10], rsi
call _$s4main1XVACyxGycfC ; main.X.init() -> main.X<A>
xor ecx, ecx
mov edi, ecx ; argument #1 for method _$s4main1XVySiGMa
call _$s4main1XVySiGMa ; type metadata accessor for main.X<Swift.Int>
mov qword [rbp+var_18], rax
mov qword [rbp+var_20], rdx
call _$s4main1XVySiGACyxGAA1QAASQRzlWl ; lazy protocol witness table accessor
; for type X<Int> and conformance
; <A where A: Equatable> X<A> : main.Q
mov rsi, qword [rbp+var_18] ; argument #2 for method main.callId
mov rdx, rax ; argument #3 for method main.callId
call _$s4main6callIdySixAA1QRzlF ; callId<A where A: main.Q>(A) -> Int
xor ecx, ecx
mov qword [_$s4main6resultSivp], rax ; _$s4main6resultSivp
mov eax, ecx
add rsp, 0x20
pop rap
ret
Yes, the X: Q
conformance is conditional, but since the table is used only in contexts in which the condition is satisfied, the table should (?) point to Q
's implementations.
Am I misunderstanding?