diff --git a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala index b5469610fa74..24060bdd6afd 100644 --- a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala +++ b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala @@ -133,6 +133,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr if (shouldBeSpecialized(original)) { val direct = directMethod(original) + // TODO also adapt this code to account for nested implicits def splitClosure(tree: Tree): (List[Type] => List[List[Tree]] => Tree, Tree) = tree match { case Block(Nil, expr) => splitClosure(expr) case Block((meth @ DefDef(nme.ANON_FUN, Nil, clparams :: Nil, _, _)) :: Nil, cl: Closure) => @@ -150,6 +151,9 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr .appliedToArgss(vparamSymss.map(_.map(ref(_))) :+ clparamSyms.map(ref(_))) val fwdClosure = cpy.Block(tree)(cpy.DefDef(meth)(rhs = forwarder) :: Nil, cl) (remappedCore, fwdClosure) + + // for nested implicits give up for now! + case b @ Block(defs, cl: Closure) => (_ => _ => b, b) case EmptyTree => (_ => _ => EmptyTree, EmptyTree) } diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index d4a9744e4faf..7ea9b9c22d8c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1568,27 +1568,73 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case _ => typedUnadapted(desugar(tree), pt) } - if (defn.isImplicitFunctionType(pt) && - xtree.isTerm && - !untpd.isImplicitClosure(xtree) && - !ctx.isAfterTyper) - makeImplicitFunction(xtree, pt) - else xtree match { - case xtree: untpd.NameTree => typedNamed(encodeName(xtree), pt) - case xtree => typedUnnamed(xtree) + etaExpandImplicitFuns(xtree, pt) match { + case Some(tree) => typedUnadapted(tree) + case None => xtree match { + case xtree: untpd.NameTree => typedNamed(encodeName(xtree), pt) + case xtree => typedUnnamed(xtree) + } } } } + protected def etaExpandImplicitFuns(xtree: untpd.Tree, pt: Type)(implicit ctx: Context): Option[untpd.Tree] = { + + var modified = false + + def loop(xtree: untpd.Tree, pt: Type): untpd.Tree = { + val isImplicitFunType = !ctx.isAfterTyper && defn.isImplicitFunctionType(pt) + val isImplicitClosure = untpd.isImplicitClosure(xtree) + + if (isImplicitFunType && xtree.isTerm && isImplicitClosure) { + + // peel away a layer of the implicit function type: + val defn.FunctionOf(formals, resType, true) = pt.dealias + + // peel away a layer of the implicit closure: + etaExpandBody(xtree, resType) + } else if (isImplicitFunType && xtree.isTerm && !isImplicitClosure) { + // peel away a layer of the implicit function type: + val defn.FunctionOf(formals, resType, true) = pt.dealias + modified = true + makeImplicitFunction(loop(xtree, resType), pt) + } else { + xtree + } + } + + // TODO do we cover all the cases already? What's with Closure? + def etaExpandBody(tree: untpd.Tree, pt: Type): untpd.Tree = + unsplice(tree) match { + case fun: untpd.ImplicitFunction => new untpd.ImplicitFunction(fun.args, loop(fun.body, pt)) + + case Block(Nil, expr) => untpd.Block(Nil, etaExpandBody(expr, pt)) + + case Block(DefDef(nme, tps, vps, tpt, body) :: Nil, cl: Closure) => + val body2 = etaExpandBody(body.asInstanceOf[Tree], pt) + untpd.Block(untpd.DefDef(nme, tps, vps, tpt, body2) :: Nil, cl) + + case els => loop(els, pt) + } + + val res = loop(xtree, pt) + + if (modified) { + Some(res) + } else { + None + } + } + protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree = untpd.rename(tree, tree.name.encode) - protected def makeImplicitFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = { + protected def makeImplicitFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): untpd.Tree = { val defn.FunctionOf(formals, resType, true) = pt.dealias val paramTypes = formals.map(fullyDefinedType(_, "implicit function parameter", tree.pos)) val ifun = desugar.makeImplicitFunction(paramTypes, tree) typr.println(i"make implicit function $tree / $pt ---> $ifun") - typedUnadapted(ifun) + ifun } def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ { diff --git a/tests/neg/t2146a.scala b/tests/neg/t2146a.scala new file mode 100644 index 000000000000..706dc458d13c --- /dev/null +++ b/tests/neg/t2146a.scala @@ -0,0 +1,13 @@ +object nestedImplicits { + + trait A + trait B + + def foo: implicit A => implicit B => Int = { implicit b: B => + implicitly[A] + implicitly[B] + 42 + } + + foo(new A{})(new B{}) +} \ No newline at end of file diff --git a/tests/pos/t2146a.scala b/tests/pos/t2146a.scala new file mode 100644 index 000000000000..163e3ee78b69 --- /dev/null +++ b/tests/pos/t2146a.scala @@ -0,0 +1,13 @@ +object nestedImplicits { + + trait A + trait B + + def foo: implicit A => implicit B => Int = { + implicitly[A] + implicitly[B] + 42 + } + + foo(new A{})(new B{}) +} \ No newline at end of file diff --git a/tests/pos/t2146b.scala b/tests/pos/t2146b.scala new file mode 100644 index 000000000000..96df4786e4fe --- /dev/null +++ b/tests/pos/t2146b.scala @@ -0,0 +1,13 @@ +object nestedImplicits { + + trait A + trait B + + def foo: implicit A => implicit B => Int = { implicit a: A => + implicitly[A] + implicitly[B] + 42 + } + + foo(new A{})(new B{}) +} \ No newline at end of file diff --git a/tests/pos/t2146c.scala b/tests/pos/t2146c.scala new file mode 100644 index 000000000000..6cba6554a611 --- /dev/null +++ b/tests/pos/t2146c.scala @@ -0,0 +1,13 @@ +object nestedImplicits { + + trait A + trait B + + def foo: implicit A => implicit B => Int = { implicit a: A => implicit b: B => + implicitly[A] + implicitly[B] + 42 + } + + foo(new A{})(new B{}) +} \ No newline at end of file