diff -r 1985e35e97b2 -r 7873d37f5b37 src/share/classes/com/sun/tools/javac/comp/Resolve.java --- a/src/share/classes/com/sun/tools/javac/comp/Resolve.java Mon Jan 21 11:16:28 2013 -0800 +++ b/src/share/classes/com/sun/tools/javac/comp/Resolve.java Mon Jan 21 20:13:56 2013 +0000 @@ -41,6 +41,7 @@ import com.sun.tools.javac.tree.*; import com.sun.tools.javac.tree.JCTree.*; import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind; +import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -92,6 +93,7 @@ public final boolean varargsEnabled; // = source.allowVarargs(); public final boolean allowMethodHandles; public final boolean allowDefaultMethods; + public final boolean allowStructuralMostSpecific; private final boolean debugResolve; final EnumSet verboseResolutionMode; @@ -127,6 +129,7 @@ Target target = Target.instance(context); allowMethodHandles = target.hasMethodHandles(); allowDefaultMethods = source.allowDefaultMethods(); + allowStructuralMostSpecific = source.allowStructuralMostSpecific(); polymorphicSignatureScope = new Scope(syms.noSymbol); inapplicableMethodException = new InapplicableMethodException(diags); @@ -835,6 +838,213 @@ } } + /** + * Most specific method applicability routine. Given a list of actual types A, + * a list of formal types F1, and a list of formal types F2, the routine determines + * as to whether the types in F1 can be considered more specific than those in F2 w.r.t. + * argument types A. + */ + class MostSpecificCheck implements MethodCheck { + + boolean strict; + List actuals; + + MostSpecificCheck(boolean strict, List actuals) { + this.strict = strict; + this.actuals = actuals; + } + + @Override + public void argumentsAcceptable(final Env env, + DeferredAttrContext deferredAttrContext, + List formals1, + List formals2, + Warner warn) { + formals2 = adjustArgs(formals2, deferredAttrContext.msym, formals1.length(), deferredAttrContext.phase.isVarargsRequired()); + while (formals2.nonEmpty()) { + ResultInfo mresult = methodCheckResult(formals2.head, deferredAttrContext, warn, actuals.head); + mresult.check(null, formals1.head); + formals1 = formals1.tail; + formals2 = formals2.tail; + actuals = actuals.isEmpty() ? actuals : actuals.tail; + } + } + + /** + * Create a method check context to be used during the most specific applicability check + */ + ResultInfo methodCheckResult(Type to, DeferredAttr.DeferredAttrContext deferredAttrContext, + Warner rsWarner, Type actual) { + return attr.new ResultInfo(Kinds.VAL, to, + new MostSpecificCheckContext(strict, deferredAttrContext, rsWarner, actual)); + } + + /** + * Subclass of method check context class that implements most specific + * method conversion. If the actual type under analysis is a deferred type + * a full blown structural analysis is carried out. + */ + class MostSpecificCheckContext extends MethodCheckContext { + + Type actual; + + public MostSpecificCheckContext(boolean strict, DeferredAttrContext deferredAttrContext, Warner rsWarner, Type actual) { + super(strict, deferredAttrContext, rsWarner); + this.actual = actual; + } + + public boolean compatible(Type found, Type req, Warner warn) { + if (!allowStructuralMostSpecific || actual == null) { + return super.compatible(found, req, warn); + } else { + switch (actual.getTag()) { + case DEFERRED: + DeferredType dt = (DeferredType) actual; + DeferredType.SpeculativeCache.Entry e = dt.speculativeCache.get(deferredAttrContext.msym, deferredAttrContext.phase); + return (e == null || e.speculativeTree == deferredAttr.stuckTree) + ? false : mostSpecific(found, req, e.speculativeTree, warn); + default: + return standaloneMostSpecific(found, req, actual, warn); + } + } + } + + private boolean mostSpecific(Type t, Type s, JCTree tree, Warner warn) { + MostSpecificChecker msc = new MostSpecificChecker(t, s, warn); + msc.scan(tree); + return msc.result; + } + + boolean polyMostSpecific(Type t1, Type t2, Warner warn) { + return (!t1.isPrimitive() && t2.isPrimitive()) + ? true : super.compatible(t1, t2, warn); + } + + boolean standaloneMostSpecific(Type t1, Type t2, Type exprType, Warner warn) { + return (exprType.isPrimitive() == t1.isPrimitive() + && exprType.isPrimitive() != t2.isPrimitive()) + ? true : super.compatible(t1, t2, warn); + } + + /** + * Structural checker for most specific. + */ + class MostSpecificChecker extends DeferredAttr.PolyScanner { + + final Type t; + final Type s; + final Warner warn; + boolean result; + + MostSpecificChecker(Type t, Type s, Warner warn) { + this.t = t; + this.s = s; + this.warn = warn; + result = true; + } + + @Override + void skip(JCTree tree) { + result &= standaloneMostSpecific(t, s, tree.type, warn); + } + + @Override + public void visitConditional(JCConditional tree) { + if (tree.polyKind == PolyKind.STANDALONE) { + result &= standaloneMostSpecific(t, s, tree.type, warn); + } else { + super.visitConditional(tree); + } + } + + @Override + public void visitApply(JCMethodInvocation tree) { + result &= (tree.polyKind == PolyKind.STANDALONE) + ? standaloneMostSpecific(t, s, tree.type, warn) + : polyMostSpecific(t, s, warn); + } + + @Override + public void visitNewClass(JCNewClass tree) { + result &= (tree.polyKind == PolyKind.STANDALONE) + ? standaloneMostSpecific(t, s, tree.type, warn) + : polyMostSpecific(t, s, warn); + } + + @Override + public void visitReference(JCMemberReference tree) { + if (types.isFunctionalInterface(t.tsym) && + types.isFunctionalInterface(s.tsym) && + types.asSuper(t, s.tsym) == null && + types.asSuper(s, t.tsym) == null) { + Type desc_t = types.findDescriptorType(t); + Type desc_s = types.findDescriptorType(s); + if (types.isSameTypes(desc_t.getParameterTypes(), desc_s.getParameterTypes())) { + if (!desc_s.getReturnType().hasTag(VOID)) { + //perform structural comparison + Type ret_t = desc_t.getReturnType(); + Type ret_s = desc_s.getReturnType(); + result &= ((tree.refPolyKind == PolyKind.STANDALONE) + ? standaloneMostSpecific(ret_t, ret_s, tree.type, warn) + : polyMostSpecific(ret_t, ret_s, warn)); + } else { + return; + } + } else { + result &= false; + } + } else { + result &= MostSpecificCheckContext.super.compatible(t, s, warn); + } + } + + @Override + public void visitLambda(JCLambda tree) { + if (types.isFunctionalInterface(t.tsym) && + types.isFunctionalInterface(s.tsym) && + types.asSuper(t, s.tsym) == null && + types.asSuper(s, t.tsym) == null) { + Type desc_t = types.findDescriptorType(t); + Type desc_s = types.findDescriptorType(s); + if (tree.paramKind == JCLambda.ParameterKind.EXPLICIT + || types.isSameTypes(desc_t.getParameterTypes(), desc_s.getParameterTypes())) { + if (!desc_s.getReturnType().hasTag(VOID)) { + //perform structural comparison + Type ret_t = desc_t.getReturnType(); + Type ret_s = desc_s.getReturnType(); + scanLambdaBody(tree, ret_t, ret_s); + } else { + return; + } + } else { + result &= false; + } + } else { + result &= MostSpecificCheckContext.super.compatible(t, s, warn); + } + } + //where + + void scanLambdaBody(JCLambda lambda, final Type t, final Type s) { + if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { + result &= MostSpecificCheckContext.this.mostSpecific(t, s, lambda.body, warn); + } else { + DeferredAttr.LambdaReturnScanner lambdaScanner = + new DeferredAttr.LambdaReturnScanner() { + @Override + public void visitReturn(JCReturn tree) { + if (tree.expr != null) { + result &= MostSpecificCheckContext.this.mostSpecific(t, s, tree.expr, warn); + } + } + }; + lambdaScanner.scan(lambda.body); + } + } + } + } + } + public static class InapplicableMethodException extends RuntimeException { private static final long serialVersionUID = 0; @@ -1142,151 +1352,30 @@ } //where private boolean signatureMoreSpecific(List actuals, Env env, Type site, Symbol m1, Symbol m2, boolean allowBoxing, boolean useVarargs) { - Symbol m12 = adjustVarargs(m1, m2, useVarargs); - Symbol m22 = adjustVarargs(m2, m1, useVarargs); - Type mtype1 = types.memberType(site, m12); - Type mtype2 = types.memberType(site, m22); - - //check if invocation is more specific - if (invocationMoreSpecific(env, site, m22, mtype1.getParameterTypes(), allowBoxing, useVarargs)) { - return true; + noteWarner.clear(); + int maxLength = Math.max( + Math.max(m1.type.getParameterTypes().length(), actuals.length()), + m2.type.getParameterTypes().length()); + Type mst = instantiate(env, site, m2, null, + adjustArgs(types.lowerBounds(types.memberType(site, m1).getParameterTypes()), m1, maxLength, useVarargs), null, + allowBoxing, useVarargs, new MostSpecificCheck(!allowBoxing, actuals), noteWarner); + return mst != null && + !noteWarner.hasLint(Lint.LintCategory.UNCHECKED); + } + private List adjustArgs(List args, Symbol msym, int length, boolean allowVarargs) { + if ((msym.flags() & VARARGS) != 0 && allowVarargs) { + Type varargsElem = types.elemtype(args.last()); + if (varargsElem == null) { + Assert.error("Bad varargs = " + args.last() + " " + msym); + } + List newArgs = args.reverse().tail.prepend(varargsElem).reverse(); + while (newArgs.length() < length) { + newArgs = newArgs.append(newArgs.last()); + } + return newArgs; + } else { + return args; } - - //perform structural check - - List formals1 = mtype1.getParameterTypes(); - Type lastFormal1 = formals1.last(); - List formals2 = mtype2.getParameterTypes(); - Type lastFormal2 = formals2.last(); - ListBuffer newFormals = ListBuffer.lb(); - - boolean hasStructuralPoly = false; - for (Type actual : actuals) { - //perform formal argument adaptation in case actuals > formals (varargs) - Type f1 = formals1.isEmpty() ? - lastFormal1 : formals1.head; - Type f2 = formals2.isEmpty() ? - lastFormal2 : formals2.head; - - //is this a structural actual argument? - boolean isStructuralPoly = actual.hasTag(DEFERRED) && - (((DeferredType)actual).tree.hasTag(LAMBDA) || - ((DeferredType)actual).tree.hasTag(REFERENCE)); - - Type newFormal = f1; - - if (isStructuralPoly) { - //for structural arguments only - check that corresponding formals - //are related - if so replace formal with - hasStructuralPoly = true; - DeferredType dt = (DeferredType)actual; - Type t1 = deferredAttr.new DeferredTypeMap(AttrMode.SPECULATIVE, m1, currentResolutionContext.step).apply(dt); - Type t2 = deferredAttr.new DeferredTypeMap(AttrMode.SPECULATIVE, m2, currentResolutionContext.step).apply(dt); - if (t1.isErroneous() || t2.isErroneous() || !isStructuralSubtype(t1, t2)) { - //not structural subtypes - simply fail - return false; - } else { - newFormal = syms.botType; - } - } - - newFormals.append(newFormal); - if (newFormals.length() > mtype2.getParameterTypes().length()) { - //expand m2's type so as to fit the new formal arity (varargs) - m22.type = types.createMethodTypeWithParameters(m22.type, m22.type.getParameterTypes().append(f2)); - } - - formals1 = formals1.isEmpty() ? formals1 : formals1.tail; - formals2 = formals2.isEmpty() ? formals2 : formals2.tail; - } - - if (!hasStructuralPoly) { - //if no structural actual was found, we're done - return false; - } - //perform additional adaptation if actuals < formals (varargs) - for (Type t : formals1) { - newFormals.append(t); - } - //check if invocation (with tweaked args) is more specific - return invocationMoreSpecific(env, site, m22, newFormals.toList(), allowBoxing, useVarargs); - } - //where - private boolean invocationMoreSpecific(Env env, Type site, Symbol m2, List argtypes1, boolean allowBoxing, boolean useVarargs) { - MethodResolutionContext prevContext = currentResolutionContext; - try { - currentResolutionContext = new MethodResolutionContext(); - currentResolutionContext.step = allowBoxing ? BOX : BASIC; - noteWarner.clear(); - Type mst = instantiate(env, site, m2, null, - types.lowerBounds(argtypes1), null, - allowBoxing, false, resolveMethodCheck, noteWarner); - return mst != null && - !noteWarner.hasLint(Lint.LintCategory.UNCHECKED); - } finally { - currentResolutionContext = prevContext; - } - } - //where - private Symbol adjustVarargs(Symbol to, Symbol from, boolean useVarargs) { - List fromArgs = from.type.getParameterTypes(); - List toArgs = to.type.getParameterTypes(); - if (useVarargs && - (from.flags() & VARARGS) != 0 && - (to.flags() & VARARGS) != 0) { - Type varargsTypeFrom = fromArgs.last(); - Type varargsTypeTo = toArgs.last(); - ListBuffer args = ListBuffer.lb(); - if (toArgs.length() < fromArgs.length()) { - //if we are checking a varargs method 'from' against another varargs - //method 'to' (where arity of 'to' < arity of 'from') then expand signature - //of 'to' to 'fit' arity of 'from' (this means adding fake formals to 'to' - //until 'to' signature has the same arity as 'from') - while (fromArgs.head != varargsTypeFrom) { - args.append(toArgs.head == varargsTypeTo ? types.elemtype(varargsTypeTo) : toArgs.head); - fromArgs = fromArgs.tail; - toArgs = toArgs.head == varargsTypeTo ? - toArgs : - toArgs.tail; - } - } else { - //formal argument list is same as original list where last - //argument (array type) is removed - args.appendList(toArgs.reverse().tail.reverse()); - } - //append varargs element type as last synthetic formal - args.append(types.elemtype(varargsTypeTo)); - Type mtype = types.createMethodTypeWithParameters(to.type, args.toList()); - return new MethodSymbol(to.flags_field & ~VARARGS, to.name, mtype, to.owner); - } else { - return to; - } - } - //where - boolean isStructuralSubtype(Type s, Type t) { - - Type ret_s = types.findDescriptorType(s).getReturnType(); - Type ret_t = types.findDescriptorType(t).getReturnType(); - - //covariant most specific check for function descriptor return type - if (!types.isSubtype(ret_s, ret_t)) { - return false; - } - - List args_s = types.findDescriptorType(s).getParameterTypes(); - List args_t = types.findDescriptorType(t).getParameterTypes(); - - //arity must be identical - if (args_s.length() != args_t.length()) { - return false; - } - - //invariant most specific check for function descriptor parameter types - if (!types.isSameTypes(args_t, args_s)) { - return false; - } - - return true; } //where Type mostSpecificReturnType(Type mt1, Type mt2) {