Javac Bug?

Zhong Yu zhong.j.yu at gmail.com
Fri Nov 15 07:36:19 PST 2013


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)


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