Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Language IDEs » Java Development Tools (JDT) » ECJ error in overload resolution, but not with javac
ECJ error in overload resolution, but not with javac [message #1769977] Mon, 07 August 2017 09:04 Go to next message
Jens Auer is currently offline Jens AuerFriend
Messages: 11
Registered: February 2017
Junior Member
I have some code which compiles with Javac 8 and 9, but not with ecj in Eclipse. I am writing some utility functions for Optional and the specializations for primitive types, and while doing this wrote two functions

package sandbox;

import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;

public class Test {
    static class OptionalUtils {
        public static <T> OptionalInt map(Optional<T> o, ToIntFunction<? super T> f) {
            if (o.isPresent()) {
                return OptionalInt.of(f.applyAsInt(o.get()));
            } else {
                return OptionalInt.empty();
            }
        }

        public static <T> OptionalDouble map(Optional<T> o, ToDoubleFunction<? super T> f) {
            if (o.isPresent()) {
                return OptionalDouble.of(f.applyAsDouble(o.get()));
            } else {
                return OptionalDouble.empty();
            }
        }
    }

    private static int double2int(double x) {
        return 2;
    }

    public static void main(String[] args) {
        Optional<Double> oEmpty = Optional.empty();
        OptionalUtils.map(oEmpty, Test::double2int);
    }
}
}


The call to OptionalUtils.map is flagged with an error:

Quote:

Description Resource Path Location Type
The method map(Optional<Double>, ToIntFunction<? super Double>) is ambiguous for the type Test.OptionalUtils Test.java /Playground/src/sandbox line 34 Java Problem


This is independent from Optional because the following modified example produces the same error, but compiles with javac:

import java.util.function.ToDoubleFunction;
import java.util.function.ToIntFunction;

public class Test {
    static class Generic<T> {
    }

    public static <T> String map(Generic<T> o, ToIntFunction<? super T> f) {
        return null;
    }

    public static <T> Number map(Generic<T> o, ToDoubleFunction<? super T> f) {
        return null;
    }

    private static int double2int(double x) {
        return 2;
    }

    public static void main(String[] args) {
        Generic<Double> oEmpty = new Generic<>();

        map(oEmpty, Test::double2int);
    }
}

double2int is assignable to both ToIntFunction and ToDoubleFunction, so intuitively I can see why it is ambiguous. I am not really familiar with the details of overload resolution in Java, but since calling the overload with ToDoubleFunction involves a conversion, I would expect that the better match is the other overload and it is preferred.

Best wishes,
Jens

[Updated on: Mon, 07 August 2017 12:04]

Report message to a moderator

Re: ECJ error in overload resolution, but not with javac [message #1770997 is a reply to message #1769977] Sun, 20 August 2017 21:49 Go to previous messageGo to next message
Stephan Herrmann is currently offline Stephan HerrmannFriend
Messages: 1504
Registered: July 2009
Senior Member
Overloading based on just the difference between two different functional interfaces is very fragile.
In this area intuition frequently fails to match the semantics in JLS.

My quick guess is: both methods require unboxing from the function type's T to the double parameter of double2int. For that reason, both methods fall into the same category, and none is better than the other, because none matches without unboxing.

There's probably more to it, though.

If you are still convinced that javac's behavior is correct in this regard, please file a bug against JDT/Core.
Re: ECJ error in overload resolution, but not with javac [message #1771456 is a reply to message #1770997] Fri, 25 August 2017 08:36 Go to previous messageGo to next message
Jens Auer is currently offline Jens AuerFriend
Messages: 11
Registered: February 2017
Junior Member
Hi Stephan,

I don't think boxing is the cause of the problem here, at least not on the paramter side. If I modify the example to use another type than Double I get the same behavior:
import java.util.function.ToIntFunction;
import java.util.function.ToDoubleFunction;
import java.time.LocalDateTime;

// one class needs to have a main() method
public class HelloWorld
{
  // arguments are passed using the text field below this editor
    static class Generic<T> {
    }

    public static <T> String map(Generic<T> o, ToIntFunction<? super T> f) {
        return null;
    }

    public static <T> Number map(Generic<T> o, ToDoubleFunction<? super T> f) {
        return null;
    }
    
    private static int double2int(LocalDateTime x) {
        return 2;
    }

    public static void main(String[] args) {
        Generic<LocalDateTime> oEmpty = new Generic<>();

        map(oEmpty, HelloWorld::double2int);
    }
}
  


The problem seems to be with the conversion int -> double. If I modify the example to use a Predicate<T> the return values become incompatible and there is no implicit conversion between bool and int. No everything works as I expect it:

    public static <T> String map(Generic<T> o, ToIntFunction<? super T> f) {
        return null;
    }

    public static <T> Number map(Generic<T> o, Predicate<? super T> f) {
        return null;
    }

    private static int double2int(LocalDateTime x) {
        return 2;
    }

    public static void main(String[] args) {
        Generic<LocalDateTime> oEmpty = new Generic<>();

        map(oEmpty, RadarSimulation::double2int);
    }


I could narrow it further down by fixing the generic type T to a custom type A. If I have

    public static <T> String g(ToIntFunction<? extends A f) {
        return null;
    }

    public static <T> String g(ToDoubleFunction<? extends A> f) {
        return null;
    }

    private static int double2int(A x) {
        return 2;
    }

    public static void main(String[] args) {
        g(HelloWorld::double2int);
    }


I get an error, if I remove the wildcard parameter it compiles:

    public static <T> String g(ToIntFunction<A> f) {
        return null;
    }

    public static <T> String g(ToDoubleFunction<A> f) {
        return null;
    }

    private static int double2int(Ax) {
        return 2;
    }

    public static void main(String[] args) {
        g(RadarSimulation::double2int);
    }


I am not sure what to make of this. I am also not sure if this a bug in javac or ejc. I looked the rules of method reference type inference, and from my understanding, the method reference is assignable to both ToIntFunction<T> and To DoubleFunction<T> because the return type int is assignment compatible to double. So the method reference is congruent with both types

Cheers,
Jens

[Updated on: Fri, 25 August 2017 08:52]

Report message to a moderator

Re: ECJ error in overload resolution, but not with javac [message #1771486 is a reply to message #1771456] Fri, 25 August 2017 14:14 Go to previous message
Jens Auer is currently offline Jens AuerFriend
Messages: 11
Registered: February 2017
Junior Member
I've asked on Stackovderflow (https://stackoverflow.com/questions/45878208/overload-resolution-with-method-references-and-function-interface-specialization) to clarify if this is a ECJ or Javac bug. Right now, I think that the code should be rejected with or without wildcards. But this is my simple interpretation of the JLS and it is probably wrong as I am not very familiar with the JLS. In summary, the problem with seems to be the interpretation of the rule to check if one type is more specific than another w.r.t. the actual argument type:

Quote:

15.12.2.5. Choosing the Most Specific Method
If e is an exact method reference expression (§15.13.1), then i) for all i (1 ≤ i ≤ k), Ui is the same as Vi, and ii) one of the following is true:

R2 is void.

R1 <: R2.

R1 is a primitive type, R2 is a reference type, and the compile-time declaration for the method reference has a return type which is a primitive type.

R1 is a reference type, R2 is a primitive type, and the compile-time declaration for the method reference has a return type which is a reference type.


In this case, the types are ToIntFunction and ToDoubleFunction. There is no clause covering the case were both return value types are primitives, so neither of the functional interfaces is more specific than the other. Thus, the code is ambiguous.

But this would leave ecj with the question of why the code is compiled when the wildcards are removed.
Previous Topic:JShell?
Next Topic:Don't understand "trigger points"
Goto Forum:
  


Current Time: Sun Sep 24 17:43:55 GMT 2017

Powered by FUDForum. Page generated in 0.02521 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software