src/share/classes/com/sun/tools/javac/comp/Infer.java

changeset 2382
14979dd5e034
parent 2369
77352397867a
child 2384
327122b01a9e
     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());

mercurial