JDK-5034571 : Wildcard capture must use the bounds of the formal
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 5.0,6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux,solaris_8
  • CPU: generic,x86
  • Submitted: 2004-04-20
  • Updated: 2006-03-14
  • Resolved: 2006-01-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6
6 b67Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
I believe the process by which we compute a performing a wildcard capture
conversion should include the formal type parameter's bounds in the result,
using glb().  The following example shows why.  In this case we have an example
of G1<? extends I2> being a subtype of G1<?>, yet an operation that succeeds on
the latter will fail on the former.

==========$ cat -n T.java
     1  interface I1 {
     2      void i1();
     3  }
     4
     5  class G1<T extends I1> {
     6      T get() { return null; }
     7  }
     8
     9  interface I2 {
    10      void i2();
    11  }
    12
    13  class Main {
    14      void f1(G1<?> g1) {
    15          g1.get().i1();
    16      }
    17      void f2(G1<? extends I2> g1) {
    18          g1.get().i1(); // oops!
    19          g1.get().i2();
    20      }
    21  }
==========$ javac T.java
T.java:18: cannot find symbol
symbol  : method i1()
location: interface I2
        g1.get().i1(); // oops!
              ^
1 error
==========$ 

Comments
SUGGESTED FIX http://sa.sfbay.sun.com/projects/langtools_data/mustang/5034571/
26-01-2006

SUGGESTED FIX Check.java @@ -344,6 +344,13 @@ log.error(pos, "assignment.to.extends-bound", req); return syms.errType; } + if (found instanceof CapturedType) { + CapturedType t = (CapturedType)found; + for (TypeVar tv : t.tvbounds) { + if (types.isAssignable(tv, req, convertWarner(pos, found, req))) + return found; + } + } return typeError(pos, Diagnostic.fragment("incompatible.types"), found, req); }
26-09-2005

SUGGESTED FIX private long nCaptures = 0; private Name makeCapturedName(WildcardType wildcard, TypeVar formal, List<Type> actuals, List<Type> formals) { if (Type.moreInfo) { Type applied = subst(formal.bound, formals, actuals); List<Type> bounds; String kind = wildcard.kind.toString(); switch (wildcard.kind) { case EXTENDS: bounds = closureMin(union(closure(upperBound(wildcard)), closure(applied))); break; case SUPER: bounds = closureMin(union(closure(lowerBound(wildcard)), closure(applied))); break; case UNBOUND: bounds = closure(applied); kind = BoundKind.EXTENDS.toString(); break; default: throw new AssertionError(wildcard.kind); } return names.fromString(formal + "#" + (nCaptures++) + " (capture of " + kind + " " + bounds.toString("&") + ")"); } else { return names.fromString(formal + "#" + (nCaptures++) + " (capture of " + wildcard + ")"); } } public Type capture(Type t) { if (t.isErroneous()) return t; if (t.tag == CLASS) { if (t.isRaw() || !t.isParameterized()) return t; ClassType cls = (ClassType)t; ClassType declaration = (ClassType)t.tsym.type; Type result = cls; List<Type> capturedArgs = List.nil(); boolean makeNew = false; List<Type> formals = declaration.typarams(); for (Type actual : cls.typarams()) { Type formal = formals.head; if (actual.tag == WILDCARD) { WildcardType wildcard = (WildcardType)actual; Name name = makeCapturedName(wildcard, (TypeVar)formal, t.typarams(), declaration.typarams()); Type capturedType = new CapturedType(name, syms.noSymbol, upperBound(wildcard), lowerBound(wildcard)); capturedArgs = capturedArgs.append(capturedType); makeNew = true; } else { capturedArgs = capturedArgs.append(actual); } formals = formals.tail; } if (makeNew) { capturedArgs = convert(capturedArgs, t.typarams(), declaration.typarams()); result = new ClassType(cls.outer(), capturedArgs, cls.tsym); } return result; } else if (t.tag == TYPEVAR && t instanceof CapturedType) { ListBuffer<Type> capturedBounds = new ListBuffer<Type>(); boolean makeNew = false; TypeVar tv = (TypeVar)t; for (Type bound : getBounds(tv)) { Type ct = capture(bound); capturedBounds.append(ct); if (ct != bound) makeNew = true; } if (makeNew) return setBounds(tv.clone(), capturedBounds.toList()); else return t; } else { return t; } } // where public List<Type> convert(List<Type> capturedArgs, List<Type> actuals, List<Type> formals) { List<Type> currentCapturedArgs = capturedArgs; List<Type> currentFormals = formals; ListBuffer<Type> convertedArgs = new ListBuffer<Type>(); for (Type currentActual : actuals) { Type capturedArg = currentCapturedArgs.head; TypeVar currentFormal = (TypeVar)currentFormals.head; Type converted = subst(currentFormal.bound, formals, capturedArgs); if (currentActual.tag == WILDCARD && capturedArg instanceof CapturedType) { WildcardType wildcard = (WildcardType)currentActual; CapturedType t = (CapturedType)capturedArg; if (!wildcard.isExtendsBound()) { Type upper = converted; if (wildcard.bound != null) upper = wildcard.bound.bound; Type lower = lowerBound(wildcard); if (upper == lower) { currentCapturedArgs.head = upper; return convert(capturedArgs, actuals, formals); } else { setBounds(t, List.of(upper)); t.lower = lower; } } else { // unbounded or upper bounded wildcard List<Type> bounds = glb(wildcard.type, converted, t); setBounds(t, bounds); } } currentCapturedArgs = currentCapturedArgs.tail; currentFormals = currentFormals.tail; } return capturedArgs; } public List<Type> glb(Type t1, Type t2, CapturedType ct) { // if (isSubTypeNoCapture(t1, t2)) // return emptyList.prepend(t1); List<Type> closure = union(closure(t1), closure(t2)); List<Type> bounds = closureMin(closure); if (!bounds.isEmpty() && !bounds.tail.isEmpty()) { closure = bounds; ListBuffer<TypeVar> tvbounds = new ListBuffer<TypeVar>(); while (closure.nonEmpty() && closure.head.tag == TYPEVAR) { tvbounds.append((TypeVar)closure.head); closure = closure.tail; } if (tvbounds.length() > 0) { for (TypeVar t : tvbounds.toList()) closure = union(getBounds(t), closure); bounds = closureMin(closure); } int classCount = 0; for (Type t : bounds) if (!t.isInterface()) { classCount++; } if (classCount > 1) { bounds = List.of(syms.errType); } ct.tvbounds = tvbounds.toList(); } return bounds; }
26-09-2005

EVALUATION JLS3 now specifies the glb() to merge the type argument and the formal's bound. ###@###.### 2004-04-20 I am deferring this for now. This appears to enough refactoring to implement correctly that it is best delayed until after tiger-beta2. ###@###.### 2004-05-04 In response to SDN comment: G<? extends I2> is valid because we want to allow something like this: interface I3 extends I1, I2 {} G<? extends I2> x = new G<I3>(); Apply capture conversion to G<? extends I2> and you get: G<X> where X is a subtype of I1 AND I2 However, this is what is not calculated correctly. ###@###.### 2005-06-04 04:44:06 GMT
04-06-2005

CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mustang
07-09-2004

PUBLIC COMMENTS ...
07-09-2004