Wed, 17 Jul 2013 14:21:12 +0100
8019942: Graph inference: avoid redundant computation during bound incorporation
Summary: Bound incorporation should not perform same operation multiple times
Reviewed-by: jjg
1.1 --- a/src/share/classes/com/sun/tools/javac/code/Type.java Wed Jul 17 14:19:25 2013 +0100 1.2 +++ b/src/share/classes/com/sun/tools/javac/code/Type.java Wed Jul 17 14:21:12 2013 +0100 1.3 @@ -1525,7 +1525,7 @@ 1.4 } 1.5 1.6 protected void addBound(InferenceBound ib, Type bound, Types types, boolean update) { 1.7 - Type bound2 = boundMap.apply(bound); 1.8 + Type bound2 = toTypeVarMap.apply(bound); 1.9 List<Type> prevBounds = bounds.get(ib); 1.10 for (Type b : prevBounds) { 1.11 //check for redundancy - use strict version of isSameType on tvars 1.12 @@ -1536,7 +1536,7 @@ 1.13 notifyChange(EnumSet.of(ib)); 1.14 } 1.15 //where 1.16 - Type.Mapping boundMap = new Mapping("boundMap") { 1.17 + Type.Mapping toTypeVarMap = new Mapping("toTypeVarMap") { 1.18 @Override 1.19 public Type apply(Type t) { 1.20 if (t.hasTag(UNDETVAR)) {
2.1 --- a/src/share/classes/com/sun/tools/javac/comp/Infer.java Wed Jul 17 14:19:25 2013 +0100 2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Infer.java Wed Jul 17 14:21:12 2013 +0100 2.3 @@ -461,9 +461,10 @@ 2.4 } 2.5 finally { 2.6 mlistener.detach(); 2.7 - if (mlistener.rounds == MAX_INCORPORATION_STEPS) { 2.8 + if (incorporationCache.size() == MAX_INCORPORATION_STEPS) { 2.9 inferenceContext.rollback(saved_undet); 2.10 } 2.11 + incorporationCache.clear(); 2.12 } 2.13 } 2.14 //where 2.15 @@ -475,7 +476,6 @@ 2.16 */ 2.17 class MultiUndetVarListener implements UndetVar.UndetVarListener { 2.18 2.19 - int rounds; 2.20 boolean changed; 2.21 List<Type> undetvars; 2.22 2.23 @@ -489,13 +489,12 @@ 2.24 2.25 public void varChanged(UndetVar uv, Set<InferenceBound> ibs) { 2.26 //avoid non-termination 2.27 - if (rounds < MAX_INCORPORATION_STEPS) { 2.28 + if (incorporationCache.size() < MAX_INCORPORATION_STEPS) { 2.29 changed = true; 2.30 } 2.31 } 2.32 2.33 void reset() { 2.34 - rounds++; 2.35 changed = false; 2.36 } 2.37 2.38 @@ -528,17 +527,17 @@ 2.39 if (uv.inst != null) { 2.40 Type inst = uv.inst; 2.41 for (Type u : uv.getBounds(InferenceBound.UPPER)) { 2.42 - if (!infer.types.isSubtypeUnchecked(inst, inferenceContext.asFree(u), warn)) { 2.43 + if (!isSubtype(inst, inferenceContext.asFree(u), warn, infer)) { 2.44 infer.reportBoundError(uv, BoundErrorKind.UPPER); 2.45 } 2.46 } 2.47 for (Type l : uv.getBounds(InferenceBound.LOWER)) { 2.48 - if (!infer.types.isSubtypeUnchecked(inferenceContext.asFree(l), inst, warn)) { 2.49 + if (!isSubtype(inferenceContext.asFree(l), inst, warn, infer)) { 2.50 infer.reportBoundError(uv, BoundErrorKind.LOWER); 2.51 } 2.52 } 2.53 for (Type e : uv.getBounds(InferenceBound.EQ)) { 2.54 - if (!infer.types.isSameType(inst, inferenceContext.asFree(e))) { 2.55 + if (!isSameType(inst, inferenceContext.asFree(e), infer)) { 2.56 infer.reportBoundError(uv, BoundErrorKind.EQ); 2.57 } 2.58 } 2.59 @@ -561,19 +560,19 @@ 2.60 Type eq = null; 2.61 for (Type e : uv.getBounds(InferenceBound.EQ)) { 2.62 Assert.check(!inferenceContext.free(e)); 2.63 - if (eq != null && !infer.types.isSameType(e, eq)) { 2.64 + if (eq != null && !isSameType(e, eq, infer)) { 2.65 infer.reportBoundError(uv, BoundErrorKind.EQ); 2.66 } 2.67 eq = e; 2.68 for (Type l : uv.getBounds(InferenceBound.LOWER)) { 2.69 Assert.check(!inferenceContext.free(l)); 2.70 - if (!infer.types.isSubtypeUnchecked(l, e, warn)) { 2.71 + if (!isSubtype(l, e, warn, infer)) { 2.72 infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER); 2.73 } 2.74 } 2.75 for (Type u : uv.getBounds(InferenceBound.UPPER)) { 2.76 if (inferenceContext.free(u)) continue; 2.77 - if (!infer.types.isSubtypeUnchecked(e, u, warn)) { 2.78 + if (!isSubtype(e, u, warn, infer)) { 2.79 infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER); 2.80 } 2.81 } 2.82 @@ -589,12 +588,12 @@ 2.83 for (Type e : uv.getBounds(InferenceBound.EQ)) { 2.84 if (e.containsAny(inferenceContext.inferenceVars())) continue; 2.85 for (Type u : uv.getBounds(InferenceBound.UPPER)) { 2.86 - if (!infer.types.isSubtypeUnchecked(e, inferenceContext.asFree(u), warn)) { 2.87 + if (!isSubtype(e, inferenceContext.asFree(u), warn, infer)) { 2.88 infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER); 2.89 } 2.90 } 2.91 for (Type l : uv.getBounds(InferenceBound.LOWER)) { 2.92 - if (!infer.types.isSubtypeUnchecked(inferenceContext.asFree(l), e, warn)) { 2.93 + if (!isSubtype(inferenceContext.asFree(l), e, warn, infer)) { 2.94 infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER); 2.95 } 2.96 } 2.97 @@ -610,7 +609,7 @@ 2.98 Infer infer = inferenceContext.infer(); 2.99 for (Type b1 : uv.getBounds(InferenceBound.UPPER)) { 2.100 for (Type b2 : uv.getBounds(InferenceBound.LOWER)) { 2.101 - infer.types.isSubtypeUnchecked(inferenceContext.asFree(b2), inferenceContext.asFree(b1)); 2.102 + isSubtype(inferenceContext.asFree(b2), inferenceContext.asFree(b1), warn , infer); 2.103 } 2.104 } 2.105 } 2.106 @@ -624,7 +623,7 @@ 2.107 Infer infer = inferenceContext.infer(); 2.108 for (Type b1 : uv.getBounds(InferenceBound.UPPER)) { 2.109 for (Type b2 : uv.getBounds(InferenceBound.EQ)) { 2.110 - infer.types.isSubtypeUnchecked(inferenceContext.asFree(b2), inferenceContext.asFree(b1)); 2.111 + isSubtype(inferenceContext.asFree(b2), inferenceContext.asFree(b1), warn, infer); 2.112 } 2.113 } 2.114 } 2.115 @@ -638,7 +637,7 @@ 2.116 Infer infer = inferenceContext.infer(); 2.117 for (Type b1 : uv.getBounds(InferenceBound.EQ)) { 2.118 for (Type b2 : uv.getBounds(InferenceBound.LOWER)) { 2.119 - infer.types.isSubtypeUnchecked(inferenceContext.asFree(b2), inferenceContext.asFree(b1)); 2.120 + isSubtype(inferenceContext.asFree(b2), inferenceContext.asFree(b1), warn, infer); 2.121 } 2.122 } 2.123 } 2.124 @@ -653,7 +652,7 @@ 2.125 for (Type b1 : uv.getBounds(InferenceBound.EQ)) { 2.126 for (Type b2 : uv.getBounds(InferenceBound.EQ)) { 2.127 if (b1 != b2) { 2.128 - infer.types.isSameType(inferenceContext.asFree(b2), inferenceContext.asFree(b1)); 2.129 + isSameType(inferenceContext.asFree(b2), inferenceContext.asFree(b1), infer); 2.130 } 2.131 } 2.132 } 2.133 @@ -672,14 +671,14 @@ 2.134 if (uv2.isCaptured()) continue; 2.135 //alpha <: beta 2.136 //0. set beta :> alpha 2.137 - uv2.addBound(InferenceBound.LOWER, uv, infer.types); 2.138 + addBound(InferenceBound.LOWER, uv2, inferenceContext.asInstType(uv.qtype), infer); 2.139 //1. copy alpha's lower to beta's 2.140 for (Type l : uv.getBounds(InferenceBound.LOWER)) { 2.141 - uv2.addBound(InferenceBound.LOWER, inferenceContext.asInstType(l), infer.types); 2.142 + addBound(InferenceBound.LOWER, uv2, inferenceContext.asInstType(l), infer); 2.143 } 2.144 //2. copy beta's upper to alpha's 2.145 for (Type u : uv2.getBounds(InferenceBound.UPPER)) { 2.146 - uv.addBound(InferenceBound.UPPER, inferenceContext.asInstType(u), infer.types); 2.147 + addBound(InferenceBound.UPPER, uv, inferenceContext.asInstType(u), infer); 2.148 } 2.149 } 2.150 } 2.151 @@ -698,14 +697,14 @@ 2.152 if (uv2.isCaptured()) continue; 2.153 //alpha :> beta 2.154 //0. set beta <: alpha 2.155 - uv2.addBound(InferenceBound.UPPER, uv, infer.types); 2.156 + addBound(InferenceBound.UPPER, uv2, inferenceContext.asInstType(uv.qtype), infer); 2.157 //1. copy alpha's upper to beta's 2.158 for (Type u : uv.getBounds(InferenceBound.UPPER)) { 2.159 - uv2.addBound(InferenceBound.UPPER, inferenceContext.asInstType(u), infer.types); 2.160 + addBound(InferenceBound.UPPER, uv2, inferenceContext.asInstType(u), infer); 2.161 } 2.162 //2. copy beta's lower to alpha's 2.163 for (Type l : uv2.getBounds(InferenceBound.LOWER)) { 2.164 - uv.addBound(InferenceBound.LOWER, inferenceContext.asInstType(l), infer.types); 2.165 + addBound(InferenceBound.LOWER, uv, inferenceContext.asInstType(l), infer); 2.166 } 2.167 } 2.168 } 2.169 @@ -724,12 +723,12 @@ 2.170 if (uv2.isCaptured()) continue; 2.171 //alpha == beta 2.172 //0. set beta == alpha 2.173 - uv2.addBound(InferenceBound.EQ, uv, infer.types); 2.174 + addBound(InferenceBound.EQ, uv2, inferenceContext.asInstType(uv.qtype), infer); 2.175 //1. copy all alpha's bounds to beta's 2.176 for (InferenceBound ib : InferenceBound.values()) { 2.177 for (Type b2 : uv.getBounds(ib)) { 2.178 if (b2 != uv2) { 2.179 - uv2.addBound(ib, inferenceContext.asInstType(b2), infer.types); 2.180 + addBound(ib, uv2, inferenceContext.asInstType(b2), infer); 2.181 } 2.182 } 2.183 } 2.184 @@ -737,7 +736,7 @@ 2.185 for (InferenceBound ib : InferenceBound.values()) { 2.186 for (Type b2 : uv2.getBounds(ib)) { 2.187 if (b2 != uv) { 2.188 - uv.addBound(ib, inferenceContext.asInstType(b2), infer.types); 2.189 + addBound(ib, uv, inferenceContext.asInstType(b2), infer); 2.190 } 2.191 } 2.192 } 2.193 @@ -751,6 +750,41 @@ 2.194 boolean accepts(UndetVar uv, InferenceContext inferenceContext) { 2.195 return !uv.isCaptured(); 2.196 } 2.197 + 2.198 + boolean isSubtype(Type s, Type t, Warner warn, Infer infer) { 2.199 + return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn, infer); 2.200 + } 2.201 + 2.202 + boolean isSameType(Type s, Type t, Infer infer) { 2.203 + return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null, infer); 2.204 + } 2.205 + 2.206 + void addBound(InferenceBound ib, UndetVar uv, Type b, Infer infer) { 2.207 + doIncorporationOp(opFor(ib), uv, b, null, infer); 2.208 + } 2.209 + 2.210 + IncorporationBinaryOpKind opFor(InferenceBound boundKind) { 2.211 + switch (boundKind) { 2.212 + case EQ: 2.213 + return IncorporationBinaryOpKind.ADD_EQ_BOUND; 2.214 + case LOWER: 2.215 + return IncorporationBinaryOpKind.ADD_LOWER_BOUND; 2.216 + case UPPER: 2.217 + return IncorporationBinaryOpKind.ADD_UPPER_BOUND; 2.218 + default: 2.219 + Assert.error("Can't get here!"); 2.220 + return null; 2.221 + } 2.222 + } 2.223 + 2.224 + boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn, Infer infer) { 2.225 + IncorporationBinaryOp newOp = infer.new IncorporationBinaryOp(opKind, op1, op2); 2.226 + Boolean res = infer.incorporationCache.get(newOp); 2.227 + if (res == null) { 2.228 + infer.incorporationCache.put(newOp, res = newOp.apply(warn)); 2.229 + } 2.230 + return res; 2.231 + } 2.232 } 2.233 2.234 /** incorporation steps to be executed when running in legacy mode */ 2.235 @@ -761,6 +795,102 @@ 2.236 EnumSet.complementOf(EnumSet.of(IncorporationStep.EQ_CHECK_LEGACY)); 2.237 2.238 /** 2.239 + * Three kinds of basic operation are supported as part of an incorporation step: 2.240 + * (i) subtype check, (ii) same type check and (iii) bound addition (either 2.241 + * upper/lower/eq bound). 2.242 + */ 2.243 + enum IncorporationBinaryOpKind { 2.244 + IS_SUBTYPE() { 2.245 + @Override 2.246 + boolean apply(Type op1, Type op2, Warner warn, Types types) { 2.247 + return types.isSubtypeUnchecked(op1, op2, warn); 2.248 + } 2.249 + }, 2.250 + IS_SAME_TYPE() { 2.251 + @Override 2.252 + boolean apply(Type op1, Type op2, Warner warn, Types types) { 2.253 + return types.isSameType(op1, op2); 2.254 + } 2.255 + }, 2.256 + ADD_UPPER_BOUND() { 2.257 + @Override 2.258 + boolean apply(Type op1, Type op2, Warner warn, Types types) { 2.259 + UndetVar uv = (UndetVar)op1; 2.260 + uv.addBound(InferenceBound.UPPER, op2, types); 2.261 + return true; 2.262 + } 2.263 + }, 2.264 + ADD_LOWER_BOUND() { 2.265 + @Override 2.266 + boolean apply(Type op1, Type op2, Warner warn, Types types) { 2.267 + UndetVar uv = (UndetVar)op1; 2.268 + uv.addBound(InferenceBound.LOWER, op2, types); 2.269 + return true; 2.270 + } 2.271 + }, 2.272 + ADD_EQ_BOUND() { 2.273 + @Override 2.274 + boolean apply(Type op1, Type op2, Warner warn, Types types) { 2.275 + UndetVar uv = (UndetVar)op1; 2.276 + uv.addBound(InferenceBound.EQ, op2, types); 2.277 + return true; 2.278 + } 2.279 + }; 2.280 + 2.281 + abstract boolean apply(Type op1, Type op2, Warner warn, Types types); 2.282 + } 2.283 + 2.284 + /** 2.285 + * This class encapsulates a basic incorporation operation; incorporation 2.286 + * operations takes two type operands and a kind. Each operation performed 2.287 + * during an incorporation round is stored in a cache, so that operations 2.288 + * are not executed unnecessarily (which would potentially lead to adding 2.289 + * same bounds over and over). 2.290 + */ 2.291 + class IncorporationBinaryOp { 2.292 + 2.293 + IncorporationBinaryOpKind opKind; 2.294 + Type op1; 2.295 + Type op2; 2.296 + 2.297 + IncorporationBinaryOp(IncorporationBinaryOpKind opKind, Type op1, Type op2) { 2.298 + this.opKind = opKind; 2.299 + this.op1 = op1; 2.300 + this.op2 = op2; 2.301 + } 2.302 + 2.303 + @Override 2.304 + public boolean equals(Object o) { 2.305 + if (!(o instanceof IncorporationBinaryOp)) { 2.306 + return false; 2.307 + } else { 2.308 + IncorporationBinaryOp that = (IncorporationBinaryOp)o; 2.309 + return opKind == that.opKind && 2.310 + types.isSameType(op1, that.op1, true) && 2.311 + types.isSameType(op2, that.op2, true); 2.312 + } 2.313 + } 2.314 + 2.315 + @Override 2.316 + public int hashCode() { 2.317 + int result = opKind.hashCode(); 2.318 + result *= 127; 2.319 + result += types.hashCode(op1); 2.320 + result *= 127; 2.321 + result += types.hashCode(op2); 2.322 + return result; 2.323 + } 2.324 + 2.325 + boolean apply(Warner warn) { 2.326 + return opKind.apply(op1, op2, warn, types); 2.327 + } 2.328 + } 2.329 + 2.330 + /** an incorporation cache keeps track of all executed incorporation-related operations */ 2.331 + Map<IncorporationBinaryOp, Boolean> incorporationCache = 2.332 + new HashMap<IncorporationBinaryOp, Boolean>(); 2.333 + 2.334 + /** 2.335 * Make sure that the upper bounds we got so far lead to a solvable inference 2.336 * variable by making sure that a glb exists. 2.337 */ 2.338 @@ -895,6 +1025,41 @@ 2.339 Assert.check(!g.nodes.isEmpty(), "No nodes to solve!"); 2.340 return g.nodes.get(0); 2.341 } 2.342 + 2.343 + boolean isSubtype(Type s, Type t, Warner warn, Infer infer) { 2.344 + return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn, infer); 2.345 + } 2.346 + 2.347 + boolean isSameType(Type s, Type t, Infer infer) { 2.348 + return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null, infer); 2.349 + } 2.350 + 2.351 + void addBound(InferenceBound ib, UndetVar uv, Type b, Infer infer) { 2.352 + doIncorporationOp(opFor(ib), uv, b, null, infer); 2.353 + } 2.354 + 2.355 + IncorporationBinaryOpKind opFor(InferenceBound boundKind) { 2.356 + switch (boundKind) { 2.357 + case EQ: 2.358 + return IncorporationBinaryOpKind.ADD_EQ_BOUND; 2.359 + case LOWER: 2.360 + return IncorporationBinaryOpKind.ADD_LOWER_BOUND; 2.361 + case UPPER: 2.362 + return IncorporationBinaryOpKind.ADD_UPPER_BOUND; 2.363 + default: 2.364 + Assert.error("Can't get here!"); 2.365 + return null; 2.366 + } 2.367 + } 2.368 + 2.369 + boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn, Infer infer) { 2.370 + IncorporationBinaryOp newOp = infer.new IncorporationBinaryOp(opKind, op1, op2); 2.371 + Boolean res = infer.incorporationCache.get(newOp); 2.372 + if (res == null) { 2.373 + infer.incorporationCache.put(newOp, res = newOp.apply(warn)); 2.374 + } 2.375 + return res; 2.376 + } 2.377 } 2.378 2.379 /**
3.1 --- a/test/tools/javac/generics/inference/8019824/T8019824.out Wed Jul 17 14:19:25 2013 +0100 3.2 +++ b/test/tools/javac/generics/inference/8019824/T8019824.out Wed Jul 17 14:21:12 2013 +0100 3.3 @@ -1,2 +1,2 @@ 3.4 -T8019824.java:9:25: compiler.err.cant.apply.symbol: kindname.method, make, java.lang.Class<C>, java.lang.Class<compiler.misc.type.captureof: 1, ? extends T8019824.Foo<?,?>>, kindname.class, T8019824, (compiler.misc.incompatible.eq.upper.bounds: C, compiler.misc.type.captureof: 1, ? extends T8019824.Foo<?,?>, T8019824.Foo<java.lang.Object,B>) 3.5 +T8019824.java:9:25: compiler.err.cant.apply.symbol: kindname.method, make, java.lang.Class<C>, java.lang.Class<compiler.misc.type.captureof: 1, ? extends T8019824.Foo<?,?>>, kindname.class, T8019824, (compiler.misc.incompatible.eq.upper.bounds: C, compiler.misc.type.captureof: 1, ? extends T8019824.Foo<?,?>, T8019824.Foo<compiler.misc.type.captureof: 2, ?,B>) 3.6 1 error