8019942: Graph inference: avoid redundant computation during bound incorporation

Wed, 17 Jul 2013 14:21:12 +0100

author
mcimadamore
date
Wed, 17 Jul 2013 14:21:12 +0100
changeset 1905
f65a807714ba
parent 1904
b577222ef7b3
child 1906
10711bd8bb2d

8019942: Graph inference: avoid redundant computation during bound incorporation
Summary: Bound incorporation should not perform same operation multiple times
Reviewed-by: jjg

src/share/classes/com/sun/tools/javac/code/Type.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/Infer.java file | annotate | diff | comparison | revisions
test/tools/javac/generics/inference/8019824/T8019824.out file | annotate | diff | comparison | revisions
     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

mercurial