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 {
    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?

let y = Y(a: X<Int>())
y.g() // What will this print?

If anyone knows for sure what this program will print (without having to
run it), please enlighten me!

/Jens


On Sat, Dec 9, 2017 at 1:54 AM, Jordan Rose <jordan_r...@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@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 <gpar...@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@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@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@swift.org
>> >> https://lists.swift.org/mailman/listinfo/swift-users
>> >
>> > _______________________________________________
>> > swift-users mailing list
>> > swift-users@swift.org
>> > https://lists.swift.org/mailman/listinfo/swift-users
>>
>>
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org
> https://lists.swift.org/mailman/listinfo/swift-users
>
>
>
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Reply via email to