Skip to content

Commit b1aee82

Browse files
committed
Keep language imports around longer
Some phases might need to know what language imports are active. Concretely, uner explicit nulls, the exhaustivity checks for patterns need to know that. We therefore keep language imports until phase PruneErasedDefs. Other imports are dropped in FirstTransform, as before. The handling of statements in MegaPhase was changed so that imports now are visible in the context for transforming the following statements. Fixes #14346
1 parent cde9565 commit b1aee82

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ object FirstTransform {
2525
}
2626

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

138139
override def transformOther(tree: Tree)(using Context): Tree = tree match {
139-
case tree: ImportOrExport => EmptyTree
140+
case tree: Import if untpd.languageImport(tree.expr).isEmpty => EmptyTree
141+
case tree: Export => EmptyTree
140142
case tree: NamedArg => transformAllDeep(tree.arg)
141143
case tree => if (tree.isType) toTypeTree(tree) else tree
142144
}

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

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -432,16 +432,44 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
432432
def transformSpecificTree[T <: Tree](tree: T, start: Int)(using Context): T =
433433
transformTree(tree, start).asInstanceOf[T]
434434

435-
def transformStats(trees: List[Tree], exprOwner: Symbol, start: Int)(using Context): List[Tree] = {
435+
def transformStats(trees: List[Tree], exprOwner: Symbol, start: Int)(using Context): List[Tree] =
436+
436437
def transformStat(stat: Tree)(using Context): Tree = stat match {
437438
case _: Import | _: DefTree => transformTree(stat, start)
438439
case Thicket(stats) => cpy.Thicket(stat)(stats.mapConserve(transformStat))
439440
case _ => transformTree(stat, start)(using ctx.exprContext(stat, exprOwner))
440441
}
442+
443+
def restContext(tree: Tree)(using Context): Context = tree match
444+
case imp: Import => ctx.importContext(imp, imp.symbol)
445+
case _ => ctx
446+
447+
// A slower implementation that avoids deep recursions and stack overflows
448+
def transformSlow(trees: List[Tree])(using Context): List[Tree] =
449+
var curCtx = ctx
450+
flatten(trees.mapConserve { tree =>
451+
val tree1 = transformStat(tree)(using curCtx)
452+
curCtx = restContext(tree)(using curCtx)
453+
tree1
454+
})
455+
456+
def recur(trees: List[Tree], count: Int)(using Context): List[Tree] =
457+
if count > MapRecursionLimit then
458+
transformSlow(trees)
459+
else trees match
460+
case tree :: rest =>
461+
val tree1 = transformStat(tree)
462+
val rest1 = recur(rest, count + 1)(using restContext(tree))
463+
if (tree1 eq tree) && (rest1 eq rest) then trees
464+
else tree1 match
465+
case Thicket(elems1) => elems1 ::: rest1
466+
case _ => tree1 :: rest1
467+
case nil => nil
468+
441469
val nestedCtx = prepStats(trees, start)
442-
val trees1 = trees.mapInline(transformStat(_)(using nestedCtx))
470+
val trees1 = recur(trees, 0)(using nestedCtx)
443471
goStats(trees1, start)(using nestedCtx)
444-
}
472+
end transformStats
445473

446474
def transformUnit(tree: Tree)(using Context): Tree = {
447475
val nestedCtx = prepUnit(tree, 0)

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import StdNames.nme
1414
import ast.tpd
1515
import SymUtils._
1616
import config.Feature
17+
import Decorators.*
1718

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

59+
override def transformOther(tree: Tree)(using Context): Tree = tree match
60+
case tree: Import => EmptyTree
61+
case _ => tree
62+
5763
def checkErasedInExperimental(sym: Symbol)(using Context): Unit =
5864
// Make an exception for Scala 2 experimental macros to allow dual Scala 2/3 macros under non experimental mode
5965
if sym.is(Erased, butNot = Macro) && sym != defn.Compiletime_erasedValue && !sym.isInExperimentalScope then
6066
Feature.checkExperimentalFeature("erased", sym.sourcePos)
67+
68+
override def checkPostCondition(tree: Tree)(using Context): Unit = tree match
69+
case _: tpd.Import => assert(false, i"illegal tree: $tree")
70+
case _ =>
6171
}
6272

6373
object PruneErasedDefs {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2556,7 +2556,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
25562556
|The selector is not a member of an object or package.""")
25572557
else typd(imp.expr, AnySelectionProto)
25582558

2559-
def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Import =
2559+
def typedImport(imp: untpd.Import, sym: Symbol)(using Context): Tree =
25602560
val expr1 = typedImportQualifier(imp, typedExpr(_, _)(using ctx.withOwner(sym)))
25612561
checkLegalImportPath(expr1)
25622562
val selectors1 = typedSelectors(imp.selectors)

0 commit comments

Comments
 (0)