diff --git a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala index 1ff9edda16d8..03ab3860a127 100644 --- a/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -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 @@ -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 _ => } @@ -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 } diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 77671fc6498c..a7a19d03e9ff 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -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 + 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) diff --git a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala index 7d8f0025eb15..e52b77379c27 100644 --- a/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/PruneErasedDefs.scala @@ -14,6 +14,7 @@ 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 @@ -21,6 +22,7 @@ import config.Feature * 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._ @@ -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 + 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 { diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 4c0c10f7c026..02f3f6b4f164 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -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)