Skip to content

Commit

Permalink
-Wstrict-tailrec possible general recursion
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Mar 26, 2024
1 parent 732f695 commit a914532
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/settings/Warnings.scala
Expand Up @@ -121,6 +121,7 @@ trait Warnings {
val warnValueDiscard = BooleanSetting("-Wvalue-discard", "Warn when non-Unit expression results are unused.") withAbbreviation "-Ywarn-value-discard"
val warnNumericWiden = BooleanSetting("-Wnumeric-widen", "Warn when numerics are widened.") withAbbreviation "-Ywarn-numeric-widen"
val warnOctalLiteral = BooleanSetting("-Woctal-literal", "Warn on obsolete octal syntax.") withAbbreviation "-Ywarn-octal-literal"
val strictTailRec = BooleanSetting("-Wstrict-tailrec", "@tailrec warns on calls in functions or by-name args.")

object PerformanceWarnings extends MultiChoiceEnumeration {
val Captured = Choice("captured", "Modification of var in closure causes boxing.")
Expand Down
7 changes: 4 additions & 3 deletions src/compiler/scala/tools/nsc/transform/TailCalls.scala
Expand Up @@ -87,6 +87,7 @@ abstract class TailCalls extends Transform {
* </p>
*/
class TailCallElimination(unit: CompilationUnit) extends AstTransformer {
private val strict = settings.strictTailRec.value
private def defaultReason = "it contains a recursive call not in tail position"
private val failPositions = perRunCaches.newMap[TailContext, Position]().withDefault(_.methodPos)
private val failReasons = perRunCaches.newMap[TailContext, String]().withDefaultValue(defaultReason)
Expand Down Expand Up @@ -172,10 +173,10 @@ abstract class TailCalls extends Transform {
case Apply(fun, args) =>
if (isRecursiveCall(fun.symbol)) detected.addOne(tree)
traverse(fun)
for ((p, a) <- fun.symbol.paramLists.head.lazyZip(args) if !p.isByNameParam)
for ((p, a) <- fun.symbol.paramLists.head.lazyZip(args) if strict || !p.isByNameParam)
traverse(a)
case _: DefDef if ignore(tree.symbol) =>
case Function(_, _) =>
case _: DefDef if !strict && ignore(tree.symbol) =>
case Function(_, _) if !strict =>
case _ => super.traverse(tree)
}
def recursiveCalls(t: Tree): List[Tree] = {
Expand Down
7 changes: 7 additions & 0 deletions test/files/neg/t4649b.check
@@ -0,0 +1,7 @@
t4649b.scala:8: error: could not optimize @tailrec annotated method lazyFilter: it contains a recursive call not in tail position
case h #:: t => if (p(h)) h #:: lazyFilter(t, p) else lazyFilter(t, p) // error
^
t4649b.scala:21: error: could not optimize @tailrec annotated method f: it contains a recursive call not in tail position
val g: Int => Int = f(_) // error
^
2 errors
25 changes: 25 additions & 0 deletions test/files/neg/t4649b.scala
@@ -0,0 +1,25 @@
//> using options -Wstrict-tailrec

import annotation.tailrec

object Test {
@tailrec
def lazyFilter[E](s: LazyList[E], p: E => Boolean): LazyList[E] = s match {
case h #:: t => if (p(h)) h #:: lazyFilter(t, p) else lazyFilter(t, p) // error
}

@tailrec
def f(i: Int): Int =
if (i <= 0) i
/* not optimized
else if (i == 27) {
val x = f(i - 1)
x
}
*/
else if (i == 42) {
val g: Int => Int = f(_) // error
f(i - 1)
}
else f(i - 1)
}

0 comments on commit a914532

Please sign in to comment.