RFR JDK-8203195: Anonymous class type inference results in NPE
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
Wed Jun 20 10:28:04 UTC 2018
More specifically, and implementation-wise, the error is swallowed
because the env.info.selectSuper is set differently during the overload
vs. check phase (as this flag is set to true only if the new instance
creation has a body, which it doesn't as explained earlier, during
overload).
This discrepancy makes the compiler see an error during speculative
attribution (in ArgumentAttr) - the error is swallowed (as all
speculative errors), but is then not reproduced when we run in full
check mode because, when that happens, env.info.selectSuper is set
correctly.
The correct fix is to set this flag either to true (that would require a
spec change?) or to false but in a consistent way across overload and
check phases.
I can't find the spec equivalent for the javac's isSuper magic
incantation - not even for non-diamond anonymous classes. E.g. if the
superclass of an anonymous class has a protected constructor, and the
superclass is defined in a different package, how is the logic in the
JLS supposed to resolve it? The spec simply says:
"The process specified in §15.12.2
<https://docs.oracle.com/javase/specs/jls/se10/html/jls-15.html#jls-15.12.2>,
modified to handle constructors, is used to choose one of the
constructors of the direct superclass of C and determine its |throws|
clause."
In both diamond, non-diamond, anon and non-anon cases. No special access
relaxation seem to be implied here?
Maurizio
On 20/06/18 10:39, Maurizio Cimadamore wrote:
>
> 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/b0d7c248/attachment-0001.html>
More information about the compiler-dev
mailing list