Tue, 25 Sep 2012 11:55:34 +0100
7175433: Inference cleanup: add helper class to handle inference variables
Summary: Add class to handle inference variables instantiation and associated info
Reviewed-by: jjg, dlsmith
1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Check.java Tue Sep 25 11:53:18 2012 +0100 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Check.java Tue Sep 25 11:55:34 2012 +0100 1.3 @@ -40,6 +40,8 @@ 1.4 import com.sun.tools.javac.code.Lint.LintCategory; 1.5 import com.sun.tools.javac.code.Type.*; 1.6 import com.sun.tools.javac.code.Symbol.*; 1.7 +import com.sun.tools.javac.comp.Infer.InferenceContext; 1.8 +import com.sun.tools.javac.comp.Infer.InferenceContext.FreeTypeListener; 1.9 1.10 import static com.sun.tools.javac.code.Flags.*; 1.11 import static com.sun.tools.javac.code.Flags.ANNOTATION; 1.12 @@ -429,6 +431,8 @@ 1.13 * Obtain a warner for this check context 1.14 */ 1.15 public Warner checkWarner(DiagnosticPosition pos, Type found, Type req); 1.16 + 1.17 + public Infer.InferenceContext inferenceContext(); 1.18 } 1.19 1.20 /** 1.21 @@ -455,6 +459,10 @@ 1.22 public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) { 1.23 return enclosingContext.checkWarner(pos, found, req); 1.24 } 1.25 + 1.26 + public Infer.InferenceContext inferenceContext() { 1.27 + return enclosingContext.inferenceContext(); 1.28 + } 1.29 } 1.30 1.31 /** 1.32 @@ -471,6 +479,10 @@ 1.33 public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) { 1.34 return convertWarner(pos, found, req); 1.35 } 1.36 + 1.37 + public InferenceContext inferenceContext() { 1.38 + return infer.emptyContext; 1.39 + } 1.40 }; 1.41 1.42 /** Check that a given type is assignable to a given proto-type. 1.43 @@ -483,7 +495,16 @@ 1.44 return checkType(pos, found, req, basicHandler); 1.45 } 1.46 1.47 - Type checkType(final DiagnosticPosition pos, Type found, Type req, CheckContext checkContext) { 1.48 + Type checkType(final DiagnosticPosition pos, final Type found, final Type req, final CheckContext checkContext) { 1.49 + final Infer.InferenceContext inferenceContext = checkContext.inferenceContext(); 1.50 + if (inferenceContext.free(req)) { 1.51 + inferenceContext.addFreeTypeListener(List.of(req), new FreeTypeListener() { 1.52 + @Override 1.53 + public void typesInferred(InferenceContext inferenceContext) { 1.54 + checkType(pos, found, inferenceContext.asInstType(req, types), checkContext); 1.55 + } 1.56 + }); 1.57 + } 1.58 if (req.tag == ERROR) 1.59 return req; 1.60 if (req.tag == NONE)
2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Infer.java Tue Sep 25 11:53:18 2012 +0100 2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java Tue Sep 25 11:55:34 2012 +0100 2.3 @@ -25,18 +25,21 @@ 2.4 2.5 package com.sun.tools.javac.comp; 2.6 2.7 +import com.sun.tools.javac.code.*; 2.8 +import com.sun.tools.javac.code.Symbol.*; 2.9 +import com.sun.tools.javac.code.Type.*; 2.10 +import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; 2.11 +import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; 2.12 import com.sun.tools.javac.tree.JCTree; 2.13 import com.sun.tools.javac.tree.JCTree.JCTypeCast; 2.14 import com.sun.tools.javac.tree.TreeInfo; 2.15 import com.sun.tools.javac.util.*; 2.16 import com.sun.tools.javac.util.List; 2.17 -import com.sun.tools.javac.code.*; 2.18 -import com.sun.tools.javac.code.Type.*; 2.19 -import com.sun.tools.javac.code.Symbol.*; 2.20 -import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; 2.21 -import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; 2.22 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 2.23 2.24 +import java.util.HashMap; 2.25 +import java.util.Map; 2.26 + 2.27 import static com.sun.tools.javac.code.TypeTags.*; 2.28 2.29 /** Helper class for type parameter inference, used by the attribution phase. 2.30 @@ -76,50 +79,48 @@ 2.31 chk = Check.instance(context); 2.32 diags = JCDiagnostic.Factory.instance(context); 2.33 inferenceException = new InferenceException(diags); 2.34 - 2.35 } 2.36 2.37 + /** 2.38 + * This exception class is design to store a list of diagnostics corresponding 2.39 + * to inference errors that can arise during a method applicability check. 2.40 + */ 2.41 public static class InferenceException extends InapplicableMethodException { 2.42 private static final long serialVersionUID = 0; 2.43 2.44 + List<JCDiagnostic> messages = List.nil(); 2.45 + 2.46 InferenceException(JCDiagnostic.Factory diags) { 2.47 super(diags); 2.48 } 2.49 + 2.50 + @Override 2.51 + InapplicableMethodException setMessage(JCDiagnostic diag) { 2.52 + messages = messages.append(diag); 2.53 + return this; 2.54 + } 2.55 + 2.56 + @Override 2.57 + public JCDiagnostic getDiagnostic() { 2.58 + return messages.head; 2.59 + } 2.60 + 2.61 + void clear() { 2.62 + messages = List.nil(); 2.63 + } 2.64 } 2.65 2.66 private final InferenceException inferenceException; 2.67 2.68 /*************************************************************************** 2.69 - * Auxiliary type values and classes 2.70 - ***************************************************************************/ 2.71 - 2.72 - /** A mapping that turns type variables into undetermined type variables. 2.73 - */ 2.74 - List<Type> makeUndetvars(List<Type> tvars) { 2.75 - List<Type> undetvars = Type.map(tvars, fromTypeVarFun); 2.76 - for (Type t : undetvars) { 2.77 - UndetVar uv = (UndetVar)t; 2.78 - uv.hibounds = types.getBounds((TypeVar)uv.qtype); 2.79 - } 2.80 - return undetvars; 2.81 - } 2.82 - //where 2.83 - Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") { 2.84 - public Type apply(Type t) { 2.85 - if (t.tag == TYPEVAR) return new UndetVar(t); 2.86 - else return t.map(this); 2.87 - } 2.88 - }; 2.89 - 2.90 -/*************************************************************************** 2.91 * Mini/Maximization of UndetVars 2.92 ***************************************************************************/ 2.93 2.94 /** Instantiate undetermined type variable to its minimal upper bound. 2.95 * Throw a NoInstanceException if this not possible. 2.96 */ 2.97 - void maximizeInst(UndetVar that, Warner warn) throws InferenceException { 2.98 - List<Type> hibounds = Type.filter(that.hibounds, errorFilter); 2.99 + void maximizeInst(UndetVar that, Warner warn) throws InferenceException { 2.100 + List<Type> hibounds = Type.filter(that.hibounds, boundFilter); 2.101 if (that.eq.isEmpty()) { 2.102 if (hibounds.isEmpty()) 2.103 that.inst = syms.objectType; 2.104 @@ -137,10 +138,10 @@ 2.105 that.qtype, hibounds); 2.106 } 2.107 2.108 - private Filter<Type> errorFilter = new Filter<Type>() { 2.109 + private Filter<Type> boundFilter = new Filter<Type>() { 2.110 @Override 2.111 public boolean accepts(Type t) { 2.112 - return !t.isErroneous(); 2.113 + return !t.isErroneous() && t.tag != BOT; 2.114 } 2.115 }; 2.116 2.117 @@ -148,11 +149,12 @@ 2.118 * Throw a NoInstanceException if this not possible. 2.119 */ 2.120 void minimizeInst(UndetVar that, Warner warn) throws InferenceException { 2.121 - List<Type> lobounds = Type.filter(that.lobounds, errorFilter); 2.122 + List<Type> lobounds = Type.filter(that.lobounds, boundFilter); 2.123 if (that.eq.isEmpty()) { 2.124 - if (lobounds.isEmpty()) 2.125 - that.inst = syms.botType; 2.126 - else if (lobounds.tail.isEmpty()) 2.127 + if (lobounds.isEmpty()) { 2.128 + //do nothing - the inference variable is under-constrained 2.129 + return; 2.130 + } else if (lobounds.tail.isEmpty()) 2.131 that.inst = lobounds.head.isPrimitive() ? syms.errType : lobounds.head; 2.132 else { 2.133 that.inst = types.lub(lobounds); 2.134 @@ -166,90 +168,70 @@ 2.135 } 2.136 } 2.137 2.138 - Type asUndetType(Type t, List<Type> undetvars) { 2.139 - return types.subst(t, inferenceVars(undetvars), undetvars); 2.140 - } 2.141 - 2.142 - List<Type> inferenceVars(List<Type> undetvars) { 2.143 - ListBuffer<Type> tvars = ListBuffer.lb(); 2.144 - for (Type uv : undetvars) { 2.145 - tvars.append(((UndetVar)uv).qtype); 2.146 - } 2.147 - return tvars.toList(); 2.148 - } 2.149 - 2.150 /*************************************************************************** 2.151 * Exported Methods 2.152 ***************************************************************************/ 2.153 2.154 - /** Try to instantiate expression type `that' to given type `to'. 2.155 - * If a maximal instantiation exists which makes this type 2.156 - * a subtype of type `to', return the instantiated type. 2.157 - * If no instantiation exists, or if several incomparable 2.158 - * best instantiations exist throw a NoInstanceException. 2.159 + /** 2.160 + * Instantiate uninferred inference variables (JLS 15.12.2.8). First 2.161 + * if the method return type is non-void, we derive constraints from the 2.162 + * expected type - then we use declared bound well-formedness to derive additional 2.163 + * constraints. If no instantiation exists, or if several incomparable 2.164 + * best instantiations exist throw a NoInstanceException. 2.165 */ 2.166 - public List<Type> instantiateUninferred(DiagnosticPosition pos, 2.167 - List<Type> undetvars, 2.168 - List<Type> tvars, 2.169 - MethodType mtype, 2.170 - Attr.ResultInfo resultInfo, 2.171 - Warner warn) throws InferenceException { 2.172 + public void instantiateUninferred(DiagnosticPosition pos, 2.173 + InferenceContext inferenceContext, 2.174 + MethodType mtype, 2.175 + Attr.ResultInfo resultInfo, 2.176 + Warner warn) throws InferenceException { 2.177 Type to = resultInfo.pt; 2.178 if (to.tag == NONE) { 2.179 to = mtype.getReturnType().tag <= VOID ? 2.180 mtype.getReturnType() : syms.objectType; 2.181 } 2.182 - Type qtype1 = types.subst(mtype.getReturnType(), tvars, undetvars); 2.183 + Type qtype1 = inferenceContext.asFree(mtype.getReturnType(), types); 2.184 if (!types.isSubtype(qtype1, 2.185 qtype1.tag == UNDETVAR ? types.boxedTypeOrType(to) : to)) { 2.186 throw inferenceException 2.187 - .setMessage("infer.no.conforming.instance.exists", 2.188 - tvars, mtype.getReturnType(), to); 2.189 + .setMessage("infer.no.conforming.instance.exists", 2.190 + inferenceContext.restvars(), mtype.getReturnType(), to); 2.191 } 2.192 2.193 - List<Type> insttypes; 2.194 while (true) { 2.195 boolean stuck = true; 2.196 - insttypes = List.nil(); 2.197 - for (Type t : undetvars) { 2.198 + for (Type t : inferenceContext.undetvars) { 2.199 UndetVar uv = (UndetVar)t; 2.200 - if (uv.inst == null && (uv.eq.nonEmpty() || !Type.containsAny(uv.hibounds, tvars))) { 2.201 + if (uv.inst == null && (uv.eq.nonEmpty() || !inferenceContext.free(uv.hibounds))) { 2.202 maximizeInst((UndetVar)t, warn); 2.203 stuck = false; 2.204 } 2.205 - insttypes = insttypes.append(uv.inst == null ? uv.qtype : uv.inst); 2.206 } 2.207 - if (!Type.containsAny(insttypes, tvars)) { 2.208 + if (inferenceContext.restvars().isEmpty()) { 2.209 //all variables have been instantiated - exit 2.210 break; 2.211 } else if (stuck) { 2.212 //some variables could not be instantiated because of cycles in 2.213 //upper bounds - provide a (possibly recursive) default instantiation 2.214 - insttypes = types.subst(insttypes, 2.215 - tvars, 2.216 - instantiateAsUninferredVars(undetvars, tvars)); 2.217 + instantiateAsUninferredVars(inferenceContext); 2.218 break; 2.219 } else { 2.220 //some variables have been instantiated - replace newly instantiated 2.221 //variables in remaining upper bounds and continue 2.222 - for (Type t : undetvars) { 2.223 + for (Type t : inferenceContext.undetvars) { 2.224 UndetVar uv = (UndetVar)t; 2.225 - uv.hibounds = types.subst(uv.hibounds, tvars, insttypes); 2.226 + uv.hibounds = inferenceContext.asInstTypes(uv.hibounds, types); 2.227 } 2.228 } 2.229 } 2.230 - return insttypes; 2.231 } 2.232 2.233 /** 2.234 * Infer cyclic inference variables as described in 15.12.2.8. 2.235 */ 2.236 - private List<Type> instantiateAsUninferredVars(List<Type> undetvars, List<Type> tvars) { 2.237 - Assert.check(undetvars.length() == tvars.length()); 2.238 - ListBuffer<Type> insttypes = ListBuffer.lb(); 2.239 + private void instantiateAsUninferredVars(InferenceContext inferenceContext) { 2.240 ListBuffer<Type> todo = ListBuffer.lb(); 2.241 //step 1 - create fresh tvars 2.242 - for (Type t : undetvars) { 2.243 + for (Type t : inferenceContext.undetvars) { 2.244 UndetVar uv = (UndetVar)t; 2.245 if (uv.inst == null) { 2.246 TypeSymbol fresh_tvar = new TypeSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner); 2.247 @@ -257,25 +239,23 @@ 2.248 todo.append(uv); 2.249 uv.inst = fresh_tvar.type; 2.250 } 2.251 - insttypes.append(uv.inst); 2.252 } 2.253 //step 2 - replace fresh tvars in their bounds 2.254 - List<Type> formals = tvars; 2.255 + List<Type> formals = inferenceContext.inferenceVars(); 2.256 for (Type t : todo) { 2.257 UndetVar uv = (UndetVar)t; 2.258 TypeVar ct = (TypeVar)uv.inst; 2.259 - ct.bound = types.glb(types.subst(types.getBounds(ct), tvars, insttypes.toList())); 2.260 + ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct), types)); 2.261 if (ct.bound.isErroneous()) { 2.262 //report inference error if glb fails 2.263 reportBoundError(uv, BoundErrorKind.BAD_UPPER); 2.264 } 2.265 formals = formals.tail; 2.266 } 2.267 - return insttypes.toList(); 2.268 } 2.269 2.270 - /** Instantiate method type `mt' by finding instantiations of 2.271 - * `tvars' so that method can be applied to `argtypes'. 2.272 + /** Instantiate a generic method type by finding instantiations for all its 2.273 + * inference variables so that it can be applied to a given argument type list. 2.274 */ 2.275 public Type instantiateMethod(Env<AttrContext> env, 2.276 List<Type> tvars, 2.277 @@ -287,83 +267,61 @@ 2.278 boolean useVarargs, 2.279 Warner warn) throws InferenceException { 2.280 //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG 2.281 - List<Type> undetvars = makeUndetvars(tvars); 2.282 + final InferenceContext inferenceContext = new InferenceContext(tvars, types); 2.283 + inferenceException.clear(); 2.284 2.285 - List<Type> capturedArgs = 2.286 - rs.checkRawArgumentsAcceptable(env, undetvars, argtypes, mt.getParameterTypes(), 2.287 - allowBoxing, useVarargs, warn, new InferenceCheckHandler(undetvars)); 2.288 + try { 2.289 + rs.checkRawArgumentsAcceptable(env, inferenceContext, argtypes, mt.getParameterTypes(), 2.290 + allowBoxing, useVarargs, warn, new InferenceCheckHandler(inferenceContext)); 2.291 2.292 - // minimize as yet undetermined type variables 2.293 - for (Type t : undetvars) 2.294 - minimizeInst((UndetVar) t, warn); 2.295 + // minimize as yet undetermined type variables 2.296 + for (Type t : inferenceContext.undetvars) { 2.297 + minimizeInst((UndetVar)t, warn); 2.298 + } 2.299 2.300 - /** Type variables instantiated to bottom */ 2.301 - ListBuffer<Type> restvars = new ListBuffer<Type>(); 2.302 + checkWithinBounds(inferenceContext, warn); 2.303 2.304 - /** Undet vars instantiated to bottom */ 2.305 - final ListBuffer<Type> restundet = new ListBuffer<Type>(); 2.306 + mt = (MethodType)inferenceContext.asInstType(mt, types); 2.307 2.308 - /** Instantiated types or TypeVars if under-constrained */ 2.309 - ListBuffer<Type> insttypes = new ListBuffer<Type>(); 2.310 + List<Type> restvars = inferenceContext.restvars(); 2.311 2.312 - /** Instantiated types or UndetVars if under-constrained */ 2.313 - ListBuffer<Type> undettypes = new ListBuffer<Type>(); 2.314 + if (!restvars.isEmpty()) { 2.315 + if (resultInfo != null) { 2.316 + instantiateUninferred(env.tree.pos(), inferenceContext, mt, resultInfo, warn); 2.317 + checkWithinBounds(inferenceContext, warn); 2.318 + mt = (MethodType)inferenceContext.asInstType(mt, types); 2.319 + if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) { 2.320 + log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt); 2.321 + } 2.322 + } 2.323 + } 2.324 2.325 - for (Type t : undetvars) { 2.326 - UndetVar uv = (UndetVar)t; 2.327 - if (uv.inst.tag == BOT) { 2.328 - restvars.append(uv.qtype); 2.329 - restundet.append(uv); 2.330 - insttypes.append(uv.qtype); 2.331 - undettypes.append(uv); 2.332 - uv.inst = null; 2.333 - } else { 2.334 - insttypes.append(uv.inst); 2.335 - undettypes.append(uv.inst); 2.336 - } 2.337 + // return instantiated version of method type 2.338 + return mt; 2.339 + } finally { 2.340 + inferenceContext.notifyChange(types); 2.341 } 2.342 - checkWithinBounds(tvars, undetvars, insttypes.toList(), warn); 2.343 - 2.344 - mt = (MethodType)types.subst(mt, tvars, insttypes.toList()); 2.345 - 2.346 - if (!restvars.isEmpty() && resultInfo != null) { 2.347 - List<Type> restInferred = 2.348 - instantiateUninferred(env.tree.pos(), restundet.toList(), restvars.toList(), mt, resultInfo, warn); 2.349 - checkWithinBounds(tvars, undetvars, 2.350 - types.subst(insttypes.toList(), restvars.toList(), restInferred), warn); 2.351 - mt = (MethodType)types.subst(mt, restvars.toList(), restInferred); 2.352 - if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) { 2.353 - log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt); 2.354 - } 2.355 - } 2.356 - 2.357 - if (restvars.isEmpty() || resultInfo != null) { 2.358 - // check that actuals conform to inferred formals 2.359 - checkArgumentsAcceptable(env, capturedArgs, mt.getParameterTypes(), allowBoxing, useVarargs, warn); 2.360 - } 2.361 - // return instantiated version of method type 2.362 - return mt; 2.363 } 2.364 //where 2.365 2.366 /** inference check handler **/ 2.367 class InferenceCheckHandler implements Resolve.MethodCheckHandler { 2.368 2.369 - List<Type> undetvars; 2.370 + InferenceContext inferenceContext; 2.371 2.372 - public InferenceCheckHandler(List<Type> undetvars) { 2.373 - this.undetvars = undetvars; 2.374 + public InferenceCheckHandler(InferenceContext inferenceContext) { 2.375 + this.inferenceContext = inferenceContext; 2.376 } 2.377 2.378 public InapplicableMethodException arityMismatch() { 2.379 - return inferenceException.setMessage("infer.arg.length.mismatch", inferenceVars(undetvars)); 2.380 + return inferenceException.setMessage("infer.arg.length.mismatch", inferenceContext.inferenceVars()); 2.381 } 2.382 public InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details) { 2.383 String key = varargs ? 2.384 "infer.varargs.argument.mismatch" : 2.385 "infer.no.conforming.assignment.exists"; 2.386 return inferenceException.setMessage(key, 2.387 - inferenceVars(undetvars), details); 2.388 + inferenceContext.inferenceVars(), details); 2.389 } 2.390 public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) { 2.391 return inferenceException.setMessage("inaccessible.varargs.type", 2.392 @@ -371,51 +329,37 @@ 2.393 } 2.394 } 2.395 2.396 - private void checkArgumentsAcceptable(Env<AttrContext> env, List<Type> actuals, List<Type> formals, 2.397 - boolean allowBoxing, boolean useVarargs, Warner warn) { 2.398 - try { 2.399 - rs.checkRawArgumentsAcceptable(env, actuals, formals, 2.400 - allowBoxing, useVarargs, warn); 2.401 - } 2.402 - catch (InapplicableMethodException ex) { 2.403 - // inferred method is not applicable 2.404 - throw inferenceException.setMessage(ex.getDiagnostic()); 2.405 - } 2.406 - } 2.407 - 2.408 /** check that type parameters are within their bounds. 2.409 */ 2.410 - void checkWithinBounds(List<Type> tvars, 2.411 - List<Type> undetvars, 2.412 - List<Type> arguments, 2.413 + void checkWithinBounds(InferenceContext inferenceContext, 2.414 Warner warn) 2.415 throws InferenceException { 2.416 - List<Type> args = arguments; 2.417 - for (Type t : undetvars) { 2.418 + List<Type> tvars = inferenceContext.inferenceVars(); 2.419 + for (Type t : inferenceContext.undetvars) { 2.420 UndetVar uv = (UndetVar)t; 2.421 - uv.hibounds = types.subst(uv.hibounds, tvars, arguments); 2.422 - uv.lobounds = types.subst(uv.lobounds, tvars, arguments); 2.423 - uv.eq = types.subst(uv.eq, tvars, arguments); 2.424 - checkCompatibleUpperBounds(uv, tvars); 2.425 - if (args.head.tag != TYPEVAR || !args.head.containsAny(tvars)) { 2.426 - Type inst = args.head; 2.427 + uv.hibounds = inferenceContext.asInstTypes(uv.hibounds, types); 2.428 + uv.lobounds = inferenceContext.asInstTypes(uv.lobounds, types); 2.429 + uv.eq = inferenceContext.asInstTypes(uv.eq, types); 2.430 + checkCompatibleUpperBounds(uv, inferenceContext.inferenceVars()); 2.431 + if (!inferenceContext.restvars().contains(tvars.head)) { 2.432 + Type inst = inferenceContext.asInstType(t, types); 2.433 for (Type u : uv.hibounds) { 2.434 - if (!types.isSubtypeUnchecked(inst, types.subst(u, tvars, undetvars), warn)) { 2.435 + if (!types.isSubtypeUnchecked(inst, inferenceContext.asFree(u, types), warn)) { 2.436 reportBoundError(uv, BoundErrorKind.UPPER); 2.437 } 2.438 } 2.439 for (Type l : uv.lobounds) { 2.440 - if (!types.isSubtypeUnchecked(types.subst(l, tvars, undetvars), inst, warn)) { 2.441 + if (!types.isSubtypeUnchecked(inferenceContext.asFree(l, types), inst, warn)) { 2.442 reportBoundError(uv, BoundErrorKind.LOWER); 2.443 } 2.444 } 2.445 for (Type e : uv.eq) { 2.446 - if (!types.isSameType(inst, types.subst(e, tvars, undetvars))) { 2.447 + if (!types.isSameType(inst, inferenceContext.asFree(e, types))) { 2.448 reportBoundError(uv, BoundErrorKind.EQ); 2.449 } 2.450 } 2.451 } 2.452 - args = args.tail; 2.453 + tvars = tvars.tail; 2.454 } 2.455 } 2.456 2.457 @@ -423,7 +367,7 @@ 2.458 // VGJ: sort of inlined maximizeInst() below. Adding 2.459 // bounds can cause lobounds that are above hibounds. 2.460 ListBuffer<Type> hiboundsNoVars = ListBuffer.lb(); 2.461 - for (Type t : Type.filter(uv.hibounds, errorFilter)) { 2.462 + for (Type t : Type.filter(uv.hibounds, boundFilter)) { 2.463 if (!t.containsAny(tvars)) { 2.464 hiboundsNoVars.append(t); 2.465 } 2.466 @@ -531,4 +475,199 @@ 2.467 return t; 2.468 } 2.469 }; 2.470 + 2.471 + /** 2.472 + * Mapping that turns inference variables into undet vars 2.473 + * (used by inference context) 2.474 + */ 2.475 + static Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") { 2.476 + public Type apply(Type t) { 2.477 + if (t.tag == TYPEVAR) return new UndetVar(t); 2.478 + else return t.map(this); 2.479 + } 2.480 + }; 2.481 + 2.482 + /** 2.483 + * An inference context keeps track of the set of variables that are free 2.484 + * in the current context. It provides utility methods for opening/closing 2.485 + * types to their corresponding free/closed forms. It also provide hooks for 2.486 + * attaching deferred post-inference action (see PendingCheck). Finally, 2.487 + * it can be used as an entry point for performing upper/lower bound inference 2.488 + * (see InferenceKind). 2.489 + */ 2.490 + static class InferenceContext { 2.491 + 2.492 + /** 2.493 + * Single-method-interface for defining inference callbacks. Certain actions 2.494 + * (i.e. subtyping checks) might need to be redone after all inference variables 2.495 + * have been fixed. 2.496 + */ 2.497 + interface FreeTypeListener { 2.498 + void typesInferred(InferenceContext inferenceContext); 2.499 + } 2.500 + 2.501 + /** list of inference vars as undet vars */ 2.502 + List<Type> undetvars; 2.503 + 2.504 + /** list of inference vars in this context */ 2.505 + List<Type> inferencevars; 2.506 + 2.507 + java.util.Map<FreeTypeListener, List<Type>> freeTypeListeners = 2.508 + new java.util.HashMap<FreeTypeListener, List<Type>>(); 2.509 + 2.510 + List<FreeTypeListener> freetypeListeners = List.nil(); 2.511 + 2.512 + public InferenceContext(List<Type> inferencevars, Types types) { 2.513 + this.undetvars = Type.map(inferencevars, fromTypeVarFun); 2.514 + this.inferencevars = inferencevars; 2.515 + for (Type t : this.undetvars) { 2.516 + UndetVar uv = (UndetVar)t; 2.517 + uv.hibounds = types.getBounds((TypeVar)uv.qtype); 2.518 + } 2.519 + } 2.520 + 2.521 + /** 2.522 + * returns the list of free variables (as type-variables) in this 2.523 + * inference context 2.524 + */ 2.525 + List<Type> inferenceVars() { 2.526 + return inferencevars; 2.527 + } 2.528 + 2.529 + /** 2.530 + * returns the list of uninstantiated variables (as type-variables) in this 2.531 + * inference context (usually called after instantiate()) 2.532 + */ 2.533 + List<Type> restvars() { 2.534 + List<Type> undetvars = this.undetvars; 2.535 + ListBuffer<Type> restvars = ListBuffer.lb(); 2.536 + for (Type t : instTypes()) { 2.537 + UndetVar uv = (UndetVar)undetvars.head; 2.538 + if (uv.qtype == t) { 2.539 + restvars.append(t); 2.540 + } 2.541 + undetvars = undetvars.tail; 2.542 + } 2.543 + return restvars.toList(); 2.544 + } 2.545 + 2.546 + /** 2.547 + * is this type free? 2.548 + */ 2.549 + final boolean free(Type t) { 2.550 + return t.containsAny(inferencevars); 2.551 + } 2.552 + 2.553 + final boolean free(List<Type> ts) { 2.554 + for (Type t : ts) { 2.555 + if (free(t)) return true; 2.556 + } 2.557 + return false; 2.558 + } 2.559 + 2.560 + /** 2.561 + * Returns a list of free variables in a given type 2.562 + */ 2.563 + final List<Type> freeVarsIn(Type t) { 2.564 + ListBuffer<Type> buf = ListBuffer.lb(); 2.565 + for (Type iv : inferenceVars()) { 2.566 + if (t.contains(iv)) { 2.567 + buf.add(iv); 2.568 + } 2.569 + } 2.570 + return buf.toList(); 2.571 + } 2.572 + 2.573 + final List<Type> freeVarsIn(List<Type> ts) { 2.574 + ListBuffer<Type> buf = ListBuffer.lb(); 2.575 + for (Type t : ts) { 2.576 + buf.appendList(freeVarsIn(t)); 2.577 + } 2.578 + ListBuffer<Type> buf2 = ListBuffer.lb(); 2.579 + for (Type t : buf) { 2.580 + if (!buf2.contains(t)) { 2.581 + buf2.add(t); 2.582 + } 2.583 + } 2.584 + return buf2.toList(); 2.585 + } 2.586 + 2.587 + /** 2.588 + * Replace all free variables in a given type with corresponding 2.589 + * undet vars (used ahead of subtyping/compatibility checks to allow propagation 2.590 + * of inference constraints). 2.591 + */ 2.592 + final Type asFree(Type t, Types types) { 2.593 + return types.subst(t, inferencevars, undetvars); 2.594 + } 2.595 + 2.596 + final List<Type> asFree(List<Type> ts, Types types) { 2.597 + ListBuffer<Type> buf = ListBuffer.lb(); 2.598 + for (Type t : ts) { 2.599 + buf.append(asFree(t, types)); 2.600 + } 2.601 + return buf.toList(); 2.602 + } 2.603 + 2.604 + List<Type> instTypes() { 2.605 + ListBuffer<Type> buf = ListBuffer.lb(); 2.606 + for (Type t : undetvars) { 2.607 + UndetVar uv = (UndetVar)t; 2.608 + buf.append(uv.inst != null ? uv.inst : uv.qtype); 2.609 + } 2.610 + return buf.toList(); 2.611 + } 2.612 + 2.613 + /** 2.614 + * Replace all free variables in a given type with corresponding 2.615 + * instantiated types - if one or more free variable has not been 2.616 + * fully instantiated, it will still be available in the resulting type. 2.617 + */ 2.618 + Type asInstType(Type t, Types types) { 2.619 + return types.subst(t, inferencevars, instTypes()); 2.620 + } 2.621 + 2.622 + List<Type> asInstTypes(List<Type> ts, Types types) { 2.623 + ListBuffer<Type> buf = ListBuffer.lb(); 2.624 + for (Type t : ts) { 2.625 + buf.append(asInstType(t, types)); 2.626 + } 2.627 + return buf.toList(); 2.628 + } 2.629 + 2.630 + /** 2.631 + * Add custom hook for performing post-inference action 2.632 + */ 2.633 + void addFreeTypeListener(List<Type> types, FreeTypeListener ftl) { 2.634 + freeTypeListeners.put(ftl, freeVarsIn(types)); 2.635 + } 2.636 + 2.637 + /** 2.638 + * Mark the inference context as complete and trigger evaluation 2.639 + * of all deferred checks. 2.640 + */ 2.641 + void notifyChange(Types types) { 2.642 + InferenceException thrownEx = null; 2.643 + for (Map.Entry<FreeTypeListener, List<Type>> entry : 2.644 + new HashMap<FreeTypeListener, List<Type>>(freeTypeListeners).entrySet()) { 2.645 + if (!Type.containsAny(entry.getValue(), restvars())) { 2.646 + try { 2.647 + entry.getKey().typesInferred(this); 2.648 + freeTypeListeners.remove(entry.getKey()); 2.649 + } catch (InferenceException ex) { 2.650 + if (thrownEx == null) { 2.651 + thrownEx = ex; 2.652 + } 2.653 + } 2.654 + } 2.655 + } 2.656 + //inference exception multiplexing - present any inference exception 2.657 + //thrown when processing listeners as a single one 2.658 + if (thrownEx != null) { 2.659 + throw thrownEx; 2.660 + } 2.661 + } 2.662 } 2.663 + 2.664 + final InferenceContext emptyContext = new InferenceContext(List.<Type>nil(), types); 2.665 +}
3.1 --- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java Tue Sep 25 11:53:18 2012 +0100 3.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java Tue Sep 25 11:55:34 2012 +0100 3.3 @@ -31,6 +31,8 @@ 3.4 import com.sun.tools.javac.code.Symbol.*; 3.5 import com.sun.tools.javac.comp.Attr.ResultInfo; 3.6 import com.sun.tools.javac.comp.Check.CheckContext; 3.7 +import com.sun.tools.javac.comp.Infer.InferenceContext; 3.8 +import com.sun.tools.javac.comp.Infer.InferenceContext.FreeTypeListener; 3.9 import com.sun.tools.javac.comp.Resolve.MethodResolutionContext.Candidate; 3.10 import com.sun.tools.javac.jvm.*; 3.11 import com.sun.tools.javac.tree.*; 3.12 @@ -586,7 +588,7 @@ 3.13 boolean allowBoxing, 3.14 boolean useVarargs, 3.15 Warner warn) { 3.16 - checkRawArgumentsAcceptable(env, List.<Type>nil(), argtypes, formals, 3.17 + checkRawArgumentsAcceptable(env, infer.emptyContext, argtypes, formals, 3.18 allowBoxing, useVarargs, warn, resolveHandler); 3.19 } 3.20 3.21 @@ -606,8 +608,8 @@ 3.22 * 3.23 * A method check handler (see above) is used in order to report errors. 3.24 */ 3.25 - List<Type> checkRawArgumentsAcceptable(Env<AttrContext> env, 3.26 - List<Type> undetvars, 3.27 + void checkRawArgumentsAcceptable(final Env<AttrContext> env, 3.28 + final Infer.InferenceContext inferenceContext, 3.29 List<Type> argtypes, 3.30 List<Type> formals, 3.31 boolean allowBoxing, 3.32 @@ -623,7 +625,7 @@ 3.33 } 3.34 3.35 while (argtypes.nonEmpty() && formals.head != varargsFormal) { 3.36 - ResultInfo resultInfo = methodCheckResult(formals.head, allowBoxing, false, undetvars, handler, warn); 3.37 + ResultInfo resultInfo = methodCheckResult(formals.head, allowBoxing, false, inferenceContext, handler, warn); 3.38 checkedArgs.append(resultInfo.check(env.tree.pos(), argtypes.head)); 3.39 argtypes = argtypes.tail; 3.40 formals = formals.tail; 3.41 @@ -638,17 +640,29 @@ 3.42 //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5) 3.43 Type elt = types.elemtype(varargsFormal); 3.44 while (argtypes.nonEmpty()) { 3.45 - ResultInfo resultInfo = methodCheckResult(elt, allowBoxing, true, undetvars, handler, warn); 3.46 + ResultInfo resultInfo = methodCheckResult(elt, allowBoxing, true, inferenceContext, handler, warn); 3.47 checkedArgs.append(resultInfo.check(env.tree.pos(), argtypes.head)); 3.48 argtypes = argtypes.tail; 3.49 } 3.50 //check varargs element type accessibility 3.51 - if (undetvars.isEmpty() && !isAccessible(env, elt)) { 3.52 + varargsAccessible(env, elt, handler, inferenceContext); 3.53 + } 3.54 + } 3.55 + 3.56 + void varargsAccessible(final Env<AttrContext> env, final Type t, final Resolve.MethodCheckHandler handler, final InferenceContext inferenceContext) { 3.57 + if (inferenceContext.free(t)) { 3.58 + inferenceContext.addFreeTypeListener(List.of(t), new FreeTypeListener() { 3.59 + @Override 3.60 + public void typesInferred(InferenceContext inferenceContext) { 3.61 + varargsAccessible(env, inferenceContext.asInstType(t, types), handler, inferenceContext); 3.62 + } 3.63 + }); 3.64 + } else { 3.65 + if (!isAccessible(env, t)) { 3.66 Symbol location = env.enclClass.sym; 3.67 - throw handler.inaccessibleVarargs(location, elt); 3.68 + throw handler.inaccessibleVarargs(location, t); 3.69 } 3.70 } 3.71 - return checkedArgs.toList(); 3.72 } 3.73 3.74 /** 3.75 @@ -659,13 +673,13 @@ 3.76 3.77 MethodCheckHandler handler; 3.78 boolean useVarargs; 3.79 - List<Type> undetvars; 3.80 + Infer.InferenceContext inferenceContext; 3.81 Warner rsWarner; 3.82 3.83 - public MethodCheckContext(MethodCheckHandler handler, boolean useVarargs, List<Type> undetvars, Warner rsWarner) { 3.84 + public MethodCheckContext(MethodCheckHandler handler, boolean useVarargs, Infer.InferenceContext inferenceContext, Warner rsWarner) { 3.85 this.handler = handler; 3.86 this.useVarargs = useVarargs; 3.87 - this.undetvars = undetvars; 3.88 + this.inferenceContext = inferenceContext; 3.89 this.rsWarner = rsWarner; 3.90 } 3.91 3.92 @@ -676,6 +690,10 @@ 3.93 public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) { 3.94 return rsWarner; 3.95 } 3.96 + 3.97 + public InferenceContext inferenceContext() { 3.98 + return inferenceContext; 3.99 + } 3.100 } 3.101 3.102 /** 3.103 @@ -684,12 +702,12 @@ 3.104 */ 3.105 class StrictMethodContext extends MethodCheckContext { 3.106 3.107 - public StrictMethodContext(MethodCheckHandler handler, boolean useVarargs, List<Type> undetvars, Warner rsWarner) { 3.108 - super(handler, useVarargs, undetvars, rsWarner); 3.109 + public StrictMethodContext(MethodCheckHandler handler, boolean useVarargs, Infer.InferenceContext inferenceContext, Warner rsWarner) { 3.110 + super(handler, useVarargs, inferenceContext, rsWarner); 3.111 } 3.112 3.113 public boolean compatible(Type found, Type req, Warner warn) { 3.114 - return types.isSubtypeUnchecked(found, infer.asUndetType(req, undetvars), warn); 3.115 + return types.isSubtypeUnchecked(found, inferenceContext.asFree(req, types), warn); 3.116 } 3.117 } 3.118 3.119 @@ -699,12 +717,12 @@ 3.120 */ 3.121 class LooseMethodContext extends MethodCheckContext { 3.122 3.123 - public LooseMethodContext(MethodCheckHandler handler, boolean useVarargs, List<Type> undetvars, Warner rsWarner) { 3.124 - super(handler, useVarargs, undetvars, rsWarner); 3.125 + public LooseMethodContext(MethodCheckHandler handler, boolean useVarargs, Infer.InferenceContext inferenceContext, Warner rsWarner) { 3.126 + super(handler, useVarargs, inferenceContext, rsWarner); 3.127 } 3.128 3.129 public boolean compatible(Type found, Type req, Warner warn) { 3.130 - return types.isConvertible(found, infer.asUndetType(req, undetvars), warn); 3.131 + return types.isConvertible(found, inferenceContext.asFree(req, types), warn); 3.132 } 3.133 } 3.134 3.135 @@ -712,10 +730,10 @@ 3.136 * Create a method check context to be used during method applicability check 3.137 */ 3.138 ResultInfo methodCheckResult(Type to, boolean allowBoxing, boolean useVarargs, 3.139 - List<Type> undetvars, MethodCheckHandler methodHandler, Warner rsWarner) { 3.140 + Infer.InferenceContext inferenceContext, MethodCheckHandler methodHandler, Warner rsWarner) { 3.141 MethodCheckContext checkContext = allowBoxing ? 3.142 - new LooseMethodContext(methodHandler, useVarargs, undetvars, rsWarner) : 3.143 - new StrictMethodContext(methodHandler, useVarargs, undetvars, rsWarner); 3.144 + new LooseMethodContext(methodHandler, useVarargs, inferenceContext, rsWarner) : 3.145 + new StrictMethodContext(methodHandler, useVarargs, inferenceContext, rsWarner); 3.146 return attr.new ResultInfo(VAL, to, checkContext) { 3.147 @Override 3.148 protected Type check(DiagnosticPosition pos, Type found) { 3.149 @@ -735,16 +753,13 @@ 3.150 this.diags = diags; 3.151 } 3.152 InapplicableMethodException setMessage() { 3.153 - this.diagnostic = null; 3.154 - return this; 3.155 + return setMessage((JCDiagnostic)null); 3.156 } 3.157 InapplicableMethodException setMessage(String key) { 3.158 - this.diagnostic = key != null ? diags.fragment(key) : null; 3.159 - return this; 3.160 + return setMessage(key != null ? diags.fragment(key) : null); 3.161 } 3.162 InapplicableMethodException setMessage(String key, Object... args) { 3.163 - this.diagnostic = key != null ? diags.fragment(key, args) : null; 3.164 - return this; 3.165 + return setMessage(key != null ? diags.fragment(key, args) : null); 3.166 } 3.167 InapplicableMethodException setMessage(JCDiagnostic diag) { 3.168 this.diagnostic = diag;
4.1 --- a/test/tools/javac/generics/inference/6638712/T6638712c.out Tue Sep 25 11:53:18 2012 +0100 4.2 +++ b/test/tools/javac/generics/inference/6638712/T6638712c.out Tue Sep 25 11:55:34 2012 +0100 4.3 @@ -1,2 +1,2 @@ 4.4 -T6638712c.java:16:9: compiler.err.cant.apply.symbol.1: kindname.method, sort, T[],java.util.Comparator<? super T>, java.lang.Enum[],java.util.Comparator<java.lang.Enum<?>>, kindname.class, T6638712c, (compiler.misc.no.conforming.assignment.exists: (compiler.misc.inconvertible.types: java.util.Comparator<java.lang.Enum<?>>, java.util.Comparator<? super java.lang.Enum>)) 4.5 +T6638712c.java:16:9: compiler.err.cant.apply.symbol.1: kindname.method, sort, T[],java.util.Comparator<? super T>, java.lang.Enum[],java.util.Comparator<java.lang.Enum<?>>, kindname.class, T6638712c, (compiler.misc.infer.no.conforming.assignment.exists: T, (compiler.misc.inconvertible.types: java.util.Comparator<java.lang.Enum<?>>, java.util.Comparator<? super java.lang.Enum>)) 4.6 1 error
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/test/tools/javac/varargs/6313164/T7175433.java Tue Sep 25 11:55:34 2012 +0100 5.3 @@ -0,0 +1,59 @@ 5.4 +/* 5.5 + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.23 + * or visit www.oracle.com if you need additional information or have any 5.24 + * questions. 5.25 + */ 5.26 + 5.27 +/* 5.28 + * @test 5.29 + * @bug 7175433 6313164 5.30 + * @summary Inference cleanup: add helper class to handle inference variables 5.31 + * 5.32 + */ 5.33 + 5.34 +import java.util.List; 5.35 + 5.36 +class Bar { 5.37 + 5.38 + private class Foo { } 5.39 + 5.40 + <Z> List<Z> m(Object... o) { T7175433.assertTrue(true); return null; } 5.41 + <Z> List<Z> m(Foo... o) { T7175433.assertTrue(false); return null; } 5.42 + 5.43 + Foo getFoo() { return null; } 5.44 +} 5.45 + 5.46 +public class T7175433 { 5.47 + 5.48 + static int assertionCount; 5.49 + 5.50 + static void assertTrue(boolean b) { 5.51 + assertionCount++; 5.52 + if (!b) { 5.53 + throw new AssertionError(); 5.54 + } 5.55 + } 5.56 + 5.57 + public static void main(String[] args) { 5.58 + Bar b = new Bar(); 5.59 + b.m(b.getFoo()); 5.60 + assertTrue(assertionCount == 1); 5.61 + } 5.62 +}