Bug 483226 - [1.8][compiler] Regression: Compiler cannot infer type arguments
Summary: [1.8][compiler] Regression: Compiler cannot infer type arguments
Status: CLOSED DUPLICATE of bug 487746
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.6   Edit
Hardware: PC All
: P3 normal (vote)
Target Milestone: 4.6 M6   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on: 483228
Blocks:
  Show dependency tree
 
Reported: 2015-11-29 05:32 EST by Tagir Valeev CLA
Modified: 2017-04-01 14:27 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tagir Valeev CLA 2015-11-29 05:32:05 EST
Always reproducible. Steps to reproduce:

1. Create EclipseBug.java:

import java.util.*;
import java.util.function.*;

public class EclipseBug {
    static class Test<T,A,R> {
        public Test(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,
                Function<A, R> finisher) {}
    }

    static class Box<T> {
        T a;
        public Box(T t) {this.a = t;}
    }

    public static <T> Test<T, ?, Optional<T>> onlyOne() {
        return new Test<>(() -> new Box<Optional<T>>(null),
        //return new Test<T, Box<Optional<T>>, Optional<T>>(() -> new Box<Optional<T>>(null),
                (box, t) -> box.a = box.a == null ? Optional.of(t) : Optional.empty(),
                (box1, box2) -> box1.a == null ? box2 : box2.a == null ? box1 : new Box<>(Optional.empty()),
                box -> box.a == null ? Optional.empty() : box.a);
    }
}

2. Compile:

java -jar org.eclipse.jdt.core_3.11.1.v20150902-1521.jar -8 EclipseBug.java 

Expected: successful compilation
Actual: compilation error:

----------
1. ERROR in L:\Lan\Projects\t\EclipseBug.java (at line 16)
	return new Test<>(() -> new Box<Optional<T>>(null),
        //return new Test<T, Box<Optional<T>>, Optional<T>>(() -> new Box<Optional<T>>(null),
                (box, t) -> box.a = box.a == null ? Optional.of(t) : Optional.empty(),
                (box1, box2) -> box1.a == null ? box2 : box2.a == null ? box1 : new Box<>(Optional.empty()),
                box -> box.a == null ? Optional.empty() : box.a);
	       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Cannot infer type arguments for Test<>
----------
1 problem (1 error)

Possible work-around: explicit specification of type arguments (as in outcommented line)

Regression: compiles fine with org.eclipse.jdt.core_3.10.2.v20150120-1634.jar and org.eclipse.jdt.core_3.10.0.v20140902-0626.jar. Also compiles fine with Javac 8u20, 8u31, 8u45, 9ea-b76.

Probably related to Bug#481649.
Comment 1 Stephan Herrmann CLA 2015-11-29 06:55:20 EST
Thanks.

The error is reported since 4.5M3. This milestone contains a major clean-up of Java 8 type inference & overload resolution - including a number of JLS changes after Java 8 GA.

From this, there is no easy way for telling whether the behavioral change is driven by such a spec change or a new implementation bug.

Will investigate.
Comment 2 Stephan Herrmann CLA 2015-11-29 08:52:21 EST
I've simplified the example and made type parameter names unique for easier reasoning:

//---
import java.util.*;
import java.util.function.*;

public class EclipseBug {
    static class Test<T,A,R> {
        public Test(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,
                Function<A, R> finisher) {}
    }

    static class Box<B> {
        B a;
        public Box(B b) {this.a = b;}
    }

    public static <S> Test<S, ?, Optional<S>> onlyOne() {
        return new Test<>(
                	() -> new Box<Optional<S>>(null),
                	(box, t) -> Optional.empty(),
                	(box1, box2) -> new Box<>(Optional.empty()), // inner
                	box -> box.a);
    }
}
//---

Dump of an comprehensive debug session:

Here's the inference context before invocation type inference for the outer diamond:

Inference Context (applicability inferred) (strict)
Inference Variables:
	T'#0	:	S
	A'#1	:	NOT INSTANTIATED
	R'#2	:	java.util.Optional<S>
	B#3	:	NOT INSTANTIATED
Initial Constraints:
	⟨Optional.empty() → B#3⟩
Type Bounds:
	TypeBound  T'#0 = S
	TypeBound  A'#1 :> EclipseBug.Box<java.util.Optional<S>>
	Dependency A'#1 :> EclipseBug.Box<B#3>
	TypeBound  R'#2 = java.util.Optional<S>

The problem is, we have no useful constraints / bounds for B#3, which represents the type parameter for Box in the inner diamond.

From there the first round of resolution (A'#1, T'#0) produces this solution:

	TypeBound  T'#0 = S
	TypeBound  A'#1 = EclipseBug.Box<? extends java.lang.Object>
	TypeBound  R'#2 = java.util.Optional<S>
	TypeBound  B#3 = java.lang.Object

Incorporation of corresponding C-set constraints in this round yields:

Type Bounds:
	TypeBound  T'#0 = S
	TypeBound  A'#1 :> EclipseBug.Box<java.util.Optional<S>>
	Dependency A'#1 :> EclipseBug.Box<B#3>
	TypeBound  R'#2 :> capture#1-of ? extends java.lang.Object
	TypeBound  R'#2 = java.util.Optional<S>

The second round (R'#2, A'#1) fails incorporation due to this unsatisfiable constraint:
    ⟨java.util.Optional<S> :> capture#1-of ? extends java.lang.Object⟩
as derived from
    TypeBound  R'#2 = java.util.Optional<S>
    TypeBound  R'#2 :> capture#1-of ? extends java.lang.Object

The latter type bound is derived from this (good) constraint:
    ⟨(<no type> box) -> box.a → java.util.function.Function<A'#1,R'#2>⟩
but with dubious substitution A'#1=Box<? extends Object> to yield:
    ⟨(<no type> box) -> box.a → Function<Box<? extends Object>,R'#2>⟩

The substitution results from the first round of resolution.
Comment 3 Stephan Herrmann CLA 2015-11-29 09:57:35 EST
To rule out special impact of diamonds, I further refactored the example to:

//---
import java.util.*;
import java.util.function.*;

public class EclipseBug {
    static class Test<T,A,R> {
        public Test(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,
                Function<A, R> finisher) {}
        static <T2,A2,R2> Test<T2,A2,R2> create(Supplier<A2> s, BiConsumer<A2, T2> c, BinaryOperator<A2> o, Function<A2, R2> f) {
        	return new Test<>(s,c,o,f);
        }
    }
    static class Box<B> {
        B a;
        public Box(B b) {this.a = b;}
        
        static <B2> Box<B2> create(B2 b) { return new Box<>(b); } 
    }
    public static <S> Test<S, ?, Optional<S>> onlyOneFactory() {
        return Test.create(
                () -> new Box<Optional<S>>(null),
                (box, t) -> Optional.empty(),
                (box1, box2) -> Box.create(Optional.empty()),
                box -> box.a);
    }
}
//---

Still the same failure. Adding explicit type arguments to either create() invocation lets inference succeed.

I still haven't a clue how JLS would let us find the solution.

Obviously, the connection must be made via the ivar for A2, for which we have the following information:
    Box<Optional<S>> <: A2#1
    Box.create(Optional.empty()) → A2#1
We have no information from the target type, where A2 is substituted to '?'.

The problem can be further described as failure to lift a deeply nested inference (Optional.empty()) into the outer-most inference. This may be aggravated by the fact that A2#1 only ever occurs in the position as an upper bound.
Comment 4 Stephan Herrmann CLA 2017-04-01 14:27:24 EDT
Bug has been fixed meanwhile (in 4.6 M6) via bug 487746.

Quoting a comment from the other bug:

(In reply to Stephan Herrmann from bug 487746 comment #20)
> For posterity: this fix was part of a sequence of attempts to properly
> implement https://bugs.openjdk.java.net/browse/JDK-8038747 

(a spec change after Java 8 GA)

*** This bug has been marked as a duplicate of bug 487746 ***