Improve forward reference handling?

Thanks for raising the issue @cluelessjoe .

The short answer: we have an attack for exactly the problem you mentioned, and we are working on a compiler plugin implementation to make it available.

With our solution, the compiler will report an error when bar is used:

object ForwardReferenceGotcha {
  val foo = Foo(bar)      // error
  val bar = Bar()

  case class Bar(i: Int = 0)
  case class Foo(bar: Bar)

  def main(args: Array[String]): Unit = {
    println(foo)
  }
}

It handles inheritance:

abstract class AbstractFile {
   def name: String
   val extension: String = Path.extension(name)     // error
}

class RemoteFile(url: String) extends AbstractFile {
   val localFile: String = url.hashCode + ".tmp"
   def name: String = localFile
}

and linearization:

trait TA {
  val x = "world"
}
trait TB {
 def x: String
 val m = "hello" + x
}
class Foo extends TA with TB  // OK
class Bar extends TB with TA  // error

and inner classes:

object Trees {
  class ValDef { counter += 1 } // error
  class EmptyValDef extends ValDef
  val theEmptyValDef = new EmptyValDef // error
  private var counter = 0
}

and functions:

abstract class Parent {
  val f: () => String = () => this.message
  def message: String
}
class Child extends Parent {
  val a = f()                          // error
  val b = "hello"
  def message: String = b
}
class Foo {
  val even: Int => Boolean = (n: Int) => n == 0 || odd(n - 1)
  val flag1: Int = even(3)                            // error
  val odd: Int => Boolean = (n: Int) => n == 1 || even(n - 1)
  val flag2: Boolean = odd(6)
}

For all the features above, no annotation is required. However, to support leaking of this , an annotation like @cold is required:

class Parent { val child = new Child(this) }
class Child(parent: Parent @cold) {
  println(parent.child)      // error
}

The analysis will reject some anti-patterns of initializaiton, e.g., assigning this to an already initialized object. However, it will only report warnings in such cases (it reports errors only if it’s certain that an uninitialized field is used), so we hope it will not create big usability or migration problems.

We are happy to hear more your thoughts on the problem.

8 Likes