Skip to content

Commit cb47197

Browse files
authored
Merge pull request scala#7876 from retronym/topic/tailrec-patrol
Tailrec patrol
2 parents 5e547be + 2849a0a commit cb47197

File tree

74 files changed

+298
-91
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+298
-91
lines changed

src/compiler/scala/reflect/macros/compiler/Errors.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ package scala.reflect.macros
1414
package compiler
1515

1616
import java.lang.System.{lineSeparator => EOL}
17+
import scala.annotation.tailrec
1718
import scala.reflect.macros.util.Traces
1819

1920
trait Errors extends Traces {
@@ -104,6 +105,7 @@ trait Errors extends Traces {
104105
private def checkConforms(slot: String, rtpe: Type, atpe: Type) = {
105106
val verbose = macroDebugVerbose
106107

108+
@tailrec
107109
def check(rtpe: Type, atpe: Type): Boolean = {
108110
def success() = { if (verbose) println(f"$rtpe <: $atpe?%ntrue"); true }
109111
(rtpe, atpe) match {

src/compiler/scala/reflect/reify/codegen/GenUtils.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
package scala.reflect.reify
1414
package codegen
1515

16+
import scala.annotation.tailrec
17+
1618
trait GenUtils {
1719
self: Reifier =>
1820

@@ -112,7 +114,8 @@ trait GenUtils {
112114
case _ => false
113115
}
114116

115-
def isCrossStageTypeBearer(tree: Tree): Boolean = tree match {
117+
@tailrec
118+
final def isCrossStageTypeBearer(tree: Tree): Boolean = tree match {
116119
case TypeApply(hk, _) => isCrossStageTypeBearer(hk)
117120
case Select(sym @ Select(_, ctor), nme.apply) if ctor == nme.WeakTypeTag || ctor == nme.TypeTag || ctor == nme.Expr => true
118121
case _ => false

src/compiler/scala/reflect/reify/phases/Reshape.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package scala.reflect.reify
1414
package phases
1515

16+
import scala.annotation.tailrec
1617
import scala.tools.nsc.symtab.Flags._
1718

1819
trait Reshape {
@@ -189,6 +190,7 @@ trait Reshape {
189190
CompoundTypeTree(Template(parents1, self, stats1))
190191
}
191192

193+
@tailrec
192194
private def toPreTyperTypedOrAnnotated(tree: Tree): Tree = tree match {
193195
case ty @ Typed(expr1, tpt) =>
194196
if (reifyDebug) println("reify typed: " + tree)
@@ -197,6 +199,7 @@ trait Reshape {
197199
case tpt => tpt
198200
}
199201
val annotatedArg = {
202+
@tailrec
200203
def loop(tree: Tree): Tree = tree match {
201204
case annotated1 @ Annotated(ann, annotated2 @ Annotated(_, _)) => loop(annotated2)
202205
case annotated1 @ Annotated(ann, arg) => arg

src/compiler/scala/tools/cmd/CommandLineParser.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ object CommandLineParser {
4949
while (!done && !terminal) pos += 1
5050
!done
5151
}
52+
@tailrec
5253
def skipToDelim(): Boolean =
5354
cur match {
5455
case q @ (DQ | SQ) => { qpos += pos; bump(); skipToQuote(q) } && { qpos += pos; bump(); skipToDelim() }

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import scala.tools.nsc.classpath._
4141
import scala.tools.nsc.profile.Profiler
4242
import scala.util.control.NonFatal
4343
import java.io.Closeable
44+
import scala.annotation.tailrec
4445

4546
class Global(var currentSettings: Settings, reporter0: LegacyReporter)
4647
extends SymbolTable
@@ -1381,7 +1382,8 @@ class Global(var currentSettings: Settings, reporter0: LegacyReporter)
13811382
// NOTE: Early initialized members temporarily typechecked before the enclosing class, see typedPrimaryConstrBody!
13821383
// Here we work around that wrinkle by claiming that a pre-initialized member is compiled in
13831384
// *every* run. This approximation works because this method is exclusively called with `this` == `currentRun`.
1384-
def compiles(sym: Symbol): Boolean =
1385+
@tailrec
1386+
final def compiles(sym: Symbol): Boolean =
13851387
if (sym == NoSymbol) false
13861388
else if (symSource.isDefinedAt(sym)) true
13871389
else if (!sym.isTopLevel) compiles(sym.originalEnclosingTopLevelClassOrDummy)
@@ -1642,6 +1644,7 @@ class Global(var currentSettings: Settings, reporter0: LegacyReporter)
16421644

16431645
/** Reset package class to state at typer (not sure what this is needed for?)
16441646
*/
1647+
@tailrec
16451648
private def resetPackageClass(pclazz: Symbol): Unit = if (typerPhase != NoPhase) {
16461649
enteringPhase(firstPhase) {
16471650
pclazz.setInfo(enteringPhase(typerPhase)(pclazz.info))

src/compiler/scala/tools/nsc/ast/DocComments.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package scala.tools.nsc
1414
package ast
1515

16+
import scala.annotation.tailrec
1617
import symtab._
1718
import util.DocStrings._
1819
import scala.collection.mutable
@@ -302,7 +303,8 @@ trait DocComments { self: Global =>
302303
* @param vble The variable for which a definition is searched
303304
* @param site The class for which doc comments are generated
304305
*/
305-
def lookupVariable(vble: String, site: Symbol): Option[String] = site match {
306+
@tailrec
307+
final def lookupVariable(vble: String, site: Symbol): Option[String] = site match {
306308
case NoSymbol => None
307309
case _ =>
308310
val searchList =
@@ -311,7 +313,8 @@ trait DocComments { self: Global =>
311313

312314
searchList collectFirst { case x if defs(x) contains vble => defs(x)(vble) } match {
313315
case Some(str) if str startsWith "$" => lookupVariable(str.tail, site)
314-
case res => res orElse lookupVariable(vble, site.owner)
316+
case s @ Some(str) => s
317+
case None => lookupVariable(vble, site.owner)
315318
}
316319
}
317320

@@ -326,6 +329,7 @@ trait DocComments { self: Global =>
326329
protected def expandVariables(initialStr: String, sym: Symbol, site: Symbol): String = {
327330
val expandLimit = 10
328331

332+
@tailrec
329333
def expandInternal(str: String, depth: Int): String = {
330334
if (depth >= expandLimit)
331335
throw new ExpansionLimitExceeded(str)
@@ -528,6 +532,7 @@ trait DocComments { self: Global =>
528532
(typeRef(NoPrefix, alias, Nil), false)
529533
}
530534

535+
@tailrec
531536
def subst(sym: Symbol, from: List[Symbol], to: List[(Type, Boolean)]): (Type, Boolean) =
532537
if (from.isEmpty) (sym.tpe, false)
533538
else if (from.head == sym) to.head

src/compiler/scala/tools/nsc/ast/TreeBrowsers.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import javax.swing.event.TreeModelListener
2424
import javax.swing.tree._
2525

2626
import java.util.concurrent.locks._
27+
import scala.annotation.tailrec
2728

2829
/**
2930
* Tree browsers can show the AST in a graphical and interactive
@@ -683,6 +684,7 @@ object TreeBrowsers {
683684
def format(width: Int, writer: Writer): Unit = {
684685
type FmtState = (Int, Boolean, Document)
685686

687+
@tailrec
686688
def fits(w: Int, state: List[FmtState]): Boolean = state match {
687689
case _ if w < 0 =>
688690
false
@@ -713,6 +715,7 @@ object TreeBrowsers {
713715
if (rem == 1) { writer write " " }
714716
}
715717

718+
@tailrec
716719
def fmt(k: Int, state: List[FmtState]): Unit = state match {
717720
case List() => ()
718721
case (_, _, DocNil) :: z =>

src/compiler/scala/tools/nsc/ast/TreeGen.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
package scala.tools.nsc
1414
package ast
1515

16+
import scala.annotation.tailrec
1617
import scala.collection.mutable.ListBuffer
1718
import symtab.Flags._
1819
import scala.reflect.internal.util.FreshNameCreator
@@ -265,6 +266,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
265266

266267
// used to create the lifted method that holds a function's body
267268
def mkLiftedFunctionBodyMethod(localTyper: global.analyzer.Typer)(owner: global.Symbol, fun: global.Function) = {
269+
@tailrec
268270
def nonLocalEnclosingMember(sym: Symbol): Symbol = {
269271
if (sym.isLocalDummy) sym.enclClass.primaryConstructor
270272
else if (sym.isLocalToBlock) nonLocalEnclosingMember(sym.originalOwner)

src/compiler/scala/tools/nsc/ast/TreeInfo.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ abstract class TreeInfo extends scala.reflect.internal.TreeInfo with MacroAnnoti
9595
case _ => super.isInterfaceMember(tree)
9696
}
9797

98-
override def isConstructorWithDefault(t: Tree) = t match {
98+
override def isConstructorWithDefault(t: Tree): Boolean = t match {
9999
case DocDef(_, definition) => isConstructorWithDefault(definition)
100100
case _ => super.isConstructorWithDefault(t)
101101
}

src/compiler/scala/tools/nsc/ast/parser/Parsers.scala

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ package ast.parser
1818

1919
import scala.collection.mutable
2020
import mutable.ListBuffer
21-
import scala.reflect.internal.{ Precedence, ModifierFlags => Flags }
21+
import scala.reflect.internal.{Precedence, ModifierFlags => Flags}
2222
import scala.reflect.internal.util.{ SourceFile, Position, FreshNameCreator, ListOfNil }
2323
import Tokens._
24+
import scala.annotation.tailrec
2425

2526
/** Historical note: JavaParsers started life as a direct copy of Parsers
2627
* but at a time when that Parsers had been replaced by a different one.
@@ -519,7 +520,8 @@ self =>
519520
t
520521
}
521522

522-
def isWildcard(t: Tree): Boolean = t match {
523+
@tailrec
524+
final def isWildcard(t: Tree): Boolean = t match {
523525
case Ident(name1) => !placeholderParams.isEmpty && name1 == placeholderParams.head.name
524526
case Typed(t1, _) => isWildcard(t1)
525527
case Annotated(t1, _) => isWildcard(t1)
@@ -955,6 +957,7 @@ self =>
955957
if (samePrecedence)
956958
checkHeadAssoc(leftAssoc)
957959

960+
@tailrec
958961
def loop(top: Tree): Tree = if (canReduce) {
959962
val info = popOpInfo()
960963
if (!isExpr && info.targs.nonEmpty) {
@@ -1087,7 +1090,8 @@ self =>
10871090
val point = if (name == tpnme.ERROR) hashOffset else nameOffset
10881091
atPos(t.pos.start, point)(SelectFromTypeTree(t, name))
10891092
}
1090-
def simpleTypeRest(t: Tree): Tree = in.token match {
1093+
@tailrec
1094+
final def simpleTypeRest(t: Tree): Tree = in.token match {
10911095
case HASH => simpleTypeRest(typeProjection(t))
10921096
case LBRACKET => simpleTypeRest(atPos(t.pos.start, t.pos.point)(AppliedTypeTree(t, typeArgs())))
10931097
case _ => t
@@ -1249,7 +1253,8 @@ self =>
12491253
t
12501254
}
12511255

1252-
def selectors(t: Tree, typeOK: Boolean, dotOffset: Offset): Tree =
1256+
@tailrec
1257+
final def selectors(t: Tree, typeOK: Boolean, dotOffset: Offset): Tree =
12531258
if (typeOK && in.token == TYPE) {
12541259
in.nextToken()
12551260
atPos(t.pos.start, dotOffset) { SingletonTypeTree(t) }
@@ -1682,6 +1687,7 @@ self =>
16821687
val start = in.offset
16831688
val base = opstack
16841689

1690+
@tailrec
16851691
def loop(top: Tree): Tree = if (!isIdent) top else {
16861692
pushOpInfo(reduceExprStack(base, top))
16871693
newLineOptWhenFollowing(isExprIntroToken)
@@ -1762,7 +1768,8 @@ self =>
17621768
simpleExprRest(t, canApply = canApply)
17631769
}
17641770

1765-
def simpleExprRest(t: Tree, canApply: Boolean): Tree = {
1771+
@tailrec
1772+
final def simpleExprRest(t: Tree, canApply: Boolean): Tree = {
17661773
if (canApply) newLineOptWhenFollowedBy(LBRACE)
17671774
in.token match {
17681775
case DOT =>
@@ -2047,6 +2054,7 @@ self =>
20472054
)
20482055
case _ => EmptyTree
20492056
}
2057+
@tailrec
20502058
def loop(top: Tree): Tree = reducePatternStack(base, top) match {
20512059
case next if isIdent && !isRawBar => pushOpInfo(next) ; loop(simplePattern(() => badPattern3()))
20522060
case next => next
@@ -2171,6 +2179,7 @@ self =>
21712179
/** Drop `private` modifier when followed by a qualifier.
21722180
* Contract `abstract` and `override` to ABSOVERRIDE
21732181
*/
2182+
@tailrec
21742183
private def normalizeModifiers(mods: Modifiers): Modifiers =
21752184
if (mods.isPrivate && mods.hasAccessBoundary)
21762185
normalizeModifiers(mods &~ Flags.PRIVATE)
@@ -2235,6 +2244,7 @@ self =>
22352244
* }}}
22362245
*/
22372246
def modifiers(): Modifiers = normalizeModifiers {
2247+
@tailrec
22382248
def loop(mods: Modifiers): Modifiers = in.token match {
22392249
case PRIVATE | PROTECTED =>
22402250
loop(accessQualifierOpt(addMod(mods, flagTokens(in.token), tokenRange(in))))
@@ -2255,6 +2265,7 @@ self =>
22552265
* }}}
22562266
*/
22572267
def localModifiers(): Modifiers = {
2268+
@tailrec
22582269
def loop(mods: Modifiers): Modifiers =
22592270
if (isLocalModifier) loop(addMod(mods, flagTokens(in.token), tokenRange(in)))
22602271
else mods

src/compiler/scala/tools/nsc/ast/parser/Scanners.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ trait Scanners extends ScannersCommon {
439439

440440
/** read next token, filling TokenData fields of Scanner.
441441
*/
442+
@tailrec
442443
protected final def fetchToken(): Unit = {
443444
offset = charOffset - 1
444445
(ch: @switch) match {
@@ -682,6 +683,7 @@ trait Scanners extends ScannersCommon {
682683
else syntaxError("unclosed quoted identifier")
683684
}
684685

686+
@tailrec
685687
private def getIdentRest(): Unit = (ch: @switch) match {
686688
case 'A' | 'B' | 'C' | 'D' | 'E' |
687689
'F' | 'G' | 'H' | 'I' | 'J' |
@@ -716,6 +718,7 @@ trait Scanners extends ScannersCommon {
716718
}
717719
}
718720

721+
@tailrec
719722
private def getOperatorRest(): Unit = (ch: @switch) match {
720723
case '~' | '!' | '@' | '#' | '%' |
721724
'^' | '*' | '+' | '-' | '<' |
@@ -1484,6 +1487,7 @@ trait Scanners extends ScannersCommon {
14841487
var tabSeen = false
14851488

14861489
def line(offset: Offset): Int = {
1490+
@tailrec
14871491
def findLine(lo: Int, hi: Int): Int = {
14881492
val mid = (lo + hi) / 2
14891493
if (offset < lineStart(mid)) findLine(lo, mid - 1)

src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
package scala.tools.nsc
1414
package backend.jvm
1515

16-
import scala.annotation.switch
16+
import scala.annotation.{switch, tailrec}
1717
import scala.reflect.internal.Flags
1818
import scala.tools.asm
1919
import scala.tools.asm.Opcodes
@@ -692,6 +692,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
692692
// Check if the Apply tree has an InlineAnnotatedAttachment, added by the typer
693693
// for callsites marked `f(): @inline/noinline`. For nullary calls, the attachment
694694
// is on the Select node (not on the Apply node added by UnCurry).
695+
@tailrec
695696
def recordInlineAnnotated(t: Tree): Unit = {
696697
if (t.hasAttachment[InlineAnnotatedAttachment]) lastInsn match {
697698
case m: MethodInsnNode =>
@@ -1115,6 +1116,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
11151116
}
11161117

11171118
/* Emit code to compare the two top-most stack values using the 'op' operator. */
1119+
@tailrec
11181120
private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label, negated: Boolean = false): Unit = {
11191121
if (targetIfNoJump == success) genCJUMP(failure, success, op.negate, tk, targetIfNoJump, negated = !negated)
11201122
else {
@@ -1136,6 +1138,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
11361138
}
11371139

11381140
/* Emits code to compare (and consume) stack-top and zero using the 'op' operator */
1141+
@tailrec
11391142
private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label, negated: Boolean = false): Unit = {
11401143
if (targetIfNoJump == success) genCZJUMP(failure, success, op.negate, tk, targetIfNoJump, negated = !negated)
11411144
else {

src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import scala.tools.nsc.backend.jvm.BCodeHelpers.ScalaSigBytes
2121
import scala.tools.nsc.reporters.NoReporter
2222

2323
import PartialFunction.cond
24+
import scala.annotation.tailrec
2425

2526
/*
2627
* Traits encapsulating functionality to convert Scala AST Trees into ASM ClassNodes.
@@ -131,7 +132,8 @@ abstract class BCodeHelpers extends BCodeIdiomatic {
131132
}
132133
}
133134

134-
def nextEnclosingClass(sym: Symbol): Symbol =
135+
@tailrec
136+
final def nextEnclosingClass(sym: Symbol): Symbol =
135137
if (sym.isClass) sym
136138
else nextEnclosingClass(nextEnclosing(sym))
137139

@@ -173,6 +175,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic {
173175
exitingPickler(enclCls.isDerivedValueClass) && method.owner != enclCls
174176
}
175177

178+
@tailrec
176179
def enclosingMethod(sym: Symbol): Option[Symbol] = {
177180
if (sym.isClass || sym == NoSymbol) None
178181
else if (sym.isMethod && !sym.isGetter) {
@@ -226,7 +229,8 @@ abstract class BCodeHelpers extends BCodeIdiomatic {
226229
* object T { def f { object U } }
227230
* the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here.
228231
*/
229-
def isOriginallyStaticOwner(sym: Symbol): Boolean =
232+
@tailrec
233+
final def isOriginallyStaticOwner(sym: Symbol): Boolean =
230234
sym.isPackageClass || sym.isModuleClass && isOriginallyStaticOwner(sym.originalOwner)
231235

232236
/**

0 commit comments

Comments
 (0)