[swift-users] Swift's method dispatch

Jens Persson jens at bitcycle.com
Sun Dec 10 16:30:07 CST 2017


Thank you Matthew, I will try to digest and incorporate your explanation.

I'm using a recent snapshot where
struct X<T> : P {
    func f() { print("this one is actually best") }
}
compiles fine without requiring that type alias.


/Jens


On Sun, Dec 10, 2017 at 10:52 PM, Matthew Johnson <matthew at anandabits.com>
wrote:

>
>
> Sent from my iPad
>
> On Dec 10, 2017, at 3:41 PM, Jens Persson via swift-users <
> swift-users at swift.org> wrote:
>
> I'm trying to get my head around the current behavior, but its very hard
> to understand and remember, and judging by the comments here and on my bug
> report (SR-6564), so does even people in the core team. It would be nice if
> someone could present a complete set of rules (all the ones I've seen are
> far to simplified and does not cover all cases).
> Here's another example to consider:
>
> protocol P {
>     associatedtype T
>     func f() // *
> }
> extension P {
>     func f() { print("T is unknown") }
> }
> extension P where T == Int {
>     func f() { print("T is Int") }
> }
>
> struct X<T> : P {
>
>
> FWIW, this does not compile.  You need to provide a typealias for T which
> you can’t do when the generic parameter is named T.
>
>     func f() { print("this one is actually best") }
> }
> extension X where T == Int {
>     func f() { print("this one is actually better than best.") }
> }
>
> struct Y<U> where U: P, U.T == Int {
>     typealias T = U.T
>     var a: U
>     func g() { a.f() }
> }
>
> let x = X<Int>()
> x.f() // What will this print?
>
>
> This prints “this one is actually better than best.” because the method
> is invoked on a concrete type.  Overload resolution is used to identify the
> most specific implementation which in this case is the method in the
> concrete extension on X.
>
>
> let y = Y(a: X<Int>())
> y.g() // What will this print?
>
>
> This prints ”this one is actually best”.  This is because the method is
> called in a generic context and is a protocol requirement.  This means it
> is dispatched through the protocol witness table.  The methods in the
> extensions on P are default implementations which are disregarded because X
> provides its own implementation.  The overload in the extension on X is not
> visible at all in a generic context because it does not participate in X’s
> conformance to P.
>
>
> If anyone knows for sure what this program will print (without having to
> run it), please enlighten me!
>
>
> I hope the above helps.  If you have further questions please ask!
>
>
> /Jens
>
>
> On Sat, Dec 9, 2017 at 1:54 AM, Jordan Rose <jordan_rose at apple.com> wrote:
>
>> Consider this example:
>>
>> protocol P {
>>     associatedtype T
>>     func f() // *
>> }
>> extension P {
>>     func f() { print("T is unknown") }
>> }
>> extension P where T == Int {
>>     func f() { print("T is Int") }
>> }
>>
>> struct X<T> : P {
>>     func f() { print("this one is actually best") }
>> }
>>
>> struct Y<U> where U: P, U.T == Int {
>>     typealias T = U.T
>>     var a: U
>>     func g() { a.f() }
>> }
>>
>> let x = X<Int>()
>> x.f() // "this one is actually best"
>>
>> let y = Y(a: X<Int>())
>> y.g() // "this one is actually best"
>>
>>
>> I can't think of any other choice for 'a.f()' that would preserve this
>> behavior, which means we're doing the best thing: prefer dynamic dispatch
>> over static dispatch when operating on a generic value. (Or at least the
>> "least bad" thing.) And of course this reasoning has to be local; Y.g can't
>> look and say "oh, I know nothing else conforms to P, and X {does, doesn't}
>> have a custom implementation, so I should pick the constrained extension
>> instead".
>>
>> The real answer might be "we should have had a different syntax for
>> default implementations vs. mixin operations", but that's a much bigger can
>> of worms.
>>
>> Jordan
>>
>>
>> On Dec 8, 2017, at 13:07, Jens Persson via swift-users <
>> swift-users at swift.org> wrote:
>>
>> Thanks Slava and Greg,
>>
>> (
>> I'm aware that it prints "T is Int" from both calls if I remove func f()
>> from P itself, that's why I wrote "... unless * is commented out." in the
>> comment of the last line
>> Note that the "U.T == Int"-part of
>>   struct Y<U> where U: P, U.T == Int {
>> is key here. If it had been only
>>   struct Y<U> where U: P {
>> then I hadn't been surprised that it printed "T is unknown".
>> )
>>
>> Filed https://bugs.swift.org/browse/SR-6564 since I think it is just
>> strange that the compiler should not use its knowledge of U.T == Int when
>> choosing between the two f()-implementations.
>> I think I will be a little disappointed if the solution is to deem it an
>> ambiguity
>> : )
>>
>> /Jens
>>
>>
>>
>> On Fri, Dec 8, 2017 at 9:19 PM, Greg Parker <gparker at apple.com> wrote:
>>
>>> Evidence in favor of Slava's analysis: if you remove `func f()` from P
>>> itself, leaving it in the extensions only, then you get "T is Int" from
>>> both calls.
>>>
>>>
>>> > On Dec 8, 2017, at 12:12 PM, Slava Pestov via swift-users <
>>> swift-users at swift.org> wrote:
>>> >
>>> > Hi Jens,
>>> >
>>> > I think the problem is that overload ranking always prefers a protocol
>>> requirement to a protocol extension member, because usually you want the
>>> dynamic dispatch through the requirement instead of calling the default
>>> implementation. But it appears that this heuristic does not take into
>>> account the fact that the protocol extension member could be more
>>> constrained than the requirement.
>>> >
>>> > Please file a bug, but it is unclear what the desired behavior
>>> actually is here. Perhaps it should just diagnose an ambiguity.
>>> >
>>> > Slava
>>> >
>>> >> On Dec 8, 2017, at 6:25 AM, Jens Persson via swift-users <
>>> swift-users at swift.org> wrote:
>>> >>
>>> >> Hi all!
>>> >>
>>> >> Can someone please explain the rationale behind the last line printing
>>> >> "T is unknown"
>>> >> rather than (what I would expect):
>>> >> "T is Int"
>>> >> in the following program?
>>> >>
>>> >>
>>> >> protocol P {
>>> >>    associatedtype T
>>> >>    func f() // *
>>> >> }
>>> >> extension P {
>>> >>    func f() { print("T is unknown") }
>>> >> }
>>> >> extension P where T == Int {
>>> >>    func f() { print("T is Int") }
>>> >> }
>>> >>
>>> >> struct X<T> : P {}
>>> >>
>>> >> struct Y<U> where U: P, U.T == Int {
>>> >>    // NOTE: The compiler/type-checker knows that U.T == Int here so
>>> ...
>>> >>    typealias T = U.T
>>> >>    var a: U
>>> >>    func g() { a.f() } // ... how/why could this print anything but "T
>>> is Int"?
>>> >> }
>>> >>
>>> >> let x = X<Int>()
>>> >> x.f() // Prints "T is Int", no matter if * is commented out or not.
>>> >>
>>> >> let y = Y(a: X<Int>())
>>> >> y.g() // Prints "T is unknown" unless * is commented out. Why?
>>> >>
>>> >>
>>> >> IMHO this looks like the compiler simply ignores that struct Y<U> has
>>> the constraint  U.T == Int.
>>> >> How else to explain this behavior?
>>> >> /Jens
>>> >>
>>> >> _______________________________________________
>>> >> swift-users mailing list
>>> >> swift-users at swift.org
>>> >> https://lists.swift.org/mailman/listinfo/swift-users
>>> >
>>> > _______________________________________________
>>> > swift-users mailing list
>>> > swift-users at swift.org
>>> > https://lists.swift.org/mailman/listinfo/swift-users
>>>
>>>
>> _______________________________________________
>> swift-users mailing list
>> swift-users at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-users
>>
>>
>>
> _______________________________________________
> swift-users mailing list
> swift-users at swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20171210/47199819/attachment.html>


More information about the swift-users mailing list