Skip to content

Commit 35d425d

Browse files
committed
Fix #6900: Support chained polymorphic extension methods
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.
1 parent 54b08e1 commit 35d425d

File tree

2 files changed

+22
-4
lines changed

2 files changed

+22
-4
lines changed

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,13 +1837,23 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
18371837
* <methodRef>(<receiver>) or
18381838
* <methodRef>[<type-args>](<receiver>)
18391839
*
1840-
* where <type-args> comes from `pt` if it is a PolyProto.
1840+
* where <type-args> comes from `pt` if it is a (possibly ignored) PolyProto.
18411841
*/
18421842
def extMethodApply(methodRef: untpd.Tree, receiver: Tree, pt: Type)(implicit ctx: Context) = {
1843-
val (core, pt1) = pt.revealIgnored match {
1844-
case PolyProto(targs, restpe) => (untpd.TypeApply(methodRef, targs.map(untpd.TypedSplice(_))), restpe)
1845-
case _ => (methodRef, pt)
1843+
/** Integrate the type arguments from `currentPt` into `methodRef`, and produce
1844+
* a matching expected type.
1845+
* If `currentPt` is ignored, the new expected type will be ignored too.
1846+
*/
1847+
def integrateTypeArgs(currentPt: Type, wasIgnored: Boolean = false): (untpd.Tree, Type) = currentPt match {
1848+
case IgnoredProto(ignored) =>
1849+
integrateTypeArgs(ignored, wasIgnored = true)
1850+
case PolyProto(targs, restpe) =>
1851+
val core = untpd.TypeApply(methodRef, targs.map(untpd.TypedSplice(_)))
1852+
(core, if (wasIgnored) IgnoredProto(restpe) else restpe)
1853+
case _ =>
1854+
(methodRef, pt)
18461855
}
1856+
val (core, pt1) = integrateTypeArgs(pt)
18471857
val app =
18481858
typed(untpd.Apply(core, untpd.TypedSplice(receiver) :: Nil), pt1, ctx.typerState.ownedVars)(
18491859
ctx.addMode(Mode.SynthesizeExtMethodReceiver))

tests/pos/i6900.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
given bla[A] { def (a: A) foo[C]: C => A = _ => a }
3+
4+
1.foo.foo
5+
1.foo.foo[String]
6+
1.foo[String].foo
7+
1.foo[String].foo[String]
8+
}

0 commit comments

Comments
 (0)