1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Infer.java Thu May 01 15:43:28 2014 -0700 1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java Fri May 02 01:25:26 2014 +0100 1.3 @@ -142,24 +142,24 @@ 1.4 * Main inference entry point - instantiate a generic method type 1.5 * using given argument types and (possibly) an expected target-type. 1.6 */ 1.7 - public Type instantiateMethod(Env<AttrContext> env, 1.8 - List<Type> tvars, 1.9 - MethodType mt, 1.10 - Attr.ResultInfo resultInfo, 1.11 - Symbol msym, 1.12 - List<Type> argtypes, 1.13 - boolean allowBoxing, 1.14 - boolean useVarargs, 1.15 - Resolve.MethodResolutionContext resolveContext, 1.16 - Warner warn) throws InferenceException { 1.17 + Type instantiateMethod( Env<AttrContext> env, 1.18 + List<Type> tvars, 1.19 + MethodType mt, 1.20 + Attr.ResultInfo resultInfo, 1.21 + MethodSymbol msym, 1.22 + List<Type> argtypes, 1.23 + boolean allowBoxing, 1.24 + boolean useVarargs, 1.25 + Resolve.MethodResolutionContext resolveContext, 1.26 + Warner warn) throws InferenceException { 1.27 //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG 1.28 - final InferenceContext inferenceContext = new InferenceContext(tvars); 1.29 + final InferenceContext inferenceContext = new InferenceContext(tvars); //B0 1.30 inferenceException.clear(); 1.31 try { 1.32 DeferredAttr.DeferredAttrContext deferredAttrContext = 1.33 resolveContext.deferredAttrContext(msym, inferenceContext, resultInfo, warn); 1.34 1.35 - resolveContext.methodCheck.argumentsAcceptable(env, deferredAttrContext, 1.36 + resolveContext.methodCheck.argumentsAcceptable(env, deferredAttrContext, //B2 1.37 argtypes, mt.getParameterTypes(), warn); 1.38 1.39 if (allowGraphInference && 1.40 @@ -167,7 +167,8 @@ 1.41 !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { 1.42 //inject return constraints earlier 1.43 checkWithinBounds(inferenceContext, warn); //propagation 1.44 - Type newRestype = generateReturnConstraints(resultInfo, mt, inferenceContext); 1.45 + Type newRestype = generateReturnConstraints(env.tree, resultInfo, //B3 1.46 + mt, inferenceContext); 1.47 mt = (MethodType)types.createMethodTypeWithReturn(mt, newRestype); 1.48 //propagate outwards if needed 1.49 if (resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) { 1.50 @@ -193,7 +194,7 @@ 1.51 inferenceContext.restvars().nonEmpty() && 1.52 resultInfo != null && 1.53 !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { 1.54 - generateReturnConstraints(resultInfo, mt, inferenceContext); 1.55 + generateReturnConstraints(env.tree, resultInfo, mt, inferenceContext); 1.56 inferenceContext.solveLegacy(false, warn, LegacyInferenceSteps.EQ_UPPER.steps); //maximizeInst 1.57 mt = (MethodType)inferenceContext.asInstType(mt); 1.58 } 1.59 @@ -210,6 +211,12 @@ 1.60 } else { 1.61 inferenceContext.notifyChange(inferenceContext.boundedVars()); 1.62 } 1.63 + if (resultInfo == null) { 1.64 + /* if the is no result info then we can clear the capture types 1.65 + * cache without affecting any result info check 1.66 + */ 1.67 + inferenceContext.captureTypeCache.clear(); 1.68 + } 1.69 } 1.70 } 1.71 1.72 @@ -218,7 +225,7 @@ 1.73 * call occurs in a context where a type T is expected, use the expected 1.74 * type to derive more constraints on the generic method inference variables. 1.75 */ 1.76 - Type generateReturnConstraints(Attr.ResultInfo resultInfo, 1.77 + Type generateReturnConstraints(JCTree tree, Attr.ResultInfo resultInfo, 1.78 MethodType mt, InferenceContext inferenceContext) { 1.79 InferenceContext rsInfoInfContext = resultInfo.checkContext.inferenceContext(); 1.80 Type from = mt.getReturnType(); 1.81 @@ -232,13 +239,29 @@ 1.82 } 1.83 } 1.84 } 1.85 - Type qtype1 = inferenceContext.asUndetVar(from); 1.86 - Type to = returnConstraintTarget(qtype1, resultInfo.pt); 1.87 + Type qtype = inferenceContext.asUndetVar(from); 1.88 + Type to = resultInfo.pt; 1.89 + 1.90 + if (qtype.hasTag(VOID)) { 1.91 + to = syms.voidType; 1.92 + } else if (to.hasTag(NONE)) { 1.93 + to = from.isPrimitive() ? from : syms.objectType; 1.94 + } else if (qtype.hasTag(UNDETVAR)) { 1.95 + if (resultInfo.pt.isReference()) { 1.96 + to = generateReturnConstraintsUndetVarToReference( 1.97 + tree, (UndetVar)qtype, to, resultInfo, inferenceContext); 1.98 + } else { 1.99 + if (to.isPrimitive()) { 1.100 + to = generateReturnConstraintsPrimitive(tree, (UndetVar)qtype, to, 1.101 + resultInfo, inferenceContext); 1.102 + } 1.103 + } 1.104 + } 1.105 Assert.check(allowGraphInference || !rsInfoInfContext.free(to), 1.106 "legacy inference engine cannot handle constraints on both sides of a subtyping assertion"); 1.107 //we need to skip capture? 1.108 Warner retWarn = new Warner(); 1.109 - if (!resultInfo.checkContext.compatible(qtype1, rsInfoInfContext.asUndetVar(to), retWarn) || 1.110 + if (!resultInfo.checkContext.compatible(qtype, rsInfoInfContext.asUndetVar(to), retWarn) || 1.111 //unchecked conversion is not allowed in source 7 mode 1.112 (!allowGraphInference && retWarn.hasLint(Lint.LintCategory.UNCHECKED))) { 1.113 throw inferenceException 1.114 @@ -248,30 +271,96 @@ 1.115 return from; 1.116 } 1.117 1.118 - Type returnConstraintTarget(Type from, Type to) { 1.119 - if (from.hasTag(VOID)) { 1.120 - return syms.voidType; 1.121 - } else if (to.hasTag(NONE)) { 1.122 - return from.isPrimitive() ? from : syms.objectType; 1.123 - } else if (from.hasTag(UNDETVAR) && to.isPrimitive()) { 1.124 - if (!allowGraphInference) { 1.125 - //if legacy, just return boxed type 1.126 - return types.boxedClass(to).type; 1.127 + private Type generateReturnConstraintsPrimitive(JCTree tree, UndetVar from, 1.128 + Type to, Attr.ResultInfo resultInfo, InferenceContext inferenceContext) { 1.129 + if (!allowGraphInference) { 1.130 + //if legacy, just return boxed type 1.131 + return types.boxedClass(to).type; 1.132 + } 1.133 + //if graph inference we need to skip conflicting boxed bounds... 1.134 + for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.UPPER, 1.135 + InferenceBound.LOWER)) { 1.136 + Type boundAsPrimitive = types.unboxedType(t); 1.137 + if (boundAsPrimitive == null || boundAsPrimitive.hasTag(NONE)) { 1.138 + continue; 1.139 } 1.140 - //if graph inference we need to skip conflicting boxed bounds... 1.141 - UndetVar uv = (UndetVar)from; 1.142 - for (Type t : uv.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) { 1.143 - Type boundAsPrimitive = types.unboxedType(t); 1.144 - if (boundAsPrimitive == null) continue; 1.145 - if (types.isConvertible(boundAsPrimitive, to)) { 1.146 - //effectively skip return-type constraint generation (compatibility) 1.147 - return syms.objectType; 1.148 + return generateReferenceToTargetConstraint(tree, from, to, 1.149 + resultInfo, inferenceContext); 1.150 + } 1.151 + return types.boxedClass(to).type; 1.152 + } 1.153 + 1.154 + private Type generateReturnConstraintsUndetVarToReference(JCTree tree, 1.155 + UndetVar from, Type to, Attr.ResultInfo resultInfo, 1.156 + InferenceContext inferenceContext) { 1.157 + Type captureOfTo = types.capture(to); 1.158 + /* T is a reference type, but is not a wildcard-parameterized type, and either 1.159 + */ 1.160 + if (captureOfTo == to) { //not a wildcard parameterized type 1.161 + /* i) B2 contains a bound of one of the forms alpha = S or S <: alpha, 1.162 + * where S is a wildcard-parameterized type, or 1.163 + */ 1.164 + for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) { 1.165 + Type captureOfBound = types.capture(t); 1.166 + if (captureOfBound != t) { 1.167 + return generateReferenceToTargetConstraint(tree, from, to, 1.168 + resultInfo, inferenceContext); 1.169 } 1.170 } 1.171 - return types.boxedClass(to).type; 1.172 - } else { 1.173 - return to; 1.174 + 1.175 + /* ii) B2 contains two bounds of the forms S1 <: alpha and S2 <: alpha, 1.176 + * where S1 and S2 have supertypes that are two different 1.177 + * parameterizations of the same generic class or interface. 1.178 + */ 1.179 + for (Type aLowerBound : from.getBounds(InferenceBound.LOWER)) { 1.180 + for (Type anotherLowerBound : from.getBounds(InferenceBound.LOWER)) { 1.181 + if (aLowerBound != anotherLowerBound && 1.182 + commonSuperWithDiffParameterization(aLowerBound, anotherLowerBound)) { 1.183 + /* self comment check if any lower bound may be and undetVar, 1.184 + * in that case the result of this call may be a false positive. 1.185 + * Should this be restricted to non free types? 1.186 + */ 1.187 + return generateReferenceToTargetConstraint(tree, from, to, 1.188 + resultInfo, inferenceContext); 1.189 + } 1.190 + } 1.191 + } 1.192 } 1.193 + 1.194 + /* T is a parameterization of a generic class or interface, G, 1.195 + * and B2 contains a bound of one of the forms alpha = S or S <: alpha, 1.196 + * where there exists no type of the form G<...> that is a 1.197 + * supertype of S, but the raw type G is a supertype of S 1.198 + */ 1.199 + if (to.isParameterized()) { 1.200 + for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) { 1.201 + Type sup = types.asSuper(t, to.tsym); 1.202 + if (sup != null && sup.isRaw()) { 1.203 + return generateReferenceToTargetConstraint(tree, from, to, 1.204 + resultInfo, inferenceContext); 1.205 + } 1.206 + } 1.207 + } 1.208 + return to; 1.209 + } 1.210 + 1.211 + private boolean commonSuperWithDiffParameterization(Type t, Type s) { 1.212 + Pair<Type, Type> supers = getParameterizedSupers(t, s); 1.213 + return (supers != null && !types.isSameType(supers.fst, supers.snd)); 1.214 + } 1.215 + 1.216 + private Type generateReferenceToTargetConstraint(JCTree tree, UndetVar from, 1.217 + Type to, Attr.ResultInfo resultInfo, 1.218 + InferenceContext inferenceContext) { 1.219 + inferenceContext.solve(List.of(from.qtype), new Warner()); 1.220 + Type capturedType = resultInfo.checkContext.inferenceContext() 1.221 + .cachedCapture(tree, from.inst, false); 1.222 + if (types.isConvertible(capturedType, 1.223 + resultInfo.checkContext.inferenceContext().asUndetVar(to))) { 1.224 + //effectively skip additional return-type constraint generation (compatibility) 1.225 + return syms.objectType; 1.226 + } 1.227 + return to; 1.228 } 1.229 1.230 /** 1.231 @@ -2082,8 +2171,10 @@ 1.232 * Copy variable in this inference context to the given context 1.233 */ 1.234 void dupTo(final InferenceContext that) { 1.235 - that.inferencevars = that.inferencevars.appendList(inferencevars); 1.236 - that.undetvars = that.undetvars.appendList(undetvars); 1.237 + that.inferencevars = that.inferencevars.appendList( 1.238 + inferencevars.diff(that.inferencevars)); 1.239 + that.undetvars = that.undetvars.appendList( 1.240 + undetvars.diff(that.undetvars)); 1.241 //set up listeners to notify original inference contexts as 1.242 //propagated vars are inferred in new context 1.243 for (Type t : inferencevars) { 1.244 @@ -2202,6 +2293,30 @@ 1.245 return "Inference vars: " + inferencevars + '\n' + 1.246 "Undet vars: " + undetvars; 1.247 } 1.248 + 1.249 + /* Method Types.capture() generates a new type every time it's applied 1.250 + * to a wildcard parameterized type. This is intended functionality but 1.251 + * there are some cases when what you need is not to generate a new 1.252 + * captured type but to check that a previously generated captured type 1.253 + * is correct. There are cases when caching a captured type for later 1.254 + * reuse is sound. In general two captures from the same AST are equal. 1.255 + * This is why the tree is used as the key of the map below. This map 1.256 + * stores a Type per AST. 1.257 + */ 1.258 + Map<JCTree, Type> captureTypeCache = new HashMap<>(); 1.259 + 1.260 + Type cachedCapture(JCTree tree, Type t, boolean readOnly) { 1.261 + Type captured = captureTypeCache.get(tree); 1.262 + if (captured != null) { 1.263 + return captured; 1.264 + } 1.265 + 1.266 + Type result = types.capture(t); 1.267 + if (result != t && !readOnly) { // then t is a wildcard parameterized type 1.268 + captureTypeCache.put(tree, result); 1.269 + } 1.270 + return result; 1.271 + } 1.272 } 1.273 1.274 final InferenceContext emptyContext = new InferenceContext(List.<Type>nil());