Javac Bug?
Zhong Yu
zhong.j.yu at gmail.com
Sat Nov 16 06:59:23 PST 2013
So this is a bug in the spec text, which indicates that the kind of
return (void or value) plays a roles here?
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