Swift's method dispatch

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

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

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

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 [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub 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@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

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 [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub 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@apple.com <mailto:gparker@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 <mailto: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 <mailto: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 <mailto:swift-users@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-users
>
> _______________________________________________
> swift-users mailing list
> swift-users@swift.org <mailto: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

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_rose@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 [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub 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@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

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!

···

Sent from my iPad

On Dec 10, 2017, at 3:41 PM, Jens Persson via swift-users <swift-users@swift.org> wrote:

/Jens

On Sat, Dec 9, 2017 at 1:54 AM, Jordan Rose <jordan_rose@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 [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub 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@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

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@anandabits.com> wrote:

Sent from my iPad

On Dec 10, 2017, at 3:41 PM, Jens Persson via swift-users < > swift-users@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@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 [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub 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@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

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.

Oh, that’s awesome! I didn’t know this works in the latest snapshots. I’m really happy to know this works now.

···

Sent from my iPad

On Dec 10, 2017, at 4:30 PM, Jens Persson <jens@bitcycle.com> wrote:

/Jens

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

Sent from my iPad

On Dec 10, 2017, at 3:41 PM, Jens Persson via swift-users <swift-users@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@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 [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub 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@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

Oh btw Matthew, you wouldn't consider [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub
a bug then?

/Jens

···

On Sun, Dec 10, 2017 at 11:30 PM, Jens Persson via swift-users < swift-users@swift.org> wrote:

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@anandabits.com> > wrote:

Sent from my iPad

On Dec 10, 2017, at 3:41 PM, Jens Persson via swift-users < >> swift-users@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@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 [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub 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@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

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

Oh btw Matthew, you wouldn't consider [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub a bug then?

It does not look like a bug to me. The witness table only contains entries based on type information available at the site of the conformance. IIUC, there is only one witness table per nominal generic type so X<Int> and X<String> will share the same conformance and witness table which obviously can’t use a default implementation which requires T to be Int.

···

Sent from my iPad

On Dec 10, 2017, at 4:32 PM, Jens Persson <jens@bitcycle.com> wrote:

/Jens

On Sun, Dec 10, 2017 at 11:30 PM, Jens Persson via swift-users <swift-users@swift.org> wrote:
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@anandabits.com> wrote:

Sent from my iPad

On Dec 10, 2017, at 3:41 PM, Jens Persson via swift-users <swift-users@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@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 [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub 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@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

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

The bug has been closed (not a bug).

I would appreciate pointers to any thorough documentation on this as I've
only found very brief and incomplete descriptions.
The information from Mathew et al in this discussion and the comments to
the bug-report are the best I've seen so far.

/Jens

···

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

Sent from my iPad

On Dec 10, 2017, at 4:32 PM, Jens Persson <jens@bitcycle.com> wrote:

Oh btw Matthew, you wouldn't consider https://bugs.swift.
org/browse/SR-6564 a bug then?

It does not look like a bug to me. The witness table only contains
entries based on type information available at the site of the
conformance. IIUC, there is only one witness table per nominal generic
type so X<Int> and X<String> will share the same conformance and witness
table which obviously can’t use a default implementation which requires T
to be Int.

/Jens

On Sun, Dec 10, 2017 at 11:30 PM, Jens Persson via swift-users < > swift-users@swift.org> wrote:

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@anandabits.com >> > wrote:

Sent from my iPad

On Dec 10, 2017, at 3:41 PM, Jens Persson via swift-users < >>> swift-users@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@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 [SR-6564] Compiler seems to ignore constraint on generic type parameter · Issue #49114 · apple/swift · GitHub 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@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

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users