Skip to content

Commit be5720c

Browse files
committed
Add @tailrec to avoid regressions.
1 parent 1a49039 commit be5720c

File tree

12 files changed

+54
-44
lines changed

12 files changed

+54
-44
lines changed

compiler/src/dotty/tools/dotc/ast/tpd.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
607607
* owner to `to`, and continue until a non-weak owner is reached.
608608
*/
609609
def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = {
610-
def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = {
610+
@tailrec def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = {
611611
if (from.isWeakOwner && !from.owner.isClass)
612612
loop(from.owner, from :: froms, to :: tos)
613613
else {

compiler/src/dotty/tools/dotc/config/Settings.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import scala.util.{ Try, Success, Failure }
66
import scala.reflect.internal.util.StringOps
77
import reflect.ClassTag
88
import core.Contexts._
9+
import scala.annotation.tailrec
910
// import annotation.unchecked
1011
// Dotty deviation: Imports take precedence over definitions in enclosing package
1112
// (Note that @unchecked is in scala, not annotation, so annotation.unchecked gives
@@ -217,7 +218,7 @@ object Settings {
217218
case "--" :: args =>
218219
checkDependencies(stateWithArgs(skipped ++ args))
219220
case x :: _ if x startsWith "-" =>
220-
def loop(settings: List[Setting[_]]): ArgsSummary = settings match {
221+
@tailrec def loop(settings: List[Setting[_]]): ArgsSummary = settings match {
221222
case setting :: settings1 =>
222223
val state1 = setting.tryToSet(state)
223224
if (state1 ne state) processArguments(state1, processAll, skipped)

compiler/src/dotty/tools/dotc/core/Signature.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ package core
44
import Names._, Types._, Contexts._, StdNames._
55
import TypeErasure.sigName
66

7+
import scala.annotation.tailrec
8+
79
/** The signature of a denotation.
810
* Overloaded denotations with the same name are distinguished by
911
* their signatures. A signature of a method (of type PolyType,MethodType, or ExprType) is
@@ -41,7 +43,7 @@ case class Signature(paramsSig: List[TypeName], resSig: TypeName) {
4143
* equal or on of them is tpnme.Uninstantiated.
4244
*/
4345
final def consistentParams(that: Signature): Boolean = {
44-
def loop(names1: List[TypeName], names2: List[TypeName]): Boolean =
46+
@tailrec def loop(names1: List[TypeName], names2: List[TypeName]): Boolean =
4547
if (names1.isEmpty) names2.isEmpty
4648
else names2.nonEmpty && consistent(names1.head, names2.head) && loop(names1.tail, names2.tail)
4749
loop(this.paramsSig, that.paramsSig)

compiler/src/dotty/tools/dotc/core/SymDenotations.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,7 +1040,7 @@ object SymDenotations {
10401040
* pre: `this.owner` is in the base class sequence of `base`.
10411041
*/
10421042
final def superSymbolIn(base: Symbol)(implicit ctx: Context): Symbol = {
1043-
def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
1043+
@tailrec def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
10441044
case bc :: bcs1 =>
10451045
val sym = matchingDecl(bcs.head, base.thisType)
10461046
.suchThat(alt => !(alt is Deferred)).symbol
@@ -1056,7 +1056,7 @@ object SymDenotations {
10561056
* (2) it is abstract override and its super symbol in `base` is
10571057
* nonexistent or incomplete.
10581058
*/
1059-
final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean =
1059+
@tailrec final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean =
10601060
(this is Deferred) ||
10611061
(this is AbsOverride) && {
10621062
val supersym = superSymbolIn(base)

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotc.transform.ExplicitOuter._
1010
import dotc.transform.ValueClasses._
1111
import util.DotClass
1212
import Definitions.MaxImplementedFunctionArity
13+
import scala.annotation.tailrec
1314

1415
/** Erased types are:
1516
*
@@ -244,7 +245,7 @@ object TypeErasure {
244245
case JavaArrayType(_) => defn.ObjectType
245246
case _ =>
246247
val cls2 = tp2.classSymbol
247-
def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match {
248+
@tailrec def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match {
248249
case bc :: bcs1 =>
249250
if (cls2.derivesFrom(bc))
250251
if (!bc.is(Trait) && bc != defn.AnyClass) bc

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ object Types {
105105
final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[PolyType]
106106

107107
/** Does this type denote a stable reference (i.e. singleton type)? */
108-
final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
108+
@tailrec final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
109109
case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable
110110
case _: SingletonType | NoPrefix => true
111111
case tp: RefinedOrRecType => tp.parent.isStable
@@ -188,7 +188,7 @@ object Types {
188188
final def isErroneous(implicit ctx: Context): Boolean = existsPart(_.isError, forceLazy = false)
189189

190190
/** Does the type carry an annotation that is an instance of `cls`? */
191-
final def hasAnnotation(cls: ClassSymbol)(implicit ctx: Context): Boolean = stripTypeVar match {
191+
@tailrec final def hasAnnotation(cls: ClassSymbol)(implicit ctx: Context): Boolean = stripTypeVar match {
192192
case AnnotatedType(tp, annot) => (annot matches cls) || (tp hasAnnotation cls)
193193
case _ => false
194194
}
@@ -277,7 +277,7 @@ object Types {
277277
// ----- Associated symbols ----------------------------------------------
278278

279279
/** The type symbol associated with the type */
280-
final def typeSymbol(implicit ctx: Context): Symbol = this match {
280+
@tailrec final def typeSymbol(implicit ctx: Context): Symbol = this match {
281281
case tp: TypeRef => tp.symbol
282282
case tp: ClassInfo => tp.cls
283283
// case ThisType(cls) => cls // needed?
@@ -292,16 +292,16 @@ object Types {
292292
*/
293293
final def classSymbol(implicit ctx: Context): Symbol = this match {
294294
case ConstantType(constant) =>
295-
constant.tpe.classSymbol
295+
constant.tpe.classSymbol: @tailrec
296296
case tp: TypeRef =>
297297
val sym = tp.symbol
298-
if (sym.isClass) sym else tp.superType.classSymbol
298+
if (sym.isClass) sym else tp.superType.classSymbol: @tailrec
299299
case tp: ClassInfo =>
300300
tp.cls
301301
case tp: SingletonType =>
302302
NoSymbol
303303
case tp: TypeProxy =>
304-
tp.underlying.classSymbol
304+
tp.underlying.classSymbol: @tailrec
305305
case AndType(l, r) =>
306306
val lsym = l.classSymbol
307307
val rsym = r.classSymbol
@@ -325,9 +325,9 @@ object Types {
325325
tp.cls :: Nil
326326
case tp: TypeRef =>
327327
val sym = tp.symbol
328-
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols
328+
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols: @tailrec
329329
case tp: TypeProxy =>
330-
tp.underlying.classSymbols
330+
tp.underlying.classSymbols: @tailrec
331331
case AndType(l, r) =>
332332
l.classSymbols union r.classSymbols
333333
case OrType(l, r) =>
@@ -337,7 +337,7 @@ object Types {
337337
}
338338

339339
/** The term symbol associated with the type */
340-
final def termSymbol(implicit ctx: Context): Symbol = this match {
340+
@tailrec final def termSymbol(implicit ctx: Context): Symbol = this match {
341341
case tp: TermRef => tp.symbol
342342
case tp: TypeProxy => tp.underlying.termSymbol
343343
case _ => NoSymbol
@@ -368,11 +368,11 @@ object Types {
368368
* Defined by ClassInfo, inherited by type proxies.
369369
* Empty scope for all other types.
370370
*/
371-
final def decls(implicit ctx: Context): Scope = this match {
371+
@tailrec final def decls(implicit ctx: Context): Scope = this match {
372372
case tp: ClassInfo =>
373373
tp.decls
374374
case tp: TypeProxy =>
375-
tp.underlying.decls
375+
tp.underlying.decls: @tailrec
376376
case _ =>
377377
EmptyScope
378378
}
@@ -394,7 +394,7 @@ object Types {
394394
* name, as seen from prefix type `pre`. Declarations that have a flag
395395
* in `excluded` are omitted.
396396
*/
397-
final def findDecl(name: Name, excluded: FlagSet)(implicit ctx: Context): Denotation = this match {
397+
@tailrec final def findDecl(name: Name, excluded: FlagSet)(implicit ctx: Context): Denotation = this match {
398398
case tp: ClassInfo =>
399399
tp.decls.denotsNamed(name).filterExcluded(excluded).toDenot(NoPrefix)
400400
case tp: TypeProxy =>
@@ -620,7 +620,7 @@ object Types {
620620
val ns = tp.parent.memberNames(keepOnly, pre)
621621
if (keepOnly(pre, tp.refinedName)) ns + tp.refinedName else ns
622622
case tp: TypeProxy =>
623-
tp.underlying.memberNames(keepOnly, pre)
623+
tp.underlying.memberNames(keepOnly, pre): @tailrec
624624
case tp: AndType =>
625625
tp.tp1.memberNames(keepOnly, pre) | tp.tp2.memberNames(keepOnly, pre)
626626
case tp: OrType =>
@@ -827,23 +827,23 @@ object Types {
827827
* def o: Outer
828828
* <o.x.type>.widen = o.C
829829
*/
830-
final def widen(implicit ctx: Context): Type = widenSingleton match {
830+
@tailrec final def widen(implicit ctx: Context): Type = widenSingleton match {
831831
case tp: ExprType => tp.resultType.widen
832832
case tp => tp
833833
}
834834

835835
/** Widen from singleton type to its underlying non-singleton
836836
* base type by applying one or more `underlying` dereferences.
837837
*/
838-
final def widenSingleton(implicit ctx: Context): Type = stripTypeVar match {
838+
@tailrec final def widenSingleton(implicit ctx: Context): Type = stripTypeVar match {
839839
case tp: SingletonType if !tp.isOverloaded => tp.underlying.widenSingleton
840840
case _ => this
841841
}
842842

843843
/** Widen from TermRef to its underlying non-termref
844844
* base type, while also skipping Expr types.
845845
*/
846-
final def widenTermRefExpr(implicit ctx: Context): Type = stripTypeVar match {
846+
@tailrec final def widenTermRefExpr(implicit ctx: Context): Type = stripTypeVar match {
847847
case tp: TermRef if !tp.isOverloaded => tp.underlying.widenExpr.widenTermRefExpr
848848
case _ => this
849849
}
@@ -857,7 +857,7 @@ object Types {
857857
}
858858

859859
/** Widen type if it is unstable (i.e. an ExprType, or TermRef to unstable symbol */
860-
final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match {
860+
@tailrec final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match {
861861
case tp: ExprType => tp.resultType.widenIfUnstable
862862
case tp: TermRef if !tp.symbol.isStable => tp.underlying.widenIfUnstable
863863
case _ => this
@@ -880,20 +880,20 @@ object Types {
880880
case tp: TypeRef =>
881881
if (tp.symbol.isClass) tp
882882
else tp.info match {
883-
case TypeAlias(tp) => tp.dealias(keepAnnots)
883+
case TypeAlias(tp) => tp.dealias(keepAnnots): @tailrec
884884
case _ => tp
885885
}
886886
case tp: TypeVar =>
887887
val tp1 = tp.instanceOpt
888-
if (tp1.exists) tp1.dealias(keepAnnots) else tp
888+
if (tp1.exists) tp1.dealias(keepAnnots): @tailrec else tp
889889
case tp: AnnotatedType =>
890890
val tp1 = tp.tpe.dealias(keepAnnots)
891891
if (keepAnnots) tp.derivedAnnotatedType(tp1, tp.annot) else tp1
892892
case tp: LazyRef =>
893-
tp.ref.dealias(keepAnnots)
893+
tp.ref.dealias(keepAnnots): @tailrec
894894
case app @ HKApply(tycon, args) =>
895895
val tycon1 = tycon.dealias(keepAnnots)
896-
if (tycon1 ne tycon) app.superType.dealias(keepAnnots)
896+
if (tycon1 ne tycon) app.superType.dealias(keepAnnots): @tailrec
897897
else this
898898
case _ => this
899899
}
@@ -913,7 +913,7 @@ object Types {
913913
dealias(keepAnnots = false)
914914

915915
/** Perform successive widenings and dealiasings until none can be applied anymore */
916-
final def widenDealias(implicit ctx: Context): Type = {
916+
@tailrec final def widenDealias(implicit ctx: Context): Type = {
917917
val res = this.widen.dealias
918918
if (res eq this) res else res.widenDealias
919919
}
@@ -996,15 +996,15 @@ object Types {
996996
* (*) normalizes means: follow instantiated typevars and aliases.
997997
*/
998998
def lookupRefined(name: Name)(implicit ctx: Context): Type = {
999-
def loop(pre: Type): Type = pre.stripTypeVar match {
999+
@tailrec def loop(pre: Type): Type = pre.stripTypeVar match {
10001000
case pre: RefinedType =>
10011001
pre.refinedInfo match {
10021002
case TypeAlias(alias) =>
10031003
if (pre.refinedName ne name) loop(pre.parent) else alias
10041004
case _ => loop(pre.parent)
10051005
}
10061006
case pre: RecType =>
1007-
val candidate = loop(pre.parent)
1007+
val candidate = pre.parent.lookupRefined(name)
10081008
if (candidate.exists && !pre.isReferredToBy(candidate)) {
10091009
//println(s"lookupRefined ${this.toString} . $name, pre: $pre ---> $candidate / ${candidate.toString}")
10101010
candidate
@@ -1051,7 +1051,7 @@ object Types {
10511051
* Inherited by all other type proxies.
10521052
* `NoType` for all other types.
10531053
*/
1054-
final def normalizedPrefix(implicit ctx: Context): Type = this match {
1054+
@tailrec final def normalizedPrefix(implicit ctx: Context): Type = this match {
10551055
case tp: NamedType =>
10561056
if (tp.symbol.info.isAlias) tp.info.normalizedPrefix else tp.prefix
10571057
case tp: ClassInfo =>
@@ -1107,14 +1107,14 @@ object Types {
11071107

11081108

11091109
/** The parameter types in the first parameter section of a generic type or MethodType, Empty list for others */
1110-
final def firstParamTypes(implicit ctx: Context): List[Type] = this match {
1110+
@tailrec final def firstParamTypes(implicit ctx: Context): List[Type] = this match {
11111111
case mt: MethodType => mt.paramTypes
11121112
case pt: PolyType => pt.resultType.firstParamTypes
11131113
case _ => Nil
11141114
}
11151115

11161116
/** Is this either not a method at all, or a parameterless method? */
1117-
final def isParameterless(implicit ctx: Context): Boolean = this match {
1117+
@tailrec final def isParameterless(implicit ctx: Context): Boolean = this match {
11181118
case mt: MethodType => false
11191119
case pt: PolyType => pt.resultType.isParameterless
11201120
case _ => true
@@ -2101,7 +2101,7 @@ object Types {
21012101
}
21022102

21032103
object RefinedType {
2104-
def make(parent: Type, names: List[Name], infos: List[Type])(implicit ctx: Context): Type =
2104+
@tailrec def make(parent: Type, names: List[Name], infos: List[Type])(implicit ctx: Context): Type =
21052105
if (names.isEmpty) parent
21062106
else make(RefinedType(parent, names.head, infos.head), names.tail, infos.tail)
21072107

@@ -3676,7 +3676,7 @@ object Types {
36763676
this(x, prefix)
36773677

36783678
case tp @ HKApply(tycon, args) =>
3679-
def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T =
3679+
@tailrec def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T =
36803680
if (args.isEmpty) {
36813681
assert(tparams.isEmpty)
36823682
x
@@ -3719,7 +3719,7 @@ object Types {
37193719
case _ => x
37203720
}
37213721

3722-
final def foldOver(x: T, ts: List[Type]): T = ts match {
3722+
@tailrec final def foldOver(x: T, ts: List[Type]): T = ts match {
37233723
case t :: ts1 => foldOver(apply(x, t), ts1)
37243724
case nil => x
37253725
}

compiler/src/dotty/tools/dotc/rewrite/Rewrites.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import util.{SourceFile, Positions}
55
import Positions.Position
66
import core.Contexts.{Context, FreshContext}
77
import collection.mutable
8+
import scala.annotation.tailrec
89

910
/** Handles rewriting of Scala2 files to Dotty */
1011
object Rewrites {
@@ -29,7 +30,7 @@ object Rewrites {
2930
p2
3031
}
3132
val ds = new Array[Char](cs.length + delta)
32-
def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = {
33+
@tailrec def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = {
3334
def copy(upTo: Int): Int = {
3435
val untouched = upTo - inIdx
3536
Array.copy(cs, inIdx, ds, outIdx, untouched)

compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ import SymUtils._
1616
import dotty.tools.dotc.ast.tpd
1717
import dotty.tools.dotc.core.Phases.Phase
1818
import util.Property
19+
1920
import collection.mutable
21+
import scala.annotation.tailrec
2022

2123
/** This phase adds outer accessors to classes and traits that need them.
2224
* Compared to Scala 2.x, it tries to minimize the set of classes
@@ -367,7 +369,7 @@ object ExplicitOuter {
367369
def path(start: Tree = This(ctx.owner.lexicallyEnclosingClass.asClass),
368370
toCls: Symbol = NoSymbol,
369371
count: Int = -1): Tree = try {
370-
def loop(tree: Tree, count: Int): Tree = {
372+
@tailrec def loop(tree: Tree, count: Int): Tree = {
371373
val treeCls = tree.tpe.widen.classSymbol
372374
val outerAccessorCtx = ctx.withPhaseNoLater(ctx.lambdaLiftPhase) // lambdalift mangles local class names, which means we cannot reliably find outer acessors anymore
373375
ctx.log(i"outer to $toCls of $tree: ${tree.tpe}, looking for ${outerAccName(treeCls.asClass)(outerAccessorCtx)} in $treeCls")

compiler/src/dotty/tools/dotc/transform/SymUtils.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import StdNames._
1212
import NameOps._
1313
import Flags._
1414
import Annotations._
15+
1516
import language.implicitConversions
17+
import scala.annotation.tailrec
1618

1719
object SymUtils {
1820
implicit def decorateSymbol(sym: Symbol): SymUtils = new SymUtils(sym)
@@ -59,14 +61,14 @@ class SymUtils(val self: Symbol) extends AnyVal {
5961
}
6062

6163
/** The closest enclosing method or class of this symbol */
62-
final def enclosingMethodOrClass(implicit ctx: Context): Symbol =
64+
@tailrec final def enclosingMethodOrClass(implicit ctx: Context): Symbol =
6365
if (self.is(Method, butNot = Label) || self.isClass) self
6466
else if (self.exists) self.owner.enclosingMethodOrClass
6567
else NoSymbol
6668

6769
/** Apply symbol/symbol substitution to this symbol */
6870
def subst(from: List[Symbol], to: List[Symbol]): Symbol = {
69-
def loop(from: List[Symbol], to: List[Symbol]): Symbol =
71+
@tailrec def loop(from: List[Symbol], to: List[Symbol]): Symbol =
7072
if (from.isEmpty) self
7173
else if (self eq from.head) to.head
7274
else loop(from.tail, to.tail)

compiler/src/dotty/tools/io/Jar.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import java.util.jar._
1212
import scala.collection.JavaConverters._
1313
import Attributes.Name
1414
import scala.language.{postfixOps, implicitConversions}
15+
import scala.annotation.tailrec
1516

1617
// Attributes.Name instances:
1718
//
@@ -109,7 +110,7 @@ class JarWriter(val file: File, val manifest: Manifest) {
109110

110111
private def transfer(in: InputStream, out: OutputStream) = {
111112
val buf = new Array[Byte](10240)
112-
def loop(): Unit = in.read(buf, 0, buf.length) match {
113+
@tailrec def loop(): Unit = in.read(buf, 0, buf.length) match {
113114
case -1 => in.close()
114115
case n => out.write(buf, 0, n) ; loop
115116
}

0 commit comments

Comments
 (0)