Skip to content

Keep export clause until first transform, use for incremental compilation #10182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -794,15 +794,31 @@ object Trees {
}


abstract class ImportOrExport[-T >: Untyped](implicit @constructorOnly src: SourceFile)
extends DenotingTree[T] {
type ThisTree[-T >: Untyped] <: ImportOrExport[T]
val expr: Tree[T]
val selectors: List[untpd.ImportSelector]
}

/** import expr.selectors
* where a selector is either an untyped `Ident`, `name` or
* an untyped thicket consisting of `name` and `rename`.
*/
case class Import[-T >: Untyped] private[ast] (expr: Tree[T], selectors: List[untpd.ImportSelector])(implicit @constructorOnly src: SourceFile)
extends DenotingTree[T] {
extends ImportOrExport[T] {
type ThisTree[-T >: Untyped] = Import[T]
}

/** export expr.selectors
* where a selector is either an untyped `Ident`, `name` or
* an untyped thicket consisting of `name` and `rename`.
*/
case class Export[-T >: Untyped] private[ast] (expr: Tree[T], selectors: List[untpd.ImportSelector])(implicit @constructorOnly src: SourceFile)
extends ImportOrExport[T] {
type ThisTree[-T >: Untyped] = Export[T]
}

/** package pid { stats } */
case class PackageDef[-T >: Untyped] private[ast] (pid: RefTree[T], stats: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
extends ProxyTree[T] {
Expand Down Expand Up @@ -990,6 +1006,7 @@ object Trees {
type TypeDef = Trees.TypeDef[T]
type Template = Trees.Template[T]
type Import = Trees.Import[T]
type Export = Trees.Export[T]
type PackageDef = Trees.PackageDef[T]
type Annotated = Trees.Annotated[T]
type Thicket = Trees.Thicket[T]
Expand Down Expand Up @@ -1200,6 +1217,10 @@ object Trees {
case tree: Import if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
case _ => finalize(tree, untpd.Import(expr, selectors)(sourceFile(tree)))
}
def Export(tree: Tree)(expr: Tree, selectors: List[untpd.ImportSelector])(using Context): Export = tree match {
case tree: Export if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
case _ => finalize(tree, untpd.Export(expr, selectors)(sourceFile(tree)))
}
def PackageDef(tree: Tree)(pid: RefTree, stats: List[Tree])(using Context): PackageDef = tree match {
case tree: PackageDef if (pid eq tree.pid) && (stats eq tree.stats) => tree
case _ => finalize(tree, untpd.PackageDef(pid, stats)(sourceFile(tree)))
Expand Down Expand Up @@ -1350,6 +1371,8 @@ object Trees {
cpy.Template(tree)(transformSub(constr), transform(tree.parents), Nil, transformSub(self), transformStats(tree.body))
case Import(expr, selectors) =>
cpy.Import(tree)(transform(expr), selectors)
case Export(expr, selectors) =>
cpy.Export(tree)(transform(expr), selectors)
case PackageDef(pid, stats) =>
cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)(using localCtx))
case Annotated(arg, annot) =>
Expand Down Expand Up @@ -1484,6 +1507,8 @@ object Trees {
this(this(this(this(x, constr), parents), self), tree.body)
case Import(expr, _) =>
this(x, expr)
case Export(expr, _) =>
this(x, expr)
case PackageDef(pid, stats) =>
this(this(x, pid), stats)(using localCtx)
case Annotated(arg, annot) =>
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def Import(expr: Tree, selectors: List[untpd.ImportSelector])(using Context): Import =
ta.assignType(untpd.Import(expr, selectors), newImportSymbol(ctx.owner, expr))

def Export(expr: Tree, selectors: List[untpd.ImportSelector])(using Context): Export =
ta.assignType(untpd.Export(expr, selectors))

def PackageDef(pid: RefTree, stats: List[Tree])(using Context): PackageDef =
ta.assignType(untpd.PackageDef(pid, stats), pid)

Expand Down
10 changes: 1 addition & 9 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class GenAlias(pat: Tree, expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
case class Export(expr: Tree, selectors: List[ImportSelector])(implicit @constructorOnly src: SourceFile) extends Tree
case class ExtMethods(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(implicit @constructorOnly src: SourceFile) extends Tree
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree

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

Expand Down Expand Up @@ -633,10 +633,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree
case _ => finalize(tree, untpd.PatDef(mods, pats, tpt, rhs)(tree.source))
}
def Export(tree: Tree)(expr: Tree, selectors: List[ImportSelector])(using Context): Tree = tree match {
case tree: Export if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
case _ => finalize(tree, untpd.Export(expr, selectors)(tree.source))
}
def ExtMethods(tree: Tree)(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(using Context): Tree = tree match
case tree: ExtMethods if (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (methods == tree.methods) => tree
case _ => finalize(tree, untpd.ExtMethods(tparams, vparamss, methods)(tree.source))
Expand Down Expand Up @@ -704,8 +700,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds))
case PatDef(mods, pats, tpt, rhs) =>
cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs))
case Export(expr, selectors) =>
cpy.Export(tree)(transform(expr), selectors)
case ExtMethods(tparams, vparamss, methods) =>
cpy.ExtMethods(tree)(transformSub(tparams), vparamss.mapConserve(transformSub(_)), transformSub(methods))
case ImportSelector(imported, renamed, bound) =>
Expand Down Expand Up @@ -765,8 +759,6 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
this(this(x, bounds), cxBounds)
case PatDef(mods, pats, tpt, rhs) =>
this(this(this(x, pats), tpt), rhs)
case Export(expr, _) =>
this(x, expr)
case ExtMethods(tparams, vparamss, methods) =>
this(vparamss.foldLeft(this(x, tparams))(apply), methods)
case ImportSelector(imported, renamed, bound) =>
Expand Down
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,12 @@ class TreePickler(pickler: TastyPickler) {
pickleTree(expr)
pickleSelectors(selectors)
}
case Export(expr, selectors) =>
writeByte(EXPORT)
withLength {
pickleTree(expr)
pickleSelectors(selectors)
}
case PackageDef(pid, stats) =>
writeByte(PACKAGE)
withLength { pickleType(pid.tpe); pickleStats(stats) }
Expand Down
12 changes: 8 additions & 4 deletions compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ class TreeUnpickler(reader: TastyReader,
else if (sym.isClass ||
sym.is(Method, butNot = Deferred) && !sym.isConstructor)
initsFlags &= NoInits
case IMPORT =>
case IMPORT | EXPORT =>
skipTree()
case PACKAGE =>
processPackage { (pid, end) => indexStats(end) }
Expand Down Expand Up @@ -970,7 +970,9 @@ class TreeUnpickler(reader: TastyReader,
case TYPEDEF | VALDEF | DEFDEF =>
readIndexedDef()
case IMPORT =>
readImport()
readImportOrExport(Import(_, _))()
case EXPORT =>
readImportOrExport(Export(_, _))()
case PACKAGE =>
val start = currentAddr
processPackage { (pid, end) =>
Expand All @@ -980,14 +982,16 @@ class TreeUnpickler(reader: TastyReader,
readTerm()(using ctx.withOwner(exprOwner))
}

def readImport()(using Context): Tree = {
inline def readImportOrExport(inline mkTree:
(Tree, List[untpd.ImportSelector]) => Tree)()(using Context): Tree = {
val start = currentAddr
assert(sourcePathAt(start).isEmpty)
readByte()
readEnd()
val expr = readTerm()
setSpan(start, Import(expr, readSelectors()))
setSpan(start, mkTree(expr, readSelectors()))
}

def readSelectors()(using Context): List[untpd.ImportSelector] =
if nextByte == IMPORTED then
val start = currentAddr
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3747,7 +3747,7 @@ object Parsers {
else if (in.token == IMPORT)
stats ++= importClause(IMPORT, mkImport(outermost))
else if (in.token == EXPORT)
stats ++= importClause(EXPORT, Export.apply)
stats ++= importClause(EXPORT, Export(_,_))
else if isIdent(nme.extension) && followingIsExtension() then
stats += extension()
else if isDefIntro(modifierTokens) then
Expand Down Expand Up @@ -3801,7 +3801,7 @@ object Parsers {
if (in.token == IMPORT)
stats ++= importClause(IMPORT, mkImport())
else if (in.token == EXPORT)
stats ++= importClause(EXPORT, Export.apply)
stats ++= importClause(EXPORT, Export(_,_))
else if isIdent(nme.extension) && followingIsExtension() then
stats += extension()
else if (isDefIntro(modifierTokensOrCase))
Expand Down
25 changes: 20 additions & 5 deletions compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -328,15 +328,16 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT

private def addInheritanceDependencies(tree: Template)(using Context): Unit =
if (tree.parents.nonEmpty) {
val depContext =
if (tree.symbol.owner.isLocal) LocalDependencyByInheritance
else DependencyByInheritance
val depContext = depContextOf(tree.symbol.owner)
val from = resolveDependencySource
tree.parents.foreach { parent =>
for parent <- tree.parents do
_dependencies += ClassDependency(from, parent.tpe.classSymbol, depContext)
}
}

private def depContextOf(cls: Symbol)(using Context): DependencyContext =
if cls.isLocal then LocalDependencyByInheritance
else DependencyByInheritance

private def ignoreDependency(sym: Symbol)(using Context) =
!sym.exists ||
sym.isAbsent(canForce = false) || // ignore dependencies that have a symbol but do not exist.
Expand Down Expand Up @@ -364,6 +365,20 @@ private class ExtractDependenciesCollector extends tpd.TreeTraverser { thisTreeT
addImported(sel.name)
if sel.rename != sel.name then
addUsedName(sel.rename, UseScope.Default)
case exp @ Export(expr, selectors) =>
val dep = expr.tpe.classSymbol
if dep.exists && selectors.exists(_.isWildcard) then
// If an export is a wildcard, that means that the enclosing class
// has forwarders to all the applicable signatures in `dep`,
// those forwarders will cause member/type ref dependencies to be
// recorded. However, if `dep` adds more members with new names,
// there has been no record that the enclosing class needs to
// recompile to capture the new members. We add an
// inheritance dependency in the presence of wildcard exports
// to ensure all new members of `dep` are forwarded to.
val depContext = depContextOf(ctx.owner.lexicallyEnclosingClass)
val from = resolveDependencySource
_dependencies += ClassDependency(from, dep, depContext)
case t: TypeTree =>
addTypeDependency(t.tpe)
case ref: RefTree =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
}

override def transformOther(tree: Tree)(using Context): Tree = tree match {
case tree: Import => EmptyTree
case tree: ImportOrExport[_] => EmptyTree
case tree: NamedArg => transformAllDeep(tree.arg)
case tree => if (tree.isType) toTypeTree(tree) else tree
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ abstract class TreeMapWithStages(@constructorOnly ictx: Context) extends TreeMap
tpd.patVars(pat).foreach(markSymbol)
mapOverTree(last)

case _: Import =>
case (_:Import | _:Export) =>
tree

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

}
}
23 changes: 20 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -719,12 +719,29 @@ trait Checking {
recur(pat, pt)
}

/** Check that `path` is a legal prefix for an import or export clause */
def checkLegalImportPath(path: Tree)(using Context): Unit = {
checkStable(path.tpe, path.srcPos, "import prefix")
private def checkLegalImportOrExportPath(path: Tree, kind: String)(using Context): Unit = {
checkStable(path.tpe, path.srcPos, kind)
if (!ctx.isAfterTyper) Checking.checkRealizable(path.tpe, path.srcPos)
}

/** Check that `path` is a legal prefix for an import clause */
def checkLegalImportPath(path: Tree)(using Context): Unit = {
checkLegalImportOrExportPath(path, "import prefix")
}

/** Check that `path` is a legal prefix for an export clause */
def checkLegalExportPath(path: Tree, selectors: List[untpd.ImportSelector])(using Context): Unit =
checkLegalImportOrExportPath(path, "export prefix")
if
selectors.exists(_.isWildcard)
&& path.tpe.classSymbol.is(PackageClass)
then
// we restrict wildcard export from package as incremental compilation does not yet
// register a dependency on "all members of a package" - see https://github.com/sbt/zinc/issues/226
report.error(
em"Implementation restriction: ${path.tpe.classSymbol} is not a valid prefix " +
"for a wildcard export, as it is a package.", path.srcPos)

/** Check that `tp` is a class type.
* Also, if `traitReq` is true, check that `tp` is a trait.
* Also, if `stablePrefixReq` is true and phase is not after RefChecks,
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -945,7 +945,7 @@ class Namer { typer: Typer =>
val buf = new mutable.ListBuffer[tpd.MemberDef]
val Export(expr, selectors) = exp
val path = typedAheadExpr(expr, AnySelectionProto)
checkLegalImportPath(path)
checkLegalExportPath(path, selectors)
lazy val wildcardBound = importBound(selectors, isGiven = false)
lazy val givenBound = importBound(selectors, isGiven = true)

Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,9 @@ trait TypeAssigner {
def assignType(tree: untpd.Import, sym: Symbol)(using Context): Import =
tree.withType(sym.termRef)

def assignType(tree: untpd.Export)(using Context): Export =
tree.withType(defn.UnitType)

def assignType(tree: untpd.Annotated, arg: Tree, annot: Tree)(using Context): Annotated = {
assert(tree.isType) // annotating a term is done via a Typed node, can't use Annotate directly
tree.withType(AnnotatedType(arg.tpe, Annotation(annot)))
Expand Down
20 changes: 16 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2235,18 +2235,28 @@ class Typer extends Namer
def localDummy(cls: ClassSymbol, impl: untpd.Template)(using Context): Symbol =
newLocalDummy(cls, impl.span)

def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Import = {
val expr1 = typedExpr(imp.expr, AnySelectionProto)
checkLegalImportPath(expr1)
val selectors1: List[untpd.ImportSelector] = imp.selectors.mapConserve { sel =>
inline private def typedSelectors(selectors: List[untpd.ImportSelector])(using Context): List[untpd.ImportSelector] =
selectors.mapConserve { sel =>
if sel.bound.isEmpty then sel
else cpy.ImportSelector(sel)(
sel.imported, sel.renamed, untpd.TypedSplice(typedType(sel.bound)))
.asInstanceOf[untpd.ImportSelector]
}

def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Import = {
val expr1 = typedExpr(imp.expr, AnySelectionProto)
checkLegalImportPath(expr1)
val selectors1 = typedSelectors(imp.selectors)
assignType(cpy.Import(imp)(expr1, selectors1), sym)
}

def typedExport(exp: untpd.Export)(using Context): Export = {
val expr1 = typedExpr(exp.expr, AnySelectionProto)
// already called `checkLegalExportPath` in Namer
val selectors1 = typedSelectors(exp.selectors)
assignType(cpy.Export(exp)(expr1, selectors1))
}

def typedPackageDef(tree: untpd.PackageDef)(using Context): Tree =
val pid1 = withMode(Mode.InPackageClauseName)(typedExpr(tree.pid, AnySelectionProto))
val pkg = pid1.symbol
Expand Down Expand Up @@ -2481,6 +2491,7 @@ class Typer extends Namer
case tree: untpd.Function => typedFunction(tree, pt)
case tree: untpd.Closure => typedClosure(tree, pt)
case tree: untpd.Import => typedImport(tree, retrieveSym(tree))
case tree: untpd.Export => typedExport(tree)
case tree: untpd.Match => typedMatch(tree, pt)
case tree: untpd.Return => typedReturn(tree)
case tree: untpd.WhileDo => typedWhileDo(tree)
Expand Down Expand Up @@ -2643,6 +2654,7 @@ class Typer extends Namer
case Thicket(stats) :: rest =>
traverse(stats ::: rest)
case (stat: untpd.Export) :: rest =>
buf += typed(stat)
buf ++= stat.attachmentOrElse(ExportForwarders, Nil)
// no attachment can happen in case of cyclic references
traverse(rest)
Expand Down
Loading