Skip to content

Commit 019de01

Browse files
committed
Keep export clause trees until FirstTransform
Add Export trees to TASTy format and QuoteContext reflection API Use Export trees to record wildcard export as dependencies by inheritance in incremental compilation. -- composed of the following squashed commits: add regression test for export clause incremental compilation Demonstrate the problem. This fails because A.scala doesn't get recompiled when B.scala is changed, so the number of iterations is 2 instead of 3. record wildcard export dependencies in ClassSymbol make Export a standard tree, but remove trees before typer todo: pickler phase and tasty need to handle exports before keeping them in typer handle export in tasty add GenericImport Support exports in tasty reflect rename to ImportOrExport, add ExportType test exports with metaprogramming tools use export in ExtractDependencies
1 parent cd15c99 commit 019de01

38 files changed

+491
-32
lines changed

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,15 +794,31 @@ object Trees {
794794
}
795795

796796

797+
abstract class ImportOrExport[-T >: Untyped](implicit @constructorOnly src: SourceFile)
798+
extends DenotingTree[T] {
799+
type ThisTree[-T >: Untyped] <: ImportOrExport[T]
800+
val expr: Tree[T]
801+
val selectors: List[untpd.ImportSelector]
802+
}
803+
797804
/** import expr.selectors
798805
* where a selector is either an untyped `Ident`, `name` or
799806
* an untyped thicket consisting of `name` and `rename`.
800807
*/
801808
case class Import[-T >: Untyped] private[ast] (expr: Tree[T], selectors: List[untpd.ImportSelector])(implicit @constructorOnly src: SourceFile)
802-
extends DenotingTree[T] {
809+
extends ImportOrExport[T] {
803810
type ThisTree[-T >: Untyped] = Import[T]
804811
}
805812

813+
/** export expr.selectors
814+
* where a selector is either an untyped `Ident`, `name` or
815+
* an untyped thicket consisting of `name` and `rename`.
816+
*/
817+
case class Export[-T >: Untyped] private[ast] (expr: Tree[T], selectors: List[untpd.ImportSelector])(implicit @constructorOnly src: SourceFile)
818+
extends ImportOrExport[T] {
819+
type ThisTree[-T >: Untyped] = Export[T]
820+
}
821+
806822
/** package pid { stats } */
807823
case class PackageDef[-T >: Untyped] private[ast] (pid: RefTree[T], stats: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
808824
extends ProxyTree[T] {
@@ -990,6 +1006,7 @@ object Trees {
9901006
type TypeDef = Trees.TypeDef[T]
9911007
type Template = Trees.Template[T]
9921008
type Import = Trees.Import[T]
1009+
type Export = Trees.Export[T]
9931010
type PackageDef = Trees.PackageDef[T]
9941011
type Annotated = Trees.Annotated[T]
9951012
type Thicket = Trees.Thicket[T]
@@ -1200,6 +1217,10 @@ object Trees {
12001217
case tree: Import if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
12011218
case _ => finalize(tree, untpd.Import(expr, selectors)(sourceFile(tree)))
12021219
}
1220+
def Export(tree: Tree)(expr: Tree, selectors: List[untpd.ImportSelector])(using Context): Export = tree match {
1221+
case tree: Export if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
1222+
case _ => finalize(tree, untpd.Export(expr, selectors)(sourceFile(tree)))
1223+
}
12031224
def PackageDef(tree: Tree)(pid: RefTree, stats: List[Tree])(using Context): PackageDef = tree match {
12041225
case tree: PackageDef if (pid eq tree.pid) && (stats eq tree.stats) => tree
12051226
case _ => finalize(tree, untpd.PackageDef(pid, stats)(sourceFile(tree)))
@@ -1350,6 +1371,8 @@ object Trees {
13501371
cpy.Template(tree)(transformSub(constr), transform(tree.parents), Nil, transformSub(self), transformStats(tree.body))
13511372
case Import(expr, selectors) =>
13521373
cpy.Import(tree)(transform(expr), selectors)
1374+
case Export(expr, selectors) =>
1375+
cpy.Export(tree)(transform(expr), selectors)
13531376
case PackageDef(pid, stats) =>
13541377
cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)(using localCtx))
13551378
case Annotated(arg, annot) =>
@@ -1484,6 +1507,8 @@ object Trees {
14841507
this(this(this(this(x, constr), parents), self), tree.body)
14851508
case Import(expr, _) =>
14861509
this(x, expr)
1510+
case Export(expr, _) =>
1511+
this(x, expr)
14871512
case PackageDef(pid, stats) =>
14881513
this(this(x, pid), stats)(using localCtx)
14891514
case Annotated(arg, annot) =>

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
361361
def Import(expr: Tree, selectors: List[untpd.ImportSelector])(using Context): Import =
362362
ta.assignType(untpd.Import(expr, selectors), newImportSymbol(ctx.owner, expr))
363363

364+
def Export(expr: Tree, selectors: List[untpd.ImportSelector])(using Context): Export =
365+
ta.assignType(untpd.Export(expr, selectors))
366+
364367
def PackageDef(pid: RefTree, stats: List[Tree])(using Context): PackageDef =
365368
ta.assignType(untpd.PackageDef(pid, stats), pid)
366369

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

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
118118
case class GenAlias(pat: Tree, expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
119119
case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree
120120
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
121-
case class Export(expr: Tree, selectors: List[ImportSelector])(implicit @constructorOnly src: SourceFile) extends Tree
122121
case class ExtMethods(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(implicit @constructorOnly src: SourceFile) extends Tree
123122
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
124123

@@ -407,6 +406,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
407406
if (derived.isEmpty) new Template(constr, parents, self, body)
408407
else new DerivingTemplate(constr, parents ++ derived, self, body, derived.length)
409408
def Import(expr: Tree, selectors: List[ImportSelector])(implicit src: SourceFile): Import = new Import(expr, selectors)
409+
def Export(expr: Tree, selectors: List[ImportSelector])(implicit src: SourceFile): Export = new Export(expr, selectors)
410410
def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats)
411411
def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot)
412412

@@ -631,10 +631,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
631631
case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree
632632
case _ => finalize(tree, untpd.PatDef(mods, pats, tpt, rhs)(tree.source))
633633
}
634-
def Export(tree: Tree)(expr: Tree, selectors: List[ImportSelector])(using Context): Tree = tree match {
635-
case tree: Export if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
636-
case _ => finalize(tree, untpd.Export(expr, selectors)(tree.source))
637-
}
638634
def ExtMethods(tree: Tree)(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(using Context): Tree = tree match
639635
case tree: ExtMethods if (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (methods == tree.methods) => tree
640636
case _ => finalize(tree, untpd.ExtMethods(tparams, vparamss, methods)(tree.source))
@@ -702,8 +698,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
702698
cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds))
703699
case PatDef(mods, pats, tpt, rhs) =>
704700
cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs))
705-
case Export(expr, selectors) =>
706-
cpy.Export(tree)(transform(expr), selectors)
707701
case ExtMethods(tparams, vparamss, methods) =>
708702
cpy.ExtMethods(tree)(transformSub(tparams), vparamss.mapConserve(transformSub(_)), transformSub(methods))
709703
case ImportSelector(imported, renamed, bound) =>
@@ -763,8 +757,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
763757
this(this(x, bounds), cxBounds)
764758
case PatDef(mods, pats, tpt, rhs) =>
765759
this(this(this(x, pats), tpt), rhs)
766-
case Export(expr, _) =>
767-
this(x, expr)
768760
case ExtMethods(tparams, vparamss, methods) =>
769761
this(vparamss.foldLeft(this(x, tparams))(apply), methods)
770762
case ImportSelector(imported, renamed, bound) =>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4813,6 +4813,11 @@ object Types {
48134813
/** The type of an import clause tree */
48144814
case class ImportType(expr: Tree) extends UncachedGroundType
48154815

4816+
/** Sentinal for typed export clauses */
4817+
@sharable case object ExportType extends CachedGroundType {
4818+
override def computeHash(bs: Binders): Int = hashSeed
4819+
}
4820+
48164821
/** Sentinel for "missing type" */
48174822
@sharable case object NoType extends CachedGroundType {
48184823
override def computeHash(bs: Binders): Int = hashSeed

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,12 @@ class TreePickler(pickler: TastyPickler) {
590590
pickleTree(expr)
591591
pickleSelectors(selectors)
592592
}
593+
case Export(expr, selectors) =>
594+
writeByte(EXPORT)
595+
withLength {
596+
pickleTree(expr)
597+
pickleSelectors(selectors)
598+
}
593599
case PackageDef(pid, stats) =>
594600
writeByte(PACKAGE)
595601
withLength { pickleType(pid.tpe); pickleStats(stats) }

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ class TreeUnpickler(reader: TastyReader,
710710
else if (sym.isClass ||
711711
sym.is(Method, butNot = Deferred) && !sym.isConstructor)
712712
initsFlags &= NoInits
713-
case IMPORT =>
713+
case IMPORT | EXPORT =>
714714
skipTree()
715715
case PACKAGE =>
716716
processPackage { (pid, end) => indexStats(end) }
@@ -951,7 +951,7 @@ class TreeUnpickler(reader: TastyReader,
951951
}
952952

953953
def isTopLevel(using Context): Boolean =
954-
nextByte == IMPORT || nextByte == PACKAGE
954+
nextByte == IMPORT || nextByte == EXPORT || nextByte == PACKAGE
955955

956956
def readTopLevel()(using Context): List[Tree] = {
957957
@tailrec def read(acc: ListBuffer[Tree]): List[Tree] =
@@ -968,7 +968,9 @@ class TreeUnpickler(reader: TastyReader,
968968
case TYPEDEF | VALDEF | DEFDEF =>
969969
readIndexedDef()
970970
case IMPORT =>
971-
readImport()
971+
readImportOrExport(Import(_, _))()
972+
case EXPORT =>
973+
readImportOrExport(Export(_, _))()
972974
case PACKAGE =>
973975
val start = currentAddr
974976
processPackage { (pid, end) =>
@@ -978,14 +980,16 @@ class TreeUnpickler(reader: TastyReader,
978980
readTerm()(using ctx.withOwner(exprOwner))
979981
}
980982

981-
def readImport()(using Context): Tree = {
983+
inline def readImportOrExport(inline mkTree:
984+
(Tree, List[untpd.ImportSelector]) => Tree)()(using Context): Tree = {
982985
val start = currentAddr
983986
assert(sourcePathAt(start).isEmpty)
984987
readByte()
985988
readEnd()
986989
val expr = readTerm()
987-
setSpan(start, Import(expr, readSelectors()))
990+
setSpan(start, mkTree(expr, readSelectors()))
988991
}
992+
989993
def readSelectors()(using Context): List[untpd.ImportSelector] =
990994
if nextByte == IMPORTED then
991995
val start = currentAddr

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3734,7 +3734,7 @@ object Parsers {
37343734
else if (in.token == IMPORT)
37353735
stats ++= importClause(IMPORT, mkImport(outermost))
37363736
else if (in.token == EXPORT)
3737-
stats ++= importClause(EXPORT, Export.apply)
3737+
stats ++= importClause(EXPORT, Export(_,_))
37383738
else if isIdent(nme.extension) && followingIsExtension() then
37393739
stats += extension()
37403740
else if isDefIntro(modifierTokens) then
@@ -3788,7 +3788,7 @@ object Parsers {
37883788
if (in.token == IMPORT)
37893789
stats ++= importClause(IMPORT, mkImport())
37903790
else if (in.token == EXPORT)
3791-
stats ++= importClause(EXPORT, Export.apply)
3791+
stats ++= importClause(EXPORT, Export(_,_))
37923792
else if isIdent(nme.extension) && followingIsExtension() then
37933793
stats += extension()
37943794
else if (isDefIntro(modifierTokensOrCase))

compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -328,15 +328,16 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT
328328

329329
private def addInheritanceDependencies(tree: Template)(using Context): Unit =
330330
if (tree.parents.nonEmpty) {
331-
val depContext =
332-
if (tree.symbol.owner.isLocal) LocalDependencyByInheritance
333-
else DependencyByInheritance
331+
val depContext = depContextOf(tree.symbol.owner)
334332
val from = resolveDependencySource
335-
tree.parents.foreach { parent =>
333+
for parent <- tree.parents do
336334
_dependencies += ClassDependency(from, parent.tpe.classSymbol, depContext)
337-
}
338335
}
339336

337+
private def depContextOf(cls: Symbol)(using Context): DependencyContext =
338+
if cls.isLocal then LocalDependencyByInheritance
339+
else DependencyByInheritance
340+
340341
private def ignoreDependency(sym: Symbol)(using Context) =
341342
!sym.exists ||
342343
sym.isAbsent(canForce = false) || // ignore dependencies that have a symbol but do not exist.
@@ -364,6 +365,12 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT
364365
addImported(sel.name)
365366
if sel.rename != sel.name then
366367
addUsedName(sel.rename, UseScope.Default)
368+
case exp @ Export(expr, selectors) =>
369+
val dep = expr.tpe.classSymbol
370+
if dep.exists && selectors.exists(_.isWildcard) then
371+
val depContext = depContextOf(ctx.owner.lexicallyEnclosingClass)
372+
val from = resolveDependencySource
373+
_dependencies += ClassDependency(from, dep, depContext)
367374
case t: TypeTree =>
368375
addTypeDependency(t.tpe)
369376
case ref: RefTree =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
152152
}
153153

154154
override def transformOther(tree: Tree)(using Context): Tree = tree match {
155-
case tree: Import => EmptyTree
155+
case tree: ImportOrExport[_] => EmptyTree
156156
case tree: NamedArg => transformAllDeep(tree.arg)
157157
case tree => if (tree.isType) toTypeTree(tree) else tree
158158
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
140140
tpd.patVars(pat).foreach(markSymbol)
141141
mapOverTree(last)
142142

143-
case _: Import =>
143+
case (_:Import | _:Export) =>
144144
tree
145145

146146
case _ =>
@@ -161,4 +161,4 @@ object TreeMapWithStages {
161161
def freshStagingContext(using Context): Context =
162162
ctx.fresh.setProperty(LevelOfKey, new mutable.HashMap[Symbol, Int])
163163

164-
}
164+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,9 @@ trait TypeAssigner {
507507
def assignType(tree: untpd.Import, sym: Symbol)(using Context): Import =
508508
tree.withType(sym.termRef)
509509

510+
def assignType(tree: untpd.Export)(using Context): Export =
511+
tree.withType(ExportType)
512+
510513
def assignType(tree: untpd.Annotated, arg: Tree, annot: Tree)(using Context): Annotated = {
511514
assert(tree.isType) // annotating a term is done via a Typed node, can't use Annotate directly
512515
tree.withType(AnnotatedType(arg.tpe, Annotation(annot)))

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,7 +2235,8 @@ class Typer extends Namer
22352235
def localDummy(cls: ClassSymbol, impl: untpd.Template)(using Context): Symbol =
22362236
newLocalDummy(cls, impl.span)
22372237

2238-
def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Import = {
2238+
inline def typedImportOrExport[T <: ImportOrExport[Untyped]](imp: T)(
2239+
inline mkTree: (Tree, List[untpd.ImportSelector]) => imp.ThisTree[Type])(using Context): imp.ThisTree[Type] = {
22392240
val expr1 = typedExpr(imp.expr, AnySelectionProto)
22402241
checkLegalImportPath(expr1)
22412242
val selectors1: List[untpd.ImportSelector] = imp.selectors.mapConserve { sel =>
@@ -2244,7 +2245,19 @@ class Typer extends Namer
22442245
sel.imported, sel.renamed, untpd.TypedSplice(typedType(sel.bound)))
22452246
.asInstanceOf[untpd.ImportSelector]
22462247
}
2247-
assignType(cpy.Import(imp)(expr1, selectors1), sym)
2248+
mkTree(expr1, selectors1)
2249+
}
2250+
2251+
def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Import = {
2252+
typedImportOrExport(imp)((expr1, selectors1) =>
2253+
assignType(cpy.Import(imp)(expr1, selectors1), sym)
2254+
)
2255+
}
2256+
2257+
def typedExport(exp: untpd.Export)(using Context): Export = {
2258+
typedImportOrExport(exp)((expr1, selectors1) =>
2259+
assignType(cpy.Export(exp)(expr1, selectors1))
2260+
)
22482261
}
22492262

22502263
def typedPackageDef(tree: untpd.PackageDef)(using Context): Tree =
@@ -2481,6 +2494,7 @@ class Typer extends Namer
24812494
case tree: untpd.Function => typedFunction(tree, pt)
24822495
case tree: untpd.Closure => typedClosure(tree, pt)
24832496
case tree: untpd.Import => typedImport(tree, retrieveSym(tree))
2497+
case tree: untpd.Export => typedExport(tree)
24842498
case tree: untpd.Match => typedMatch(tree, pt)
24852499
case tree: untpd.Return => typedReturn(tree)
24862500
case tree: untpd.WhileDo => typedWhileDo(tree)
@@ -2643,6 +2657,7 @@ class Typer extends Namer
26432657
case Thicket(stats) :: rest =>
26442658
traverse(stats ::: rest)
26452659
case (stat: untpd.Export) :: rest =>
2660+
buf += typed(stat)
26462661
buf ++= stat.attachmentOrElse(ExportForwarders, Nil)
26472662
// no attachment can happen in case of cyclic references
26482663
traverse(rest)

compiler/src/scala/quoted/internal/impl/QuoteContextImpl.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,30 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
168168
end extension
169169
end ImportMethodsImpl
170170

171+
type Export = tpd.Export
172+
173+
object ExportTypeTest extends TypeTest[Tree, Export]:
174+
def unapply(x: Tree): Option[Export & x.type] = x match
175+
case tree: (tpd.Export & x.type) => Some(tree)
176+
case _ => None
177+
end ExportTypeTest
178+
179+
object Export extends ExportModule:
180+
def apply(expr: Term, selectors: List[ImportSelector]): Export =
181+
withDefaultPos(tpd.Export(expr, selectors))
182+
def copy(original: Tree)(expr: Term, selectors: List[ImportSelector]): Export =
183+
tpd.cpy.Export(original)(expr, selectors)
184+
def unapply(tree: Export): Option[(Term, List[ImportSelector])] =
185+
Some((tree.expr, tree.selectors))
186+
end Export
187+
188+
object ExportMethodsImpl extends ExportMethods:
189+
extension (self: Export):
190+
def expr: Term = self.expr
191+
def selectors: List[ImportSelector] = self.selectors
192+
end extension
193+
end ExportMethodsImpl
194+
171195
type Statement = tpd.Tree
172196

173197
object StatementTypeTest extends TypeTest[Tree, Statement]:

compiler/src/scala/quoted/internal/impl/printers/Extractors.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ object Extractors {
123123
this += ", " += self += ", " ++= body += ")"
124124
case Import(expr, selectors) =>
125125
this += "Import(" += expr += ", " ++= selectors += ")"
126+
case Export(expr, selectors) =>
127+
this += "Export(" += expr += ", " ++= selectors += ")"
126128
case PackageClause(pid, stats) =>
127129
this += "PackageClause(" += pid += ", " ++= stats += ")"
128130
case Inferred() =>

0 commit comments

Comments
 (0)