From 234e59ceb1aaa210eb10dfa40f2ba91255ee0453 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 12 Aug 2020 22:37:13 +0200 Subject: [PATCH 1/2] Fix #9509: Reveal further arguments in extension method applications They were hidden in an IgnroedProto before for fear that we might need an implicit conversion. But none is possible at this point. --- .../src/dotty/tools/dotc/typer/Applications.scala | 11 +++++++++-- tests/pos/i9509.scala | 11 +++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i9509.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 5329c29ce0b3..25a0f5e110ad 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -2086,6 +2086,13 @@ trait Applications extends Compatibility { * where comes from `pt` if it is a (possibly ignored) PolyProto. */ def extMethodApply(methodRef: untpd.Tree, receiver: Tree, pt: Type)(using Context): Tree = { + // (1) always reveal further arguments in extension method applications, + // (2) but do not reveal anything else in a prototype. + // (1) is needed for i9509.scala (2) is needed for i6900.scala + val normPt = pt match + case IgnoredProto(ignored: FunProto) => ignored + case _ => pt + /** Integrate the type arguments from `currentPt` into `methodRef`, and produce * a matching expected type. * If `currentPt` is ignored, the new expected type will be ignored too. @@ -2097,9 +2104,9 @@ trait Applications extends Compatibility { val core = untpd.TypeApply(methodRef, targs.map(untpd.TypedSplice(_))) (core, if (wasIgnored) IgnoredProto(restpe) else restpe) case _ => - (methodRef, pt) + (methodRef, normPt) } - val (core, pt1) = integrateTypeArgs(pt) + val (core, pt1) = integrateTypeArgs(normPt) val app = withMode(Mode.SynthesizeExtMethodReceiver) { typed(untpd.Apply(core, untpd.TypedSplice(receiver) :: Nil), pt1, ctx.typerState.ownedVars) } diff --git a/tests/pos/i9509.scala b/tests/pos/i9509.scala new file mode 100644 index 000000000000..77273b1e3763 --- /dev/null +++ b/tests/pos/i9509.scala @@ -0,0 +1,11 @@ +enum AList[+A] { + case Cons(head: A, tail: AList[A]) + case Nil +} + +object AList { + extension [A](l: AList[A]) def sum(using numeric: Numeric[A]): A = l match { + case Cons(x, xs) => numeric.plus(x, xs.sum(using numeric)) + case Nil => numeric.zero + } +} \ No newline at end of file From 826ffb3a24f0c91e4cf8c886e344c258552396e8 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 13 Aug 2020 19:26:25 +0200 Subject: [PATCH 2/2] extMethodApply: refactor prototype handling Previously, both `normPt` and `integrateTypeArgs` could tweak the expected type, this commit merges them together in one `normalizePt` method which should be slightly more general. Also updated i9509 to make the invariance of the leaf case explicit (since we might change the desugaring of enums to preserve the parent variance by default). --- .../dotty/tools/dotc/typer/Applications.scala | 32 ++++++++----------- tests/pos/i9509.scala | 4 +-- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 25a0f5e110ad..511219a4f2df 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -2086,27 +2086,23 @@ trait Applications extends Compatibility { * where comes from `pt` if it is a (possibly ignored) PolyProto. */ def extMethodApply(methodRef: untpd.Tree, receiver: Tree, pt: Type)(using Context): Tree = { - // (1) always reveal further arguments in extension method applications, - // (2) but do not reveal anything else in a prototype. - // (1) is needed for i9509.scala (2) is needed for i6900.scala - val normPt = pt match - case IgnoredProto(ignored: FunProto) => ignored - case _ => pt - - /** Integrate the type arguments from `currentPt` into `methodRef`, and produce - * a matching expected type. - * If `currentPt` is ignored, the new expected type will be ignored too. + /** Integrate the type arguments (if any) from `currentPt` into `tree`, and produce + * an expected type that hides the appropriate amount of information through IgnoreProto. */ - def integrateTypeArgs(currentPt: Type, wasIgnored: Boolean = false): (untpd.Tree, Type) = currentPt match { - case IgnoredProto(ignored) => - integrateTypeArgs(ignored, wasIgnored = true) + def normalizePt(tree: untpd.Tree, currentPt: Type): (untpd.Tree, Type) = currentPt match + // Always reveal expected arguments to guide inference (needed for i9509.scala) + case IgnoredProto(ignored: FunOrPolyProto) => + normalizePt(tree, ignored) + // Always hide expected member to allow for chained extensions (needed for i6900.scala) + case _: SelectionProto => + (tree, IgnoredProto(currentPt)) case PolyProto(targs, restpe) => - val core = untpd.TypeApply(methodRef, targs.map(untpd.TypedSplice(_))) - (core, if (wasIgnored) IgnoredProto(restpe) else restpe) + val tree1 = untpd.TypeApply(tree, targs.map(untpd.TypedSplice(_))) + normalizePt(tree1, restpe) case _ => - (methodRef, normPt) - } - val (core, pt1) = integrateTypeArgs(normPt) + (tree, currentPt) + + val (core, pt1) = normalizePt(methodRef, pt) val app = withMode(Mode.SynthesizeExtMethodReceiver) { typed(untpd.Apply(core, untpd.TypedSplice(receiver) :: Nil), pt1, ctx.typerState.ownedVars) } diff --git a/tests/pos/i9509.scala b/tests/pos/i9509.scala index 77273b1e3763..e713242244b6 100644 --- a/tests/pos/i9509.scala +++ b/tests/pos/i9509.scala @@ -1,5 +1,5 @@ enum AList[+A] { - case Cons(head: A, tail: AList[A]) + case Cons[X](head: X, tail: AList[X]) extends AList[X] case Nil } @@ -8,4 +8,4 @@ object AList { case Cons(x, xs) => numeric.plus(x, xs.sum(using numeric)) case Nil => numeric.zero } -} \ No newline at end of file +}