But that wouldn’t allow surfacing a func type(named: String) Standard Library 
function that worked on any type, wouldn’t it (like the Dictionary with 
non-Hashable key)?

> On 6 Dec 2017, at 00:42, John McCall via swift-dev <swift-dev@swift.org> 
> wrote:
> 
>> On Dec 5, 2017, at 5:28 PM, Douglas Gregor via swift-dev 
>> <swift-dev@swift.org> wrote:
>> Hi all,
>> 
>> The main missing piece for conditional conformances 
>> (https://github.com/apple/swift-evolution/blob/master/proposals/0143-conditional-conformances.md)
>>  is support for dynamic casting. Right now, dynamic casting that queries a 
>> conditional conformance will always fail. Here’s an example:
>> 
>> protocol P {
>>   func foo()
>> }
>> 
>> struct X : P {
>>   func foo() { print("X.P") }
>> }
>> 
>> struct Y<T> {
>>   var wrapped: T
>> }
>> 
>> extension Y: P where T: P {
>>   func foo() { wrapped.foo() }
>> }
>> 
>> func tryAsP(_ value: Any) {
>>   if let p = value as? P {
>>     p.foo()
>>   }
>> }
>> 
>> let yx = Y(wrapped: X())
>> tryAsP(yx)
>> 
>> This won’t print anything, but should print “X.P”. We’d like to fix that :)
>> 
>> Joe Groff, Slava, and I discussed an approach to implement dynamic casting 
>> for conditional conformances, which I’d like to outline here.
>> 
>> Background
>> The Swift runtime expresses the conformance of a specific type (say 
>> Array<Int>) to a particular protocol (say, Equatable) using a witness table. 
>> Witness tables contain the actual associated types as well as function 
>> pointers for each of the requirements of a protocol. Slava and John’s talk 
>> on Implementing Swift Generics (https://llvm.org/devmtg/2017-10/#talk15) 
>> gives a bunch of background here.
>> 
>> When a conformance is conditional, the witness table also stores the 
>> conditional requirements. So, the witness table for Array<Int>: Equatable 
>> has to store the witness table Int: Equatable. In the example above, the 
>> witness table for Y<X>: P needs to store a pointer to the witness table X: 
>> P. The compiler knows how to pass along the witness tables needed for a 
>> conditional conformance, but for runtime casting to work, we need to (1) 
>> evaluate all of the conditional requirements to determine whether they apply 
>> (e.g., does T: P for a specific T?) and then (2) pass those witness tables 
>> along when building the witness table. The cast should fail if any of the 
>> conditional requirements goes unsatisfied, and produce a valid witness table 
>> otherwise.
>> 
>> Protocol Conformance Records
>> Whenever code declares conformance of a particular type to a given protocol, 
>> the compiler emits a “protocol conformance record” (documented at 
>> https://github.com/apple/swift/blob/master/docs/ABI/TypeMetadata.rst#protocol-conformance-records).
>>  These protocol conformance records state the type declaring conformance 
>> (e.g., Y) and the protocol (e.g., P), and then provide a witness table 
>> accessor to form the witness table given type metadata for a specific type 
>> instance (e.g., Y<X>). In the case of a conditional conformance, this 
>> accessor also needs to be passed the witness tables for any conditional 
>> requirements, e.g., the witness table for X: P. 
>> 
>> Conditional Requirements in Protocol Conformance Records
>> Somehow we need to dynamically evaluate the conditional requirements. For 
>> example, we need to know that for the X<T>: P conformance to be valid, we 
>> need T: P to hold for the given T. So, we’ll extend the protocol conformance 
>> record with an encoding of those requirements. Fortunately, we already have 
>> a way to encode arbitrary generic requirements: the mangling of a 
>> generic-signature (see 
>> https://github.com/apple/swift/blob/master/docs/ABI/Mangling.rst#generics) 
>> already encodes arbitrary generic requirements, so we can put a string 
>> comprised of the mangled conditional requirements into the protocol 
>> conformance record.
>> 
>> When evaluating a conditional conformance, we demangle the conditional 
>> requirements to get something like: “T must conform to P”. We then need to 
>> substitute the type arguments (e.g., X) for the  corresponding type 
>> parameters (T) to form type metadata for the requirements. In this case, 
>> we’d get the type metadata pointer for Y and the protocol descriptor for P, 
>> and then call swift_conformsToProtocol() to determine whether the 
>> requirement holds. If it does, swift_conformsToProtocol() produces the Y: P 
>> witness table we need to pass along to the witness table accessor.
>> 
>> Note that the conditional requirements can be arbitrarily complicated. For 
>> example, given:
>> 
>> extension Dictionary: P where Value == (Key) -> Bool { … }
>> 
>> Even though the result of evaluating this same-type requirement doesn’t need 
>> to be passed along to the witness table accessor, we still need to evaluate 
>> the requirement to determine whether the conditional conformance to P 
>> applies. To do so, we will need to substitute type arguments for both Value 
>> and Key, and need to form the type metadata representing the (substituted) 
>> function type (Key) -> Bool to determine if it is equivalent to the 
>> (substituted) type of Value (which is then determined by type metadata 
>> pointer equality because the runtime uniques type metadata). 
>> 
>> Looking up Type Metadata For a Mangled Name
>> To perform that mapping from a mangled type in a conditional requirement to 
>> type metadata, we effectively need an operation to take a mangled type name 
>> and turn it into a type metadata pointer. This is something we could surface 
>> in the Swift standard library/runtime as, e.g.,
>> 
>> func type(named: String) -> Any.Type?
>> 
>> to take a mangled type name and try to get the type metadata for it. From 
>> there, one can query protocol conformances that (say) allow one to construct 
>> instances of the arbitrarily-named type. Think of it as NSClassFromString 
>> for any type in the Swift language, including specializations of generic 
>> types.
>> 
>> To get here, we’ll also need to extend the nominal type descriptor 
>> (https://github.com/apple/swift/blob/master/docs/ABI/TypeMetadata.rst#nominal-type-descriptor)
>>  to contain the generic signature of a nominal type. That let’s us safely 
>> create specializations of generic types. For example, if one tries to form 
>> Dictionary<A, B> where A does not conform to Hashable, type(named:) should 
>> return “nil” rather than an invalid type. The same logic that will be used 
>> to form type metadata when checking conditional requirements will apply 
>> here. Indeed, it’s probably worth proposing type(named:) as a separate 
>> language feature on the path to conditional conformances.
>> 
>> Looking Forward: Generalized Existentials
>> Generalized existentials 
>> (https://github.com/apple/swift/blob/master/docs/GenericsManifesto.md#generalized-existentials)
>>  allows us to express more kinds of “existential” type in the language, 
>> e.g., “A Collection of some type of Element that we know is Equatable”, e.g.,
>> 
>> var a: Any<Collection where .Element: Equatable>
>> a = [1, 2, 3, 4, 5]
>> a = Set([“a”, “b”, “c”])
>> 
>> To get there, we can change (or extend) the protocol metadata 
>> (https://github.com/apple/swift/blob/master/docs/ABI/TypeMetadata.rst#protocol-metadata)
>>  to contain a complete generic signature, which can be evaluated dynamically 
>> using the same mechanisms described above. For example:
>> 
>> func f(any: Any) {
>>   if let c = any as? Any<Collection where .Element: Equatable> { … }
>> }
>> 
>> We should make the metadata change to allow this form of generalized 
>> existentials before locking down the ABI, even if we don’t settle on the 
>> feature in the surface language.
>> 
>> Thoughts?
> 
> Couldn't we just encode a metadata path for the associated type and then do 
> the conformance lookup?
> 
> John.
> _______________________________________________
> swift-dev mailing list
> swift-dev@swift.org
> https://lists.swift.org/mailman/listinfo/swift-dev
_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev

Reply via email to