diff --git a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala index 3cc8b68233d0..a1900a3f5e7f 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.nonEmpty) { + // 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/i4611.scala b/tests/pos/i4611.scala new file mode 100644 index 000000000000..4d82fd7368a1 --- /dev/null +++ b/tests/pos/i4611.scala @@ -0,0 +1,23 @@ +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/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) + } +}