Skip to content

Commit 743a6b6

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. Motivation for these changes: - enable semantic tools such as doc tools to inspect where exports occur - when a class A exports members of (b: B) with a wildcard, there was no dependency recorded to trigger a recompile of A if B adds new members. Keeping the export trees in the dependencies phase lets us inspect for the wildcard case which has a different dependency relationship than named exports.
1 parent 85e48f7 commit 743a6b6

39 files changed

+580
-96
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

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

@@ -633,10 +633,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
633633
case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree
634634
case _ => finalize(tree, untpd.PatDef(mods, pats, tpt, rhs)(tree.source))
635635
}
636-
def Export(tree: Tree)(expr: Tree, selectors: List[ImportSelector])(using Context): Tree = tree match {
637-
case tree: Export if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
638-
case _ => finalize(tree, untpd.Export(expr, selectors)(tree.source))
639-
}
640636
def ExtMethods(tree: Tree)(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(using Context): Tree = tree match
641637
case tree: ExtMethods if (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (methods == tree.methods) => tree
642638
case _ => finalize(tree, untpd.ExtMethods(tparams, vparamss, methods)(tree.source))
@@ -704,8 +700,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
704700
cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds))
705701
case PatDef(mods, pats, tpt, rhs) =>
706702
cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs))
707-
case Export(expr, selectors) =>
708-
cpy.Export(tree)(transform(expr), selectors)
709703
case ExtMethods(tparams, vparamss, methods) =>
710704
cpy.ExtMethods(tree)(transformSub(tparams), vparamss.mapConserve(transformSub(_)), transformSub(methods))
711705
case ImportSelector(imported, renamed, bound) =>
@@ -765,8 +759,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
765759
this(this(x, bounds), cxBounds)
766760
case PatDef(mods, pats, tpt, rhs) =>
767761
this(this(this(x, pats), tpt), rhs)
768-
case Export(expr, _) =>
769-
this(x, expr)
770762
case ExtMethods(tparams, vparamss, methods) =>
771763
this(vparamss.foldLeft(this(x, tparams))(apply), methods)
772764
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
@@ -4815,6 +4815,11 @@ object Types {
48154815
/** The type of an import clause tree */
48164816
case class ImportType(expr: Tree) extends UncachedGroundType
48174817

4818+
/** Sentinal for typed export clauses */
4819+
@sharable case object ExportType extends CachedGroundType {
4820+
override def computeHash(bs: Binders): Int = hashSeed
4821+
}
4822+
48184823
/** Sentinel for "missing type" */
48194824
@sharable case object NoType extends CachedGroundType {
48204825
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
@@ -593,6 +593,12 @@ class TreePickler(pickler: TastyPickler) {
593593
pickleTree(expr)
594594
pickleSelectors(selectors)
595595
}
596+
case Export(expr, selectors) =>
597+
writeByte(EXPORT)
598+
withLength {
599+
pickleTree(expr)
600+
pickleSelectors(selectors)
601+
}
596602
case PackageDef(pid, stats) =>
597603
writeByte(PACKAGE)
598604
withLength { pickleType(pid.tpe); pickleStats(stats) }

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ class TreeUnpickler(reader: TastyReader,
711711
else if (sym.isClass ||
712712
sym.is(Method, butNot = Deferred) && !sym.isConstructor)
713713
initsFlags &= NoInits
714-
case IMPORT =>
714+
case IMPORT | EXPORT =>
715715
skipTree()
716716
case PACKAGE =>
717717
processPackage { (pid, end) => indexStats(end) }
@@ -970,7 +970,9 @@ class TreeUnpickler(reader: TastyReader,
970970
case TYPEDEF | VALDEF | DEFDEF =>
971971
readIndexedDef()
972972
case IMPORT =>
973-
readImport()
973+
readImportOrExport(Import(_, _))()
974+
case EXPORT =>
975+
readImportOrExport(Export(_, _))()
974976
case PACKAGE =>
975977
val start = currentAddr
976978
processPackage { (pid, end) =>
@@ -980,14 +982,16 @@ class TreeUnpickler(reader: TastyReader,
980982
readTerm()(using ctx.withOwner(exprOwner))
981983
}
982984

983-
def readImport()(using Context): Tree = {
985+
inline def readImportOrExport(inline mkTree:
986+
(Tree, List[untpd.ImportSelector]) => Tree)()(using Context): Tree = {
984987
val start = currentAddr
985988
assert(sourcePathAt(start).isEmpty)
986989
readByte()
987990
readEnd()
988991
val expr = readTerm()
989-
setSpan(start, Import(expr, readSelectors()))
992+
setSpan(start, mkTree(expr, readSelectors()))
990993
}
994+
991995
def readSelectors()(using Context): List[untpd.ImportSelector] =
992996
if nextByte == IMPORTED then
993997
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
@@ -3730,7 +3730,7 @@ object Parsers {
37303730
else if (in.token == IMPORT)
37313731
stats ++= importClause(IMPORT, mkImport(outermost))
37323732
else if (in.token == EXPORT)
3733-
stats ++= importClause(EXPORT, Export.apply)
3733+
stats ++= importClause(EXPORT, Export(_,_))
37343734
else if isIdent(nme.extension) && followingIsExtension() then
37353735
stats += extension()
37363736
else if isDefIntro(modifierTokens) then
@@ -3784,7 +3784,7 @@ object Parsers {
37843784
if (in.token == IMPORT)
37853785
stats ++= importClause(IMPORT, mkImport())
37863786
else if (in.token == EXPORT)
3787-
stats ++= importClause(EXPORT, Export.apply)
3787+
stats ++= importClause(EXPORT, Export(_,_))
37883788
else if isIdent(nme.extension) && followingIsExtension() then
37893789
stats += extension()
37903790
else if (isDefIntro(modifierTokensOrCase))

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

Lines changed: 20 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,20 @@ 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+
// If an export is a wildcard, that means that the enclosing class
372+
// has forwarders to all the applicable signatures in `dep`,
373+
// those forwarders will cause member/type ref dependencies to be
374+
// recorded. However, if `dep` adds more members with new names,
375+
// there has been no record that the enclosing class needs to
376+
// recompile to capture the new members. We add an
377+
// inheritance dependency in the presence of wildcard exports
378+
// to ensure all new members of `dep` are forwarded to.
379+
val depContext = depContextOf(ctx.owner.lexicallyEnclosingClass)
380+
val from = resolveDependencySource
381+
_dependencies += ClassDependency(from, dep, depContext)
367382
case t: TypeTree =>
368383
addTypeDependency(t.tpe)
369384
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
@@ -511,6 +511,9 @@ trait TypeAssigner {
511511
def assignType(tree: untpd.Import, sym: Symbol)(using Context): Import =
512512
tree.withType(sym.termRef)
513513

514+
def assignType(tree: untpd.Export)(using Context): Export =
515+
tree.withType(ExportType)
516+
514517
def assignType(tree: untpd.Annotated, arg: Tree, annot: Tree)(using Context): Annotated = {
515518
assert(tree.isType) // annotating a term is done via a Typed node, can't use Annotate directly
516519
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)

0 commit comments

Comments
 (0)