Bug 98538 - [1.5][compiler] Inference broken for subtypes of subtypes of F-bounded types
Summary: [1.5][compiler] Inference broken for subtypes of subtypes of F-bounded types
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.1   Edit
Hardware: PC Windows 2000
: P3 major (vote)
Target Milestone: 3.1.1   Edit
Assignee: Philipe Mulet CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-06-06 11:56 EDT by Matt McHenry CLA
Modified: 2005-09-26 09:46 EDT (History)
2 users (show)

See Also:


Attachments
Full source code that exhibits compilation errors (2.91 KB, text/plain)
2005-06-06 11:59 EDT, Matt McHenry CLA
no flags Details
Patch for ParameterizedGenericMethodBinding (5.09 KB, patch)
2005-06-20 07:57 EDT, Philipe Mulet CLA
no flags Details | Diff
Patch for GenericTypeTest (9.18 KB, patch)
2005-06-21 04:53 EDT, Philipe Mulet CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Matt McHenry CLA 2005-06-06 11:56:40 EDT
Given the following class declarations:

  class SelfType<T extends SelfType<T>>

  class SuperType extends SelfType<SuperType>

  class SubType extends SuperType

and the following method declaration:

  public static <T extends SelfType<T>> List<T> makeSingletonList(T t)

The following method-call compiles:

  makeSingletonList(new SuperType());

But this one does not:

  makeSingletonList(new SubType());

The error given is:

Bound mismatch: The generic method makeSingletonList(T) of type TestGenerics is
not applicable for the arguments (TestGenerics.SubType) since the type
TestGenerics.SubType is not a valid substitute for the bounded parameter <T
extends TestGenerics.SelfType<T>>

See corresponding bug in sun's javac:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6278587
Comment 1 Matt McHenry CLA 2005-06-06 11:59:04 EDT
Created attachment 22429 [details]
Full source code that exhibits compilation errors

Full source code for the examples in the original description, along with some
others along the same lines.

This fails with 3.1M7 and 3.1RC1.
Comment 2 Philipe Mulet CLA 2005-06-06 18:53:19 EDT
Thanks for the testcase. Is it really copyrighted ?
Comment 3 Matt McHenry CLA 2005-06-07 09:49:19 EDT
That's just a standard header that I have eclipse configured to insert in all
new .java files; you can probably safely ignore it in this case, since there's
nothing proprietary or specific to my company in there.  :)
Comment 4 Philipe Mulet CLA 2005-06-10 12:41:51 EDT
Remember that inference performs in 2 passes. 
One pass using invocation arguments (15.12.2.7), and if some variables are still
unresolved
it will consider return types and variable formal bounds in a second iteration
(15.12.2.8).

Now let's look at each case:


/*making lists of super types works fine ...*/
makeSingletonList(new SuperType());


  This one is fine. T gets inferred to be 'SuperType' in first pass, no further
  inference needed, and T boundcheck is happy, since: SuperType <:
SelfType<SuperType>
  per construction.


List<SuperType> lsup = makeSingletonList(new SuperType());

  Fine as well, though some expectation is set on return type, it isn't used since
  second pass is unneeded. Same resolution as above.


 
/*but we can't make a list of sub types; seems weird ...*/
List<SubType> lsub = makeSingletonList(new SubType()); //ERROR

  Error. First pass is all you need. It infers T to be 'SubType'. 
  But T boundcheck fails, since:  SubType  is not compatible with:  
SelfType<SubType>
  as mandated by T extends SelfType<T>.   SubType is only compatible with: 
SelfType<SuperType>
  like List<String> is not compatible with List<Object>.


/*can't even call it w/o assigning the return value:*/
makeSingletonList(new SubType()); //ERROR

  Same as above. As said already first pass on arguments is all you needed anyway.


To be continued...
Comment 5 Philipe Mulet CLA 2005-06-10 12:44:07 EDT
Reposting to improve line wrapping.

Remember that inference performs in 2 passes. One pass using invocation
arguments (15.12.2.7), and if some variables are still unresolved it will
consider return types and variable formal bounds in a second iteration (15.12.2.8).

Now let's look at each case:


/*making lists of super types works fine ...*/
makeSingletonList(new SuperType());


  This one is fine. T gets inferred to be 'SuperType' in first pass, no further
inference needed, and T boundcheck is happy, since: SuperType <:
SelfType<SuperType> per construction.


List<SuperType> lsup = makeSingletonList(new SuperType());

  Fine as well, though some expectation is set on return type, it isn't used
since second pass is unneeded. Same resolution as above.


 
/*but we can't make a list of sub types; seems weird ...*/
List<SubType> lsub = makeSingletonList(new SubType()); //ERROR

  Error. First pass is all you need. It infers T to be 'SubType'. 
  But T boundcheck fails, since:  SubType  is not compatible with:
SelfType<SubType> as mandated by T extends SelfType<T>. SubType is only
compatible with:  SelfType<SuperType>
  like List<String> is not compatible with List<Object>.


/*can't even call it w/o assigning the return value:*/
makeSingletonList(new SubType()); //ERROR

  Same as above. As said already first pass on arguments is all you needed anyway.


To be continued...
Comment 6 Philipe Mulet CLA 2005-06-13 06:43:16 EDT
+1 for RC3.
Comment 7 Philipe Mulet CLA 2005-06-17 08:44:16 EDT
Will not address within RC3. Too risky.

import java.util.*;

public class X {
 
static abstract class SelfType<T extends SelfType<T>>{
}
 
static class SuperType extends SelfType<SuperType>{
}
 
static class SubType extends SuperType{}
 
static <T extends SelfType<T>> List<T> makeSingletonList(T t){
	return Collections.singletonList(t);
}
 
static <T extends SelfType<T>,S extends T> List<T> makeSingletonList2(S s){
	return Collections.singletonList((T)s); // #0
}
 
public static void main(String[] args){
	makeSingletonList(new SuperType()); // #1 - OK
	List<SuperType> lsup = makeSingletonList(new SuperType()); // #2 - OK
	List<SubType> lsub = makeSingletonList(new SubType()); // #3 - ERROR
	makeSingletonList(new SubType()); // #4 - ERROR
	makeSingletonList2(new SubType()); // #5 - ERROR
	lsup = makeSingletonList2(new SubType()); // #6 - OK
	lsub = makeSingletonList2(new SubType()); // #7 - ERROR
	makeSingletonList2(new SuperType()); // #8 - OK
	lsup = makeSingletonList2(new SuperType()); // #9 - OK
}
}

Also our first discrepancy (#6) with javac got assessed to be a bug in javac
(see javac bug). The remaining one (#8) is likely a bug in our land due.

This is not critical for 3.1, however worth considering for some 3.1.1 update.
Comment 8 Philipe Mulet CLA 2005-06-20 07:57:19 EDT
Created attachment 23536 [details]
Patch for ParameterizedGenericMethodBinding
Comment 9 Philipe Mulet CLA 2005-06-20 08:02:34 EDT
Patch has 2 parts in generic method inference.

1. in absence of explicit expected return type (from assignment conversion)
Object should be picked up. Old code was using the upperbound instead when
return type is a type variable, which is redundant with what the inference will
do anyway. Simply removed upper bound access (as mandated by the spec). This
part is trivial, and removes unnecessary complexity (cleanup).

2. our inference on type parameter formal bounds was performing backwards: Ti <<
Bi, instead of Bi >> Ti as described in the spec. Fix is to make it consistent
with the spec, but still preserve Ti << Bi in cases type parameter got resolved,
to exploit possible type information for building constraints. This behavior
matches javac expectation as well (though disagrees slightly with the spec).
Comment 10 Philipe Mulet CLA 2005-06-21 04:53:06 EDT
Created attachment 23598 [details]
Patch for GenericTypeTest
Comment 11 Philipe Mulet CLA 2005-06-29 04:11:52 EDT
Fixed in 3.1 maintenance branch
Comment 12 Maxime Daniel CLA 2005-08-09 07:56:27 EDT
Verified in 3.2 M1 with build I20050808-2000.
Comment 13 David Audel CLA 2005-09-26 09:46:55 EDT
Verified using M20050923-1430 for 3.1.1