Javac Bug?

Zhong Yu zhong.j.yu at gmail.com
Tue Dec 3 12:05:25 PST 2013


Any update on this issue?

My preference would be to fix the spec to remove the clauses that
compare return types, which will only create confusions - sometimes it
works, but very often it doesn't work - when lambda body is a
statement expression.

Zhong Yu

On Sat, Nov 16, 2013 at 11:07 AM, David Holmes <david.holmes at oracle.com> wrote:
> 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