diff -r 9ac68cf0048b -r b7439971a094 langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java --- a/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java Fri Oct 05 14:21:09 2012 -0700 +++ b/langtools/src/share/classes/com/sun/tools/javac/comp/Resolve.java Sat Oct 06 10:35:38 2012 +0100 @@ -40,6 +40,7 @@ import com.sun.tools.javac.jvm.*; 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.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; @@ -98,10 +99,6 @@ varNotFound = new SymbolNotFoundError(ABSENT_VAR); - wrongMethod = new - InapplicableSymbolError(); - wrongMethods = new - InapplicableSymbolsError(); methodNotFound = new SymbolNotFoundError(ABSENT_MTH); typeNotFound = new @@ -133,8 +130,6 @@ /** error symbols, which are returned when resolution fails */ private final SymbolNotFoundError varNotFound; - private final InapplicableSymbolError wrongMethod; - private final InapplicableSymbolsError wrongMethods; private final SymbolNotFoundError methodNotFound; private final SymbolNotFoundError typeNotFound; @@ -454,8 +449,18 @@ boolean useVarargs, Warner warn) throws Infer.InferenceException { - if (useVarargs && (m.flags() & VARARGS) == 0) - throw inapplicableMethodException.setMessage(); + if (useVarargs && (m.flags() & VARARGS) == 0) { + //better error recovery - if we stumbled upon a non-varargs method + //during varargs applicability phase, the method should be treated as + //not applicable; the reason for inapplicability can be found in the + //candidate for 'm' that was created during the BOX phase. + Candidate prevCandidate = currentResolutionContext.getCandidate(m, BOX); + JCDiagnostic details = null; + if (prevCandidate != null && !prevCandidate.isApplicable()) { + details = prevCandidate.details; + } + throw inapplicableMethodException.setMessage(details); + } Type mt = types.memberType(site, m); // tvars is the list of formal type variables for which type arguments @@ -1028,11 +1033,10 @@ currentResolutionContext.addInapplicableCandidate(sym, ex.getDiagnostic()); switch (bestSoFar.kind) { case ABSENT_MTH: - return wrongMethod; + return new InapplicableSymbolError(currentResolutionContext); case WRONG_MTH: if (operator) return bestSoFar; - case WRONG_MTHS: - return wrongMethods; + bestSoFar = new InapplicableSymbolsError(currentResolutionContext); default: return bestSoFar; } @@ -1181,7 +1185,8 @@ //is this a structural actual argument? boolean isStructuralPoly = actual.tag == DEFERRED && - ((DeferredType)actual).tree.hasTag(LAMBDA); + (((DeferredType)actual).tree.hasTag(LAMBDA) || + ((DeferredType)actual).tree.hasTag(REFERENCE)); Type newFormal = f1; @@ -2210,7 +2215,7 @@ final JCDiagnostic details = errSym.kind == WRONG_MTH ? ((InapplicableSymbolError)errSym).errCandidate().details : null; - errSym = new InapplicableSymbolError(errSym.kind, "diamondError") { + errSym = new InapplicableSymbolError(errSym.kind, "diamondError", currentResolutionContext) { @Override JCDiagnostic getDiagnostic(DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { @@ -2276,6 +2281,335 @@ return bestSoFar; } + /** + * Resolution of member references is typically done as a single + * overload resolution step, where the argument types A are inferred from + * the target functional descriptor. + * + * If the member reference is a method reference with a type qualifier, + * a two-step lookup process is performed. The first step uses the + * expected argument list A, while the second step discards the first + * type from A (which is treated as a receiver type). + * + * There are two cases in which inference is performed: (i) if the member + * reference is a constructor reference and the qualifier type is raw - in + * which case diamond inference is used to infer a parameterization for the + * type qualifier; (ii) if the member reference is an unbound reference + * where the type qualifier is raw - in that case, during the unbound lookup + * the receiver argument type is used to infer an instantiation for the raw + * qualifier type. + * + * When a multi-step resolution process is exploited, it is an error + * if two candidates are found (ambiguity). + * + * This routine returns a pair (T,S), where S is the member reference symbol, + * and T is the type of the class in which S is defined. This is necessary as + * the type T might be dynamically inferred (i.e. if constructor reference + * has a raw qualifier). + */ + Pair resolveMemberReference(DiagnosticPosition pos, + Env env, + JCMemberReference referenceTree, + Type site, + Name name, List argtypes, + List typeargtypes, + boolean boxingAllowed) { + //step 1 - bound lookup + ReferenceLookupHelper boundLookupHelper = name.equals(names.init) ? + new ConstructorReferenceLookupHelper(referenceTree, site, argtypes, typeargtypes, boxingAllowed) : + new MethodReferenceLookupHelper(referenceTree, name, site, argtypes, typeargtypes, boxingAllowed); + Env boundEnv = env.dup(env.tree, env.info.dup()); + Symbol boundSym = findMemberReference(boundEnv, boundLookupHelper); + + //step 2 - unbound lookup + ReferenceLookupHelper unboundLookupHelper = boundLookupHelper.unboundLookup(); + Env unboundEnv = env.dup(env.tree, env.info.dup()); + Symbol unboundSym = findMemberReference(unboundEnv, unboundLookupHelper); + + //merge results + Pair res; + if (unboundSym.kind != MTH) { + res = new Pair(boundSym, boundLookupHelper); + env.info.pendingResolutionPhase = boundEnv.info.pendingResolutionPhase; + } else if (boundSym.kind == MTH) { + res = new Pair(ambiguityError(boundSym, unboundSym), boundLookupHelper); + env.info.pendingResolutionPhase = boundEnv.info.pendingResolutionPhase; + } else { + res = new Pair(unboundSym, unboundLookupHelper); + env.info.pendingResolutionPhase = unboundEnv.info.pendingResolutionPhase; + } + + return res; + } + + /** + * Helper for defining custom method-like lookup logic; a lookup helper + * provides hooks for (i) the actual lookup logic and (ii) accessing the + * lookup result (this step might result in compiler diagnostics to be generated) + */ + abstract class LookupHelper { + + /** name of the symbol to lookup */ + Name name; + + /** location in which the lookup takes place */ + Type site; + + /** actual types used during the lookup */ + List argtypes; + + /** type arguments used during the lookup */ + List typeargtypes; + + LookupHelper(Name name, Type site, List argtypes, List typeargtypes) { + this.name = name; + this.site = site; + this.argtypes = argtypes; + this.typeargtypes = typeargtypes; + } + + /** + * Search for a symbol under a given overload resolution phase - this method + * is usually called several times, once per each overload resolution phase + */ + abstract Symbol lookup(Env env, MethodResolutionPhase phase); + + /** + * Validate the result of the lookup + */ + abstract Symbol access(Env env, Symbol symbol); + } + + /** + * Helper class for member reference lookup. A reference lookup helper + * defines the basic logic for member reference lookup; a method gives + * access to an 'unbound' helper used to perform an unbound member + * reference lookup. + */ + abstract class ReferenceLookupHelper extends LookupHelper { + + /** The member reference tree */ + JCMemberReference referenceTree; + + /** Max overload resolution phase handled by this helper */ + MethodResolutionPhase maxPhase; + + ReferenceLookupHelper(JCMemberReference referenceTree, Name name, Type site, + List argtypes, List typeargtypes, boolean boxingAllowed) { + super(name, site, argtypes, typeargtypes); + this.referenceTree = referenceTree; + this.maxPhase = boxingAllowed ? VARARITY : BASIC; + } + + /** + * Returns an unbound version of this lookup helper. By default, this + * method returns an dummy lookup helper. + */ + ReferenceLookupHelper unboundLookup() { + //dummy loopkup helper that always return 'methodNotFound' + return new ReferenceLookupHelper(referenceTree, name, site, argtypes, typeargtypes, maxPhase.isBoxingRequired()) { + @Override + ReferenceLookupHelper unboundLookup() { + return this; + } + @Override + Symbol lookupReference(Env env, MethodResolutionPhase phase) { + return methodNotFound; + } + @Override + ReferenceKind referenceKind(Symbol sym) { + Assert.error(); + return null; + } + }; + } + + /** + * Get the kind of the member reference + */ + abstract JCMemberReference.ReferenceKind referenceKind(Symbol sym); + + @Override + Symbol lookup(Env env, MethodResolutionPhase phase) { + return (env.info.pendingResolutionPhase.ordinal() > maxPhase.ordinal()) ? + methodNotFound : lookupReference(env, phase); + } + + abstract Symbol lookupReference(Env env, MethodResolutionPhase phase); + + Symbol access(Env env, Symbol sym) { + if (sym.kind >= AMBIGUOUS) { + MethodResolutionPhase errPhase = currentResolutionContext.firstErroneousResolutionPhase(); + if (errPhase.ordinal() > maxPhase.ordinal()) { + errPhase = maxPhase; + } + env.info.pendingResolutionPhase = errPhase; + sym = currentResolutionContext.resolutionCache.get(errPhase); + } + return sym; + } + } + + /** + * Helper class for method reference lookup. The lookup logic is based + * upon Resolve.findMethod; in certain cases, this helper class has a + * corresponding unbound helper class (see UnboundMethodReferenceLookupHelper). + * In such cases, non-static lookup results are thrown away. + */ + class MethodReferenceLookupHelper extends ReferenceLookupHelper { + + MethodReferenceLookupHelper(JCMemberReference referenceTree, Name name, Type site, + List argtypes, List typeargtypes, boolean boxingAllowed) { + super(referenceTree, name, site, argtypes, typeargtypes, boxingAllowed); + } + + protected Symbol lookupReferenceInternal(Env env, MethodResolutionPhase phase) { + return findMethod(env, site, name, argtypes, typeargtypes, + phase.isBoxingRequired(), phase.isVarargsRequired(), syms.operatorNames.contains(name)); + } + + protected Symbol adjustLookupResult(Env env, Symbol sym) { + return !TreeInfo.isStaticSelector(referenceTree.expr, names) || + sym.kind != MTH || + sym.isStatic() ? sym : new StaticError(sym); + } + + @Override + final Symbol lookupReference(Env env, MethodResolutionPhase phase) { + return adjustLookupResult(env, lookupReferenceInternal(env, phase)); + } + + @Override + ReferenceLookupHelper unboundLookup() { + if (TreeInfo.isStaticSelector(referenceTree.expr, names) && + argtypes.nonEmpty() && + types.isSubtypeUnchecked(argtypes.head, site)) { + return new UnboundMethodReferenceLookupHelper(referenceTree, name, + site, argtypes, typeargtypes, maxPhase.isBoxingRequired()); + } else { + return super.unboundLookup(); + } + } + + @Override + ReferenceKind referenceKind(Symbol sym) { + if (sym.isStatic()) { + return TreeInfo.isStaticSelector(referenceTree.expr, names) ? + ReferenceKind.STATIC : ReferenceKind.STATIC_EVAL; + } else { + Name selName = TreeInfo.name(referenceTree.getQualifierExpression()); + return selName != null && selName == names._super ? + ReferenceKind.SUPER : + ReferenceKind.BOUND; + } + } + } + + /** + * Helper class for unbound method reference lookup. Essentially the same + * as the basic method reference lookup helper; main difference is that static + * lookup results are thrown away. If qualifier type is raw, an attempt to + * infer a parameterized type is made using the first actual argument (that + * would otherwise be ignored during the lookup). + */ + class UnboundMethodReferenceLookupHelper extends MethodReferenceLookupHelper { + + UnboundMethodReferenceLookupHelper(JCMemberReference referenceTree, Name name, Type site, + List argtypes, List typeargtypes, boolean boxingAllowed) { + super(referenceTree, name, + site.isRaw() ? types.asSuper(argtypes.head, site.tsym) : site, + argtypes.tail, typeargtypes, boxingAllowed); + } + + @Override + protected Symbol adjustLookupResult(Env env, Symbol sym) { + return sym.kind != MTH || !sym.isStatic() ? sym : new StaticError(sym); + } + + @Override + ReferenceLookupHelper unboundLookup() { + return this; + } + + @Override + ReferenceKind referenceKind(Symbol sym) { + return ReferenceKind.UNBOUND; + } + } + + /** + * Helper class for constructor reference lookup. The lookup logic is based + * upon either Resolve.findMethod or Resolve.findDiamond - depending on + * whether the constructor reference needs diamond inference (this is the case + * if the qualifier type is raw). A special erroneous symbol is returned + * if the lookup returns the constructor of an inner class and there's no + * enclosing instance in scope. + */ + class ConstructorReferenceLookupHelper extends ReferenceLookupHelper { + + boolean needsInference; + + ConstructorReferenceLookupHelper(JCMemberReference referenceTree, Type site, List argtypes, + List typeargtypes, boolean boxingAllowed) { + super(referenceTree, names.init, site, argtypes, typeargtypes, boxingAllowed); + if (site.isRaw()) { + this.site = new ClassType(site.getEnclosingType(), site.tsym.type.getTypeArguments(), site.tsym); + needsInference = true; + } + } + + @Override + protected Symbol lookupReference(Env env, MethodResolutionPhase phase) { + Symbol sym = needsInference ? + findDiamond(env, site, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()) : + findMethod(env, site, name, argtypes, typeargtypes, + phase.isBoxingRequired(), phase.isVarargsRequired(), syms.operatorNames.contains(name)); + return sym.kind != MTH || + site.getEnclosingType().tag == NONE || + hasEnclosingInstance(env, site) ? + sym : new InvalidSymbolError(Kinds.MISSING_ENCL, sym, null) { + @Override + JCDiagnostic getDiagnostic(DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { + return diags.create(dkind, log.currentSource(), pos, + "cant.access.inner.cls.constr", site.tsym.name, argtypes, site.getEnclosingType()); + } + }; + } + + @Override + ReferenceKind referenceKind(Symbol sym) { + return site.getEnclosingType().tag == NONE ? + ReferenceKind.TOPLEVEL : ReferenceKind.IMPLICIT_INNER; + } + } + + /** + * Resolution step for member reference. This generalizes a standard + * method/constructor lookup - on each overload resolution step, a + * lookup helper class is used to perform the reference lookup; at the end + * of the lookup, the helper is used to validate the results. + */ + Symbol findMemberReference(Env env, LookupHelper lookupHelper) { + MethodResolutionContext prevResolutionContext = currentResolutionContext; + try { + currentResolutionContext = new MethodResolutionContext(); + Symbol sym = methodNotFound; + List steps = methodResolutionSteps; + while (steps.nonEmpty() && + steps.head.isApplicable(boxingEnabled, varargsEnabled) && + sym.kind >= ERRONEOUS) { + currentResolutionContext.step = env.info.pendingResolutionPhase = steps.head; + sym = lookupHelper.lookup(env, steps.head); + currentResolutionContext.resolutionCache.put(steps.head, sym); + steps = steps.tail; + } + return lookupHelper.access(env, sym); + } + finally { + currentResolutionContext = prevResolutionContext; + } + } + /** Resolve constructor. * @param pos The position to use for error reporting. * @param env The environment current at the constructor invocation. @@ -2425,6 +2759,23 @@ Env env, Symbol member, boolean isSuperCall) { + Symbol sym = resolveSelfContainingInternal(env, member, isSuperCall); + if (sym == null) { + log.error(pos, "encl.class.required", member); + return syms.errSymbol; + } else { + return accessBase(sym, pos, env.enclClass.sym.type, sym.name, true); + } + } + + boolean hasEnclosingInstance(Env env, Type type) { + Symbol encl = resolveSelfContainingInternal(env, type.tsym, false); + return encl != null && encl.kind < ERRONEOUS; + } + + private Symbol resolveSelfContainingInternal(Env env, + Symbol member, + boolean isSuperCall) { Name name = names._this; Env env1 = isSuperCall ? env.outer : env; boolean staticOnly = false; @@ -2435,8 +2786,7 @@ Symbol sym = env1.info.scope.lookup(name).sym; if (sym != null) { if (staticOnly) sym = new StaticError(sym); - return accessBase(sym, pos, env.enclClass.sym.type, - name, true); + return sym; } } if ((env1.enclClass.sym.flags() & STATIC) != 0) @@ -2444,8 +2794,7 @@ env1 = env1.outer; } } - log.error(pos, "encl.class.required", member); - return syms.errSymbol; + return null; } /** @@ -2513,7 +2862,7 @@ * represent a different kinds of resolution error - as such they must * specify how they map into concrete compiler diagnostics. */ - private abstract class ResolveError extends Symbol { + abstract class ResolveError extends Symbol { /** The name of the kind of error, for debugging only. */ final String debugName; @@ -2703,12 +3052,15 @@ */ class InapplicableSymbolError extends ResolveError { - InapplicableSymbolError() { - super(WRONG_MTH, "inapplicable symbol error"); + protected MethodResolutionContext resolveContext; + + InapplicableSymbolError(MethodResolutionContext context) { + this(WRONG_MTH, "inapplicable symbol error", context); } - protected InapplicableSymbolError(int kind, String debugName) { + protected InapplicableSymbolError(int kind, String debugName, MethodResolutionContext context) { super(kind, debugName); + this.resolveContext = context; } @Override @@ -2746,7 +3098,7 @@ Candidate c = errCandidate(); Symbol ws = c.sym.asMemberOf(site, types); return diags.create(dkind, log.currentSource(), pos, - "cant.apply.symbol" + (c.details != null ? ".1" : ""), + "cant.apply.symbol", kindName(ws), ws.name == names.init ? ws.owner.name : ws.name, methodArguments(ws.type.getParameterTypes()), @@ -2763,13 +3115,13 @@ } protected boolean shouldReport(Candidate c) { + MethodResolutionPhase errPhase = resolveContext.firstErroneousResolutionPhase(); return !c.isApplicable() && - (((c.sym.flags() & VARARGS) != 0 && c.step == VARARITY) || - (c.sym.flags() & VARARGS) == 0 && c.step == (boxingEnabled ? BOX : BASIC)); + c.step == errPhase; } private Candidate errCandidate() { - for (Candidate c : currentResolutionContext.candidates) { + for (Candidate c : resolveContext.candidates) { if (shouldReport(c)) { return c; } @@ -2786,8 +3138,8 @@ */ class InapplicableSymbolsError extends InapplicableSymbolError { - InapplicableSymbolsError() { - super(WRONG_MTHS, "inapplicable symbols"); + InapplicableSymbolsError(MethodResolutionContext context) { + super(WRONG_MTHS, "inapplicable symbols", context); } @Override @@ -2798,7 +3150,7 @@ Name name, List argtypes, List typeargtypes) { - if (currentResolutionContext.candidates.nonEmpty()) { + if (!resolveContext.candidates.isEmpty()) { JCDiagnostic err = diags.create(dkind, log.currentSource(), pos, @@ -2816,7 +3168,7 @@ //where List candidateDetails(Type site) { List details = List.nil(); - for (Candidate c : currentResolutionContext.candidates) { + for (Candidate c : resolveContext.candidates) { if (!shouldReport(c)) continue; JCDiagnostic detailDiag = diags.fragment("inapplicable.method", Kinds.kindName(c.sym), @@ -2829,7 +3181,7 @@ } private Name getName() { - Symbol sym = currentResolutionContext.candidates.head.sym; + Symbol sym = resolveContext.candidates.head.sym; return sym.name == names.init ? sym.owner.name : sym.name; @@ -3023,6 +3375,7 @@ steps.head.isApplicable(boxingEnabled, varargsEnabled) && sym.kind >= WRONG_MTHS) { sym = resolutionCache.get(steps.head); + if (sym.kind == ABSENT_MTH) break; //ignore spurious empty entries bestSoFar = steps.head; steps = steps.tail; } @@ -3031,8 +3384,7 @@ void addInapplicableCandidate(Symbol sym, JCDiagnostic details) { Candidate c = new Candidate(currentResolutionContext.step, sym, details, null); - if (!candidates.contains(c)) - candidates = candidates.append(c); + candidates = candidates.append(c); } void addApplicableCandidate(Symbol sym, Type mtype) { @@ -3040,6 +3392,16 @@ candidates = candidates.append(c); } + Candidate getCandidate(Symbol sym, MethodResolutionPhase phase) { + for (Candidate c : currentResolutionContext.candidates) { + if (c.step == phase && + c.sym.baseSymbol() == sym.baseSymbol()) { + return c; + } + } + return null; + } + /** * This class represents an overload resolution candidate. There are two * kinds of candidates: applicable methods and inapplicable methods; @@ -3067,9 +3429,9 @@ Symbol s1 = this.sym; Symbol s2 = ((Candidate)o).sym; if ((s1 != s2 && - (s1.overrides(s2, s1.owner.type.tsym, types, false) || - (s2.overrides(s1, s2.owner.type.tsym, types, false)))) || - ((s1.isConstructor() || s2.isConstructor()) && s1.owner != s2.owner)) + (s1.overrides(s2, s1.owner.type.tsym, types, false) || + (s2.overrides(s1, s2.owner.type.tsym, types, false)))) || + ((s1.isConstructor() || s2.isConstructor()) && s1.owner != s2.owner)) return true; } return false;