Skip to content

Commit c2a30ec

Browse files
Merge pull request #3080 from dotty-staging/improve-#2924
Fix #2924: make TreeChecker.typedBlock iterative
2 parents a3aa6e0 + 20391ec commit c2a30ec

File tree

5 files changed

+2090
-58
lines changed

5 files changed

+2090
-58
lines changed

compiler/src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,6 @@ object Decorators {
112112
else x1 :: xs1
113113
}
114114

115-
def foldRightBN[U](z: => U)(op: (T, => U) => U): U = {
116-
@tailrec def foldLeftBN(xs: List[T], acc: => U): U = xs match {
117-
case x :: xs1 => foldLeftBN(xs1, op(x, acc))
118-
case Nil => acc
119-
}
120-
foldLeftBN(xs.reverse, z)
121-
}
122-
123115
final def hasSameLengthAs[U](ys: List[U]): Boolean = {
124116
@tailrec def loop(xs: List[T], ys: List[U]): Boolean =
125117
if (xs.isEmpty) ys.isEmpty

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

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -92,26 +92,33 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota
9292
private def reorderAndComplete(stats: List[Tree])(implicit ctx: Context): List[Tree] = {
9393
val moduleClassDefs, singleClassDefs = mutable.Map[Name, Tree]()
9494

95-
def reorder(stats: List[Tree]): List[Tree] = stats match {
95+
/* Returns the result of reordering stats and prepending revPrefix in reverse order to it.
96+
* The result of reorder is equivalent to reorder(stats, revPrefix) = revPrefix.reverse ::: reorder(stats, Nil).
97+
* This implementation is tail recursive as long as the element is not a module TypeDef.
98+
*/
99+
def reorder(stats: List[Tree], revPrefix: List[Tree]): List[Tree] = stats match {
96100
case (stat: TypeDef) :: stats1 if stat.symbol.isClass =>
97101
if (stat.symbol is Flags.Module) {
102+
def pushOnTop(xs: List[Tree], ys: List[Tree]): List[Tree] =
103+
(ys /: xs)((ys, x) => x :: ys)
98104
moduleClassDefs += (stat.name -> stat)
99105
singleClassDefs -= stat.name.stripModuleClassSuffix
100-
val stats1r = reorder(stats1)
101-
if (moduleClassDefs contains stat.name) stat :: stats1r else stats1r
106+
val stats1r = reorder(stats1, Nil)
107+
pushOnTop(revPrefix, if (moduleClassDefs contains stat.name) stat :: stats1r else stats1r)
102108
} else {
103-
def stats1r = reorder(stats1)
104-
val normalized = moduleClassDefs remove stat.name.moduleClassName match {
105-
case Some(mcdef) =>
106-
mcdef :: stats1r
107-
case None =>
108-
singleClassDefs += (stat.name -> stat)
109-
stats1r
110-
}
111-
stat :: normalized
109+
reorder(
110+
stats1,
111+
moduleClassDefs remove stat.name.moduleClassName match {
112+
case Some(mcdef) =>
113+
mcdef :: stat :: revPrefix
114+
case None =>
115+
singleClassDefs += (stat.name -> stat)
116+
stat :: revPrefix
117+
}
118+
)
112119
}
113-
case stat :: stats1 => stat :: reorder(stats1)
114-
case Nil => Nil
120+
case stat :: stats1 => reorder(stats1, stat :: revPrefix)
121+
case Nil => revPrefix.reverse
115122
}
116123

117124
def registerCompanion(name: TermName, forClass: Symbol): TermSymbol = {
@@ -136,7 +143,7 @@ class FirstTransform extends MiniPhaseTransform with InfoTransformer with Annota
136143
case stat => stat
137144
}
138145

139-
addMissingCompanions(reorder(stats))
146+
addMissingCompanions(reorder(stats, Nil))
140147
}
141148

142149
private def newCompanion(name: TermName, forClass: Symbol)(implicit ctx: Context) = {

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

Lines changed: 32 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -147,44 +147,41 @@ class TreeChecker extends Phase with SymTransformer {
147147
// don't check value classes after typer, as the constraint about constructors doesn't hold after transform
148148
override def checkDerivedValueClass(clazz: Symbol, stats: List[Tree])(implicit ctx: Context) = ()
149149

150-
def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = tree match {
151-
case tree: untpd.DefTree =>
150+
def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(implicit ctx: Context) = {
151+
var locally = List.empty[Symbol]
152+
for (tree <- trees) {
152153
val sym = tree.symbol
153-
assert(isValidJVMName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm")
154-
everDefinedSyms.get(sym) match {
155-
case Some(t) =>
156-
if (t ne tree)
157-
ctx.warning(i"symbol ${sym.fullName} is defined at least twice in different parts of AST")
158-
// should become an error
159-
case None =>
160-
everDefinedSyms(sym) = tree
161-
}
162-
assert(!nowDefinedSyms.contains(sym), i"doubly defined symbol: ${sym.fullName} in $tree")
163-
164-
if (ctx.settings.YcheckMods.value) {
165-
tree match {
166-
case t: untpd.MemberDef =>
167-
if (t.name ne sym.name) ctx.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}")
168-
// todo: compare trees inside annotations
169-
case _ =>
170-
}
154+
tree match {
155+
case tree: untpd.DefTree =>
156+
assert(isValidJVMName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm")
157+
everDefinedSyms.get(sym) match {
158+
case Some(t) =>
159+
if (t ne tree)
160+
ctx.warning(i"symbol ${sym.fullName} is defined at least twice in different parts of AST")
161+
// should become an error
162+
case None =>
163+
everDefinedSyms(sym) = tree
164+
}
165+
assert(!nowDefinedSyms.contains(sym), i"doubly defined symbol: ${sym.fullName} in $tree")
166+
167+
if (ctx.settings.YcheckMods.value) {
168+
tree match {
169+
case t: untpd.MemberDef =>
170+
if (t.name ne sym.name) ctx.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}")
171+
// todo: compare trees inside annotations
172+
case _ =>
173+
}
174+
}
175+
locally = sym :: locally
176+
nowDefinedSyms += sym
177+
case _ =>
171178
}
172-
173-
nowDefinedSyms += tree.symbol
174-
//ctx.echo(i"defined: ${tree.symbol}")
175-
val res = op
176-
nowDefinedSyms -= tree.symbol
177-
//ctx.echo(i"undefined: ${tree.symbol}")
178-
res
179-
case _ => op
179+
}
180+
val res = op
181+
nowDefinedSyms --= locally
182+
res
180183
}
181184

182-
def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(implicit ctx: Context) =
183-
trees.foldRightBN(op)(withDefinedSym(_)(_))
184-
185-
def withDefinedSymss[T](vparamss: List[List[untpd.ValDef]])(op: => T)(implicit ctx: Context): T =
186-
vparamss.foldRightBN(op)(withDefinedSyms(_)(_))
187-
188185
def assertDefined(tree: untpd.Tree)(implicit ctx: Context) =
189186
if (
190187
tree.symbol.maybeOwner.isTerm &&
@@ -388,7 +385,7 @@ class TreeChecker extends Phase with SymTransformer {
388385

389386
override def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) =
390387
withDefinedSyms(ddef.tparams) {
391-
withDefinedSymss(ddef.vparamss) {
388+
withDefinedSyms(ddef.vparamss.flatten) {
392389
if (!sym.isClassConstructor && !(sym.name eq nme.STATIC_CONSTRUCTOR))
393390
assert(isValidJVMMethodName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm")
394391

0 commit comments

Comments
 (0)