8000 Be more selectinv in REPL dead backreference elimination · scala/scala@fd6f95c · GitHub
[go: up one dir, main page]

Skip to content

Commit fd6f95c

Browse files
committed
Be more selectinv in REPL dead backreference elimination
- Only run the transform under `-Yrepl-class-based` - Only eliminate the `$lineXY$read` fields and accessors to be sure we don't accidentally eliminate statically unused user code, which could be intended for reflective use or the like. - Colocate the logic that creates and tests for `$lineXY$read` val names.
1 parent 4ae09b3 commit fd6f95c

File tree

4 files changed

+32
-12
lines changed

4 files changed

+32
-12
lines changed

src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ trait TypeDiagnostics {
497497

498498
class UnusedPrivates extends Traverser {
499499
def ignoreNames: Set[TermName] = UnusedPrivates.ignoreNames
500+
def isEffectivelyPrivate(sym: Symbol): Boolean = false
500501
val defnTrees = ListBuffer[MemberDef]()
501502
val targets = mutable.Set[Symbol]()
502503
val setVars = mutable.Set[Symbol]()
@@ -506,10 +507,9 @@ trait TypeDiagnostics {
506507

507508
def defnSymbols = defnTrees.toList map (_.symbol)
508509
def localVars = defnSymbols filter (t => t.isLocalToBlock && t.isVar)
509-
def isInterpreterLineReadVal(sym: Symbol): Boolean = sym.ownerChain.exists(_.isInterpreterWrapper) && sym.name.startsWith("$line")
510510

511511
def qualifiesTerm(sym: Symbol) = (
512-
(sym.isModule || sym.isMethod || sym.isPrivateLocal || sym.isLocalToBlock || isInterpreterLineReadVal(sym))
512+
(sym.isModule || sym.isMethod || sym.isPrivateLocal || sym.isLocalToBlock || isEffectivelyPrivate(sym))
513513
&& !nme.isLocalName(sym.name)
514514
&& !sym.isParameter
515515
&& !sym.isParamAccessor // could improve this, but it's a pain
@@ -583,7 +583,7 @@ trait TypeDiagnostics {
583583
def isUnusedType(m: Symbol): Boolean = (
584584
m.isType
585585
&& !m.isTypeParameterOrSkolem // would be nice to improve this
586-
&& (m.isPrivate || m.isLocalToBlock || isInterpreterLineReadVal(m))
586+
&& (m.isPrivate || m.isLocalToBlock || isEffectivelyPrivate(m))
587587
&& !(treeTypes.exists(_.exists(_.typeSymbolDirect == m)))
588588
)
589589
def isSyntheticWarnable(sym: Symbol) = (
@@ -592,7 +592,7 @@ trait TypeDiagnostics {
592592
def isUnusedTerm(m: Symbol): Boolean = (
593593
m.isTerm
594594
&& (!m.isSynthetic || isSyntheticWarnable(m))
595-
&& ((m.isPrivate && !(m.isConstructor && m.owner.isAbstract)) || m.isLocalToBlock || (m.owner.isInterpreterWrapper && !m.isConstructor) || isInterpreterLineReadVal(m))
595+
&& ((m.isPrivate && !(m.isConstructor && m.owner.isAbstract)) || m.isLocalToBlock || isEffectivelyPrivate(m))
596596
&& !targets(m)
597597
&& !(m.name == nme.WILDCARD) // e.g. val _ = foo
598598
&& (m.isValueParameter || !ignoreNames(m.name.toTermName)) // serialization/repl methods

src/repl/scala/tools/nsc/interpreter/IMain.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
702702
class ReadEvalPrint(val lineId: Int) {
703703
def this() = this(freshLineId())
704704

705-
val packageName = sessionNames.line + lineId
705+
val packageName = sessionNames.packageName(lineId)
706706
val readName = sessionNames.read
707707
val evalName = sessionNames.eval
708708
val printName = sessionNames.print

src/repl/scala/tools/nsc/interpreter/Naming.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ trait Naming {
5858
(raw"""($lineNRead|${q(sn.read)}(\.INSTANCE)?(\$$${q(sn.iw)})?|${q(sn.eval)}|${q(sn.print)}|${q(sn.iw)})""" + """(\.this\.|\.|/|\$\$(?=\$Lambda)|\$|$)""").r
5959
}
6060

61+
private val PositiveInt = """\d+""".r
6162
trait SessionNames {
6263
// All values are configurable by passing e.g. -Dscala.repl.name.read=XXX
6364
final def propOr(name: String): String = propOr(name, "$" + name)
@@ -71,6 +72,17 @@ trait Naming {
7172
def eval = propOr("eval")
7273
def print = propOr("print")
7374
def result = propOr("result")
75+
def packageName(lineId: Int) = line + lineId
76+
77+
/** Create the name for the temp val used in the -Yclass-based REPL wrapper to refer to the state of a previous line. */
78+
final def lineReadValName(linePackageName: String) = s"${linePackageName}${read}"
79+
/** Is the given name of the form created by `lineReadValName`? */
80+
final def isLineReadVal(name: Global#Name) = {
81+
name.startsWith(line) && name.endsWith(read) && (name.subSequence(line.length, name.length - read.length) match {
82+
case PositiveInt() => true
83+
case _ => false
84+
})
85+
}
7486

7587
// The prefix for unnamed results: by default res0, res1, etc.
7688
def res = propOr("res", "res") // INTERPRETER_VAR_PREFIX

src/repl/scala/tools/nsc/interpreter/ReplGlobal.scala

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,34 @@ trait ReplGlobal extends Global {
5656
override val runsAfter: List[String] = List("refchecks")
5757
/** Name of the phase that this phase must follow immediately. */
5858
override val runsRightAfter: Option[String] = None
59-
override protected def newTransformer(unit: CompilationUnit): Transformer = new WrapperCleanupTransformer(unit)
60-
class WrapperCleanupTransformer(unit: CompilationUnit) extends Transformer {
59+
override protected def newTransformer(unit: CompilationUnit): Transformer = new WrapperCleanupTransformer
60+
class WrapperCleanupTransformer extends Transformer {
61+
override def transformUnit(unit: CompilationUnit): Unit = {
62+
if (settings.Yreplclassbased.value) super.transformUnit(unit)
63+
}
64+
6165
def newUnusedPrivates: analyzer.UnusedPrivates = new analyzer.UnusedPrivates() {
6266
override lazy val ignoreNames = super.ignoreNames ++ {
6367
val sn = sessionNames
6468
Set(sn.line, sn.read, "INSTANCE", sn.iw, sn.eval, sn.print, sn.result).map(TermName(_))
6569
}
70+
override def isEffectivelyPrivate(sym: Symbol): Boolean = {
71+
sessionNames.isLineReadVal(sym.name)
72+
}
6673
}
6774

6875
override def transform(tree: Tree): Tree = super.transform(tree) match {
6976
case tree @ Template(parents, self, body) if nme.isReplWrapperName(tree.symbol.name) =>
7077
val unusedPrivates = newUnusedPrivates
7178
unusedPrivates.traverse(tree)
72-
val unusedSet = unusedPrivates.unusedTerms.iterator.flatMap(tree => List(tree.symbol, tree.symbol.accessedOrSelf)).toSet
73-
val (unused, used) = tree.body.partition (t => unusedSet(t.symbol))
74-
if (unused.isEmpty ) tree
79+
val unusedSyms = unusedPrivates.unusedTerms.iterator.map(_.symbol)
80+
val unusedLineReadVals = unusedSyms.filter(sym => sessionNames.isLineReadVal(sym.name)).flatMap(sym => List(sym, sym.accessedOrSelf)).toSet
81+
val (removedStats, retainedStats) = tree.body.partition (t => unusedLineReadVals(t.symbol))
82+
if (removedStats.isEmpty) tree
7583
else {
7684
val decls = tree.symbol.info.decls
77-
unused.foreach(tree => decls.unlink(tree.symbol))
78-
deriveTemplate(tree)(_ => used)
85+
removedStats.foreach(tree => decls.unlink(tree.symbol))
86+
deriveTemplate(tree)(_ => retainedStats)
7987
}
8088
case tree => tree
8189
}

0 commit comments

Comments
 (0)
0