Skip to content

Commit fc72bbb

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
1 parent a52482c commit fc72bbb

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
@@ -413,6 +413,9 @@ trait Contexts { self: Analyzer =>
413413
@inline final def withinSecondTry[T](op: => T): T = withMode(enabled = SecondTry)(op)
414414
@inline final def withinPatAlternative[T](op: => T): T = withMode(enabled = PatternAlternative)(op)
415415

416+
@inline final def withSuppressDeadArgWarning[T](suppress: Boolean)(op: => T): T =
417+
if (suppress) withMode(enabled = SuppressDeadArgWarning)(op) else withMode(disabled = SuppressDeadArgWarning)(op)
418+
416419
/** TypeConstructorAllowed is enabled when we are typing a higher-kinded type.
417420
* adapt should then check kind-arity based on the prototypical type's kind
418421
* arity. Type arguments should not be inferred.
@@ -1590,6 +1593,9 @@ object ContextMode {
15901593
/** Are unapplied type constructors allowed here? Formerly HKmode. */
15911594
final val TypeConstructorAllowed: ContextMode = 1 << 16
15921595

1596+
/** Should a dead code warning be issued for a Nothing-typed argument to the current application. */
1597+
final val SuppressDeadArgWarning: ContextMode = 1 << 17
1598+
15931599
/** TODO: The "sticky modes" are EXPRmode, PATTERNmode, TYPEmode.
15941600
* To mimic the sticky mode behavior, when captain stickyfingers
15951601
* comes around we need to propagate those modes but forget the other
@@ -1613,7 +1619,8 @@ object ContextMode {
16131619
StarPatterns -> "StarPatterns",
16141620
SuperInit -> "SuperInit",
16151621
SecondTry -> "SecondTry",
1616-
TypeConstructorAllowed -> "TypeConstructorAllowed"
1622+
TypeConstructorAllowed -> "TypeConstructorAllowed",
1623+
SuppressDeadArgWarning -> "SuppressDeadArgWarning"
16171624
)
16181625
}
16191626

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
@@ -2048,7 +2048,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
20482048
} else tpt1.tpe
20492049
transformedOrTyped(vdef.rhs, EXPRmode | BYVALmode, tpt2)
20502050
}
2051-
val vdef1 = treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(rhs1)) setType NoType
2051+
val vdef1 = treeCopy.ValDef(vdef, typedMods, sym.name, tpt1, checkDead(context, rhs1)) setType NoType
20522052
if (sym.isSynthetic && sym.name.startsWith(nme.RIGHT_ASSOC_OP_PREFIX))
20532053
rightAssocValDefs += ((sym, vdef1.rhs))
20542054
vdef1
@@ -2282,7 +2282,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
22822282
}
22832283

22842284
if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass)
2285-
rhs1 = checkDead(rhs1)
2285+
rhs1 = checkDead(context, rhs1)
22862286

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

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

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

31093109
if (treeInfo.isSelfOrSuperConstrCall(result)) {
31103110
context.inConstructorSuffix = true
@@ -3265,7 +3265,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
32653265
def typedArg(arg: Tree, mode: Mode, newmode: Mode, pt: Type): Tree = {
32663266
val typedMode = mode.onlySticky | newmode
32673267
val t = withCondConstrTyper(mode.inSccMode)(_.typed(arg, typedMode, pt))
3268-
checkDead.inMode(typedMode, t)
3268+
checkDead.inMode(context, typedMode, t)
32693269
}
32703270

32713271
def typedArgs(args: List[Tree], mode: Mode) =
@@ -3662,9 +3662,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
36623662
else
36633663
constfold(treeCopy.Apply(tree, fun, args2) setType ifPatternSkipFormals(restpe))
36643664
}
3665-
checkDead.updateExpr(fun) {
3666-
handleMonomorphicCall
3667-
}
3665+
if (settings.warnDeadCode) {
3666+
val sym = fun.symbol
3667+
if (sym != null && sym.isMethod && !sym.isConstructor) {
3668+
val suppress = sym == Object_synchronized || (sym.isLabel && treeInfo.isSynthCaseSymbol(sym))
3669+
context.withSuppressDeadArgWarning(suppress) {
3670+
handleMonomorphicCall
3671+
}
3672+
} else handleMonomorphicCall
3673+
} else handleMonomorphicCall
36683674
} else if (needsInstantiation(tparams, formals, args)) {
36693675
//println("needs inst "+fun+" "+tparams+"/"+(tparams map (_.info)))
36703676
inferExprInstance(fun, tparams)
@@ -4433,7 +4439,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
44334439
// (phase.erasedTypes && varsym.isValue && !varsym.isMethod)) {
44344440
if (varsym.isVariable || varsym.isValue && phase.assignsFields) {
44354441
val rhs1 = typedByValueExpr(rhs, lhs1.tpe)
4436-
treeCopy.Assign(tree, lhs1, checkDead(rhs1)) setType UnitTpe
4442+
treeCopy.Assign(tree, lhs1, checkDead(context, rhs1)) setType UnitTpe
44374443
}
44384444
else if(dyna.isDynamicallyUpdatable(lhs1)) {
44394445
val t = atPos(lhs1.pos.withEnd(rhs.pos.end)) {
@@ -4445,7 +4451,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
44454451
}
44464452

44474453
def typedIf(tree: If): If = {
4448-
val cond1 = checkDead(typedByValueExpr(tree.cond, BooleanTpe))
4454+
val cond1 = checkDead(context, typedByValueExpr(tree.cond, BooleanTpe))
44494455
// One-legged ifs don't need a lot of analysis
44504456
if (tree.elsep.isEmpty)
44514457
return treeCopy.If(tree, cond1, typed(tree.thenp, UnitTpe), tree.elsep) setType UnitTpe
@@ -4533,7 +4539,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
45334539
if (typed(expr).tpe.typeSymbol != UnitClass)
45344540
context.warning(tree.pos, "enclosing method " + name + " has result type Unit: return value discarded")
45354541
}
4536-
val res = treeCopy.Return(tree, checkDead(expr1)).setSymbol(enclMethod.owner)
4542+
val res = treeCopy.Return(tree, checkDead(context, expr1)).setSymbol(enclMethod.owner)
45374543
val tp = pluginsTypedReturn(NothingTpe, this, res, restpt.tpe)
45384544
res.setType(tp)
45394545
}
@@ -5104,7 +5110,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
51045110
typedSelect(tree, qualStableOrError, name)
51055111
} else {
51065112
if (StatisticsStatics.areSomeColdStatsEnabled) statistics.incCounter(typedSelectCount)
5107-
val qualTyped = checkDead(typedQualifier(qual, mode))
5113+
val qualTyped = checkDead(context, typedQualifier(qual, mode))
51085114
val tree1 = typedSelect(tree, qualTyped, name)
51095115

51105116
if (tree.isInstanceOf[PostfixSelect])
@@ -5406,7 +5412,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
54065412
if (funTyped.tpe.isInstanceOf[OverloadedType]) inferExprAlternative(funTyped, pt)
54075413
funTyped match {
54085414
case macroDef if treeInfo.isMacroApplication(macroDef) => MacroEtaError(macroDef)
5409-
case methodValue => typedEta(checkDead(methodValue))
5415+
case methodValue => typedEta(checkDead(context, methodValue))
54105416
}
54115417
case Typed(expr, tpt) =>
54125418
val tpt1 = typedType(tpt, mode) // type the ascribed type first

0 commit comments

Comments
 (0)