Skip to content

Tailrec for derivesFrom/lookupRefined/classSymbol/classSymbols #2043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
* owner to `to`, and continue until a non-weak owner is reached.
*/
def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = {
def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = {
@tailrec def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = {
if (from.isWeakOwner && !from.owner.isClass)
loop(from.owner, from :: froms, to :: tos)
else {
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/config/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import scala.util.{ Try, Success, Failure }
import scala.reflect.internal.util.StringOps
import reflect.ClassTag
import core.Contexts._
import scala.annotation.tailrec
// import annotation.unchecked
// Dotty deviation: Imports take precedence over definitions in enclosing package
// (Note that @unchecked is in scala, not annotation, so annotation.unchecked gives
Expand Down Expand Up @@ -217,7 +218,7 @@ object Settings {
case "--" :: args =>
checkDependencies(stateWithArgs(skipped ++ args))
case x :: _ if x startsWith "-" =>
def loop(settings: List[Setting[_]]): ArgsSummary = settings match {
@tailrec def loop(settings: List[Setting[_]]): ArgsSummary = settings match {
case setting :: settings1 =>
val state1 = setting.tryToSet(state)
if (state1 ne state) processArguments(state1, processAll, skipped)
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/core/Signature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package core
import Names._, Types._, Contexts._, StdNames._
import TypeErasure.sigName

import scala.annotation.tailrec

/** The signature of a denotation.
* Overloaded denotations with the same name are distinguished by
* their signatures. A signature of a method (of type PolyType,MethodType, or ExprType) is
Expand Down Expand Up @@ -41,7 +43,7 @@ case class Signature(paramsSig: List[TypeName], resSig: TypeName) {
* equal or on of them is tpnme.Uninstantiated.
*/
final def consistentParams(that: Signature): Boolean = {
def loop(names1: List[TypeName], names2: List[TypeName]): Boolean =
@tailrec def loop(names1: List[TypeName], names2: List[TypeName]): Boolean =
if (names1.isEmpty) names2.isEmpty
else names2.nonEmpty && consistent(names1.head, names2.head) && loop(names1.tail, names2.tail)
loop(this.paramsSig, that.paramsSig)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1040,7 +1040,7 @@ object SymDenotations {
* pre: `this.owner` is in the base class sequence of `base`.
*/
final def superSymbolIn(base: Symbol)(implicit ctx: Context): Symbol = {
def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
@tailrec def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
case bc :: bcs1 =>
val sym = matchingDecl(bcs.head, base.thisType)
.suchThat(alt => !(alt is Deferred)).symbol
Expand All @@ -1056,7 +1056,7 @@ object SymDenotations {
* (2) it is abstract override and its super symbol in `base` is
* nonexistent or incomplete.
*/
final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean =
@tailrec final def isIncompleteIn(base: Symbol)(implicit ctx: Context): Boolean =
(this is Deferred) ||
(this is AbsOverride) && {
val supersym = superSymbolIn(base)
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import dotc.transform.ExplicitOuter._
import dotc.transform.ValueClasses._
import util.DotClass
import Definitions.MaxImplementedFunctionArity
import scala.annotation.tailrec

/** Erased types are:
*
Expand Down Expand Up @@ -244,7 +245,7 @@ object TypeErasure {
case JavaArrayType(_) => defn.ObjectType
case _ =>
val cls2 = tp2.classSymbol
def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match {
@tailrec def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match {
case bc :: bcs1 =>
if (cls2.derivesFrom(bc))
if (!bc.is(Trait) && bc != defn.AnyClass) bc
Expand Down
72 changes: 36 additions & 36 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ object Types {
final def isValueTypeOrLambda: Boolean = isValueType || this.isInstanceOf[PolyType]

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

/** Is this type an instance of a non-bottom subclass of the given class `cls`? */
final def derivesFrom(cls: Symbol)(implicit ctx: Context): Boolean = {
def loop(tp: Type) = tp match {
def loop(tp: Type): Boolean = tp match {
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym.derivesFrom(cls) else tp.superType.derivesFrom(cls)
if (sym.isClass) sym.derivesFrom(cls) else loop(tp.superType): @tailrec
case tp: TypeProxy =>
tp.underlying.derivesFrom(cls)
loop(tp.underlying): @tailrec
case tp: AndType =>
tp.tp1.derivesFrom(cls) || tp.tp2.derivesFrom(cls)
loop(tp.tp1) || loop(tp.tp2): @tailrec
case tp: OrType =>
tp.tp1.derivesFrom(cls) && tp.tp2.derivesFrom(cls)
loop(tp.tp1) && loop(tp.tp2): @tailrec
case tp: JavaArrayType =>
cls == defn.ObjectClass
case _ =>
Expand All @@ -188,7 +188,7 @@ object Types {
final def isErroneous(implicit ctx: Context): Boolean = existsPart(_.isError, forceLazy = false)

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

/** The type symbol associated with the type */
final def typeSymbol(implicit ctx: Context): Symbol = this match {
@tailrec final def typeSymbol(implicit ctx: Context): Symbol = this match {
case tp: TypeRef => tp.symbol
case tp: ClassInfo => tp.cls
// case ThisType(cls) => cls // needed?
Expand All @@ -292,16 +292,16 @@ object Types {
*/
final def classSymbol(implicit ctx: Context): Symbol = this match {
case ConstantType(constant) =>
constant.tpe.classSymbol
constant.tpe.classSymbol: @tailrec
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym else tp.superType.classSymbol
if (sym.isClass) sym else tp.superType.classSymbol: @tailrec
case tp: ClassInfo =>
tp.cls
case tp: SingletonType =>
NoSymbol
case tp: TypeProxy =>
tp.underlying.classSymbol
tp.underlying.classSymbol: @tailrec
case AndType(l, r) =>
val lsym = l.classSymbol
val rsym = r.classSymbol
Expand All @@ -325,9 +325,9 @@ object Types {
tp.cls :: Nil
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols: @tailrec
case tp: TypeProxy =>
tp.underlying.classSymbols
tp.underlying.classSymbols: @tailrec
case AndType(l, r) =>
l.classSymbols union r.classSymbols
case OrType(l, r) =>
Expand All @@ -337,7 +337,7 @@ object Types {
}

/** The term symbol associated with the type */
final def termSymbol(implicit ctx: Context): Symbol = this match {
@tailrec final def termSymbol(implicit ctx: Context): Symbol = this match {
case tp: TermRef => tp.symbol
case tp: TypeProxy => tp.underlying.termSymbol
case _ => NoSymbol
Expand Down Expand Up @@ -368,11 +368,11 @@ object Types {
* Defined by ClassInfo, inherited by type proxies.
* Empty scope for all other types.
*/
final def decls(implicit ctx: Context): Scope = this match {
@tailrec final def decls(implicit ctx: Context): Scope = this match {
case tp: ClassInfo =>
tp.decls
case tp: TypeProxy =>
tp.underlying.decls
tp.underlying.decls: @tailrec
case _ =>
EmptyScope
}
Expand All @@ -394,7 +394,7 @@ object Types {
* name, as seen from prefix type `pre`. Declarations that have a flag
* in `excluded` are omitted.
*/
final def findDecl(name: Name, excluded: FlagSet)(implicit ctx: Context): Denotation = this match {
@tailrec final def findDecl(name: Name, excluded: FlagSet)(implicit ctx: Context): Denotation = this match {
case tp: ClassInfo =>
tp.decls.denotsNamed(name).filterExcluded(excluded).toDenot(NoPrefix)
case tp: TypeProxy =>
Expand Down Expand Up @@ -620,7 +620,7 @@ object Types {
val ns = tp.parent.memberNames(keepOnly, pre)
if (keepOnly(pre, tp.refinedName)) ns + tp.refinedName else ns
case tp: TypeProxy =>
tp.underlying.memberNames(keepOnly, pre)
tp.underlying.memberNames(keepOnly, pre): @tailrec
case tp: AndType =>
tp.tp1.memberNames(keepOnly, pre) | tp.tp2.memberNames(keepOnly, pre)
case tp: OrType =>
Expand Down Expand Up @@ -827,23 +827,23 @@ object Types {
* def o: Outer
* <o.x.type>.widen = o.C
*/
final def widen(implicit ctx: Context): Type = widenSingleton match {
@tailrec final def widen(implicit ctx: Context): Type = widenSingleton match {
case tp: ExprType => tp.resultType.widen
case tp => tp
}

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

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

/** Widen type if it is unstable (i.e. an ExprType, or TermRef to unstable symbol */
final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match {
@tailrec final def widenIfUnstable(implicit ctx: Context): Type = stripTypeVar match {
case tp: ExprType => tp.resultType.widenIfUnstable
case tp: TermRef if !tp.symbol.isStable => tp.underlying.widenIfUnstable
case _ => this
Expand All @@ -880,20 +880,20 @@ object Types {
case tp: TypeRef =>
if (tp.symbol.isClass) tp
else tp.info match {
case TypeAlias(tp) => tp.dealias(keepAnnots)
case TypeAlias(tp) => tp.dealias(keepAnnots): @tailrec
case _ => tp
}
case tp: TypeVar =>
val tp1 = tp.instanceOpt
if (tp1.exists) tp1.dealias(keepAnnots) else tp
if (tp1.exists) tp1.dealias(keepAnnots): @tailrec else tp
case tp: AnnotatedType =>
val tp1 = tp.tpe.dealias(keepAnnots)
if (keepAnnots) tp.derivedAnnotatedType(tp1, tp.annot) else tp1
case tp: LazyRef =>
tp.ref.dealias(keepAnnots)
tp.ref.dealias(keepAnnots): @tailrec
case app @ HKApply(tycon, args) =>
val tycon1 = tycon.dealias(keepAnnots)
if (tycon1 ne tycon) app.superType.dealias(keepAnnots)
if (tycon1 ne tycon) app.superType.dealias(keepAnnots): @tailrec
else this
case _ => this
}
Expand All @@ -913,7 +913,7 @@ object Types {
dealias(keepAnnots = false)

/** Perform successive widenings and dealiasings until none can be applied anymore */
final def widenDealias(implicit ctx: Context): Type = {
@tailrec final def widenDealias(implicit ctx: Context): Type = {
val res = this.widen.dealias
if (res eq this) res else res.widenDealias
}
Expand Down Expand Up @@ -996,22 +996,22 @@ object Types {
* (*) normalizes means: follow instantiated typevars and aliases.
*/
def lookupRefined(name: Name)(implicit ctx: Context): Type = {
def loop(pre: Type): Type = pre.stripTypeVar match {
@tailrec def loop(pre: Type): Type = pre.stripTypeVar match {
case pre: RefinedType =>
pre.refinedInfo match {
case TypeAlias(alias) =>
if (pre.refinedName ne name) loop(pre.parent) else alias
case _ => loop(pre.parent)
}
case pre: RecType =>
val candidate = loop(pre.parent)
val candidate = pre.parent.lookupRefined(name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change is not necessary. It will still get tail-rec optimized even without it.
You can use #1221 to mark per-callsite @tailrec-s

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I used it in several places. In this instance it was easier to use the old notation.

if (candidate.exists && !pre.isReferredToBy(candidate)) {
//println(s"lookupRefined ${this.toString} . $name, pre: $pre ---> $candidate / ${candidate.toString}")
candidate
}
else NoType
case SkolemType(tp) =>
tp.lookupRefined(name)
loop(tp)
case pre: WildcardType =>
WildcardType
case pre: TypeRef =>
Expand Down Expand Up @@ -1051,7 +1051,7 @@ object Types {
* Inherited by all other type proxies.
* `NoType` for all other types.
*/
final def normalizedPrefix(implicit ctx: Context): Type = this match {
@tailrec final def normalizedPrefix(implicit ctx: Context): Type = this match {
case tp: NamedType =>
if (tp.symbol.info.isAlias) tp.info.normalizedPrefix else tp.prefix
case tp: ClassInfo =>
Expand Down Expand Up @@ -1107,14 +1107,14 @@ object Types {


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

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

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

Expand Down Expand Up @@ -3676,7 +3676,7 @@ object Types {
this(x, prefix)

case tp @ HKApply(tycon, args) =>
def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T =
@tailrec def foldArgs(x: T, tparams: List[TypeParamInfo], args: List[Type]): T =
if (args.isEmpty) {
assert(tparams.isEmpty)
x
Expand Down Expand Up @@ -3719,7 +3719,7 @@ object Types {
case _ => x
}

final def foldOver(x: T, ts: List[Type]): T = ts match {
@tailrec final def foldOver(x: T, ts: List[Type]): T = ts match {
case t :: ts1 => foldOver(apply(x, t), ts1)
case nil => x
}
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/rewrite/Rewrites.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import util.{SourceFile, Positions}
import Positions.Position
import core.Contexts.{Context, FreshContext}
import collection.mutable
import scala.annotation.tailrec

/** Handles rewriting of Scala2 files to Dotty */
object Rewrites {
Expand All @@ -29,7 +30,7 @@ object Rewrites {
p2
}
val ds = new Array[Char](cs.length + delta)
def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = {
@tailrec def loop(ps: List[Patch], inIdx: Int, outIdx: Int): Unit = {
def copy(upTo: Int): Int = {
val untouched = upTo - inIdx
Array.copy(cs, inIdx, ds, outIdx, untouched)
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ import SymUtils._
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Phases.Phase
import util.Property

import collection.mutable
import scala.annotation.tailrec

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