Skip to content

Extension methods can't be chained when the the first has an explicit type parameter but no other parameters #6900

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
travisbrown opened this issue Jul 21, 2019 · 1 comment

Comments

@travisbrown
Copy link
Contributor

minimized code

given [A] { def (a: A) foo[C]: C => A = _ => a }

1.foo[String].foo[String]

Fails to compile with:

1 |1.foo[String].foo[String]
  |^^^^^
  |value foo is not a member of Int - did you mean Int(1).+?.
  |An extension method was tried, but could not be fully constructed:
  |
  |    foo_of_A_given[Int].foo[String](1)

expectation

It should compile.

Note that defining an intermediate val works:

scala> val result = 1.foo[String]
val result: String => Int = foo_of_A_given$$Lambda$10912/547292714@e0b8c6a

scala> result.foo[String]
val res0: String => String => Int = foo_of_A_given$$Lambda$10912/547292714@6038a356

As does letting C be inferred:

scala> 1.foo.foo
val res1: Any => Any => Int = foo_of_A_given$$Lambda$10913/181900017@5642dd8c

It's also just fine if you add an empty parameter list:

scala> given [A] { def (a: A) bar[C](): C => A = _ => a }
// defined class bar_of_A_given

scala> 1.bar[String]().bar[String]()
val res2: String => String => Int = bar_of_A_given$$Lambda$10914/798714466@2c6c2f2e
smarter added a commit to smarter/dotty that referenced this issue Jul 21, 2019
smarter added a commit to dotty-staging/dotty that referenced this issue Jul 21, 2019
Given a tree `qual.foo`, when we typecheck `qual` we now that the result
must have a member named `foo`, if it doesn't we'll try to adapt it
through implicit conversion / extension method selection to something
which does have such a member.

Given a tree `qual.foo.bar`, the same process happens, but when we
perform the implicit conversion on `qual` we actually know more about
the member `foo`: it must have a member named `bar`, however in general
we cannot use this information: this member might actually require
another conversion and we don't chain conversions as a rule. Therefore,
we need to ignore the expected type of the member, this is implemented
by `ProtoTypes#selectionProto` wrapping the expected member type in
`IgnoredProto`.

However, the following failed before this commit:

    given bla[A] { def (a: A) foo[C]: C => A = _ => a }
    1.foo[String].foo[String]

This happens because when `Applications#extMethodApply` extracts the
type arguments of the extension method from its expected type, it might
end up unpealing an `IgnoredProto`. The solution is to rewrap the new
expeted type into an `IgnoredProto` if the original was ignored.
smarter added a commit to dotty-staging/dotty that referenced this issue Jul 21, 2019
Given a tree `qual.foo`, when we typecheck `qual` we now that the result
must have a member named `foo`, if it doesn't we'll try to adapt it
through implicit conversion / extension method selection to something
which does have such a member.

Given a tree `qual.foo.bar`, the same process happens, but when we
perform the implicit conversion on `qual` we actually now more about
the member `foo`: it must have a member named `bar`, however in general
we cannot use this information: this member might actually require
another conversion and we don't chain conversions as a rule. Therefore,
we need to ignore the expected type of the member, this is implemented
by `ProtoTypes#selectionProto` wrapping the expected member type in
`IgnoredProto`.

However, the following failed before this commit:

    given bla[A] { def (a: A) foo[C]: C => A = _ => a }
    1.foo[String].foo[String]

This happens because when `Applications#extMethodApply` extracts the
type arguments of the extension method from its expected type, it might
end up unpealing an `IgnoredProto`. The solution is to rewrap the new
expeted type into an `IgnoredProto` if the original was ignored.
smarter added a commit to dotty-staging/dotty that referenced this issue Jul 21, 2019
Given a tree `qual.foo`, when we typecheck `qual` we now that the result
must have a member named `foo`, if it doesn't we'll try to adapt it
through implicit conversion / extension method selection to something
which does have such a member.

Given a tree `qual.foo.bar`, the same process happens, but when we
perform the implicit conversion on `qual` we actually now more about
the member `foo`: it must have a member named `bar`. However in general
we cannot use this information: this member might actually require
another conversion and we don't chain conversions as a rule. Therefore,
we need to ignore the expected type of the member, this is implemented
by `ProtoTypes#selectionProto` wrapping the expected member type in
`IgnoredProto`.

However, the following failed before this commit:

    given bla[A] { def (a: A) foo[C]: C => A = _ => a }
    1.foo[String].foo[String]

This happens because when `Applications#extMethodApply` extracts the
type arguments of the extension method from its expected type, it might
end up unpealing an `IgnoredProto`. The solution is to rewrap the new
expeted type into an `IgnoredProto` if the original was ignored.
smarter added a commit to dotty-staging/dotty that referenced this issue Jul 21, 2019
Given a tree `qual.foo`, when we typecheck `qual` we now that the result
must have a member named `foo`, if it doesn't we'll try to adapt it
through implicit conversion / extension method selection to something
which does have such a member.

Given a tree `qual.foo.bar`, the same process happens, but when we
perform the conversion on `qual` we actually now more about the member
`foo`: it must have a member named `bar`. However in general we cannot
use this information: this member might actually require another
conversion and as a rule we don't chain conversions. Therefore, we need
to ignore the expected type of `foo`, this is implemented by
`ProtoTypes#selectionProto` wrapping the expected member type in
`IgnoredProto`.

However, the following failed before this commit:

    given bla[A] { def (a: A) foo[C]: C => A = _ => a }
    1.foo[String].foo[String]

This happens because when `Applications#extMethodApply` extracts the
type arguments of the extension method from its expected type, it might
end up unpealing an `IgnoredProto`. The solution is to rewrap the new
expeted type into an `IgnoredProto` if the original was ignored.
smarter added a commit to dotty-staging/dotty that referenced this issue Jul 21, 2019
Given a tree `qual.foo`, when we typecheck `qual` we now that the result
must have a member named `foo`, if it doesn't we'll try to adapt it
through implicit conversion / extension method selection to something
which does have such a member.

Given a tree `qual.foo.bar`, the same process happens, but when we
perform the conversion on `qual` we actually now more about the member
`foo`: it must have a member named `bar`. However in general we cannot
use this information: this member might actually require another
conversion and as a rule we don't chain conversions. Therefore, we need
to ignore the expected type of `foo`, this is implemented by
`ProtoTypes#selectionProto` wrapping the expected member type in
`IgnoredProto`.

However, the following failed before this commit:

    given bla[A] { def (a: A) foo[C]: C => A = _ => a }
    1.foo[String].foo[String]

This happens because when `Applications#extMethodApply` extracts the
type arguments of the extension method from its expected type, it might
end up unpealing an `IgnoredProto`. The solution is to rewrap the new
expeted type into an `IgnoredProto` if the original was ignored.
@smarter
Copy link
Member

smarter commented Jul 21, 2019

Thanks for the report! PR and explanations of what happened here: #6901

odersky added a commit that referenced this issue Jul 22, 2019
Fix #6900: Support chained polymorphic extension methods
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants