[swift-users] Weird protocol behaviour.

Slava Pestov spestov at apple.com
Fri Dec 30 18:21:36 CST 2016


When you call foo<A : P>(_: A) with a concrete type C, the type checker performs a conformance check of C to P. If C is a protocol type, the conformance check fails. Ie, unlike the subtype relation (roughly, “I can assign a value of this type to that”), the conformance relation (“I can substitute this for a generic parameter conforming to”) is not reflexive.

Swift compiles generic code in a way where values of generic type have the most efficient representation in memory — you pay a CPU cost because they’re still manipulated indirectly (unlike C++ templates, which are cloned and inlined at compile time), but there’s no representational cost as in Java’s erased generics for example.

What this means is that all types that you can substitute for a generic parameter have to be interchangeable in some basic sense.

The main problem is with class-bound protocols:

protocol CP : class {
  func foo()
  func bar()
}

And a function taking an array of CP’s:

func foo<T : CP>(t: [T]) {}

All concrete types that conform to a class-bound protocol have a single retainable-pointer representation, so the function takes an array of pointers.

However, a value of protocol type ‘CP’ is represented as the underlying concrete value — a pointer to a class instance conforming to CP — together with the conformance table that stores the function pointers for the implementations of ‘foo’ and ‘bar’.

So unlike in Java, where method implementations for an interface are stored “inside” the instance, in Swift protocol requirement implementations are always passed ‘on the side’.

With opaque protocols, a type parameter has variable size, so there’s no representational issue. Instead you just get a double indirection.

Say you have

protocol P {}
func foo<T : P>(t: [T]) {}

If you call foo with the substitution P := P, then you’re passing in an array of values *where each value has its own conformance to P*. But foo() itself wants the conformance P : P. So when you call a protocol requirement on an element of ’t’. you will first call a method in the ‘dummy’ conformance P : P, which will unpack the “real” concrete type from the value of type P and call the right method on that.

So we could implement self-conformance for non-class-bound protocols. For class-bound protocols, I’m not sure how to do it efficiently.

Slava

> On Dec 30, 2016, at 8:26 AM, Mikhail Seriukov <zloisop at gmail.com> wrote:
> 
> So as the foo<A:P>(_ x:A) function is generic, when we call foo(x) compiler needs to determine what type is A to be able to create concrete function. But x defined as let x = X() as P so we only know about it that it conforms to P but not its real type to put instead of A. Right?
> But where is the "Protocols do not conform to themselves" limitation is coming out?
> 
> 2016-12-30 18:25 GMT+07:00 Rien <Rien at balancingrock.nl <mailto:Rien at balancingrock.nl>>:
> 
> > On 30 Dec 2016, at 12:14, Mikhail Seriukov via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
> >
> > Ok,
> > But I think I still do not get it.
> > What does really happen when we write this?
> >> let x = X() as P
> >>
> 
> 'X()' creates a value.
> 'as P’ constrains the value such that the only things we know about it is that the value will conform to the protocol P
> ‘let x =‘ assigns the value to a constant, and the only thing we know about that constant is that we can call an operation of protocol P on it.
> 
> Rien.
> 
> > As I said, I expect x to be Any<P> after that. If it is, then it should be ok IMO.
> > But if it is not then what is the actual type of x?
> >
> > So the real question is how the type checker works here?
> >
> >
> > 2016-12-25 22:13 GMT+07:00 Slava Pestov <spestov at apple.com <mailto:spestov at apple.com>>:
> >
> >> On Dec 22, 2016, at 4:43 PM, Howard Lovatt via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
> >>
> >> The following variation works:
> >>
> >> protocol P {}
> >>
> >> class P1:P {}
> >>
> >> class X:P1 {}
> >>
> >> func foo<A:P>(_ x:A) {}
> >>
> >> func bar() {
> >>     //let x = X() // this compiles
> >>     let x = X() as P1 // this does not compile. Why?
> >>     foo(x)
> >> }
> >>
> >> Which adds credence to the bug theory.
> >
> > It’s an intentional limitation. Protocols do not conform to themselves. Lifting the restriction would be difficult to do efficiently given our representation of generics and protocols at runtime.
> >
> > Slava
> >
> >>
> >> Note two changes: 1. two levels of inheritance and 2. change to classes. If you do two levels using protocols it doesn't work if you use either classes or structs.
> >>
> >>
> >>   -- Howard.
> >>
> >> On 23 December 2016 at 07:29, Kevin Nattinger <swift at nattinger.net <mailto:swift at nattinger.net>> wrote:
> >> I recall seeing a request on the -evolution list for something like `T := X` to indicate it could be X itself or anything inheriting / implementing it, so it’s certainly known behavior, if not desired. IMO it’s a bug and `:` should be fixed to include the root type, whether or not that requires a discussion on -evolution.
> >>
> >>> On Dec 22, 2016, at 2:17 PM, Howard Lovatt via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
> >>>
> >>> I suspect a compiler bug since A is a P. The equivalent in Java works:
> >>>
> >>> interface P {}
> >>> class X implements P {}
> >>>
> >>> <A extends P> void foo(A x) {}
> >>>
> >>> void bar() {
> >>>     final P x = new X();
> >>>     foo(x);
> >>> }
> >>>
> >>> -- Howard.
> >>>
> >>> On 23 Dec 2016, at 3:19 am, Rien via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
> >>>
> >>>> IMO the error message says it all:
> >>>>
> >>>> Playground execution failed: error: MyPlayground8.playground:9:5: error: cannot invoke 'foo' with an argument list of type '(P)'
> >>>>    foo(x)
> >>>>    ^
> >>>>
> >>>> MyPlayground8.playground:9:5: note: expected an argument list of type '(A)'
> >>>>    foo(x)
> >>>>    ^
> >>>>
> >>>> I.e. you are passing in a protocol while the function is specified for a type.
> >>>> Said other way: On which data do you expect the protocol to operate?
> >>>>
> >>>> Regards,
> >>>> Rien
> >>>>
> >>>> Site: http://balancingrock.nl <http://balancingrock.nl/>
> >>>> Blog: http://swiftrien.blogspot.com <http://swiftrien.blogspot.com/>
> >>>> Github: http://github.com/Swiftrien <http://github.com/Swiftrien>
> >>>> Project: http://swiftfire.nl <http://swiftfire.nl/>
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>> On 22 Dec 2016, at 17:05, Mikhail Seriukov via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> wrote:
> >>>>>
> >>>>> Hello community! I' wondering if somebody can explain this to me.
> >>>>> Please take look at the snippet.
> >>>>>
> >>>>> protocol P {}
> >>>>> struct X:P {}
> >>>>>
> >>>>> func foo<A:P>(_ x:A) {}
> >>>>>
> >>>>> func bar() {
> >>>>>    //let x = X() // this compiles
> >>>>>    let x = X() as P // this does not compile. Why?
> >>>>>    foo(x)
> >>>>> }
> >>>>>
> >>>>> I expect the both cases to work though. But only first works? And I do not understand why.
> >>>>> My coworkers said that it is a compiler bug, but I'm not shure it is.
> >>>>> Thanks for the help.
> >>>>> _______________________________________________
> >>>>> swift-users mailing list
> >>>>> swift-users at swift.org <mailto:swift-users at swift.org>
> >>>>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
> >>>>
> >>>> _______________________________________________
> >>>> swift-users mailing list
> >>>> swift-users at swift.org <mailto:swift-users at swift.org>
> >>>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
> >>> _______________________________________________
> >>> swift-users mailing list
> >>> swift-users at swift.org <mailto:swift-users at swift.org>
> >>> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
> >>
> >>
> >> _______________________________________________
> >> swift-users mailing list
> >> swift-users at swift.org <mailto:swift-users at swift.org>
> >> https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
> >
> >
> > _______________________________________________
> > swift-users mailing list
> > swift-users at swift.org <mailto:swift-users at swift.org>
> > https://lists.swift.org/mailman/listinfo/swift-users <https://lists.swift.org/mailman/listinfo/swift-users>
> 
> 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20161230/e6410790/attachment.html>


More information about the swift-users mailing list