Also the witness. The point is that within a P context, id refers to the protocol requirement P.id.
These are concrete instances, and they use static dispatch. The implementations in extensions on P and Q are both “visible” on a concrete instance, and they are treated as overloads. The one that comes from extension Q is “more specific”, so it is used.
This is not a generic context, so no dynamic dispatch occurs.
In a generic context, protocol requirements are dynamically dispatched through witness tables. That is how Swift generics work.
So a.id is looked up in the witness table for the conformance of T to Q. That table includes an entry for id, since it is a requirement of P and Q refines P.
From an implementation perspective, the witness table for Q has a reference to the witness table for P, and finds the requirement id there.
If instead the witness table for Q had its own separate entry for the id requirement, which can be achieved with the undocumented, unsupported, underscored attribute @_nonoverride:
protocol Q: P {
@_nonoverride var id: String { get }
}
Then a type like Y would provide different implementations for its P.id and Q.id witnesses. This means that the left side of line (5) now prints "Q", because it uses the witness for Q.id.
However, the right side of line (5) still prints "P", because id2 comes from a P context and thus calls the witness for P.id.
Note however that if we include the @_nonoverride line in Q, and also reimplement id2 in an extension of Q, then, finally, the program will print "Q" in all 6 spots.
Even then, though, we don’t get the fully dynamic behavior we are used to from class inheritance. For example, in a context expecting a superclass, calling a member that is overridden by a subclass will dispatch to the subclass implementation.
Conversely, with our @_nonoverride shenanigans, in a context expecting a type conforming to P, accessing id will still use the witness for the P.id requirement, even if the type actually conforms to Q as well.

mattrips:
[ UPDATE : Ah, yes. The id requirement is declared on P . The requirement is inherited by Q . But, for witness table/dispatch purposes, the requirement remains designated as a requirement of P . Is that correct?
Right, the protocol P has a requirement id. The protocol Q inherits it without change (unless redeclared as @_nonoverride, in which case both requirements exist side-by-side and get used in different generic contexts).

mattrips:
But, then, that takes me to your last sentence:
And as we saw above, the witnesses for X and Z print "Q", but the witness for Y prints "P".
I'm not sure what you mean. That sentence seems to be incorrect. The left-hand column for the first three examples printed "Q" in all cases, and did not print "P" as the witness for Y . How do you explain the "P" for the left-hand side of example 5?
]
Thank you again for your time and insight.
In the first three examples, the left-hand column does not use protocol witnesses at all. It uses static overload resolution to choose the most specific match available.
It is the right side of the first three examples that uses the protocol witness for id, within the implementation for id2, as I explained previously.
2-right is dynamically dispatched from a generic context (the implementation of id2 in extension P), and thus uses the protocol witness for the conforming type.
2-left is statically dispatched from a concrete instance, and thus uses the most-specific overload available.