Skip to content

TailRec breaks path-dependent types in local-to-block method #1089

Closed
@sjrd

Description

@sjrd

This is a minimization of the bug I am encountering in #1061. As @smarter predicted, it is related to TailRec.

package hello

import scala.annotation.tailrec

class Enclosing {
  class SomeData(val x: Int)

  def localDef(): Unit = {
    def foo(data: SomeData): Int = data.x

    @tailrec
    def test(i: Int, data: SomeData): Unit = {
      if (i != 0) {
        println(foo(data))
        test(i - 1, data)
      }
    }

    test(3, new SomeData(42))
  }
}

object world extends App {
  println("hello dotty!")
  new Enclosing().localDef()
}

Compiling with -Ycheck:tailrec causes:

> run -Ycheck:checkReentrant,tailrec examples/hello.scala
[warn] Multiple main classes detected.  Run 'show discoveredMainClasses' to see the list
[info] Packaging /localhome/doeraene/projects/dotty/target/scala-2.11/dotty_2.11-0.1-SNAPSHOT.jar ...
[info] Done packaging.
[info] Running dotty.tools.dotc.Main -Ycheck:checkReentrant,tailrec examples/hello.scala
[error] checking examples/hello.scala after phase TreeTransform:{firstTransform, checkReentrant}
[error] checking examples/hello.scala after phase TreeTransform:{refchecks, elimRepeated, normalizeFlags, extmethods, expandSAMs, tailrec, liftTry, classOf}
[error] *** error while checking examples/hello.scala after phase classOf ***
[error] java.lang.AssertionError: assertion failed: error at examples/hello.scala:14
[error] type mismatch:
[error]  found   : $this.SomeData(data)
[error]  required: Enclosing.this.SomeData
[error] tree = Ident(data)
[error] scala.Predef$.assert(Predef.scala:165)
[error] dotty.tools.dotc.transform.TreeChecker$Checker.adapt(TreeChecker.scala:334)
[error] dotty.tools.dotc.typer.ProtoTypes$FunProto.typedArg(ProtoTypes.scala:205)
[error] dotty.tools.dotc.typer.Applications$ApplyToUntyped.typedArg(Applications.scala:513)
[error] dotty.tools.dotc.typer.Applications$ApplyToUntyped.typedArg(Applications.scala:511)
[error] dotty.tools.dotc.typer.Applications$Application.addTyped$1(Applications.scala:316)
[error] dotty.tools.dotc.typer.Applications$Application.matchArgs(Applications.scala:355)
[error] dotty.tools.dotc.typer.Applications$Application.init(Applications.scala:192)
[error] dotty.tools.dotc.typer.Applications$TypedApply.<init>(Applications.scala:434)
[error] dotty.tools.dotc.typer.Applications$ApplyToUntyped.<init>(Applications.scala:512)
... // and many more

Changing any of the following things will make it pass Ycheck:

  • Change Enclosing from class to object
  • Use String instead of SomeData
  • Move foo() outside of localDef()
  • Move both foo() and test() outside of localDef()
  • Alter test() so that it is not tail-recursive anymore
  • Move SomeData inside localDef()

The presence or absence of the annotation @tailrec has no impact. What matters is that test() is effectively tail-recursive.
Things that

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions