-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix #2924: make TreeChecker.typedBlock iterative #3080
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
Changes from 2 commits
60591f8
82196a5
02f2688
7a3146a
20391ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -149,34 +149,42 @@ class TreeChecker extends Phase with SymTransformer { | |
|
||
def withDefinedSym[T](tree: untpd.Tree)(op: => T)(implicit ctx: Context): T = tree match { | ||
case tree: untpd.DefTree => | ||
val sym = tree.symbol | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the original logic was complicated - that foldRightBN is sneaky because it mixes laziness with side-effects ! The changes remove the foldRightBN complexity but make everything else more complex. Proposal: make
|
||
assert(isValidJVMName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm") | ||
everDefinedSyms.get(sym) match { | ||
case Some(t) => | ||
if (t ne tree) | ||
ctx.warning(i"symbol ${sym.fullName} is defined at least twice in different parts of AST") | ||
// should become an error | ||
case None => | ||
everDefinedSyms(sym) = tree | ||
} | ||
assert(!nowDefinedSyms.contains(sym), i"doubly defined symbol: ${sym.fullName} in $tree") | ||
|
||
if (ctx.settings.YcheckMods.value) { | ||
tree match { | ||
case t: untpd.MemberDef => | ||
if (t.name ne sym.name) ctx.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}") | ||
// todo: compare trees inside annotations | ||
case _ => | ||
} | ||
preWithDefinedSym(tree) | ||
postWithDefinedSym(tree)(op) | ||
case _ => op | ||
} | ||
|
||
private def preWithDefinedSym[T](tree: untpd.DefTree)(implicit ctx: Context): Unit = { | ||
val sym = tree.symbol | ||
assert(isValidJVMName(sym.name.encode), s"${sym.name.debugString} name is invalid on jvm") | ||
everDefinedSyms.get(sym) match { | ||
case Some(t) => | ||
if (t ne tree) | ||
ctx.warning(i"symbol ${sym.fullName} is defined at least twice in different parts of AST") | ||
// should become an error | ||
case None => | ||
everDefinedSyms(sym) = tree | ||
} | ||
assert(!nowDefinedSyms.contains(sym), i"doubly defined symbol: ${sym.fullName} in $tree") | ||
|
||
if (ctx.settings.YcheckMods.value) { | ||
tree match { | ||
case t: untpd.MemberDef => | ||
if (t.name ne sym.name) ctx.warning(s"symbol ${sym.fullName} name doesn't correspond to AST: ${t}") | ||
// todo: compare trees inside annotations | ||
case _ => | ||
} | ||
} | ||
} | ||
|
||
nowDefinedSyms += tree.symbol | ||
//ctx.echo(i"defined: ${tree.symbol}") | ||
val res = op | ||
nowDefinedSyms -= tree.symbol | ||
//ctx.echo(i"undefined: ${tree.symbol}") | ||
res | ||
case _ => op | ||
private def postWithDefinedSym[T](tree: untpd.DefTree)(op: => T)(implicit ctx: Context): T = { | ||
val sym = tree.symbol | ||
nowDefinedSyms += sym | ||
//ctx.echo(i"defined: ${tree.symbol}") | ||
val res = op | ||
nowDefinedSyms -= sym | ||
//ctx.echo(i"undefined: ${tree.symbol}") | ||
res | ||
} | ||
|
||
def withDefinedSyms[T](trees: List[untpd.Tree])(op: => T)(implicit ctx: Context) = | ||
|
@@ -411,8 +419,21 @@ class TreeChecker extends Phase with SymTransformer { | |
} | ||
} | ||
|
||
override def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = | ||
withDefinedSyms(tree.stats) { super.typedBlock(tree, pt) } | ||
override def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = { | ||
var locally = List.empty[Symbol] | ||
for (stat <- tree.stats) { | ||
stat match { | ||
case stat: untpd.DefTree => | ||
preWithDefinedSym(stat) | ||
locally = stat.symbol :: locally | ||
nowDefinedSyms += stat.symbol | ||
case _ => | ||
} | ||
} | ||
val res = super.typedBlock(tree, pt) | ||
nowDefinedSyms --= locally | ||
res | ||
} | ||
|
||
override def typedInlined(tree: untpd.Inlined, pt: Type)(implicit ctx: Context) = | ||
withDefinedSyms(tree.bindings) { super.typedInlined(tree, pt) } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a rather complex algorithm. Whenever I see reversed lists my head starts to hurt. Can we simplify it using ListBuffers? Also, if we keep it we need more docs to explain it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To clarify: I believe the original reorder algorithm was already close to the limit of tolerable complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking more about it, I believe the
revPrefix
idea is the best option. So OK to leave the code as is. Just document whatrevPrefix
stands for. I.e. at any point:Also, I'd not make
revPrefix
have a default parameter. To easy to make errors that way.