Skip to content

Commit 141fb4b

Browse files
authored
Merge pull request #2043 from dotty-staging/tailrec-derivesFrom
Tailrec for derivesFrom/lookupRefined/classSymbol/classSymbols
2 parents 934da77 + be5720c commit 141fb4b

File tree

12 files changed

+60
-50
lines changed

12 files changed

+60
-50
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
@@ -5,6 +5,7 @@ import collection.mutable.{ ArrayBuffer }
55
import scala.util.{ Try, Success, Failure }
66
import reflect.ClassTag
77
import core.Contexts._
8+
import scala.annotation.tailrec
89
// import annotation.unchecked
910
// Dotty deviation: Imports take precedence over definitions in enclosing package
1011
// (Note that @unchecked is in scala, not annotation, so annotation.unchecked gives
@@ -216,7 +217,7 @@ object Settings {
216217
case "--" :: args =>
217218
checkDependencies(stateWithArgs(skipped ++ args))
218219
case x :: _ if x startsWith "-" =>
219-
def loop(settings: List[Setting[_]]): ArgsSummary = settings match {
220+
@tailrec def loop(settings: List[Setting[_]]): ArgsSummary = settings match {
220221
case setting :: settings1 =>
221222
val state1 = setting.tryToSet(state)
222223
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
@@ -1044,7 +1044,7 @@ object SymDenotations {
10441044
* pre: `this.owner` is in the base class sequence of `base`.
10451045
*/
10461046
final def superSymbolIn(base: Symbol)(implicit ctx: Context): Symbol = {
1047-
def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
1047+
@tailrec def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
10481048
case bc :: bcs1 =>
10491049
val sym = matchingDecl(bcs.head, base.thisType)
10501050
.suchThat(alt => !(alt is Deferred)).symbol
@@ -1060,7 +1060,7 @@ object SymDenotations {
10601060
* (2) it is abstract override and its super symbol in `base` is
10611061
* nonexistent or incomplete.
10621062
*/
1063-
final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean =
1063+
@tailrec final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean =
10641064
(this is Deferred) ||
10651065
(this is AbsOverride) && {
10661066
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
val newBest = if (bestSoFar.derivesFrom(bc)) bestSoFar else bc

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

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

108108
/** Does this type denote a stable reference (i.e. singleton type)? */
109-
final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
109+
@tailrec final def isStable(implicit ctx: Context): Boolean = stripTypeVar match {
110110
case tp: TermRef => tp.termSymbol.isStable && tp.prefix.isStable
111111
case _: SingletonType | NoPrefix => true
112112
case tp: RefinedOrRecType => tp.parent.isStable
@@ -154,16 +154,16 @@ object Types {
154154

155155
/** Is this type an instance of a non-bottom subclass of the given class `cls`? */
156156
final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = {
157-
def loop(tp: Type) = tp match {
157+
def loop(tp: Type): Boolean = tp match {
158158
case tp: TypeRef =>
159159
val sym = tp.symbol
160-
if (sym.isClass) sym.derivesFrom(cls) else tp.superType.derivesFrom(cls)
160+
if (sym.isClass) sym.derivesFrom(cls) else loop(tp.superType): @tailrec
161161
case tp: TypeProxy =>
162-
tp.underlying.derivesFrom(cls)
162+
loop(tp.underlying): @tailrec
163163
case tp: AndType =>
164-
tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls)
164+
loop(tp.tp1) || loop(tp.tp2): @tailrec
165165
case tp: OrType =>
166-
tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls)
166+
loop(tp.tp1) && loop(tp.tp2): @tailrec
167167
case tp: JavaArrayType =>
168168
cls == defn.ObjectClass
169169
case _ =>
@@ -189,7 +189,7 @@ object Types {
189189
final def isErroneous(implicit ctx: Context): Boolean = existsPart(_.isError, forceLazy = false)
190190

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

280280
/** The type symbol associated with the type */
281-
final def typeSymbol(implicit ctx: Context): Symbol = this match {
281+
@tailrec final def typeSymbol(implicit ctx: Context): Symbol = this match {
282282
case tp: TypeRef => tp.symbol
283283
case tp: ClassInfo => tp.cls
284284
// case ThisType(cls) => cls // needed?
@@ -293,16 +293,16 @@ object Types {
293293
*/
294294
final def classSymbol(implicit ctx: Context): Symbol = this match {
295295
case ConstantType(constant) =>
296-
constant.tpe.classSymbol
296+
constant.tpe.classSymbol: @tailrec
297297
case tp: TypeRef =>
298298
val sym = tp.symbol
299-
if (sym.isClass) sym else tp.superType.classSymbol
299+
if (sym.isClass) sym else tp.superType.classSymbol: @tailrec
300300
case tp: ClassInfo =>
301301
tp.cls
302302
case tp: SingletonType =>
303303
NoSymbol
304304
case tp: TypeProxy =>
305-
tp.underlying.classSymbol
305+
tp.underlying.classSymbol: @tailrec
306306
case AndType(l, r) =>
307307
val lsym = l.classSymbol
308308
val rsym = r.classSymbol
@@ -326,9 +326,9 @@ object Types {
326326
tp.cls :: Nil
327327
case tp: TypeRef =>
328328
val sym = tp.symbol
329-
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols
329+
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols: @tailrec
330330
case tp: TypeProxy =>
331-
tp.underlying.classSymbols
331+
tp.underlying.classSymbols: @tailrec
332332
case AndType(l, r) =>
333333
l.classSymbols union r.classSymbols
334334
case OrType(l, r) =>
@@ -338,7 +338,7 @@ object Types {
338338
}
339339

340340
/** The term symbol associated with the type */
341-
final def termSymbol(implicit ctx: Context): Symbol = this match {
341+
@tailrec final def termSymbol(implicit ctx: Context): Symbol = this match {
342342
case tp: TermRef => tp.symbol
343343
case tp: TypeProxy => tp.underlying.termSymbol
344344
case _ => NoSymbol
@@ -369,11 +369,11 @@ object Types {
369369
* Defined by ClassInfo, inherited by type proxies.
370370
* Empty scope for all other types.
371371
*/
372-
final def decls(implicit ctx: Context): Scope = this match {
372+
@tailrec final def decls(implicit ctx: Context): Scope = this match {
373373
case tp: ClassInfo =>
374374
tp.decls
375375
case tp: TypeProxy =>
376-
tp.underlying.decls
376+
tp.underlying.decls: @tailrec
377377
case _ =>
378378
EmptyScope
379379
}
@@ -395,7 +395,7 @@ object Types {
395395
* name, as seen from prefix type `pre`. Declarations that have a flag
396396
* in `excluded` are omitted.
397397
*/
398-
final def findDecl(name: Name, excluded: FlagSet)(implicit ctx: Context): Denotation = this match {
398+
@tailrec final def findDecl(name: Name, excluded: FlagSet)(implicit ctx: Context): Denotation = this match {
399399
case tp: ClassInfo =>
400400
tp.decls.denotsNamed(name).filterExcluded(excluded).toDenot(NoPrefix)
401401
case tp: TypeProxy =>
@@ -615,7 +615,7 @@ object Types {
615615
val ns = tp.parent.memberNames(keepOnly, pre)
616616
if (keepOnly(pre, tp.refinedName)) ns + tp.refinedName else ns
617617
case tp: TypeProxy =>
618-
tp.underlying.memberNames(keepOnly, pre)
618+
tp.underlying.memberNames(keepOnly, pre): @tailrec
619619
case tp: AndType =>
620620
tp.tp1.memberNames(keepOnly, pre) | tp.tp2.memberNames(keepOnly, pre)
621621
case tp: OrType =>
@@ -822,23 +822,23 @@ object Types {
822822
* def o: Outer
823823
* <o.x.type>.widen = o.C
824824
*/
825-
final def widen(implicit ctx: Context): Type = widenSingleton match {
825+
@tailrec final def widen(implicit ctx: Context): Type = widenSingleton match {
826826
case tp: ExprType => tp.resultType.widen
827827
case tp => tp
828828
}
829829

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

838838
/** Widen from TermRef to its underlying non-termref
839839
* base type, while also skipping Expr types.
840840
*/
841-
final def widenTermRefExpr(implicit ctx: Context): Type = stripTypeVar match {
841+
@tailrec final def widenTermRefExpr(implicit ctx: Context): Type = stripTypeVar match {
842842
case tp: TermRef if !tp.isOverloaded => tp.underlying.widenExpr.widenTermRefExpr
843843
case _ => this
844844
}
@@ -852,7 +852,7 @@ object Types {
852852
}
853853

854854
/** Widen type if it is unstable (i.e. an ExprType, or TermRef to unstable symbol */
855-
final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match {
855+
@tailrec final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match {
856856
case tp: ExprType => tp.resultType.widenIfUnstable
857857
case tp: TermRef if !tp.symbol.isStable => tp.underlying.widenIfUnstable
858858
case _ => this
@@ -875,20 +875,20 @@ object Types {
875875
case tp: TypeRef =>
876876
if (tp.symbol.isClass) tp
877877
else tp.info match {
878-
case TypeAlias(tp) => tp.dealias(keepAnnots)
878+
case TypeAlias(tp) => tp.dealias(keepAnnots): @tailrec
879879
case _ => tp
880880
}
881881
case tp: TypeVar =>
882882
val tp1 = tp.instanceOpt
883-
if (tp1.exists) tp1.dealias(keepAnnots) else tp
883+
if (tp1.exists) tp1.dealias(keepAnnots): @tailrec else tp
884884
case tp: AnnotatedType =>
885885
val tp1 = tp.tpe.dealias(keepAnnots)
886886
if (keepAnnots) tp.derivedAnnotatedType(tp1, tp.annot) else tp1
887887
case tp: LazyRef =>
888-
tp.ref.dealias(keepAnnots)
888+
tp.ref.dealias(keepAnnots): @tailrec
889889
case app @ HKApply(tycon, args) =>
890890
val tycon1 = tycon.dealias(keepAnnots)
891-
if (tycon1 ne tycon) app.superType.dealias(keepAnnots)
891+
if (tycon1 ne tycon) app.superType.dealias(keepAnnots): @tailrec
892892
else this
893893
case _ => this
894894
}
@@ -908,7 +908,7 @@ object Types {
908908
dealias(keepAnnots = false)
909909

910910
/** Perform successive widenings and dealiasings until none can be applied anymore */
911-
final def widenDealias(implicit ctx: Context): Type = {
911+
@tailrec final def widenDealias(implicit ctx: Context): Type = {
912912
val res = this.widen.dealias
913913
if (res eq this) res else res.widenDealias
914914
}
@@ -991,22 +991,22 @@ object Types {
991991
* (*) normalizes means: follow instantiated typevars and aliases.
992992
*/
993993
def lookupRefined(name: Name)(implicit ctx: Context): Type = {
994-
def loop(pre: Type): Type = pre.stripTypeVar match {
994+
@tailrec def loop(pre: Type): Type = pre.stripTypeVar match {
995995
case pre: RefinedType =>
996996
pre.refinedInfo match {
997997
case TypeAlias(alias) =>
998998
if (pre.refinedName ne name) loop(pre.parent) else alias
999999
case _ => loop(pre.parent)
10001000
}
10011001
case pre: RecType =>
1002-
val candidate = loop(pre.parent)
1002+
val candidate = pre.parent.lookupRefined(name)
10031003
if (candidate.exists && !pre.isReferredToBy(candidate)) {
10041004
//println(s"lookupRefined ${this.toString} . $name, pre: $pre ---> $candidate / ${candidate.toString}")
10051005
candidate
10061006
}
10071007
else NoType
10081008
case SkolemType(tp) =>
1009-
tp.lookupRefined(name)
1009+
loop(tp)
10101010
case pre: WildcardType =>
10111011
WildcardType
10121012
case pre: TypeRef =>
@@ -1046,7 +1046,7 @@ object Types {
10461046
* Inherited by all other type proxies.
10471047
* `NoType` for all other types.
10481048
*/
1049-
final def normalizedPrefix(implicit ctx: Context): Type = this match {
1049+
@tailrec final def normalizedPrefix(implicit ctx: Context): Type = this match {
10501050
case tp: NamedType =>
10511051
if (tp.symbol.info.isAlias) tp.info.normalizedPrefix else tp.prefix
10521052
case tp: ClassInfo =>
@@ -1102,14 +1102,14 @@ object Types {
11021102

11031103

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

11111111
/** Is this either not a method at all, or a parameterless method? */
1112-
final def isParameterless(implicit ctx: Context): Boolean = this match {
1112+
@tailrec final def isParameterless(implicit ctx: Context): Boolean = this match {
11131113
case mt: MethodType => false
11141114
case pt: PolyType => pt.resultType.isParameterless
11151115
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

@@ -3709,7 +3709,7 @@ object Types {
37093709
this(x, prefix)
37103710

37113711
case tp @ HKApply(tycon, args) =>
3712-
def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T =
3712+
@tailrec def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T =
37133713
if (args.isEmpty) {
37143714
assert(tparams.isEmpty)
37153715
x
@@ -3752,7 +3752,7 @@ object Types {
37523752
case _ => x
37533753
}
37543754

3755-
final def foldOver(x: T, ts: List[Type]): T = ts match {
3755+
@tailrec final def foldOver(x: T, ts: List[Type]): T = ts match {
37563756
case t :: ts1 => foldOver(apply(x, t), ts1)
37573757
case nil => x
37583758
}

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")

0 commit comments

Comments
 (0)