Javac Bug?
Dan Smith
daniel.smith at oracle.com
Fri Dec 6 16:30:41 PST 2013
Okay, we've had a change to confer on this and I've filed a javac bug:
https://bugs.openjdk.java.net/browse/JDK-8029718
—Dan
On Dec 3, 2013, at 1:05 PM, Zhong Yu <zhong.j.yu at gmail.com> wrote:
> 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