Javac Bug?

Ali Ebrahimi ali.ebrahimi1781 at gmail.com
Fri Nov 15 09:57:36 PST 2013


+1. I raised this issue multiple times in this list when Maurizio was in
oracle.


On Fri, Nov 15, 2013 at 8:55 PM, David Holmes <david.holmes at oracle.com>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
> -----
>
> >
> >>
> >>
> >>>
> >>>
> >>> 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