Javac Bug?

David Holmes david.holmes at oracle.com
Sat Nov 16 09:07:53 PST 2013


On 17/11/2013 12:59 AM, Zhong Yu wrote:
> So this is a bug in the spec text, which indicates that the kind of
> return (void or value) plays a roles here?

Or a bug in javac as it doesn't implement the current spec?

Either way there is a bug. Even if there is a desire to do something 
different/better in 9, there should be consistency between the spec and 
javac in 8.

David
-----

> Zhong Yu
>
>
> On Sat, Nov 16, 2013 at 8:14 AM, Remi Forax <forax at univ-mlv.fr> wrote:
>> On 11/15/2013 06:25 PM, David Holmes wrote:
>>> On 16/11/2013 1:36 AM, Zhong Yu wrote:
>>>> On Fri, Nov 15, 2013 at 9:15 AM, Vicente-Arturo Romero-Zaldivar
>>>> <vicente.romero at oracle.com> wrote:
>>>>> On 15/11/13 14:51, Zhong Yu wrote:
>>>>>> The return type seems also play a role here, see
>>>>>> http://cr.openjdk.java.net/~dlsmith/jsr335-0.7.0/F.html
>>>>>>
>>>>>> ""
>>>>>> A lambda expression (15.27) is potentially compatible with a
>>>>>> functional interface type (9.8) if all of the following are true:
>>>>>>
>>>>>> The arity of the targeted type's function type is the same as the
>>>>>> arity of the lambda expression.
>>>>>> If the targeted type's function type has a void return, then the
>>>>>> lambda body is either a statement expression (14.8) or a
>>>>>> void-compatible block (15.27.2).
>>>>>> If the targeted type's function type has a (non-void) return type,
>>>>>> then the lambda body is either an expression or a value-compatible
>>>>>> block (15.27.2).
>>>>>> ""
>>>>>>
>>>>>> Therefore, method
>>>>>>         public void forEach(Consumer<? super T> consumer)
>>>>>> is not potentially applicable to invocation
>>>>>>         forEach(v -> { return biPredicate.test(1, v); } );
>>>>>> because the function type of Consumer has a void return, yet the
>>>>>> lambda body is a value-compatible block.
>>>>>
>>>>> The compiler analyze lambda expressions in several phases:
>>>>>
>>>>> first analysis is by arity, if there are two possible applicable methods by
>>>>> arity the compiler issues an error. If a method pass to the next phase, then
>>>>> the rest of the information is taken into account.
>>>> But the spec text I quoted mentions both arity and return type in the
>>>> phase of identifying potentially applicable methods (15.12.2.1)
>>> I have to agree - the compiler needs to consider all of the conditions
>>> combined as is written in the spec. It should not declare ambiguity
>>> based on arity alone when the return type would have reduced things to a
>>> single applicable method.
>>>
>>> David
>>> -----
>>
>> Hi David, Hi all,
>> the lambda EG has decided that for 8 we will spec a very basic inference
>> algorithm
>> when overloading and lambdas are used at the same time, we have explored
>> many algorithms
>> and even found one that seems to work but we don't have the time to be
>> sure that it doesn't
>> introduce weird behavior (as the previous proposed algorithms).
>> So instead of releasing a half baked algorithm, we backoff to a simple
>> algorithm
>> that disambiguate using the arity only to be sure to not hamper our
>> chance to have a better one in 9.
>>
>> cheers,
>> Rémi
>>
>>>
>>>>>
>>>>>>
>>>>>> On Fri, Nov 15, 2013 at 7:57 AM, Vicente-Arturo Romero-Zaldivar
>>>>>> <vicente.romero at oracle.com> wrote:
>>>>>>> On 13/11/13 19:04, Zhong Yu wrote:
>>>>>>>> This program does not compile in javac8 build 115. I think it should
>>>>>>>> compile (but who knows really:)
>>>>>>>>
>>>>>>>> public class Bug<T> {
>>>>>>>>          public void forEach(Consumer<? super T> consumer) {
>>>>>>>>              // ...
>>>>>>>>          }
>>>>>>>>          public void forEach(Predicate<? super T> predicate) {
>>>>>>>>              // ...
>>>>>>>>          }
>>>>>>>>          public void forEach(BiConsumer<? super Integer, ? super T>
>>>>>>>> biConsumer) {
>>>>>>>>              forEach((v) -> { biConsumer.accept(1, v); });
>>>>>>>>          }
>>>>>>>>          public void forEach(BiPredicate<? super Integer, ? super T>
>>>>>>>> biPredicate) {
>>>>>>>>              forEach(v -> { return biPredicate.test(1, v); } );
>>>>>>>>          }
>>>>>>>> }
>>>>>>> Hi Zhong Yu,
>>>>>>>
>>>>>>> No the compiler is right functional descriptors of Consumer and Predicate
>>>>>>> have the same arity, so the compiler determines that there is an
>>>>>>> ambiguity
>>>>>>> error.
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Vicente
>>>>>>>
>>>>>>>>
>>>>>>>> On Wed, Nov 13, 2013 at 12:59 PM, Zhong Yu <zhong.j.yu at gmail.com> wrote:
>>>>>>>>> Here's my understanding how the process works.
>>>>>>>>>
>>>>>>>>> When javac sees
>>>>>>>>>
>>>>>>>>>          forEach( v->... )
>>>>>>>>>
>>>>>>>>> two pieces of information of the implicit lambda are used to prune
>>>>>>>>> overloading methods:
>>>>>>>>> 1. the arity. therefore BiConsumer/BiPredicate are excluded
>>>>>>>>> 2. whether the lambda body returns void or a real type.
>>>>>>>>>
>>>>>>>>> If the lambda body is a block, it's quite clear whether it's void or
>>>>>>>>> not. It seems to me that this would work:
>>>>>>>>>
>>>>>>>>>          public void forEach(BiConsumer<? super Integer, ? super T>
>>>>>>>>> biConsumer) {
>>>>>>>>>              forEach((v) -> { biConsumer.accept(1, v); });
>>>>>>>>>          }
>>>>>>>>>          public void forEach(BiPredicate<? super Integer, ? super T>
>>>>>>>>> biPredicate) {
>>>>>>>>>              forEach(v -> { return biPredicate.test(1, v); } );
>>>>>>>>>          }
>>>>>>>>>
>>>>>>>>> ( ... But it fails in javac build 115. javac bug?)
>>>>>>>>>
>>>>>>>>> If the lambda body is a "statement expression", like in your original
>>>>>>>>> example
>>>>>>>>>
>>>>>>>>>          public void forEach(BiConsumer<? super Integer, ? super T>
>>>>>>>>> biConsumer) {
>>>>>>>>>              forEach((v) -> biConsumer.accept(1, v) );
>>>>>>>>>          }
>>>>>>>>>          public void forEach(BiPredicate<? super Integer, ? super T>
>>>>>>>>> biPredicate) {
>>>>>>>>>              forEach(v -> biPredicate.test(1, v) );
>>>>>>>>>          }
>>>>>>>>>
>>>>>>>>> It's unclear what the return type is, so javac cannot use the return
>>>>>>>>> type to prune. Note that even though `biConsumer.accept(1, v)`
>>>>>>>>> obviously returns void, javac does not know that, because the type of
>>>>>>>>> `v` is not known yet. And even though `biPredicate.test(1, v)`
>>>>>>>>> obviously returns boolean, it is compatible to a function type that
>>>>>>>>> expects void return.
>>>>>>>>>
>>>>>>>>> Now, for each invocation, we have two potentially applicable methods.
>>>>>>>>>
>>>>>>>>> The next step tries to prune methods by checking argument types
>>>>>>>>> against method parameter types. Unfortunately, implicit lambda
>>>>>>>>> expressions are too vague, so they are simply ignored in this step.
>>>>>>>>>
>>>>>>>>> Therefore both methods are considered applicable methods. The most
>>>>>>>>> specific method is sought after. However neither method is more
>>>>>>>>> specific than the other, therefore we see the error message of
>>>>>>>>> ambiguity.
>>>>>>>>>
>>>>>>>>> If you use explicit lambda expressions, javac is able to do more
>>>>>>>>> pruning, so this works:
>>>>>>>>>
>>>>>>>>>          public void forEach(BiConsumer<? super Integer, ? super T>
>>>>>>>>> biConsumer) {
>>>>>>>>>              forEach((T v) -> biConsumer.accept(1, v) );
>>>>>>>>>          }
>>>>>>>>>          public void forEach(BiPredicate<? super Integer, ? super T>
>>>>>>>>> biPredicate) {
>>>>>>>>>              forEach( (T v) -> biPredicate.test(1, v) );
>>>>>>>>>          }
>>>>>>>>>
>>>>>>>>> So if you have overloading methods like this
>>>>>>>>>
>>>>>>>>>          public void forEach(Consumer<? super T> consumer) {
>>>>>>>>>              // ...
>>>>>>>>>          }
>>>>>>>>>          public void forEach(Predicate<? super T> predicate) {
>>>>>>>>>              // ...
>>>>>>>>>          }
>>>>>>>>>
>>>>>>>>> client code often cannot use implicit lambda expressions as arguments;
>>>>>>>>> and experts have advised against this kind of overloading scheme (two
>>>>>>>>> functional interfaces with the same arity)
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Zhong Yu
>>>>>>>>>
>>>>>>>>> On Wed, Nov 13, 2013 at 8:01 AM, Dávid Karnok <akarnokd at gmail.com>
>>>>>>>>> wrote:
>>>>>>>>>> Hello,
>>>>>>>>>>
>>>>>>>>>> I'm rewriting a library and run into a strange ambiguity error
>>>>>>>>>> reported
>>>>>>>>>> by
>>>>>>>>>> javac in JDK 8 b115, Windows 7 x86:
>>>>>>>>>>
>>>>>>>>>> public class Bug<T> {
>>>>>>>>>>          public void forEach(Consumer<? super T> consumer) {
>>>>>>>>>>              // ...
>>>>>>>>>>          }
>>>>>>>>>>          public void forEach(BiConsumer<? super Integer, ? super T>
>>>>>>>>>> consumer) {
>>>>>>>>>>              forEach((v) -> consumer.accept(1, v));
>>>>>>>>>>          }
>>>>>>>>>>          public void forEach(Predicate<? super T> stoppableConsumer) {
>>>>>>>>>>              // ...
>>>>>>>>>>          }
>>>>>>>>>>          public void forEach(BiPredicate<? super Integer, ? super T>
>>>>>>>>>> stoppableConsumer) {
>>>>>>>>>>              forEach(v -> stoppableConsumer.test(1, v));
>>>>>>>>>>          }
>>>>>>>>>> }
>>>>>>>>>>
>>>>>>>>>> error: reference to forEach is ambiguous
>>>>>>>>>>              forEach((v) -> consumer.accept(1, v));
>>>>>>>>>>        both method forEach(Consumer<? super T>) in Bug and method
>>>>>>>>>> forEach(Predicate<? super T>) in Bug match
>>>>>>>>>>        where T is a type-variable:
>>>>>>>>>>          T extends Object declared in class Bug
>>>>>>>>>>
>>>>>>>>>> error: reference to forEach is ambiguous
>>>>>>>>>>              forEach(v -> stoppableConsumer.test(1, v));
>>>>>>>>>>        both method forEach(Consumer<? super T>) in Bug and method
>>>>>>>>>> forEach(Predicate<? super T>) in Bug match
>>>>>>>>>>        where T is a type-variable:
>>>>>>>>>>          T extends Object declared in class Bug
>>>>>>>>>>
>>>>>>>>>> I'm using NetBeans 7.4 (Build 201310111528) which doesn't report this
>>>>>>>>>> ambiguity in the editor, and the "highlight occurrences" correctly
>>>>>>>>>> selects
>>>>>>>>>> the 3rd method when I select the forEach in the 4th method. However,
>>>>>>>>>> the
>>>>>>>>>> clean and build fails with the errors above. I have to add explicit
>>>>>>>>>> cast
>>>>>>>>>> to
>>>>>>>>>> the problematic places.
>>>>>>>>>>
>>>>>>>>>> So I'm a bit confused right now. Is it a bug with NetBeans' editor,
>>>>>>>>>> with
>>>>>>>>>> javac or simply the artifact of how overload resolution works?
>>>>>>>>>> --
>>>>>>>>>> Best regards,
>>>>>>>>>> David Karnok
>>>>>>>>>>
>>
>>
>


More information about the lambda-dev mailing list