Bug 111208 - [1.5][compiler] Compiler gets confused by multiple generic-extends'
Summary: [1.5][compiler] Compiler gets confused by multiple generic-extends'
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 4.7 M2   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 177715 321485 441905 469297 472851 (view as bug list)
Depends on:
Blocks:
 
Reported: 2005-09-30 09:51 EDT by Tobias Riemenschneider CLA
Modified: 2016-09-13 10:11 EDT (History)
6 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tobias Riemenschneider CLA 2005-09-30 09:51:35 EDT
When I upgraded from M1 to M2, a new compiler error was introduced to my code.
####
import java.util.Iterator;
import java.util.List;

public class GenericsTest<A> {
  
  interface Factory<T> {
    T invoke();
  }

  public static <E> Iterator<E> iterate(Iterable<E> iterable) {
    return iterable.iterator();
  }

  public Factory<Iterator<? extends A>> factory(final Factory<? extends List<? 
extends A>> factory) {
    return new Factory<Iterator<? extends A>>() {
      public Iterator<? extends A> invoke() {
        return iterate(factory.invoke());
      }
    };
  }
}
####
Removing one of the '? extends ...' parts in the declaration of the parameter 
factory
  public Factory<Iterator<? extends A>> factory(final FactoryList<? extends A>> 
factory)
or
  public Factory<Iterator<? extends A>> factory(final Factory<? extends 
List<A>> factory)
or calling the iterator method directly on the generated object
  return factory.invoke().iterator();
causes M2 to remove the error, but javac successfully compiles both the 
original and the changed code.
Comment 1 Philipe Mulet CLA 2005-10-03 07:40:18 EDT
This is a consequence of fix for bug 107079 which got addressed in between 3.2m1
and m2. Note it also affects 3.1.1, and code was accepted in 3.1.0.
Comment 2 Philipe Mulet CLA 2005-10-04 05:45:11 EDT
According to JLS15.12.2.7, it seems to imply that wildcards should be ignored
during inference when involved in the actual (A) type part.

e.g. p 453 (2nd discussion)
----------------------------------
DISCUSSION
For simplicity, assume that G takes a single type argument. If the method
invocation being examined is to be applicable, it must be the case that A is a
subtype of some invocation of G. Otherwise, A << F would never be true.
In other words, A << F, where F = G<U>, implies that A << G<V> for some V. Now,
since U is a type expression (and therefore, U is not a wildcard type argument),
it must be the case that U = V, by the non-variance of ordinary parameterized
type invocations.
The formulation above merely generalizes this reasoning to generics with an
arbitrary number of type arguments.
----------------------------------

However, if doing so, the program of this bug report would be rejected.
The invocation of #iterate performs inference based on initial constraint:

capture-of ? extends List<? extends A>      <<      Iterable<E>
i.e.   Iterable<? extends A>      <<      Iterable<E>
        and according to discussion quoted above, would yield nothing.
Comment 3 Philipe Mulet CLA 2005-10-04 05:47:24 EDT
If we said:  capture(Factory<? extends List<? extends A>>)
is:	      Factory<capture-of ? extends List<capture-of ? extends A>>
(observe double capturing)

Then this program would compile ok, and still we would reject code from bug
107079, as first program no longer uses wildcards during inference, but capture
type variables.
Now, this contradicts the spec near capture definition where it says that
capture doesn't apply recursively. This would likely mean recursively on nested
type arguments; but still should perform on wildcard bound (if any). 
Comment 4 Philipe Mulet CLA 2005-10-04 05:48:54 EDT
Fix would then be to uncomment the following 2 lines in
CaptureBinding#initializeBounds(...)

TypeBinding substitutedWildcardBound = originalWildcardBound == null ? null :
originalWildcardBound.capture(scope, this.position);
if (substitutedWildcardBound == this) substitutedWildcardBound =
originalWildcardBound;

And enable GenericTypeTest#test836-837
Comment 5 Philipe Mulet CLA 2005-10-04 05:49:12 EDT
Ping'ed spec lead for clarification.
Comment 6 Philipe Mulet CLA 2005-10-06 13:08:50 EDT
Applied suggested fix. Released in 3.1 maintenance since can be perceived as a
regression over 3.1.0 (though it was passing for the wrong reason back then).

Also releasing fix in 3.2 stream.
Comment 7 Philipe Mulet CLA 2005-10-17 04:27:14 EDT
Changed fix due to regression, see bug 112666.
Only recapturing for '? extends X' case. 
Comment 8 Olivier Thomann CLA 2006-01-09 10:59:11 EST
Verified for 3.1.2 in M20060109-0800.
Comment 9 Olivier Thomann CLA 2006-01-10 10:33:32 EST
Verified for 3.2M4 in I20051215-1506
Comment 10 Stephan Herrmann CLA 2016-09-08 11:21:37 EDT
Re-opening because the fix in this bug appears to be bogus and has caused numerous bugs in the sequel.

Based on Philipe's comments I assumed that performing recursive capture, while being against the spec, was coordinated with a spec author.

Meanwhile I was told that javac does not perform any form of recursive capturing, but still produces the results we have been seeing all the time.

Hence, the capture algorithm may not even be the culprit here.
Comment 11 Stephan Herrmann CLA 2016-09-08 14:07:21 EDT
After reverting the bogus change in capturing, we can fix the issue (in 1.8) by reducing
   ? extends A <= E#0
to a type bound
   E#0 = ? extends A 

To me this looks like a fair extension of JLS 18.2.3, I've asked Dan Smith for comments.

In all of RunJava8Tests this only conflicts with bug 321485, but perhaps that test just needs its expectation adjusted.

I don't have an answer for Java 7, though.
Comment 12 Eclipse Genie CLA 2016-09-08 14:13:03 EDT
New Gerrit change created: https://git.eclipse.org/r/80726
Comment 13 Stephan Herrmann CLA 2016-09-08 14:19:55 EDT
(In reply to Eclipse Genie from comment #12)
> New Gerrit change created: https://git.eclipse.org/r/80726

Contained in this change:

1. stop recursive capture at 1.8 and above (I don't yet have a good replacement at 1.7)

2. speculatively add the new reduction rule mentioned in comment 11, to fix the current bug at 1.8

3. added tests for related bug 472851 and bug 441905

4. adjusted tests:

  GTT.test0910(): expect different capture ids, depending on (1)

  GTT.test1118(): at 1.8 expect the correct answer (reject) (see bug 177715)

  AMT.test087(): at 1.8 expect different answer (which I assume to be right)
Comment 14 Stephan Herrmann CLA 2016-09-08 16:21:53 EDT
Release for 4.7 M2 via http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=84c10c2837d11d68071e121c7bfdefafa4489d33 

I filed bug 501106 for some follow-up.
Comment 15 Stephan Herrmann CLA 2016-09-08 16:23:04 EDT
*** Bug 177715 has been marked as a duplicate of this bug. ***
Comment 16 Stephan Herrmann CLA 2016-09-08 16:26:38 EDT
*** Bug 321485 has been marked as a duplicate of this bug. ***
Comment 17 Stephan Herrmann CLA 2016-09-08 16:28:55 EDT
*** Bug 441905 has been marked as a duplicate of this bug. ***
Comment 18 Stephan Herrmann CLA 2016-09-08 16:34:54 EDT
*** Bug 472851 has been marked as a duplicate of this bug. ***
Comment 19 Stephan Herrmann CLA 2016-09-08 16:50:37 EDT
*** Bug 469297 has been marked as a duplicate of this bug. ***
Comment 20 Manoj N Palat CLA 2016-09-13 10:11:43 EDT
Verified for Eclipse Oxygen 4.7 M2 with build id: Build id: I20160912-1005