Skip to content
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

Keep language imports around longer #14364

Merged
merged 4 commits into from
Jan 28, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
8 changes: 5 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/FirstTransform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ object FirstTransform {
}

/** The first tree transform
* - eliminates some kinds of trees: Imports, NamedArgs
* - eliminates some kinds of trees: Imports other than language imports,
* Exports, NamedArgs, type trees other than TypeTree
* - stubs out native methods
* - eliminates self tree in Template and self symbol in ClassInfo
* - collapses all type trees to trees of class TypeTree
Expand Down Expand Up @@ -58,7 +59,7 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
tree.symbol.is(JavaStatic) && qualTpe.derivesFrom(tree.symbol.enclosingClass),
i"non member selection of ${tree.symbol.showLocated} from ${qualTpe} in $tree")
case _: TypeTree =>
case _: Import | _: NamedArg | _: TypTree =>
case _: Export | _: NamedArg | _: TypTree =>
assert(false, i"illegal tree: $tree")
case _ =>
}
Expand Down Expand Up @@ -136,7 +137,8 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
}

override def transformOther(tree: Tree)(using Context): Tree = tree match {
case tree: ImportOrExport => EmptyTree
case tree: Import if untpd.languageImport(tree.expr).isEmpty => EmptyTree
case tree: Export => EmptyTree
case tree: NamedArg => transformAllDeep(tree.arg)
case tree => if (tree.isType) toTypeTree(tree) else tree
}
Expand Down
34 changes: 31 additions & 3 deletions compiler/src/dotty/tools/dotc/transform/MegaPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -432,16 +432,44 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
def transformSpecificTree[T <: Tree](tree: T, start: Int)(using Context): T =
transformTree(tree, start).asInstanceOf[T]

def transformStats(trees: List[Tree], exprOwner: Symbol, start: Int)(using Context): List[Tree] = {
def transformStats(trees: List[Tree], exprOwner: Symbol, start: Int)(using Context): List[Tree] =

def transformStat(stat: Tree)(using Context): Tree = stat match {
case _: Import | _: DefTree => transformTree(stat, start)
case Thicket(stats) => cpy.Thicket(stat)(stats.mapConserve(transformStat))
case _ => transformTree(stat, start)(using ctx.exprContext(stat, exprOwner))
}

def restContext(tree: Tree)(using Context): Context = tree match
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add this for MacroTransform as well? I need the import information in PostTyper to fix #14319.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, let's do that.

case imp: Import => ctx.importContext(imp, imp.symbol)
case _ => ctx

// A slower implementation that avoids deep recursions and stack overflows
def transformSlow(trees: List[Tree])(using Context): List[Tree] =
var curCtx = ctx
flatten(trees.mapConserve { tree =>
val tree1 = transformStat(tree)(using curCtx)
curCtx = restContext(tree)(using curCtx)
tree1
})

def recur(trees: List[Tree], count: Int)(using Context): List[Tree] =
if count > MapRecursionLimit then
transformSlow(trees)
else trees match
case tree :: rest =>
val tree1 = transformStat(tree)
val rest1 = recur(rest, count + 1)(using restContext(tree))
if (tree1 eq tree) && (rest1 eq rest) then trees
else tree1 match
case Thicket(elems1) => elems1 ::: rest1
case _ => tree1 :: rest1
case nil => nil

val nestedCtx = prepStats(trees, start)
val trees1 = trees.mapInline(transformStat(_)(using nestedCtx))
val trees1 = recur(trees, 0)(using nestedCtx)
goStats(trees1, start)(using nestedCtx)
}
end transformStats

def transformUnit(tree: Tree)(using Context): Tree = {
val nestedCtx = prepUnit(tree, 0)
Expand Down
10 changes: 10 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import StdNames.nme
import ast.tpd
import SymUtils._
import config.Feature
import Decorators.*

/** This phase makes all erased term members of classes private so that they cannot
* conflict with non-erased members. This is needed so that subsequent phases like
* ResolveSuper that inspect class members work correctly.
* The phase also replaces all expressions that appear in an erased context by
* default values. This is necessary so that subsequent checking phases such
* as IsInstanceOfChecker don't give false negatives.
* Finally, the phase drops (language-) imports.
*/
class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
import tpd._
Expand Down Expand Up @@ -54,10 +56,18 @@ class PruneErasedDefs extends MiniPhase with SymTransformer { thisTransform =>
checkErasedInExperimental(tree.symbol)
tree

override def transformOther(tree: Tree)(using Context): Tree = tree match
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about making a separate miniphase for this but it's probably not worth the overhead of boilerplate. If we want to move the dropping of imports, we can easily split it out into a separate miniphase later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that was my thinking also.

case tree: Import => EmptyTree
case _ => tree

def checkErasedInExperimental(sym: Symbol)(using Context): Unit =
// Make an exception for Scala 2 experimental macros to allow dual Scala 2/3 macros under non experimental mode
if sym.is(Erased, butNot = Macro) && sym != defn.Compiletime_erasedValue && !sym.isInExperimentalScope then
Feature.checkExperimentalFeature("erased", sym.sourcePos)

override def checkPostCondition(tree: Tree)(using Context): Unit = tree match
case _: tpd.Import => assert(false, i"illegal tree: $tree")
case _ =>
}

object PruneErasedDefs {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2556,7 +2556,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
|The selector is not a member of an object or package.""")
else typd(imp.expr, AnySelectionProto)

def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Import =
def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Tree =
val expr1 = typedImportQualifier(imp, typedExpr(_, _)(using ctx.withOwner(sym)))
checkLegalImportPath(expr1)
val selectors1 = typedSelectors(imp.selectors)
Expand Down