RFR JDK-8203195: Anonymous class type inference results in NPE
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Jun 20 09:39:23 UTC 2018
Hi Vicente,
I think the fix might not be aiming at the right problem. If I recall
correctly (Srikanth, please correct me if I'm wrong), when you have an
anonymous diamond of the kind:
new Foo<>() { }
in a method context, during overload resolution javac drops the anon
definition, and uses something like this:
new Foo<>()
This is a per JLS/design, since the body of the anon class might depend
on the target type (which becomes the supertype of the anon class).
So what I suspect is happening here is that this:
new B<Object>() { }
is correct, because B has a protected constructor, which is called by
the synthetic default constructor added to the anon class.
But this:
new B<Object>()
is not correct, because it's invoking the protected constructor directly
from another package, so you get a spurious access error which is the
result of the specific lowering that has been applied to anonymous
diamond during overload selection.
This is in JLS 15.9.3:
"If the class instance creation expression uses <>, then:
If C is not an anonymous class, let D be the same as C. *If C is an
anonymous class, let D be the superclass or superinterface of C named by
the class instance creation expression*. "
Then:
"If D is a class, let |c_1 |...|c_n | be the constructors of class D"
And then:
"A list of methods |m_1 |...|m_n | is defined for the purpose of
overload resolution and type argument inference. For all /j/ (1 ≤ /j/ ≤
/n/), |m_j | is defined in terms of |c_j | as follows:"
Among the description on how to map mj to cj, we find this:
"*The modifiers of **|m_j |**are those of **|c_j |*"
And finally:
"To choose a constructor, we temporarily consider |m_1 |...|m_n | to be
members of D. One of |m_1 |...|m_n | is chosen, as determined by the
class instance creation expression's argument expressions, using the
process specified in §15.12.2
<https://docs.oracle.com/javase/specs/jls/se10/html/jls-15.html#jls-15.12.2>."
So, in this case, m1 used by the logic described above is simply B's
protected constructor - and it inherits its 'protected' status. This
means that m1 won't be callable from A (which resides in a different
package) and overload selection for the constructors yields an error.
In other words, without a spec change, I believe this code should be
rejected by javac (at least I can't find a basis to accept it with
current JLS). The NPE is probably coming from the fact that the
resolution error is swallowed (likely, by deferred attribution), not
reported, and the compiler goes ahead with flow analysis on a partially
unattributed tree.
Maurizio
On 20/06/18 02:47, Vicente Romero wrote:
> Please review the patch for [1] which can be found at [2]. The issue
> is a bit weird. This is the test case that shows it:
>
> A.java:
> ---------------------------------------------------------
> import foo.B;
>
> public class A {
>
> interface Foo {
> <T> T foo(B<T> key);
> }
>
> private Foo foo;
>
> A() {
> Object baz = foo.foo(new B<>() {});
> }
> }
> ---------------------------------------------------------
>
> B.java:
> ---------------------------------------------------------
> package foo;
>
> public class B<T> {
> /*
> // if the constructor is placed here the compilation is accepted
> B(int baz) {
> }
> */
>
> protected B() {
> }
>
>
> // but fails if placed here
> B(int baz) {
> }
>
> }
> ---------------------------------------------------------
>
> so depending on the order in which the constructor, B(int), appears,
> the output of the compiler is different. This code path is used for
> all methods so I had some doubts about modifying it but in any case I
> think that having a different outcome depending on the order in which
> a method appears in a class should be fixed. The proposed fix is to
> return an access error symbol in more cases than before at
> Resolve::selectBest.
>
> Thanks,
> Vicente
>
> [1] https://bugs.openjdk.java.net/browse/JDK-8203195
> [2] http://cr.openjdk.java.net/~vromero/8203195/webrev.00/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20180620/52e50010/attachment.html>
More information about the compiler-dev
mailing list