Skip to content

Fix #4611: Change implicit function types rewriting rules #4621

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
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
Expand Down Expand Up @@ -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
}
Expand Down
23 changes: 23 additions & 0 deletions tests/pos/i4611.scala
Original file line number Diff line number Diff line change
@@ -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
}
}
28 changes: 28 additions & 0 deletions tests/run/i4611.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}