From 1369658b990f98808d8be71b306a183397f2833d Mon Sep 17 00:00:00 2001 From: Allan Renucci Date: Tue, 5 Jun 2018 14:46:42 +0200 Subject: [PATCH 1/2] Fix #4611: Change implicit function types rewriting rules An abstract method definition def m(xs: Ts): IF is expanded to: def m(xs: Ts): IF = implicit (ys: Us) => m$direct(xs)(ys) def m$direct(xs: Ts)(ys: Us): R An overriding method definition override def m(xs: Ts): IF = implicit (ys: Us) => E is expanded to: def m$direct(xs: Ts)(ys: Us): R = E --- .../dotc/transform/ShortcutImplicits.scala | 38 ++++++++++++++++--- tests/pos/i4611a.scala | 24 ++++++++++++ tests/pos/i4611b.scala | 19 ++++++++++ 3 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 tests/pos/i4611a.scala create mode 100644 tests/pos/i4611b.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala index 3cc8b68233d0..1e59cf4c7791 100644 --- a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala +++ b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala @@ -34,12 +34,20 @@ import collection.mutable * (and equivalently for methods with type parameters or a different number of value parameter lists). * An abstract method definition * - * def m(xs: Ts): IF + * def m(xs: Ts): IF * * is expanded to: * - * def m(xs: Ts): IF - * def m$direct(xs: Ts)(ys: Us): R + * def m(xs: Ts): IF = implicit (ys: Us) => m$direct(xs)(ys) + * def m$direct(xs: Ts)(ys: Us): R + * + * An overriding method definition + * + * override def m(xs: Ts): IF = implicit (ys: Us) => E + * + * is expanded to: + * + * def m$direct(xs: Ts)(ys: Us): R = E * * (2) A reference `qual.apply` where `qual` has implicit function type and * `qual` refers to a method `m` is rewritten to a reference to `m$direct`, @@ -164,13 +172,31 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPh val fwdClosure = cpy.Block(tree)(cpy.DefDef(meth)(rhs = forwarder) :: Nil, cl) (remappedCore, fwdClosure) case EmptyTree => - (_ => _ => EmptyTree, EmptyTree) + original.resetFlag(Deferred) // original is no longer abstract + val tpe = original.info.finalResultType.dealias.asInstanceOf[AppliedType] + val methTpe = ImplicitMethodType(tpe.args.init, tpe.args.last) + val tparams = mdef.tparams.map(tp => ref(tp.symbol)) + val vparams = mdef.vparamss.map(_.map(vp => ref(vp.symbol))) + def forwarder(args: List[Tree]) = ref(direct) + .appliedToTypeTrees(tparams) + .appliedToArgss(vparams) + .appliedToArgs(args) + val fwdClosure = Lambda(methTpe, forwarder(_))(ctx.withOwner(original)) + (_ => _ => EmptyTree, fwdClosure) } val (remappedCore, fwdClosure) = splitClosure(mdef.rhs) - val originalDef = cpy.DefDef(mdef)(rhs = fwdClosure) val directDef = transformDefDef(polyDefDef(direct.asTerm, remappedCore)) - flatTree(List(originalDef, directDef)) + if (original.allOverriddenSymbols.exists(!_.is(Deferred))) { + // if original overrides something we only generate + // the direct method and remove original + original.owner.asClass.delete(original) + directDef + } + else { + val originalDef = cpy.DefDef(mdef)(rhs = fwdClosure) + flatTree(List(originalDef, directDef)) + } } else mdef } diff --git a/tests/pos/i4611a.scala b/tests/pos/i4611a.scala new file mode 100644 index 000000000000..8175c5d2cd20 --- /dev/null +++ b/tests/pos/i4611a.scala @@ -0,0 +1,24 @@ +import scala.concurrent.Future + +class Response +class Request +object Request { + type To[T] = implicit Request => T +} + +trait Responder[T] { + def responseFor(value: T): Request.To[Future[Response]] +} + +object Responder { + // with SAM + val responseResponder: Responder[Response] = + response => Future.successful(response) + + // with anonymous class + val futureResponseResponder: Responder[Future[Response]] = new Responder[Future[Response]] { + override def responseFor(value: Future[Response]): Request.To[Future[Response]] = + value + } + +} diff --git a/tests/pos/i4611b.scala b/tests/pos/i4611b.scala new file mode 100644 index 000000000000..d724b009ec6e --- /dev/null +++ b/tests/pos/i4611b.scala @@ -0,0 +1,19 @@ +class A +class B +class C + +trait Foo { + def foo: implicit A => implicit B => C +} + +class FooImpl extends Foo { + def foo: implicit A => implicit B => C = new C +} + +trait Bar extends Foo { + def bar: implicit A => implicit B => C = foo +} + +class BarImpl { + def foo = new C +} From 937a923adc32524b7db9501cd10184cfac0a5819 Mon Sep 17 00:00:00 2001 From: Allan Renucci Date: Tue, 5 Jun 2018 15:05:14 +0200 Subject: [PATCH 2/2] WIP --- .../dotc/transform/ShortcutImplicits.scala | 2 +- tests/pos/{i4611a.scala => i4611.scala} | 1 - tests/pos/i4611b.scala | 19 ------------- tests/run/i4611.scala | 28 +++++++++++++++++++ 4 files changed, 29 insertions(+), 21 deletions(-) rename tests/pos/{i4611a.scala => i4611.scala} (99%) delete mode 100644 tests/pos/i4611b.scala create mode 100644 tests/run/i4611.scala diff --git a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala index 1e59cf4c7791..a1900a3f5e7f 100644 --- a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala +++ b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala @@ -187,7 +187,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPh val (remappedCore, fwdClosure) = splitClosure(mdef.rhs) val directDef = transformDefDef(polyDefDef(direct.asTerm, remappedCore)) - if (original.allOverriddenSymbols.exists(!_.is(Deferred))) { + if (original.allOverriddenSymbols.nonEmpty) { // if original overrides something we only generate // the direct method and remove original original.owner.asClass.delete(original) diff --git a/tests/pos/i4611a.scala b/tests/pos/i4611.scala similarity index 99% rename from tests/pos/i4611a.scala rename to tests/pos/i4611.scala index 8175c5d2cd20..4d82fd7368a1 100644 --- a/tests/pos/i4611a.scala +++ b/tests/pos/i4611.scala @@ -20,5 +20,4 @@ object Responder { override def responseFor(value: Future[Response]): Request.To[Future[Response]] = value } - } diff --git a/tests/pos/i4611b.scala b/tests/pos/i4611b.scala deleted file mode 100644 index d724b009ec6e..000000000000 --- a/tests/pos/i4611b.scala +++ /dev/null @@ -1,19 +0,0 @@ -class A -class B -class C - -trait Foo { - def foo: implicit A => implicit B => C -} - -class FooImpl extends Foo { - def foo: implicit A => implicit B => C = new C -} - -trait Bar extends Foo { - def bar: implicit A => implicit B => C = foo -} - -class BarImpl { - def foo = new C -} diff --git a/tests/run/i4611.scala b/tests/run/i4611.scala new file mode 100644 index 000000000000..034d7dd9cd18 --- /dev/null +++ b/tests/run/i4611.scala @@ -0,0 +1,28 @@ +class A +class B + +trait Foo { + def foo: implicit A => implicit B => Int +} + +class Foo1 extends Foo { + def foo: implicit A => implicit B => Int = 1 +} + +class Foo2 extends Foo1 { + override def foo: implicit A => implicit B => Int = 2 +} + +trait Foo3 extends Foo { + override def foo: implicit A => implicit B => Int = 3 +} + +object Test { + def main(args: Array[String]): Unit = { + implicit val a = new A + implicit val b = new B + assert((new Foo1).foo == 1) + assert((new Foo2).foo == 2) + assert(new Foo3{}.foo == 3) + } +}