Skip to content

Commit dc6b2c9

Browse files
committed
Add MacroTransform that allows implicit search
Also, drop redundant clause in existing MacroTransform
1 parent d3b6438 commit dc6b2c9

File tree

8 files changed

+108
-13
lines changed

8 files changed

+108
-13
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,15 @@ object Contexts {
359359
else if (untpd.isSuperConstrCall(stat) && this.owner.isClass) superCallContext
360360
else ctx.fresh.setOwner(exprOwner)
361361

362+
/** A new context that summarizes an import statement */
363+
def importContext(imp: Import[_], sym: Symbol) = {
364+
val impNameOpt = imp.expr match {
365+
case ref: RefTree[_] => Some(ref.name.asTermName)
366+
case _ => None
367+
}
368+
ctx.fresh.setImportInfo(new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt))
369+
}
370+
362371
/** The current source file; will be derived from current
363372
* compilation unit.
364373
*/

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ object Phases {
276276
* and type applications.
277277
*/
278278
def relaxedTyping: Boolean = false
279+
280+
/** If set, implicit search is enabled */
281+
def allowsImplicitSearch: Boolean = false
282+
279283
/** List of names of phases that should precede this phase */
280284
def runsAfter: Set[Class[_ <: Phase]] = Set.empty
281285

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ abstract class MacroTransform extends Phase {
4141
def transformStats(trees: List[Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = {
4242
def transformStat(stat: Tree): Tree = stat match {
4343
case _: Import | _: DefTree => transform(stat)
44-
case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat)
4544
case _ => transform(stat)(ctx.exprContext(stat, exprOwner))
4645
}
4746
flatten(trees.mapconserve(transformStat(_)))
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import typer._
6+
import ast.Trees._
7+
import Contexts._
8+
import Symbols._
9+
import Decorators._
10+
import collection.mutable
11+
import annotation.tailrec
12+
13+
/** A Macrotransform that maintains the necessary infrastructore to support
14+
* contxtual implicit searches (type-scope implicits are supported anyway).
15+
*/
16+
abstract class MacroTransformWithImplicits extends MacroTransform {
17+
import ast.tpd._
18+
19+
override def allowsImplicitSearch = true
20+
21+
class ImplicitsTransformer extends Transformer {
22+
23+
/** Transform statements, while maintaining import contexts and expression contexts
24+
* in the same way as Typer does. The code addresses additional concerns:
25+
* - be tail-recursive where possible
26+
* - don't re-allocate trees where nothing has changed
27+
*/
28+
override def transformStats(stats: List[Tree], exprOwner: Symbol)(implicit ctx: Context): List[Tree] = {
29+
30+
@tailrec def traverse(curStats: List[Tree])(implicit ctx: Context): List[Tree] = {
31+
32+
def recur(stats: List[Tree], changed: Tree, rest: List[Tree])(implicit ctx: Context): List[Tree] = {
33+
if (stats eq curStats) {
34+
val rest1 = transformStats(rest, exprOwner)
35+
changed match {
36+
case Thicket(trees) => trees ::: rest1
37+
case tree => tree :: rest1
38+
}
39+
}
40+
else stats.head :: recur(stats.tail, changed, rest)
41+
}
42+
43+
curStats match {
44+
case stat :: rest =>
45+
val statCtx = stat match {
46+
case stat: DefTree => ctx
47+
case _ => ctx.exprContext(stat, exprOwner)
48+
}
49+
val restCtx = stat match {
50+
case stat: Import => ctx.importContext(stat, stat.symbol)
51+
case _ => ctx
52+
}
53+
val stat1 = transform(stat)(statCtx)
54+
if (stat1 ne stat) recur(stats, stat1, rest)(restCtx)
55+
else traverse(rest)(restCtx)
56+
case nil =>
57+
stats
58+
}
59+
}
60+
traverse(stats)
61+
}
62+
63+
private def nestedScopeCtx(defs: List[Tree])(implicit ctx: Context): Context = {
64+
val nestedCtx = ctx.fresh.setNewScope
65+
defs foreach {
66+
case d: DefTree => nestedCtx.enter(d.symbol)
67+
case _ =>
68+
}
69+
nestedCtx
70+
}
71+
72+
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
73+
def localCtx = ctx.withOwner(tree.symbol)
74+
tree match {
75+
case tree: Block =>
76+
super.transform(tree)(nestedScopeCtx(tree.stats))
77+
case tree: DefDef =>
78+
implicit val ctx = localCtx
79+
cpy.DefDef(tree)(
80+
tree.name,
81+
transformSub(tree.tparams),
82+
tree.vparamss mapConserve (transformSub(_)),
83+
transform(tree.tpt),
84+
transform(tree.rhs)(nestedScopeCtx(tree.vparamss.flatten)))
85+
case _ =>
86+
super.transform(tree)
87+
}
88+
}
89+
}
90+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ class FrontEnd extends Phase {
1919
override def isTyper = true
2020
import ast.tpd
2121

22+
override def allowsImplicitSearch = true
23+
2224
/** The contexts for compilation units that are parsed but not yet entered */
2325
private[this] var remaining: List[Context] = Nil
2426

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ trait Implicits { self: Typer =>
749749
* !!! todo: catch potential cycles
750750
*/
751751
def inferImplicit(pt: Type, argument: Tree, pos: Position)(implicit ctx: Context): SearchResult = track("inferImplicit") {
752-
assert(!ctx.isAfterTyper,
752+
assert(ctx.phase.allowsImplicitSearch,
753753
if (argument.isEmpty) i"missing implicit parameter of type $pt after typer"
754754
else i"type error: ${argument.tpe} does not conform to $pt${err.whyNoMatchStr(argument.tpe, pt)}")
755755
trace(s"search implicit ${pt.show}, arg = ${argument.show}: ${argument.tpe.show}", implicits, show = true) {

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

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -385,15 +385,6 @@ class Namer { typer: Typer =>
385385
case _ => tree
386386
}
387387

388-
/** A new context that summarizes an import statement */
389-
def importContext(imp: Import, sym: Symbol)(implicit ctx: Context) = {
390-
val impNameOpt = imp.expr match {
391-
case ref: RefTree => Some(ref.name.asTermName)
392-
case _ => None
393-
}
394-
ctx.fresh.setImportInfo(new ImportInfo(implicit ctx => sym, imp.selectors, impNameOpt))
395-
}
396-
397388
/** A new context for the interior of a class */
398389
def inClassContext(selfInfo: DotClass /* Should be Type | Symbol*/)(implicit ctx: Context): Context = {
399390
val localCtx: Context = ctx.fresh.setNewScope
@@ -441,7 +432,7 @@ class Namer { typer: Typer =>
441432
setDocstring(pkg, stat)
442433
ctx
443434
case imp: Import =>
444-
importContext(imp, createSymbol(imp))
435+
ctx.importContext(imp, createSymbol(imp))
445436
case mdef: DefTree =>
446437
val sym = enterSymbol(createSymbol(mdef))
447438
setDocstring(sym, origStat)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1775,7 +1775,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
17751775
case (imp: untpd.Import) :: rest =>
17761776
val imp1 = typed(imp)
17771777
buf += imp1
1778-
traverse(rest)(importContext(imp, imp1.symbol))
1778+
traverse(rest)(ctx.importContext(imp, imp1.symbol))
17791779
case (mdef: untpd.DefTree) :: rest =>
17801780
mdef.removeAttachment(ExpandedTree) match {
17811781
case Some(xtree) =>

0 commit comments

Comments
 (0)