Skip to content

Commit 437a1e9

Browse files
committed
Improve efficiency of dead code checks
- Move the checkDead module out of Typer - Use a new bit in ContextMode to track when to suppress the warning, rather than maintaining the stack of the symbols of enclosing applications - Only do any of this when under -Ywarn-dead code References scala/scala-dev#501 (cherry picked from commit fc72bbb)
1 parent aac607b commit 437a1e9

File tree

3 files changed

+43
-44
lines changed

3 files changed

+43
-44
lines changed

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,9 @@ trait Contexts { self: Analyzer =>
405405
@inline final def withinSecondTry[T](op: => T): T = withMode(enabled = SecondTry)(op)
406406
@inline final def withinPatAlternative[T](op: => T): T = withMode(enabled = PatternAlternative)(op)
407407

408+
@inline final def withSuppressDeadArgWarning[T](suppress: Boolean)(op: => T): T =
409+
if (suppress) withMode(enabled = SuppressDeadArgWarning)(op) else withMode(disabled = SuppressDeadArgWarning)(op)
410+
408411
/** TypeConstructorAllowed is enabled when we are typing a higher-kinded type.
409412
* adapt should then check kind-arity based on the prototypical type's kind
410413
* arity. Type arguments should not be inferred.
@@ -1581,6 +1584,9 @@ object ContextMode {
15811584
/** Are unapplied type constructors allowed here? Formerly HKmode. */
15821585
final val TypeConstructorAllowed: ContextMode = 1 << 16
15831586

1587+
/** Should a dead code warning be issued for a Nothing-typed argument to the current application. */
1588+
final val SuppressDeadArgWarning: ContextMode = 1 << 17
1589+
15841590
/** TODO: The "sticky modes" are EXPRmode, PATTERNmode, TYPEmode.
15851591
* To mimic the sticky mode behavior, when captain stickyfingers
15861592
* comes around we need to propagate those modes but forget the other
@@ -1604,7 +1610,8 @@ object ContextMode {
16041610
StarPatterns -> "StarPatterns",
16051611
SuperInit -> "SuperInit",
16061612
SecondTry -> "SecondTry",
1607-
TypeConstructorAllowed -> "TypeConstructorAllowed"
1613+
TypeConstructorAllowed -> "TypeConstructorAllowed",
1614+
SuppressDeadArgWarning -> "SuppressDeadArgWarning"
16081615
)
16091616
}
16101617

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

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,22 @@ trait TypeDiagnostics {
466466
}
467467
}
468468

469+
object checkDead {
470+
private def treeOK(tree: Tree) = {
471+
val isLabelDef = tree match { case _: LabelDef => true; case _ => false}
472+
tree.tpe != null && tree.tpe.typeSymbol == NothingClass && !isLabelDef
473+
}
474+
475+
def apply(context: Context, tree: Tree): Tree = {
476+
if (settings.warnDeadCode && context.unit.exists && treeOK(tree) && !context.contextMode.inAny(ContextMode.SuppressDeadArgWarning))
477+
context.warning(tree.pos, "dead code following this construct")
478+
tree
479+
}
480+
481+
// The checkDead call from typedArg is more selective.
482+
def inMode(context: Context, mode: Mode, tree: Tree): Tree = if (mode.typingMonoExprByValue) apply(context, tree) else tree
483+
}
484+
469485
trait TyperDiagnostics {
470486
self: Typer =>
471487

@@ -714,36 +730,6 @@ trait TypeDiagnostics {
714730
}
715731
}
716732

717-
object checkDead {
718-
private val exprStack: mutable.Stack[Symbol] = mutable.Stack(NoSymbol)
719-
// The method being applied to `tree` when `apply` is called.
720-
private def expr = exprStack.top
721-
722-
private def exprOK =
723-
(expr != Object_synchronized) &&
724-
!(expr.isLabel && treeInfo.isSynthCaseSymbol(expr)) // it's okay to jump to matchEnd (or another case) with an argument of type nothing
725-
726-
private def treeOK(tree: Tree) = {
727-
val isLabelDef = tree match { case _: LabelDef => true; case _ => false}
728-
tree.tpe != null && tree.tpe.typeSymbol == NothingClass && !isLabelDef
729-
}
730-
731-
@inline def updateExpr[A](fn: Tree)(f: => A) = {
732-
if (fn.symbol != null && fn.symbol.isMethod && !fn.symbol.isConstructor) {
733-
exprStack push fn.symbol
734-
try f finally exprStack.pop()
735-
} else f
736-
}
737-
def apply(tree: Tree): Tree = {
738-
if (settings.warnDeadCode && context.unit.exists && treeOK(tree) && exprOK)
739-
context.warning(tree.pos, "dead code following this construct")
740-
tree
741-
}
742-
743-
// The checkDead call from typedArg is more selective.
744-
def inMode(mode: Mode, tree: Tree): Tree = if (mode.typingMonoExprByValue) apply(tree) else tree
745-
}
746-
747733
private def symWasOverloaded(sym: Symbol) = sym.owner.isClass && sym.owner.info.member(sym.name).isOverloaded
748734
private def cyclicAdjective(sym: Symbol) = if (symWasOverloaded(sym)) "overloaded" else "recursive"
749735

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

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2086,7 +2086,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
20862086
} else tpt1.tpe
20872087
transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2)
20882088
}
2089-
treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(rhs1)) setType NoType
2089+
treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(context, rhs1)) setType NoType
20902090
}
20912091

20922092
/** Enter all aliases of local parameter accessors.
@@ -2317,7 +2317,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
23172317
}
23182318

23192319
if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass)
2320-
rhs1 = checkDead(rhs1)
2320+
rhs1 = checkDead(context, rhs1)
23212321

23222322
if (!isPastTyper && meth.owner.isClass &&
23232323
meth.paramss.exists(ps => ps.exists(_.hasDefault) && isRepeatedParamType(ps.last.tpe)))
@@ -2557,7 +2557,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
25572557

25582558
// takes untyped sub-trees of a match and type checks them
25592559
def typedMatch(selector: Tree, cases: List[CaseDef], mode: Mode, pt: Type, tree: Tree = EmptyTree): Match = {
2560-
val selector1 = checkDead(typedByValueExpr(selector))
2560+
val selector1 = checkDead(context, typedByValueExpr(selector))
25612561
val selectorTp = packCaptured(selector1.tpe.widen).skolemizeExistential(context.owner, selector)
25622562
val casesTyped = typedCases(cases, selectorTp, pt)
25632563

@@ -3126,7 +3126,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
31263126
else newTyper(context.make(stat, exprOwner))
31273127
// XXX this creates a spurious dead code warning if an exception is thrown
31283128
// in a constructor, even if it is the only thing in the constructor.
3129-
val result = checkDead(localTyper.typedByValueExpr(stat))
3129+
val result = checkDead(context, localTyper.typedByValueExpr(stat))
31303130

31313131
if (treeInfo.isSelfOrSuperConstrCall(result)) {
31323132
context.inConstructorSuffix = true
@@ -3288,7 +3288,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
32883288
def typedArg(arg: Tree, mode: Mode, newmode: Mode, pt: Type): Tree = {
32893289
val typedMode = mode.onlySticky | newmode
32903290
val t = withCondConstrTyper(mode.inSccMode)(_.typed(arg, typedMode, pt))
3291-
checkDead.inMode(typedMode, t)
3291+
checkDead.inMode(context, typedMode, t)
32923292
}
32933293

32943294
def typedArgs(args: List[Tree], mode: Mode) =
@@ -3657,9 +3657,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
36573657
else
36583658
constfold(treeCopy.Apply(tree, fun, args1) setType ifPatternSkipFormals(restpe))
36593659
}
3660-
checkDead.updateExpr(fun) {
3661-
handleMonomorphicCall
3662-
}
3660+
if (settings.warnDeadCode) {
3661+
val sym = fun.symbol
3662+
if (sym != null && sym.isMethod && !sym.isConstructor) {
3663+
val suppress = sym == Object_synchronized || (sym.isLabel && treeInfo.isSynthCaseSymbol(sym))
3664+
context.withSuppressDeadArgWarning(suppress) {
3665+
handleMonomorphicCall
3666+
}
3667+
} else handleMonomorphicCall
3668+
} else handleMonomorphicCall
36633669
} else if (needsInstantiation(tparams, formals, args)) {
36643670
//println("needs inst "+fun+" "+tparams+"/"+(tparams map (_.info)))
36653671
inferExprInstance(fun, tparams)
@@ -4406,7 +4412,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
44064412
// (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) {
44074413
if (varsym.isVariable || varsym.isValue && phase.assignsFields) {
44084414
val rhs1 = typedByValueExpr(rhs, lhs1.tpe)
4409-
treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitTpe
4415+
treeCopy.Assign(tree, lhs1, checkDead(context, rhs1)) setType UnitTpe
44104416
}
44114417
else if(dyna.isDynamicallyUpdatable(lhs1)) {
44124418
val t = atPos(lhs1.pos.withEnd(rhs.pos.end)) {
@@ -4418,7 +4424,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
44184424
}
44194425

44204426
def typedIf(tree: If): If = {
4421-
val cond1 = checkDead(typedByValueExpr(tree.cond, BooleanTpe))
4427+
val cond1 = checkDead(context, typedByValueExpr(tree.cond, BooleanTpe))
44224428
// One-legged ifs don't need a lot of analysis
44234429
if (tree.elsep.isEmpty)
44244430
return treeCopy.If(tree, cond1, typed(tree.thenp, UnitTpe), tree.elsep) setType UnitTpe
@@ -4506,7 +4512,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
45064512
if (typed(expr).tpe.typeSymbol != UnitClass)
45074513
context.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded")
45084514
}
4509-
val res = treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner)
4515+
val res = treeCopy.Return(tree, checkDead(context, expr1)).setSymbol(enclMethod.owner)
45104516
val tp = pluginsTypedReturn(NothingTpe, this, res, restpt.tpe)
45114517
res.setType(tp)
45124518
}
@@ -5060,7 +5066,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
50605066
typedSelect(tree, qualStableOrError, name)
50615067
} else {
50625068
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(typedSelectCount)
5063-
val qualTyped = checkDead(typedQualifier(qual, mode))
5069+
val qualTyped = checkDead(context, typedQualifier(qual, mode))
50645070
val tree1 = typedSelect(tree, qualTyped, name)
50655071

50665072
if (tree.isInstanceOf[PostfixSelect])
@@ -5352,7 +5358,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
53525358
case MethodValue(expr) =>
53535359
typed1(suppressMacroExpansion(expr), mode, pt) match {
53545360
case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(macroDef)
5355-
case methodValue => typedEta(checkDead(methodValue).updateAttachment(MethodValueAttachment))
5361+
case methodValue => typedEta(checkDead(context, methodValue).updateAttachment(MethodValueAttachment))
53565362
}
53575363
case Typed(expr, tpt) =>
53585364
val tpt1 = typedType(tpt, mode) // type the ascribed type first

0 commit comments

Comments
 (0)