Javac Bug?

David Holmes david.holmes at oracle.com
Fri Nov 15 09:25:08 PST 2013


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
-----

>
>>
>>
>>>
>>>
>>> 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