abeln / dotty

Scala with explicit nulls
https://github.com/abeln/dotty/wiki/scala-with-explicit-nulls
Other
23 stars 2 forks source link

JavaNull not propagated by type inference as anonymous function argument #15

Open abeln opened 5 years ago

abeln commented 5 years ago

This doesn't typecheck

object Test {

  def main(args: Array[String]): Unit = { 
    val f = new Foo 
    println(1)
    println(f.foo)
    println(2)
    println(f.foo)

    // TODO: Erase
    // Currently not erasing fields for lazy vals
    assert(f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?")
  }

}

class Foo {
  lazy val foo: Null = { 
    println("foo")
    null
  }
}

because

-- [E008] Member Not Found Error: tests/run/null-lazy-val.scala:13:49 ----------
13 |    assert(f.getClass.getDeclaredFields.exists(_.getName.startsWith("foo")), "Field foo erased. Optimized accidentally?")
   |                                               ^^^^^^^^^
   |       value `getName` is not a member of java.lang.reflect.Field | Null

The argument type for the lambda is inferred to be Field|Null, when it should be Field|JavaNull.

After frontend

        assert(
          refArrayOps[(java.lang.reflect.Field | Null)](
            f.getClass().getDeclaredFields().asInstanceOf[
              Array[java.lang.reflect.Field | JavaNull]
            ]
          ).exists(
            {
              def $anonfun(_$1: java.lang.reflect.Field | Null): Boolean = 
                _$1.getName.startsWith()
              closure($anonfun)
            }
          )

So the implicit conversion is applied, but the argument type is incorrectly inferred. This is because annotations are dropped when subtyping constraints are added to the set of current constraints.

The current workaround is to say .nn.getName.startsWith. I discussed this with Ondrej and we're gonna let it be for now.

Opening this to track the status.

abeln commented 5 years ago

In particular, lots of tests need this to do e.g. getClass.getMethods.find. So this could be alleviated by having more precise Java null conversion for methods in the Class class.