Bug 399527 - Type inference problem
Summary: Type inference problem
Status: VERIFIED INVALID
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.8   Edit
Hardware: PC Linux
: P3 normal (vote)
Target Milestone: 4.4 RC1   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords: test
Depends on:
Blocks:
 
Reported: 2013-01-30 13:19 EST by Terry Parker CLA
Modified: 2014-05-19 02:54 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 Terry Parker CLA 2013-01-30 13:19:09 EST
The following issue came up in a JUnit test that uses EasyMock. OpenJDK 6 and 7
do not have problems inferring the type, but Eclipse 3.8 does. Here is a
condensed version of the code:

public class TypeInferenceProblem {
  interface HeaderAccess<T> {
    T getHeader();
  }

  interface IExpectationSetters<T> {
    IExpectationSetters<T> andReturn(T value);
  }

  static class MocksControl implements IExpectationSetters<Object> {
    @Override
    public IExpectationSetters<Object> andReturn(Object value) {
      return null;
    }
  }

  @SuppressWarnings("unchecked")
  public static <T> IExpectationSetters<T> expect(final T value) {
    return (IExpectationSetters<T>) new MocksControl();
  }

  private HeaderAccess<Object> mockHeaderAccess;
  private HeaderAccess<?> unboundedMockHeaderAccess;

  public void test() {
    // No error
    expect(mockHeaderAccess.getHeader()).andReturn(new Object());
    /*
     * Error: The method andReturn(capture#1-of ?) in the type
     * TypeInferenceProblem.IExpectationSetters<capture#1-of ?> 
     * is not applicable for the arguments (Object)
     */
    expect(unboundedMockHeaderAccess.getHeader()).andReturn(new Object());
  }
}
Comment 1 Stephan Herrmann CLA 2013-02-01 16:23:18 EST
Here's an even simpler version for discussion:

public class TypeInferenceProblemMin {
  interface HeaderAccess<T> {
    T getHeader();
  }

  interface IExpectationSetters<T> {
  }

  public static <T> IExpectationSetters<T> expect(final T value) {
	  return null;
  }

  private HeaderAccess<?> unboundedMockHeaderAccess;
  
  public void test() {
    // no error:
    Object header = unboundedMockHeaderAccess.getHeader();
    IExpectationSetters<Object> exp1 = expect(header);

    // Type mismatch: cannot convert from TypeInferenceProblemMin.IExpectationSetters<capture#2-of ?> to TypeInferenceProblemMin.IExpectationSetters<Object>
    IExpectationSetters<Object> exp2 = expect(unboundedMockHeaderAccess.getHeader());
  }
}

This shows: ecj does not consider to convert the intermediate capture type to Object before continuing with the inference. When given this hint by way of an intermediate assignment (header), ecj finds this path of compatibility, too.

Question: does the JLS mandate this intermediate conversion, or should the capture be kept during inference? (Knowing that a solution for inference exists doesn't necessarily mean that the JLS defines a strategy that would find this solution).
Comment 2 Terry Parker CLA 2014-04-08 12:09:52 EDT
> This shows: ecj does not consider to convert the intermediate capture type
> to Object before continuing with the inference. When given this hint by way
> of an intermediate assignment (header), ecj finds this path of
> compatibility, too.
> 
> Question: does the JLS mandate this intermediate conversion, or should the
> capture be kept during inference? (Knowing that a solution for inference
> exists doesn't necessarily mean that the JLS defines a strategy that would
> find this solution).

Stephan, has your deep dive into the JLS for the Java8 work given you any more insight into this issue?
Comment 3 Stephan Herrmann CLA 2014-04-08 12:27:20 EDT
(In reply to Terry Parker from comment #2)
> Stephan, has your deep dive into the JLS for the Java8 work given you any
> more insight into this issue?

Are you asking about JLS 7 or JLS 8?

I don't readily have an answer, but in JLS 8 I'd be confident to find a sufficiently precise answer. For JLS 7 I'd be less confident...
Comment 4 Terry Parker CLA 2014-04-08 12:42:37 EDT
(In reply to Stephan Herrmann from comment #3)
> (In reply to Terry Parker from comment #2)
> > Stephan, has your deep dive into the JLS for the Java8 work given you any
> > more insight into this issue?
> 
> Are you asking about JLS 7 or JLS 8?
> 
> I don't readily have an answer, but in JLS 8 I'd be confident to find a
> sufficiently precise answer. For JLS 7 I'd be less confident...

Do the two specifications vary much the area of dealing with captured types? (Pardon my ignorance.) We are standardized on Java 7 now, so that is of more immediate interest.

The issue here is an identified difference between the behavior of javac and ecj, requiring close parsing of the specification to determine if and where there is a bug.
Comment 5 Stephan Herrmann CLA 2014-04-08 12:52:06 EDT
(In reply to Terry Parker from comment #4)
> Do the two specifications vary much the area of dealing with captured types?

The specification for type inference has been re-written from scratch in JLS 8 :)
Comment 6 Stephan Herrmann CLA 2014-04-13 17:12:09 EDT
For the records: ecj -1.8 does accept the example from comment 1 but not the full example from comment 0. At -1.7 we still reject both.

Tentatively pulling into 4.4
Comment 7 Stephan Herrmann CLA 2014-05-13 18:08:49 EDT
For 1.8 I'm pretty sure that both ecj results are correct:
- accept comment 1 due to target typing
- reject comment 0 because inference for expect() cannot be influenced by the Object argument to the subsequent method call andReturn().

Regarding comment 1 at source level 1.8 both compilers agree, good.


The original problem in comment 0 is the same issue as in bug 432603 (see bug 432603 comment 6 for final reasoning).

Diagnosis in a nutshell: 
  javac illegally ignores capture types.
Canonical solution to make it legal java code accepted by both compilers:
  this.<Object>expect(unboundedMockHeaderAccess.getHeader()).andReturn(new Object());


Remaining question: is the behavioral difference between 1.7 vs. 1.8 at comment 1 justified?

- at 1.8 we leverage target typing whereby the type of "exp2" drives inference into a solution where T=Object.

- at 1.7 we don't have such a rule, inference is driven only by actual vs. formal arguments.

=> Difference seems to justified.

Again the deviating behavior of javac is most likely covered by https://bugs.openjdk.java.net/browse/JDK-8016207
Comment 8 Stephan Herrmann CLA 2014-05-13 18:16:58 EDT
Regression tests in variants have been added via http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=299c63114977fb41c44f086c7b4cf110448a0c23

That seems to be all we can do here.
Comment 9 Jay Arthanareeswaran CLA 2014-05-19 02:54:16 EDT
Verified for 4.4 RC1