Skip to content

Commit f6fb4ec

Browse files
committed
Create implicit closures to math expected implicit functions
When the expected type is an implicit function, create an implicit closure to match it.
1 parent 8c76595 commit f6fb4ec

File tree

8 files changed

+84
-25
lines changed

8 files changed

+84
-25
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ import dotty.tools.dotc.core.Names.TypeName
3939
import scala.annotation.tailrec
4040

4141
class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Map[Symbol, Set[ClassSymbol]])(implicit ctx: Context) extends BackendInterface{
42+
import Symbols.{toDenot, toClassDenot}
43+
// Dotty deviation: Need to (re-)import implicit decorators here because otherwise
44+
// they would be shadowed by the more deeply nested `symHelper` decorator.
45+
4246
type Symbol = Symbols.Symbol
4347
type Type = Types.Type
4448
type Tree = tpd.Tree
@@ -686,8 +690,6 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
686690
else sym.enclosingClass(ctx.withPhase(ctx.flattenPhase.prev))
687691
} //todo is handled specially for JavaDefined symbols in scalac
688692

689-
690-
691693
// members
692694
def primaryConstructor: Symbol = toDenot(sym).primaryConstructor
693695

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,13 @@ object desugar {
124124
else vdef
125125
}
126126

127+
def makeImplicitParameters(tpts: List[Tree], forPrimaryConstructor: Boolean)(implicit ctx: Context) =
128+
for (tpt <- tpts) yield {
129+
val paramFlags: FlagSet = if (forPrimaryConstructor) PrivateLocalParamAccessor else Param
130+
val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName
131+
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit)
132+
}
133+
127134
/** Expand context bounds to evidence params. E.g.,
128135
*
129136
* def f[T >: L <: H : B](params)
@@ -144,11 +151,7 @@ object desugar {
144151
val epbuf = new ListBuffer[ValDef]
145152
val tparams1 = tparams mapConserve {
146153
case tparam @ TypeDef(_, ContextBounds(tbounds, cxbounds)) =>
147-
for (cxbound <- cxbounds) {
148-
val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param
149-
val epname = ctx.freshName(nme.EVIDENCE_PARAM_PREFIX).toTermName
150-
epbuf += ValDef(epname, cxbound, EmptyTree).withFlags(paramFlags | Implicit)
151-
}
154+
epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor)
152155
cpy.TypeDef(tparam)(rhs = tbounds)
153156
case tparam =>
154157
tparam
@@ -676,6 +679,11 @@ object desugar {
676679
Function(param :: Nil, Block(vdefs, body))
677680
}
678681

682+
def makeImplicitFunction(formals: List[Type], body: Tree)(implicit ctx: Context): Tree = {
683+
val params = makeImplicitParameters(formals.map(TypeTree), forPrimaryConstructor = false)
684+
new ImplicitFunction(params, body)
685+
}
686+
679687
/** Add annotation with class `cls` to tree:
680688
* tree @cls
681689
*/

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,16 @@ trait UntypedTreeInfo extends TreeInfo[Untyped] { self: Trees.Instance[Untyped]
290290
case _ => false
291291
}
292292

293+
/** Is `tree` an implicit function or closure, possibly nested in a block? */
294+
def isImplicitClosure(tree: Tree)(implicit ctx: Context): Boolean = unsplice(tree) match {
295+
case Function((param: untpd.ValDef) :: _, _) => param.mods.is(Implicit)
296+
case Closure(_, meth, _) => true
297+
case Block(Nil, expr) => isImplicitClosure(expr)
298+
case Block(DefDef(nme.ANON_FUN, _, (param :: _) :: _, _, _) :: Nil, _: Closure) =>
299+
param.mods.is(Implicit)
300+
case _ => false
301+
}
302+
293303
// todo: fill with other methods from TreeInfo that only apply to untpd.Tree's
294304
}
295305

@@ -507,8 +517,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
507517
}
508518
}
509519

510-
def isClosure(tree: Tree) = closure.unapply(tree).isDefined
511-
512520
/** If tree is a closure, its body, otherwise tree itself */
513521
def closureBody(tree: Tree)(implicit ctx: Context): Tree = tree match {
514522
case Block((meth @ DefDef(nme.ANON_FUN, _, _, _, _)) :: Nil, Closure(_, _, _)) => meth.rhs

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
5555
}
5656

5757
/** An implicit function type */
58-
class ImplicitFunction(args: List[Tree], body: Tree) extends Function(args, body)
58+
class ImplicitFunction(args: List[Tree], body: Tree) extends Function(args, body) {
59+
override def toString = s"ImplicitFunction($args, $body"
60+
}
5961

6062
/** A function created from a wildcard expression
6163
* @param placeHolderParams a list of definitions of synthetic parameters
@@ -274,8 +276,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
274276

275277
// ------ Additional creation methods for untyped only -----------------
276278

277-
// def TypeTree(tpe: Type): TypeTree = TypeTree().withType(tpe) todo: move to untpd/tpd
278-
279279
/** new pre.C[Ts](args1)...(args_n)
280280
* ==>
281281
* (new pre.C).<init>[Ts](args1)...(args_n)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,9 @@ class Definitions {
780780

781781
def functionArity(tp: Type)(implicit ctx: Context) = tp.dealias.argInfos.length - 1
782782

783+
def isImplicitFunctionType(tp: Type)(implicit ctx: Context) =
784+
isFunctionType(tp) && tp.dealias.typeSymbol.name.startsWith(tpnme.ImplicitFunction)
785+
783786
// ----- primitive value class machinery ------------------------------------------
784787

785788
/** This class would also be obviated by the implicit function type design */

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import Inferencing.fullyDefinedType
2929
import Trees._
3030
import Hashable._
3131
import config.Config
32-
import config.Printers.{implicits, implicitsDetailed}
32+
import config.Printers.{implicits, implicitsDetailed, typr}
3333
import collection.mutable
3434

3535
/** Implicit resolution */
@@ -146,7 +146,8 @@ object Implicits {
146146

147147
override val level: Int =
148148
if (outerImplicits == null) 1
149-
else if (ctx.scope eq outerImplicits.ctx.scope) outerImplicits.level
149+
else if ((ctx.owner eq outerImplicits.ctx.owner) &&
150+
(ctx.scope eq outerImplicits.ctx.scope)) outerImplicits.level
150151
else outerImplicits.level + 1
151152

152153
/** The implicit references that are eligible for type `tp`. */
@@ -211,7 +212,7 @@ object Implicits {
211212
* @param ctx The context after the implicit search
212213
*/
213214
case class SearchSuccess(tree: tpd.Tree, ref: TermRef, level: Int, tstate: TyperState) extends SearchResult {
214-
override def toString = s"SearchSuccess($tree, $ref)"
215+
override def toString = s"SearchSuccess($tree, $ref, $level)"
215216
}
216217

217218
/** A failed search */
@@ -733,6 +734,7 @@ trait Implicits { self: Typer =>
733734
case best :: alts =>
734735
alts find (alt => isAsGood(alt.ref, best.ref, alt.level, best.level)(ctx.fresh.setExploreTyperState)) match {
735736
case Some(alt) =>
737+
typr.println(i"ambiguous implicits for $pt: ${best.ref} @ ${best.level}, ${alt.ref} @ ${alt.level}")
736738
/* !!! DEBUG
737739
println(i"ambiguous refs: ${hits map (_.ref) map (_.show) mkString ", "}")
738740
isAsGood(best.ref, alt.ref, explain = true)(ctx.fresh.withExploreTyperState)

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

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
14901490
case tree: untpd.If => typedIf(tree, pt)
14911491
case tree: untpd.Function => typedFunction(tree, pt)
14921492
case tree: untpd.Closure => typedClosure(tree, pt)
1493+
case tree: untpd.Import => typedImport(tree, retrieveSym(tree))
14931494
case tree: untpd.Match => typedMatch(tree, pt)
14941495
case tree: untpd.Return => typedReturn(tree)
14951496
case tree: untpd.Try => typedTry(tree, pt)
@@ -1517,9 +1518,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
15171518
case _ => typedUnadapted(desugar(tree), pt)
15181519
}
15191520

1520-
xtree match {
1521+
if (defn.isImplicitFunctionType(pt) &&
1522+
xtree.isTerm &&
1523+
!untpd.isImplicitClosure(xtree) &&
1524+
!ctx.isAfterTyper)
1525+
makeImplicitFunction(xtree, pt)
1526+
else xtree match {
15211527
case xtree: untpd.NameTree => typedNamed(encodeName(xtree), pt)
1522-
case xtree: untpd.Import => typedImport(xtree, retrieveSym(xtree))
15231528
case xtree => typedUnnamed(xtree)
15241529
}
15251530
}
@@ -1528,6 +1533,14 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
15281533
protected def encodeName(tree: untpd.NameTree)(implicit ctx: Context): untpd.NameTree =
15291534
untpd.rename(tree, tree.name.encode)
15301535

1536+
protected def makeImplicitFunction(tree: untpd.Tree, pt: Type)(implicit ctx: Context): Tree = {
1537+
val defn.FunctionOf(formals, resType, true) = pt.dealias
1538+
val paramTypes = formals.map(fullyDefinedType(_, "implicit function parameter", tree.pos))
1539+
val ifun = desugar.makeImplicitFunction(paramTypes, tree)
1540+
typr.println(i"make implicit function $tree / $pt ---> $ifun")
1541+
typedUnadapted(ifun)
1542+
}
1543+
15311544
def typed(tree: untpd.Tree, pt: Type = WildcardType)(implicit ctx: Context): Tree = /*>|>*/ ctx.traceIndented (i"typing $tree", typr, show = true) /*<|<*/ {
15321545
assertPositioned(tree)
15331546
try adapt(typedUnadapted(tree, pt), pt, tree)
@@ -1874,16 +1887,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
18741887
err.typeMismatch(tree, pt)
18751888
else
18761889
missingArgs
1877-
case wtp: RefinedType
1878-
if defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) &&
1879-
!isClosure(tree) &&
1880-
!isApplyProto(pt) &&
1881-
!ctx.isAfterTyper =>
1882-
typr.println("insert apply on implicit $tree")
1883-
typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
18841890
case _ =>
18851891
ctx.typeComparer.GADTused = false
1886-
if (ctx.mode is Mode.Pattern) {
1892+
if (defn.isImplicitFunctionClass(wtp.underlyingClassRef(refinementOK = false).classSymbol) &&
1893+
!untpd.isImplicitClosure(tree) &&
1894+
!isApplyProto(pt) &&
1895+
!ctx.isAfterTyper) {
1896+
typr.println("insert apply on implicit $tree")
1897+
typed(untpd.Select(untpd.TypedSplice(tree), nme.apply), pt)
1898+
}
1899+
else if (ctx.mode is Mode.Pattern) {
18871900
tree match {
18881901
case _: RefTree | _: Literal
18891902
if !isVarPattern(tree) &&

tests/pending/run/implicitFuns.scala renamed to tests/run/implicitFuns.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,31 @@ object Test {
1515

1616
val y: String => Boolean = x
1717

18+
object nested {
19+
implicit val empty: String = ""
20+
assert(!x)
21+
}
22+
1823
val yy: (String, Int) => Any = xx
1924

25+
val z1: implicit String => Boolean = implicitly[String].length >= 2
26+
assert(z1)
27+
28+
type StringlyBool = implicit String => Boolean
29+
30+
val z2: StringlyBool = implicitly[String].length >= 2
31+
assert(z2)
32+
33+
type Stringly[T] = implicit String => T
34+
35+
val z3: Stringly[Boolean] = implicitly[String].length >= 2
36+
assert(z3)
37+
38+
type GenericImplicit[X] = implicit X => Boolean
39+
40+
val z4: GenericImplicit[String] = implicitly[String].length >= 2
41+
assert(z4)
42+
2043
val b = x("hello")
2144

2245
val b1: Boolean = b

0 commit comments

Comments
 (0)