dotty
dotty copied to clipboard
JavaNull not propagated by type inference as anonymous function argument
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.
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.