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