RFR: JDK-8210197: candidate selection during overload resolution can lead to unexpected results for diamond expressions

Vicente Romero vicente.romero at oracle.com
Wed Nov 7 13:40:23 UTC 2018

Hi Maurizio,

Thanks for the feedback.

On 11/7/18 7:03 AM, Maurizio Cimadamore wrote:
> Btw,
> I think where javac is really tripping up is that it's not setting up 
> the env.info.selectSuper parameter ahead of performing the resolution 
> step. This parameter is setup like this:
> diamondEnv.info.selectSuper = cdef != null;

right I had the initial idea of going for this alternative first and 
wrote a patch see [3], but later convinced myself that the patch I sent 
in the original mail was the way to go :(. So [3] is a fix on the lines 
of setting diamondEnv.info.selectSuper to true for diamonds that had a 
class definition attached even during speculative attribution. But the 
patch seemed a bit hacky to me. I guess that we should probably just add 
a field to JCNewClass to indicate if the ClassDef part has been removed 
just during speculative attribution as a cleaner solution, what do you 

> I wonder, is speculative attribution completely messing this up (since 
> it is removing bodies from anon classes using diamond) ? If so, then 
> that is your issue (but it would still be nice if the spec text would 
> be made clearer).
> Maurizio

[3] http://cr.openjdk.java.net/~vromero/8210197/webrev.01/

> On 07/11/2018 08:59, Maurizio Cimadamore wrote:
>> Hi Vicente,
>> I have some reservation about the fix; the idea behind the code in 
>> Resolve is that there exist some 'ordering' of error messages (by 
>> usefulness) - typically if you have something that is NOT APPLICABLE 
>> and ACCESSIBLE you'd want that to be reported instead of some 
>> inaccessible symbol found somewhere else. This logic makes a lot of 
>> sense given that javac scans supertypes from the bottom up - meaning 
>> that if you reported every single access error, you'd get a lot of 
>> spurious hits. So this patch has the problem of altering that 
>> behavior: now accessibility errors can 'trump' applicability ones.
>> Another problem in the fix (maybe latent) is that you only create the 
>> access error if the bestSoFar is WRONG_MTH - but not if it's 
>> WRONG_MTHS, meaning that if the arity of the inapplicable symbols > 1 
>> your patch is not effective.
>> That said, instead of going down this route, I think it's worth 
>> asking a deeper (and JLS-related) question: what should be the 
>> modifiers of the synthetic constructor symbols that are used to 
>> resolve an anonymous class expression as per 15.9.3; that section 
>> says that the new constructors to be checked must be defined in the 
>> supertype of C (in this case MyClass itself) and that they must 
>> retain same modifiers (protected in this case):
>> "If the class instance creation expression uses <>, then:
>> [...]If C is an anonymous class, let D be the superclass or 
>> superinterface of C named by the class instance creation expression.
>> If D is a class, let c1...cn be the constructors of class D. [...]
>> A list of methods m1...mn is defined for the purpose of overload 
>> resolution and type argument inference. For all j (1 ≤ j ≤ n), mj is 
>> defined in terms of cj as follows:
>> ...
>> The modifiers of |m_j | are those of |c_j |"
>> Under those rules, it is not clear to me how could the logic 
>> described in the spec succeed in finding the right method - they 
>> should both appear as non accessible (they are defined in D and in a 
>> different package, but with 'protected' modifier), so this should apply:
>> "To choose a constructor, we temporarily consider m1...mn to be 
>> members of D. One of m1...mn is chosen, as determined by the class 
>> instance creation expression's argument expressions, using the 
>> process specified in §15.12.2.
>> If there is no unique most specific method that is both applicable 
>> and accessible, then a compile-time error occurs."
>> For what is worth, even the non-diamond path of the spec text seems 
>> to suffer from this problem: it delegates blindly to 15.12.2 w/o 
>> saying what should happen if one of the constructors has protected 
>> access, but for some reason, javac doesn't seem to have a problem 
>> with that - to show that, simply remove all generic arguments for the 
>> instance creation expressions in the test case (e.g. create raw 
>> instances of MyClass) and see that it works w/o issues.
>> There are a bunch of possible fixes here - if we are checking a 
>> diamond anon class we could either run 15.12.2 _pretending we are in 
>> D_, or we could drop any protected modifier. Both changes, I think, 
>> seem to imply some spec update.
>> Maurizio
>> On 07/11/2018 01:45, Vicente Romero wrote:
>>> Hi,
>>> Please review patch for [1] at [2]. The issue was can be explained 
>>> with these two simple classes:
>>> ------------------------------------------------------------------------------------------------------ 
>>> package pkg1;
>>> public class MyClass<T> {
>>>     protected MyClass() {}                // (i)
>>>     protected MyClass(String s) {}    // (ii)
>>> }
>>> ------------------------------------------------------------------------------------------------------ 
>>> package pkg2;
>>> import pkg1.*;
>>> class Client {
>>>     <T> void foo(MyClass<T> m) {}
>>>     void bar() {
>>>         foo(new MyClass<>(){});
>>>     }
>>> }
>>> ------------------------------------------------------------------------------------------------------ 
>>> both constructors are protected and given that we are dealing with a 
>>> diamond expression, speculative attribution is used. During 
>>> speculative attribution, javac removes the class definition from the 
>>> equation meaning that:
>>> new MyClass<>(){} is rewritten as:
>>> new MyClass<>()
>>> the first expression has access to the protected constructors, the 
>>> other one not.  So as both are protected, they are considered not 
>>> accessible during overload resolution. Still constructor (i) is 
>>> applicable but constructor (ii) is not applicable. So my proposal is 
>>> to create an access error at Resolve::selectBest also for this case. 
>>> Right now an access error is created only if no other method is 
>>> found. When an access error is returned during overload resolution, 
>>> the inaccessible symbol is still used and attached to the AST till 
>>> the check phase can double check and issue an error or not. As 
>>> during the check phase the diamond expression is attributed without 
>>> rewriting, the compiler can then find out that constructor (i) is 
>>> both applicable and accessible.
>>> Thanks,
>>> Vicente
>>> [1] https://bugs.openjdk.java.net/browse/JDK-8210197
>>> [2] http://cr.openjdk.java.net/~vromero/8210197/webrev.00/

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20181107/4e0180e3/attachment-0001.html>

More information about the compiler-dev mailing list