Skip to content

Commit 2f85c86

Browse files
authored
Merge pull request #2235 from dotty-staging/fix-#2146
Fix #2146: Make nested implicit function types work
2 parents f1c3529 + c148301 commit 2f85c86

File tree

7 files changed

+68
-15
lines changed

7 files changed

+68
-15
lines changed

compiler/src/dotty/tools/dotc/core/NameKinds.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ object NameKinds {
283283
val ProtectedAccessorName = new PrefixNameKind(PROTECTEDACCESSOR, "protected$")
284284
val ProtectedSetterName = new PrefixNameKind(PROTECTEDSETTER, "protected$set") // dubious encoding, kept for Scala2 compatibility
285285
val AvoidClashName = new SuffixNameKind(AVOIDCLASH, "$_avoid_name_clash_$")
286-
val DirectName = new SuffixNameKind(DIRECT, "$direct")
286+
val DirectMethodName = new SuffixNameKind(DIRECT, "$direct") { override def definesNewName = true }
287287
val FieldName = new SuffixNameKind(FIELD, "$$local")
288288
val ExtMethName = new SuffixNameKind(EXTMETH, "$extension")
289289
val ModuleVarName = new SuffixNameKind(OBJECTVAR, "$module")

compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import core.Decorators._
1111
import core.StdNames.nme
1212
import core.Names._
1313
import core.NameOps._
14-
import core.NameKinds.DirectName
14+
import core.NameKinds.DirectMethodName
1515
import ast.Trees._
1616
import ast.tpd
1717
import collection.mutable
@@ -77,6 +77,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr
7777
private def shouldBeSpecialized(sym: Symbol)(implicit ctx: Context) =
7878
sym.is(Method, butNot = Accessor) &&
7979
defn.isImplicitFunctionType(sym.info.finalResultType) &&
80+
!sym.isAnonymousFunction &&
8081
(specializeMonoTargets || !sym.isEffectivelyFinal || sym.allOverriddenSymbols.nonEmpty)
8182

8283
/** @pre The type's final result type is an implicit function type `implicit Ts => R`.
@@ -92,7 +93,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr
9293
/** A new `m$direct` method to accompany the given method `m` */
9394
private def newDirectMethod(sym: Symbol)(implicit ctx: Context): Symbol = {
9495
val direct = sym.copy(
95-
name = DirectName(sym.name.asTermName).asInstanceOf[sym.ThisName],
96+
name = DirectMethodName(sym.name.asTermName).asInstanceOf[sym.ThisName],
9697
flags = sym.flags | Synthetic,
9798
info = directInfo(sym.info))
9899
if (direct.allOverriddenSymbols.isEmpty) direct.resetFlag(Override)
@@ -104,14 +105,13 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr
104105
*/
105106
private def directMethod(sym: Symbol)(implicit ctx: Context): Symbol =
106107
if (sym.owner.isClass) {
107-
val direct = sym.owner.info.member(DirectName(sym.name.asTermName))
108+
val direct = sym.owner.info.member(DirectMethodName(sym.name.asTermName))
108109
.suchThat(_.info matches directInfo(sym.info)).symbol
109110
if (direct.maybeOwner == sym.owner) direct
110111
else newDirectMethod(sym).enteredAfter(thisTransform)
111112
}
112113
else directMeth.getOrElseUpdate(sym, newDirectMethod(sym))
113114

114-
115115
/** Transform `qual.apply` occurrences according to rewrite rule (2) above */
116116
override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) =
117117
if (tree.name == nme.apply &&
@@ -122,7 +122,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr
122122
case TypeApply(fn, args) => cpy.TypeApply(tree)(directQual(fn), args)
123123
case Block(stats, expr) => cpy.Block(tree)(stats, directQual(expr))
124124
case tree: RefTree =>
125-
cpy.Ref(tree)(DirectName(tree.name.asTermName))
125+
cpy.Ref(tree)(DirectMethodName(tree.name.asTermName))
126126
.withType(directMethod(tree.symbol).termRef)
127127
}
128128
directQual(tree.qualifier)
@@ -157,8 +157,8 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisTr
157157

158158
val (remappedCore, fwdClosure) = splitClosure(mdef.rhs)
159159
val originalDef = cpy.DefDef(mdef)(rhs = fwdClosure)
160-
val directDef = polyDefDef(direct.asTerm, remappedCore)
161-
Thicket(originalDef, directDef)
160+
val directDef = transformDefDef(polyDefDef(direct.asTerm, remappedCore))
161+
flatTree(List(originalDef, directDef))
162162
}
163163
else mdef
164164
}

compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,8 @@ object ProtoTypes {
423423

424424
/** The normalized form of a type
425425
* - unwraps polymorphic types, tracking their parameters in the current constraint
426-
* - skips implicit parameters; if result type depends on implicit parameter,
427-
* replace with Wildcard.
426+
* - skips implicit parameters of methods and functions;
427+
* if result type depends on implicit parameter, replace with fresh type dependent parameter.
428428
* - converts non-dependent method types to the corresponding function types
429429
* - dereferences parameterless method types
430430
* - dereferences nullary method types provided the corresponding function type
@@ -438,9 +438,10 @@ object ProtoTypes {
438438
*/
439439
def normalize(tp: Type, pt: Type)(implicit ctx: Context): Type = Stats.track("normalize") {
440440
tp.widenSingleton match {
441-
case poly: PolyType => normalize(constrained(poly).resultType, pt)
441+
case poly: PolyType =>
442+
normalize(constrained(poly).resultType, pt)
442443
case mt: MethodType =>
443-
if (mt.isImplicit) resultTypeApprox(mt)
444+
if (mt.isImplicit) normalize(resultTypeApprox(mt), pt)
444445
else if (mt.isDependent) tp
445446
else {
446447
val rt = normalize(mt.resultType, pt)
@@ -452,8 +453,11 @@ object ProtoTypes {
452453
if (mt.paramInfos.nonEmpty || ft <:< pt) ft else rt
453454
}
454455
}
455-
case et: ExprType => et.resultType
456-
case _ => tp
456+
case et: ExprType =>
457+
normalize(et.resultType, pt)
458+
case wtp =>
459+
if (defn.isImplicitFunctionType(wtp)) normalize(wtp.dealias.argInfos.last, pt)
460+
else tp
457461
}
458462
}
459463

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1582,7 +1582,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
15821582
val paramTypes = formals.map(fullyDefinedType(_, "implicit function parameter", tree.pos))
15831583
val ifun = desugar.makeImplicitFunction(paramTypes, tree)
15841584
typr.println(i"make implicit function $tree / $pt ---> $ifun")
1585-
typedUnadapted(ifun)
1585+
typed(ifun, pt)
15861586
}
15871587

15881588
def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ {

tests/neg/i2146.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
case class A()
3+
case class B()
4+
5+
def foo[A, B]: implicit A => implicit B => Int = { implicit b: B =>
6+
42 // error: found Int, required: implicit A => implicit B => Int
7+
}
8+
}

tests/pos/i2146.check

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
(A(),B())
2+
(A(),B())
3+
(A(),B())
4+
A()
5+
(A(),B())
6+
(A(),B())
7+
(A(),B())
8+
(A(),B())
9+
(A(),B())

tests/pos/i2146.scala

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
object Test {
2+
case class A()
3+
case class B()
4+
5+
def simple[A]: implicit A => A = implicitly[A]
6+
7+
def foo[A, B]: implicit A => implicit B => (A, B) =
8+
(implicitly[A], implicitly[B])
9+
10+
def bar[A, B]: implicit A => implicit B => (A, B) = { implicit a: A =>
11+
(implicitly[A], implicitly[B])
12+
}
13+
14+
implicit val a: A = A()
15+
implicit val b: B = B()
16+
17+
def main(args: Array[String]) = {
18+
println(foo[A, B])
19+
println(foo[A, B](a))
20+
println(foo(a)(b))
21+
val s: implicit A => A = simple[A]
22+
println(s)
23+
val x0: implicit A => implicit B => (A, B) = foo[A, B]
24+
println(x0)
25+
val x1: implicit B => (A, B) = foo[A, B]
26+
println(x1)
27+
28+
println(bar[A, B])
29+
println(bar[A, B](a))
30+
println(bar(a)(b))
31+
}
32+
}

0 commit comments

Comments
 (0)