All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.tools.javac.comp.Resolve Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.tools.javac.comp;

import com.sun.tools.javac.api.Formattable.LocalizedString;
import com.sun.tools.javac.code.*;
import com.sun.tools.javac.code.Scope.WriteableScope;
import com.sun.tools.javac.code.Symbol.*;
import com.sun.tools.javac.code.Type.*;
import com.sun.tools.javac.comp.Attr.ResultInfo;
import com.sun.tools.javac.comp.Check.CheckContext;
import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext;
import com.sun.tools.javac.comp.DeferredAttr.DeferredType;
import com.sun.tools.javac.comp.Infer.FreeTypeListener;
import com.sun.tools.javac.comp.Resolve.MethodResolutionContext.Candidate;
import com.sun.tools.javac.comp.Resolve.MethodResolutionDiagHelper.Template;
import com.sun.tools.javac.comp.Resolve.ReferenceLookupResult.StaticKind;
import com.sun.tools.javac.jvm.*;
import com.sun.tools.javac.main.Option;
import com.sun.tools.javac.resources.CompilerProperties.Fragments;
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.DefinedBy.Api;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;

import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

import javax.lang.model.element.ElementVisitor;

import static com.sun.tools.javac.code.Flags.*;
import static com.sun.tools.javac.code.Flags.BLOCK;
import static com.sun.tools.javac.code.Flags.STATIC;
import static com.sun.tools.javac.code.Kinds.*;
import static com.sun.tools.javac.code.Kinds.Kind.*;
import static com.sun.tools.javac.code.TypeTag.*;
import static com.sun.tools.javac.comp.Resolve.MethodResolutionPhase.*;
import static com.sun.tools.javac.tree.JCTree.Tag.*;
import static com.sun.tools.javac.util.Iterators.createCompoundIterator;

/** Helper class for name resolution, used mostly by the attribution phase.
 *
 *  

This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice. */ public class Resolve { protected static final Context.Key resolveKey = new Context.Key<>(); Names names; Log log; Symtab syms; Attr attr; DeferredAttr deferredAttr; Check chk; Infer infer; ClassFinder finder; ModuleFinder moduleFinder; Types types; JCDiagnostic.Factory diags; public final boolean allowMethodHandles; public final boolean allowFunctionalInterfaceMostSpecific; public final boolean allowModules; public final boolean checkVarargsAccessAfterResolution; private final boolean compactMethodDiags; final EnumSet verboseResolutionMode; WriteableScope polymorphicSignatureScope; protected Resolve(Context context) { context.put(resolveKey, this); syms = Symtab.instance(context); varNotFound = new SymbolNotFoundError(ABSENT_VAR); methodNotFound = new SymbolNotFoundError(ABSENT_MTH); typeNotFound = new SymbolNotFoundError(ABSENT_TYP); referenceNotFound = new ReferenceLookupResult(methodNotFound, null); names = Names.instance(context); log = Log.instance(context); attr = Attr.instance(context); deferredAttr = DeferredAttr.instance(context); chk = Check.instance(context); infer = Infer.instance(context); finder = ClassFinder.instance(context); moduleFinder = ModuleFinder.instance(context); types = Types.instance(context); diags = JCDiagnostic.Factory.instance(context); Source source = Source.instance(context); Options options = Options.instance(context); compactMethodDiags = options.isSet(Option.XDIAGS, "compact") || options.isUnset(Option.XDIAGS) && options.isUnset("rawDiagnostics"); verboseResolutionMode = VerboseResolutionMode.getVerboseResolutionMode(options); Target target = Target.instance(context); allowMethodHandles = target.hasMethodHandles(); allowFunctionalInterfaceMostSpecific = source.allowFunctionalInterfaceMostSpecific(); checkVarargsAccessAfterResolution = source.allowPostApplicabilityVarargsAccessCheck(); polymorphicSignatureScope = WriteableScope.create(syms.noSymbol); inapplicableMethodException = new InapplicableMethodException(diags); allowModules = source.allowModules(); } /** error symbols, which are returned when resolution fails */ private final SymbolNotFoundError varNotFound; private final SymbolNotFoundError methodNotFound; private final SymbolNotFoundError typeNotFound; /** empty reference lookup result */ private final ReferenceLookupResult referenceNotFound; public static Resolve instance(Context context) { Resolve instance = context.get(resolveKey); if (instance == null) instance = new Resolve(context); return instance; } private static Symbol bestOf(Symbol s1, Symbol s2) { return s1.kind.betterThan(s2.kind) ? s1 : s2; } // enum VerboseResolutionMode { SUCCESS("success"), FAILURE("failure"), APPLICABLE("applicable"), INAPPLICABLE("inapplicable"), DEFERRED_INST("deferred-inference"), PREDEF("predef"), OBJECT_INIT("object-init"), INTERNAL("internal"); final String opt; private VerboseResolutionMode(String opt) { this.opt = opt; } static EnumSet getVerboseResolutionMode(Options opts) { String s = opts.get("debug.verboseResolution"); EnumSet res = EnumSet.noneOf(VerboseResolutionMode.class); if (s == null) return res; if (s.contains("all")) { res = EnumSet.allOf(VerboseResolutionMode.class); } Collection args = Arrays.asList(s.split(",")); for (VerboseResolutionMode mode : values()) { if (args.contains(mode.opt)) { res.add(mode); } else if (args.contains("-" + mode.opt)) { res.remove(mode); } } return res; } } void reportVerboseResolutionDiagnostic(DiagnosticPosition dpos, Name name, Type site, List argtypes, List typeargtypes, Symbol bestSoFar) { boolean success = !bestSoFar.kind.isResolutionError(); if (success && !verboseResolutionMode.contains(VerboseResolutionMode.SUCCESS)) { return; } else if (!success && !verboseResolutionMode.contains(VerboseResolutionMode.FAILURE)) { return; } if (bestSoFar.name == names.init && bestSoFar.owner == syms.objectType.tsym && !verboseResolutionMode.contains(VerboseResolutionMode.OBJECT_INIT)) { return; //skip diags for Object constructor resolution } else if (site == syms.predefClass.type && !verboseResolutionMode.contains(VerboseResolutionMode.PREDEF)) { return; //skip spurious diags for predef symbols (i.e. operators) } else if (currentResolutionContext.internalResolution && !verboseResolutionMode.contains(VerboseResolutionMode.INTERNAL)) { return; } int pos = 0; int mostSpecificPos = -1; ListBuffer subDiags = new ListBuffer<>(); for (Candidate c : currentResolutionContext.candidates) { if (currentResolutionContext.step != c.step || (c.isApplicable() && !verboseResolutionMode.contains(VerboseResolutionMode.APPLICABLE)) || (!c.isApplicable() && !verboseResolutionMode.contains(VerboseResolutionMode.INAPPLICABLE))) { continue; } else { subDiags.append(c.isApplicable() ? getVerboseApplicableCandidateDiag(pos, c.sym, c.mtype) : getVerboseInapplicableCandidateDiag(pos, c.sym, c.details)); if (c.sym == bestSoFar) mostSpecificPos = pos; pos++; } } String key = success ? "verbose.resolve.multi" : "verbose.resolve.multi.1"; List argtypes2 = argtypes.map(deferredAttr.new RecoveryDeferredTypeMap(AttrMode.SPECULATIVE, bestSoFar, currentResolutionContext.step)); JCDiagnostic main = diags.note(log.currentSource(), dpos, key, name, site.tsym, mostSpecificPos, currentResolutionContext.step, methodArguments(argtypes2), methodArguments(typeargtypes)); JCDiagnostic d = new JCDiagnostic.MultilineDiagnostic(main, subDiags.toList()); log.report(d); } JCDiagnostic getVerboseApplicableCandidateDiag(int pos, Symbol sym, Type inst) { JCDiagnostic subDiag = null; if (sym.type.hasTag(FORALL)) { subDiag = diags.fragment("partial.inst.sig", inst); } String key = subDiag == null ? "applicable.method.found" : "applicable.method.found.1"; return diags.fragment(key, pos, sym, subDiag); } JCDiagnostic getVerboseInapplicableCandidateDiag(int pos, Symbol sym, JCDiagnostic subDiag) { return diags.fragment("not.applicable.method.found", pos, sym, subDiag); } // /* ************************************************************************ * Identifier resolution *************************************************************************/ /** An environment is "static" if its static level is greater than * the one of its outer environment */ protected static boolean isStatic(Env env) { return env.outer != null && env.info.staticLevel > env.outer.info.staticLevel; } /** An environment is an "initializer" if it is a constructor or * an instance initializer. */ static boolean isInitializer(Env env) { Symbol owner = env.info.scope.owner; return owner.isConstructor() || owner.owner.kind == TYP && (owner.kind == VAR || owner.kind == MTH && (owner.flags() & BLOCK) != 0) && (owner.flags() & STATIC) == 0; } /** Is class accessible in given evironment? * @param env The current environment. * @param c The class whose accessibility is checked. */ public boolean isAccessible(Env env, TypeSymbol c) { return isAccessible(env, c, false); } public boolean isAccessible(Env env, TypeSymbol c, boolean checkInner) { /* 15.9.5.1: Note that it is possible for the signature of the anonymous constructor to refer to an inaccessible type */ if (env.enclMethod != null && (env.enclMethod.mods.flags & ANONCONSTR) != 0) return true; if (env.info.visitingServiceImplementation && env.toplevel.modle == c.packge().modle) { return true; } boolean isAccessible = false; switch ((short)(c.flags() & AccessFlags)) { case PRIVATE: isAccessible = env.enclClass.sym.outermostClass() == c.owner.outermostClass(); break; case 0: isAccessible = env.toplevel.packge == c.owner // fast special case || env.toplevel.packge == c.packge(); break; default: // error recovery isAccessible = true; break; case PUBLIC: if (allowModules) { ModuleSymbol currModule = env.toplevel.modle; currModule.complete(); PackageSymbol p = c.packge(); isAccessible = (currModule == p.modle) || currModule.visiblePackages.get(p.fullname) == p || p == syms.rootPackage; } else { isAccessible = true; } break; case PROTECTED: isAccessible = env.toplevel.packge == c.owner // fast special case || env.toplevel.packge == c.packge() || isInnerSubClass(env.enclClass.sym, c.owner); break; } return (checkInner == false || c.type.getEnclosingType() == Type.noType) ? isAccessible : isAccessible && isAccessible(env, c.type.getEnclosingType(), checkInner); } //where /** Is given class a subclass of given base class, or an inner class * of a subclass? * Return null if no such class exists. * @param c The class which is the subclass or is contained in it. * @param base The base class */ private boolean isInnerSubClass(ClassSymbol c, Symbol base) { while (c != null && !c.isSubClass(base, types)) { c = c.owner.enclClass(); } return c != null; } boolean isAccessible(Env env, Type t) { return isAccessible(env, t, false); } boolean isAccessible(Env env, Type t, boolean checkInner) { return (t.hasTag(ARRAY)) ? isAccessible(env, types.cvarUpperBound(types.elemtype(t))) : isAccessible(env, t.tsym, checkInner); } /** Is symbol accessible as a member of given type in given environment? * @param env The current environment. * @param site The type of which the tested symbol is regarded * as a member. * @param sym The symbol. */ public boolean isAccessible(Env env, Type site, Symbol sym) { return isAccessible(env, site, sym, false); } public boolean isAccessible(Env env, Type site, Symbol sym, boolean checkInner) { if (sym.name == names.init && sym.owner != site.tsym) return false; /* 15.9.5.1: Note that it is possible for the signature of the anonymous constructor to refer to an inaccessible type */ if (env.enclMethod != null && (env.enclMethod.mods.flags & ANONCONSTR) != 0) return true; if (env.info.visitingServiceImplementation && env.toplevel.modle == sym.packge().modle) { return true; } switch ((short)(sym.flags() & AccessFlags)) { case PRIVATE: return (env.enclClass.sym == sym.owner // fast special case || env.enclClass.sym.outermostClass() == sym.owner.outermostClass()) && sym.isInheritedIn(site.tsym, types); case 0: return (env.toplevel.packge == sym.owner.owner // fast special case || env.toplevel.packge == sym.packge()) && isAccessible(env, site, checkInner) && sym.isInheritedIn(site.tsym, types) && notOverriddenIn(site, sym); case PROTECTED: return (env.toplevel.packge == sym.owner.owner // fast special case || env.toplevel.packge == sym.packge() || isProtectedAccessible(sym, env.enclClass.sym, site) || // OK to select instance method or field from 'super' or type name // (but type names should be disallowed elsewhere!) env.info.selectSuper && (sym.flags() & STATIC) == 0 && sym.kind != TYP) && isAccessible(env, site, checkInner) && notOverriddenIn(site, sym); default: // this case includes erroneous combinations as well return isAccessible(env, site, checkInner) && notOverriddenIn(site, sym); } } //where /* `sym' is accessible only if not overridden by * another symbol which is a member of `site' * (because, if it is overridden, `sym' is not strictly * speaking a member of `site'). A polymorphic signature method * cannot be overridden (e.g. MH.invokeExact(Object[])). */ private boolean notOverriddenIn(Type site, Symbol sym) { if (sym.kind != MTH || sym.isConstructor() || sym.isStatic()) return true; else { Symbol s2 = ((MethodSymbol)sym).implementation(site.tsym, types, true); return (s2 == null || s2 == sym || sym.owner == s2.owner || !types.isSubSignature(types.memberType(site, s2), types.memberType(site, sym))); } } //where /** Is given protected symbol accessible if it is selected from given site * and the selection takes place in given class? * @param sym The symbol with protected access * @param c The class where the access takes place * @site The type of the qualifier */ private boolean isProtectedAccessible(Symbol sym, ClassSymbol c, Type site) { Type newSite = site.hasTag(TYPEVAR) ? site.getUpperBound() : site; while (c != null && !(c.isSubClass(sym.owner, types) && (c.flags() & INTERFACE) == 0 && // In JLS 2e 6.6.2.1, the subclass restriction applies // only to instance fields and methods -- types are excluded // regardless of whether they are declared 'static' or not. ((sym.flags() & STATIC) != 0 || sym.kind == TYP || newSite.tsym.isSubClass(c, types)))) c = c.owner.enclClass(); return c != null; } /** * Performs a recursive scan of a type looking for accessibility problems * from current attribution environment */ void checkAccessibleType(Env env, Type t) { accessibilityChecker.visit(t, env); } /** * Accessibility type-visitor */ Types.SimpleVisitor> accessibilityChecker = new Types.SimpleVisitor>() { void visit(List ts, Env env) { for (Type t : ts) { visit(t, env); } } public Void visitType(Type t, Env env) { return null; } @Override public Void visitArrayType(ArrayType t, Env env) { visit(t.elemtype, env); return null; } @Override public Void visitClassType(ClassType t, Env env) { visit(t.getTypeArguments(), env); if (!isAccessible(env, t, true)) { accessBase(new AccessError(env, null, t.tsym), env.tree.pos(), env.enclClass.sym, t, t.tsym.name, true); } return null; } @Override public Void visitWildcardType(WildcardType t, Env env) { visit(t.type, env); return null; } @Override public Void visitMethodType(MethodType t, Env env) { visit(t.getParameterTypes(), env); visit(t.getReturnType(), env); visit(t.getThrownTypes(), env); return null; } }; /** Try to instantiate the type of a method so that it fits * given type arguments and argument types. If successful, return * the method's instantiated type, else return null. * The instantiation will take into account an additional leading * formal parameter if the method is an instance method seen as a member * of an under determined site. In this case, we treat site as an additional * parameter and the parameters of the class containing the method as * additional type variables that get instantiated. * * @param env The current environment * @param site The type of which the method is a member. * @param m The method symbol. * @param argtypes The invocation's given value arguments. * @param typeargtypes The invocation's given type arguments. * @param allowBoxing Allow boxing conversions of arguments. * @param useVarargs Box trailing arguments into an array for varargs. */ Type rawInstantiate(Env env, Type site, Symbol m, ResultInfo resultInfo, List argtypes, List typeargtypes, boolean allowBoxing, boolean useVarargs, Warner warn) throws Infer.InferenceException { Type mt = types.memberType(site, m); // tvars is the list of formal type variables for which type arguments // need to inferred. List tvars = List.nil(); if (typeargtypes == null) typeargtypes = List.nil(); if (!mt.hasTag(FORALL) && typeargtypes.nonEmpty()) { // This is not a polymorphic method, but typeargs are supplied // which is fine, see JLS 15.12.2.1 } else if (mt.hasTag(FORALL) && typeargtypes.nonEmpty()) { ForAll pmt = (ForAll) mt; if (typeargtypes.length() != pmt.tvars.length()) // not enough args throw inapplicableMethodException.setMessage("wrong.number.type.args", Integer.toString(pmt.tvars.length())); // Check type arguments are within bounds List formals = pmt.tvars; List actuals = typeargtypes; while (formals.nonEmpty() && actuals.nonEmpty()) { List bounds = types.subst(types.getBounds((TypeVar)formals.head), pmt.tvars, typeargtypes); for (; bounds.nonEmpty(); bounds = bounds.tail) { if (!types.isSubtypeUnchecked(actuals.head, bounds.head, warn)) throw inapplicableMethodException.setMessage("explicit.param.do.not.conform.to.bounds",actuals.head, bounds); } formals = formals.tail; actuals = actuals.tail; } mt = types.subst(pmt.qtype, pmt.tvars, typeargtypes); } else if (mt.hasTag(FORALL)) { ForAll pmt = (ForAll) mt; List tvars1 = types.newInstances(pmt.tvars); tvars = tvars.appendList(tvars1); mt = types.subst(pmt.qtype, pmt.tvars, tvars1); } // find out whether we need to go the slow route via infer boolean instNeeded = tvars.tail != null; /*inlined: tvars.nonEmpty()*/ for (List l = argtypes; l.tail != null/*inlined: l.nonEmpty()*/ && !instNeeded; l = l.tail) { if (l.head.hasTag(FORALL)) instNeeded = true; } if (instNeeded) { return infer.instantiateMethod(env, tvars, (MethodType)mt, resultInfo, (MethodSymbol)m, argtypes, allowBoxing, useVarargs, currentResolutionContext, warn); } DeferredAttr.DeferredAttrContext dc = currentResolutionContext.deferredAttrContext(m, infer.emptyContext, resultInfo, warn); currentResolutionContext.methodCheck.argumentsAcceptable(env, dc, argtypes, mt.getParameterTypes(), warn); dc.complete(); return mt; } Type checkMethod(Env env, Type site, Symbol m, ResultInfo resultInfo, List argtypes, List typeargtypes, Warner warn) { MethodResolutionContext prevContext = currentResolutionContext; try { currentResolutionContext = new MethodResolutionContext(); currentResolutionContext.attrMode = (resultInfo.pt == Infer.anyPoly) ? AttrMode.SPECULATIVE : DeferredAttr.AttrMode.CHECK; if (env.tree.hasTag(JCTree.Tag.REFERENCE)) { //method/constructor references need special check class //to handle inference variables in 'argtypes' (might happen //during an unsticking round) currentResolutionContext.methodCheck = new MethodReferenceCheck(resultInfo.checkContext.inferenceContext()); } MethodResolutionPhase step = currentResolutionContext.step = env.info.pendingResolutionPhase; return rawInstantiate(env, site, m, resultInfo, argtypes, typeargtypes, step.isBoxingRequired(), step.isVarargsRequired(), warn); } finally { currentResolutionContext = prevContext; } } /** Same but returns null instead throwing a NoInstanceException */ Type instantiate(Env env, Type site, Symbol m, ResultInfo resultInfo, List argtypes, List typeargtypes, boolean allowBoxing, boolean useVarargs, Warner warn) { try { return rawInstantiate(env, site, m, resultInfo, argtypes, typeargtypes, allowBoxing, useVarargs, warn); } catch (InapplicableMethodException ex) { return null; } } /** * This interface defines an entry point that should be used to perform a * method check. A method check usually consist in determining as to whether * a set of types (actuals) is compatible with another set of types (formals). * Since the notion of compatibility can vary depending on the circumstances, * this interfaces allows to easily add new pluggable method check routines. */ interface MethodCheck { /** * Main method check routine. A method check usually consist in determining * as to whether a set of types (actuals) is compatible with another set of * types (formals). If an incompatibility is found, an unchecked exception * is assumed to be thrown. */ void argumentsAcceptable(Env env, DeferredAttrContext deferredAttrContext, List argtypes, List formals, Warner warn); /** * Retrieve the method check object that will be used during a * most specific check. */ MethodCheck mostSpecificCheck(List actuals); } /** * Helper enum defining all method check diagnostics (used by resolveMethodCheck). */ enum MethodCheckDiag { /** * Actuals and formals differs in length. */ ARITY_MISMATCH("arg.length.mismatch", "infer.arg.length.mismatch"), /** * An actual is incompatible with a formal. */ ARG_MISMATCH("no.conforming.assignment.exists", "infer.no.conforming.assignment.exists"), /** * An actual is incompatible with the varargs element type. */ VARARG_MISMATCH("varargs.argument.mismatch", "infer.varargs.argument.mismatch"), /** * The varargs element type is inaccessible. */ INACCESSIBLE_VARARGS("inaccessible.varargs.type", "inaccessible.varargs.type"); final String basicKey; final String inferKey; MethodCheckDiag(String basicKey, String inferKey) { this.basicKey = basicKey; this.inferKey = inferKey; } String regex() { return String.format("([a-z]*\\.)*(%s|%s)", basicKey, inferKey); } } /** * Dummy method check object. All methods are deemed applicable, regardless * of their formal parameter types. */ MethodCheck nilMethodCheck = new MethodCheck() { public void argumentsAcceptable(Env env, DeferredAttrContext deferredAttrContext, List argtypes, List formals, Warner warn) { //do nothing - method always applicable regardless of actuals } public MethodCheck mostSpecificCheck(List actuals) { return this; } }; /** * Base class for 'real' method checks. The class defines the logic for * iterating through formals and actuals and provides and entry point * that can be used by subclasses in order to define the actual check logic. */ abstract class AbstractMethodCheck implements MethodCheck { @Override public void argumentsAcceptable(final Env env, DeferredAttrContext deferredAttrContext, List argtypes, List formals, Warner warn) { //should we expand formals? boolean useVarargs = deferredAttrContext.phase.isVarargsRequired(); JCTree callTree = treeForDiagnostics(env); List trees = TreeInfo.args(callTree); //inference context used during this method check InferenceContext inferenceContext = deferredAttrContext.inferenceContext; Type varargsFormal = useVarargs ? formals.last() : null; if (varargsFormal == null && argtypes.size() != formals.size()) { reportMC(callTree, MethodCheckDiag.ARITY_MISMATCH, inferenceContext); // not enough args } while (argtypes.nonEmpty() && formals.head != varargsFormal) { DiagnosticPosition pos = trees != null ? trees.head : null; checkArg(pos, false, argtypes.head, formals.head, deferredAttrContext, warn); argtypes = argtypes.tail; formals = formals.tail; trees = trees != null ? trees.tail : trees; } if (formals.head != varargsFormal) { reportMC(callTree, MethodCheckDiag.ARITY_MISMATCH, inferenceContext); // not enough args } if (useVarargs) { //note: if applicability check is triggered by most specific test, //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5) final Type elt = types.elemtype(varargsFormal); while (argtypes.nonEmpty()) { DiagnosticPosition pos = trees != null ? trees.head : null; checkArg(pos, true, argtypes.head, elt, deferredAttrContext, warn); argtypes = argtypes.tail; trees = trees != null ? trees.tail : trees; } } } // where private JCTree treeForDiagnostics(Env env) { return env.info.preferredTreeForDiagnostics != null ? env.info.preferredTreeForDiagnostics : env.tree; } /** * Does the actual argument conforms to the corresponding formal? */ abstract void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn); protected void reportMC(DiagnosticPosition pos, MethodCheckDiag diag, InferenceContext inferenceContext, Object... args) { boolean inferDiag = inferenceContext != infer.emptyContext; InapplicableMethodException ex = inferDiag ? infer.inferenceException : inapplicableMethodException; if (inferDiag && (!diag.inferKey.equals(diag.basicKey))) { Object[] args2 = new Object[args.length + 1]; System.arraycopy(args, 0, args2, 1, args.length); args2[0] = inferenceContext.inferenceVars(); args = args2; } String key = inferDiag ? diag.inferKey : diag.basicKey; throw ex.setMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args)); } public MethodCheck mostSpecificCheck(List actuals) { return nilMethodCheck; } } /** * Arity-based method check. A method is applicable if the number of actuals * supplied conforms to the method signature. */ MethodCheck arityMethodCheck = new AbstractMethodCheck() { @Override void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) { //do nothing - actual always compatible to formals } @Override public String toString() { return "arityMethodCheck"; } }; List dummyArgs(int length) { ListBuffer buf = new ListBuffer<>(); for (int i = 0 ; i < length ; i++) { buf.append(Type.noType); } return buf.toList(); } /** * Main method applicability routine. Given a list of actual types A, * a list of formal types F, determines whether the types in A are * compatible (by method invocation conversion) with the types in F. * * Since this routine is shared between overload resolution and method * type-inference, a (possibly empty) inference context is used to convert * formal types to the corresponding 'undet' form ahead of a compatibility * check so that constraints can be propagated and collected. * * Moreover, if one or more types in A is a deferred type, this routine uses * DeferredAttr in order to perform deferred attribution. If one or more actual * deferred types are stuck, they are placed in a queue and revisited later * after the remainder of the arguments have been seen. If this is not sufficient * to 'unstuck' the argument, a cyclic inference error is called out. * * A method check handler (see above) is used in order to report errors. */ MethodCheck resolveMethodCheck = new AbstractMethodCheck() { @Override void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) { ResultInfo mresult = methodCheckResult(varargs, formal, deferredAttrContext, warn); mresult.check(pos, actual); } @Override public void argumentsAcceptable(final Env env, DeferredAttrContext deferredAttrContext, List argtypes, List formals, Warner warn) { super.argumentsAcceptable(env, deferredAttrContext, argtypes, formals, warn); // should we check varargs element type accessibility? if (deferredAttrContext.phase.isVarargsRequired()) { if (deferredAttrContext.mode == AttrMode.CHECK || !checkVarargsAccessAfterResolution) { varargsAccessible(env, types.elemtype(formals.last()), deferredAttrContext.inferenceContext); } } } /** * Test that the runtime array element type corresponding to 't' is accessible. 't' should be the * varargs element type of either the method invocation type signature (after inference completes) * or the method declaration signature (before inference completes). */ private void varargsAccessible(final Env env, final Type t, final InferenceContext inferenceContext) { if (inferenceContext.free(t)) { inferenceContext.addFreeTypeListener(List.of(t), solvedContext -> varargsAccessible(env, solvedContext.asInstType(t), solvedContext)); } else { if (!isAccessible(env, types.erasure(t))) { Symbol location = env.enclClass.sym; reportMC(env.tree, MethodCheckDiag.INACCESSIBLE_VARARGS, inferenceContext, t, Kinds.kindName(location), location); } } } private ResultInfo methodCheckResult(final boolean varargsCheck, Type to, final DeferredAttr.DeferredAttrContext deferredAttrContext, Warner rsWarner) { CheckContext checkContext = new MethodCheckContext(!deferredAttrContext.phase.isBoxingRequired(), deferredAttrContext, rsWarner) { MethodCheckDiag methodDiag = varargsCheck ? MethodCheckDiag.VARARG_MISMATCH : MethodCheckDiag.ARG_MISMATCH; @Override public void report(DiagnosticPosition pos, JCDiagnostic details) { reportMC(pos, methodDiag, deferredAttrContext.inferenceContext, details); } }; return new MethodResultInfo(to, checkContext); } @Override public MethodCheck mostSpecificCheck(List actuals) { return new MostSpecificCheck(actuals); } @Override public String toString() { return "resolveMethodCheck"; } }; /** * This class handles method reference applicability checks; since during * these checks it's sometime possible to have inference variables on * the actual argument types list, the method applicability check must be * extended so that inference variables are 'opened' as needed. */ class MethodReferenceCheck extends AbstractMethodCheck { InferenceContext pendingInferenceContext; MethodReferenceCheck(InferenceContext pendingInferenceContext) { this.pendingInferenceContext = pendingInferenceContext; } @Override void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) { ResultInfo mresult = methodCheckResult(varargs, formal, deferredAttrContext, warn); mresult.check(pos, actual); } private ResultInfo methodCheckResult(final boolean varargsCheck, Type to, final DeferredAttr.DeferredAttrContext deferredAttrContext, Warner rsWarner) { CheckContext checkContext = new MethodCheckContext(!deferredAttrContext.phase.isBoxingRequired(), deferredAttrContext, rsWarner) { MethodCheckDiag methodDiag = varargsCheck ? MethodCheckDiag.VARARG_MISMATCH : MethodCheckDiag.ARG_MISMATCH; @Override public boolean compatible(Type found, Type req, Warner warn) { found = pendingInferenceContext.asUndetVar(found); if (found.hasTag(UNDETVAR) && req.isPrimitive()) { req = types.boxedClass(req).type; } return super.compatible(found, req, warn); } @Override public void report(DiagnosticPosition pos, JCDiagnostic details) { reportMC(pos, methodDiag, deferredAttrContext.inferenceContext, details); } }; return new MethodResultInfo(to, checkContext); } @Override public MethodCheck mostSpecificCheck(List actuals) { return new MostSpecificCheck(actuals); } @Override public String toString() { return "MethodReferenceCheck"; } } /** * Check context to be used during method applicability checks. A method check * context might contain inference variables. */ abstract class MethodCheckContext implements CheckContext { boolean strict; DeferredAttrContext deferredAttrContext; Warner rsWarner; public MethodCheckContext(boolean strict, DeferredAttrContext deferredAttrContext, Warner rsWarner) { this.strict = strict; this.deferredAttrContext = deferredAttrContext; this.rsWarner = rsWarner; } public boolean compatible(Type found, Type req, Warner warn) { InferenceContext inferenceContext = deferredAttrContext.inferenceContext; return strict ? types.isSubtypeUnchecked(inferenceContext.asUndetVar(found), inferenceContext.asUndetVar(req), warn) : types.isConvertible(inferenceContext.asUndetVar(found), inferenceContext.asUndetVar(req), warn); } public void report(DiagnosticPosition pos, JCDiagnostic details) { throw inapplicableMethodException.setMessage(details); } public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) { return rsWarner; } public InferenceContext inferenceContext() { return deferredAttrContext.inferenceContext; } public DeferredAttrContext deferredAttrContext() { return deferredAttrContext; } @Override public String toString() { return "MethodCheckContext"; } } /** * ResultInfo class to be used during method applicability checks. Check * for deferred types goes through special path. */ class MethodResultInfo extends ResultInfo { public MethodResultInfo(Type pt, CheckContext checkContext) { attr.super(KindSelector.VAL, pt, checkContext); } @Override protected Type check(DiagnosticPosition pos, Type found) { if (found.hasTag(DEFERRED)) { DeferredType dt = (DeferredType)found; return dt.check(this); } else { Type uResult = U(found); Type capturedType = pos == null || pos.getTree() == null ? types.capture(uResult) : checkContext.inferenceContext() .cachedCapture(pos.getTree(), uResult, true); return super.check(pos, chk.checkNonVoid(pos, capturedType)); } } /** * javac has a long-standing 'simplification' (see 6391995): * given an actual argument type, the method check is performed * on its upper bound. This leads to inconsistencies when an * argument type is checked against itself. For example, given * a type-variable T, it is not true that {@code U(T) <: T}, * so we need to guard against that. */ private Type U(Type found) { return found == pt ? found : types.cvarUpperBound(found); } @Override protected MethodResultInfo dup(Type newPt) { return new MethodResultInfo(newPt, checkContext); } @Override protected ResultInfo dup(CheckContext newContext) { return new MethodResultInfo(pt, newContext); } @Override protected ResultInfo dup(Type newPt, CheckContext newContext) { return new MethodResultInfo(newPt, newContext); } } /** * 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 { List actuals; MostSpecificCheck(List actuals) { 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(KindSelector.VAL, to, new MostSpecificCheckContext(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(DeferredAttrContext deferredAttrContext, Warner rsWarner, Type actual) { super(true, deferredAttrContext, rsWarner); this.actual = actual; } public boolean compatible(Type found, Type req, Warner warn) { if (allowFunctionalInterfaceMostSpecific && unrelatedFunctionalInterfaces(found, req) && (actual != null && actual.getTag() == DEFERRED)) { DeferredType dt = (DeferredType) actual; JCTree speculativeTree = dt.speculativeTree(deferredAttrContext); if (speculativeTree != deferredAttr.stuckTree) { return functionalInterfaceMostSpecific(found, req, speculativeTree); } } return compatibleBySubtyping(found, req); } private boolean compatibleBySubtyping(Type found, Type req) { if (!strict && found.isPrimitive() != req.isPrimitive()) { found = found.isPrimitive() ? types.boxedClass(found).type : types.unboxedType(found); } return types.isSubtypeNoCapture(found, deferredAttrContext.inferenceContext.asUndetVar(req)); } /** Whether {@code t} and {@code s} are unrelated functional interface types. */ private boolean unrelatedFunctionalInterfaces(Type t, Type s) { return types.isFunctionalInterface(t.tsym) && types.isFunctionalInterface(s.tsym) && unrelatedInterfaces(t, s); } /** Whether {@code t} and {@code s} are unrelated interface types; recurs on intersections. **/ private boolean unrelatedInterfaces(Type t, Type s) { if (t.isCompound()) { for (Type ti : types.interfaces(t)) { if (!unrelatedInterfaces(ti, s)) { return false; } } return true; } else if (s.isCompound()) { for (Type si : types.interfaces(s)) { if (!unrelatedInterfaces(t, si)) { return false; } } return true; } else { return types.asSuper(t, s.tsym) == null && types.asSuper(s, t.tsym) == null; } } /** Parameters {@code t} and {@code s} are unrelated functional interface types. */ private boolean functionalInterfaceMostSpecific(Type t, Type s, JCTree tree) { Type tDesc = types.findDescriptorType(types.capture(t)); Type tDescNoCapture = types.findDescriptorType(t); Type sDesc = types.findDescriptorType(s); final List tTypeParams = tDesc.getTypeArguments(); final List tTypeParamsNoCapture = tDescNoCapture.getTypeArguments(); final List sTypeParams = sDesc.getTypeArguments(); // compare type parameters if (tDesc.hasTag(FORALL) && !types.hasSameBounds((ForAll) tDesc, (ForAll) tDescNoCapture)) { return false; } // can't use Types.hasSameBounds on sDesc because bounds may have ivars List tIter = tTypeParams; List sIter = sTypeParams; while (tIter.nonEmpty() && sIter.nonEmpty()) { Type tBound = tIter.head.getUpperBound(); Type sBound = types.subst(sIter.head.getUpperBound(), sTypeParams, tTypeParams); if (tBound.containsAny(tTypeParams) && inferenceContext().free(sBound)) { return false; } if (!types.isSameType(tBound, inferenceContext().asUndetVar(sBound))) { return false; } tIter = tIter.tail; sIter = sIter.tail; } if (!tIter.isEmpty() || !sIter.isEmpty()) { return false; } // compare parameters List tParams = tDesc.getParameterTypes(); List tParamsNoCapture = tDescNoCapture.getParameterTypes(); List sParams = sDesc.getParameterTypes(); while (tParams.nonEmpty() && tParamsNoCapture.nonEmpty() && sParams.nonEmpty()) { Type tParam = tParams.head; Type tParamNoCapture = types.subst(tParamsNoCapture.head, tTypeParamsNoCapture, tTypeParams); Type sParam = types.subst(sParams.head, sTypeParams, tTypeParams); if (tParam.containsAny(tTypeParams) && inferenceContext().free(sParam)) { return false; } if (!types.isSubtype(inferenceContext().asUndetVar(sParam), tParam)) { return false; } if (!types.isSameType(tParamNoCapture, inferenceContext().asUndetVar(sParam))) { return false; } tParams = tParams.tail; tParamsNoCapture = tParamsNoCapture.tail; sParams = sParams.tail; } if (!tParams.isEmpty() || !tParamsNoCapture.isEmpty() || !sParams.isEmpty()) { return false; } // compare returns Type tRet = tDesc.getReturnType(); Type sRet = types.subst(sDesc.getReturnType(), sTypeParams, tTypeParams); if (tRet.containsAny(tTypeParams) && inferenceContext().free(sRet)) { return false; } MostSpecificFunctionReturnChecker msc = new MostSpecificFunctionReturnChecker(tRet, sRet); msc.scan(tree); return msc.result; } /** * Tests whether one functional interface type can be considered more specific * than another unrelated functional interface type for the scanned expression. */ class MostSpecificFunctionReturnChecker extends DeferredAttr.PolyScanner { final Type tRet; final Type sRet; boolean result; /** Parameters {@code t} and {@code s} are unrelated functional interface types. */ MostSpecificFunctionReturnChecker(Type tRet, Type sRet) { this.tRet = tRet; this.sRet = sRet; result = true; } @Override void skip(JCTree tree) { result &= false; } @Override public void visitConditional(JCConditional tree) { scan(asExpr(tree.truepart)); scan(asExpr(tree.falsepart)); } @Override public void visitReference(JCMemberReference tree) { if (sRet.hasTag(VOID)) { result &= true; } else if (tRet.hasTag(VOID)) { result &= false; } else if (tRet.isPrimitive() != sRet.isPrimitive()) { boolean retValIsPrimitive = tree.refPolyKind == PolyKind.STANDALONE && tree.sym.type.getReturnType().isPrimitive(); result &= (retValIsPrimitive == tRet.isPrimitive()) && (retValIsPrimitive != sRet.isPrimitive()); } else { result &= compatibleBySubtyping(tRet, sRet); } } @Override public void visitParens(JCParens tree) { scan(asExpr(tree.expr)); } @Override public void visitLambda(JCLambda tree) { if (sRet.hasTag(VOID)) { result &= true; } else if (tRet.hasTag(VOID)) { result &= false; } else { List lambdaResults = lambdaResults(tree); if (!lambdaResults.isEmpty() && unrelatedFunctionalInterfaces(tRet, sRet)) { for (JCExpression expr : lambdaResults) { result &= functionalInterfaceMostSpecific(tRet, sRet, expr); } } else if (!lambdaResults.isEmpty() && tRet.isPrimitive() != sRet.isPrimitive()) { for (JCExpression expr : lambdaResults) { boolean retValIsPrimitive = expr.isStandalone() && expr.type.isPrimitive(); result &= (retValIsPrimitive == tRet.isPrimitive()) && (retValIsPrimitive != sRet.isPrimitive()); } } else { result &= compatibleBySubtyping(tRet, sRet); } } } //where private List lambdaResults(JCLambda lambda) { if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { return List.of(asExpr((JCExpression) lambda.body)); } else { final ListBuffer buffer = new ListBuffer<>(); DeferredAttr.LambdaReturnScanner lambdaScanner = new DeferredAttr.LambdaReturnScanner() { @Override public void visitReturn(JCReturn tree) { if (tree.expr != null) { buffer.append(asExpr(tree.expr)); } } }; lambdaScanner.scan(lambda.body); return buffer.toList(); } } private JCExpression asExpr(JCExpression expr) { if (expr.type.hasTag(DEFERRED)) { JCTree speculativeTree = ((DeferredType)expr.type).speculativeTree(deferredAttrContext); if (speculativeTree != deferredAttr.stuckTree) { expr = (JCExpression)speculativeTree; } } return expr; } } } public MethodCheck mostSpecificCheck(List actuals) { Assert.error("Cannot get here!"); return null; } } public static class InapplicableMethodException extends RuntimeException { private static final long serialVersionUID = 0; JCDiagnostic diagnostic; JCDiagnostic.Factory diags; InapplicableMethodException(JCDiagnostic.Factory diags) { this.diagnostic = null; this.diags = diags; } InapplicableMethodException setMessage() { return setMessage((JCDiagnostic)null); } InapplicableMethodException setMessage(String key) { return setMessage(key != null ? diags.fragment(key) : null); } InapplicableMethodException setMessage(String key, Object... args) { return setMessage(key != null ? diags.fragment(key, args) : null); } InapplicableMethodException setMessage(JCDiagnostic diag) { this.diagnostic = diag; return this; } public JCDiagnostic getDiagnostic() { return diagnostic; } } private final InapplicableMethodException inapplicableMethodException; /* *************************************************************************** * Symbol lookup * the following naming conventions for arguments are used * * env is the environment where the symbol was mentioned * site is the type of which the symbol is a member * name is the symbol's name * if no arguments are given * argtypes are the value arguments, if we search for a method * * If no symbol was found, a ResolveError detailing the problem is returned. ****************************************************************************/ /** Find field. Synthetic fields are always skipped. * @param env The current environment. * @param site The original type from where the selection takes place. * @param name The name of the field. * @param c The class to search for the field. This is always * a superclass or implemented interface of site's class. */ Symbol findField(Env env, Type site, Name name, TypeSymbol c) { while (c.type.hasTag(TYPEVAR)) c = c.type.getUpperBound().tsym; Symbol bestSoFar = varNotFound; Symbol sym; for (Symbol s : c.members().getSymbolsByName(name)) { if (s.kind == VAR && (s.flags_field & SYNTHETIC) == 0) { return isAccessible(env, site, s) ? s : new AccessError(env, site, s); } } Type st = types.supertype(c.type); if (st != null && (st.hasTag(CLASS) || st.hasTag(TYPEVAR))) { sym = findField(env, site, name, st.tsym); bestSoFar = bestOf(bestSoFar, sym); } for (List l = types.interfaces(c.type); bestSoFar.kind != AMBIGUOUS && l.nonEmpty(); l = l.tail) { sym = findField(env, site, name, l.head.tsym); if (bestSoFar.exists() && sym.exists() && sym.owner != bestSoFar.owner) bestSoFar = new AmbiguityError(bestSoFar, sym); else bestSoFar = bestOf(bestSoFar, sym); } return bestSoFar; } /** Resolve a field identifier, throw a fatal error if not found. * @param pos The position to use for error reporting. * @param env The environment current at the method invocation. * @param site The type of the qualifying expression, in which * identifier is searched. * @param name The identifier's name. */ public VarSymbol resolveInternalField(DiagnosticPosition pos, Env env, Type site, Name name) { Symbol sym = findField(env, site, name, site.tsym); if (sym.kind == VAR) return (VarSymbol)sym; else throw new FatalError( diags.fragment("fatal.err.cant.locate.field", name)); } /** Find unqualified variable or field with given name. * Synthetic fields always skipped. * @param env The current environment. * @param name The name of the variable or field. */ Symbol findVar(Env env, Name name) { Symbol bestSoFar = varNotFound; Env env1 = env; boolean staticOnly = false; while (env1.outer != null) { Symbol sym = null; if (isStatic(env1)) staticOnly = true; for (Symbol s : env1.info.scope.getSymbolsByName(name)) { if (s.kind == VAR && (s.flags_field & SYNTHETIC) == 0) { sym = s; break; } } if (sym == null) { sym = findField(env1, env1.enclClass.sym.type, name, env1.enclClass.sym); } if (sym.exists()) { if (staticOnly && sym.kind == VAR && sym.owner.kind == TYP && (sym.flags() & STATIC) == 0) return new StaticError(sym); else return sym; } else { bestSoFar = bestOf(bestSoFar, sym); } if ((env1.enclClass.sym.flags() & STATIC) != 0) staticOnly = true; env1 = env1.outer; } Symbol sym = findField(env, syms.predefClass.type, name, syms.predefClass); if (sym.exists()) return sym; if (bestSoFar.exists()) return bestSoFar; Symbol origin = null; for (Scope sc : new Scope[] { env.toplevel.namedImportScope, env.toplevel.starImportScope }) { for (Symbol currentSymbol : sc.getSymbolsByName(name)) { if (currentSymbol.kind != VAR) continue; // invariant: sym.kind == Symbol.Kind.VAR if (!bestSoFar.kind.isResolutionError() && currentSymbol.owner != bestSoFar.owner) return new AmbiguityError(bestSoFar, currentSymbol); else if (!bestSoFar.kind.betterThan(VAR)) { origin = sc.getOrigin(currentSymbol).owner; bestSoFar = isAccessible(env, origin.type, currentSymbol) ? currentSymbol : new AccessError(env, origin.type, currentSymbol); } } if (bestSoFar.exists()) break; } if (bestSoFar.kind == VAR && bestSoFar.owner.type != origin.type) return bestSoFar.clone(origin); else return bestSoFar; } Warner noteWarner = new Warner(); /** Select the best method for a call site among two choices. * @param env The current environment. * @param site The original type from where the * selection takes place. * @param argtypes The invocation's value arguments, * @param typeargtypes The invocation's type arguments, * @param sym Proposed new best match. * @param bestSoFar Previously found best match. * @param allowBoxing Allow boxing conversions of arguments. * @param useVarargs Box trailing arguments into an array for varargs. */ @SuppressWarnings("fallthrough") Symbol selectBest(Env env, Type site, List argtypes, List typeargtypes, Symbol sym, Symbol bestSoFar, boolean allowBoxing, boolean useVarargs) { if (sym.kind == ERR || !sym.isInheritedIn(site.tsym, types)) { return bestSoFar; } else if (useVarargs && (sym.flags() & VARARGS) == 0) { return bestSoFar.kind.isResolutionError() ? new BadVarargsMethod((ResolveError)bestSoFar.baseSymbol()) : bestSoFar; } Assert.check(!sym.kind.isResolutionError()); try { types.noWarnings.clear(); Type mt = rawInstantiate(env, site, sym, null, argtypes, typeargtypes, allowBoxing, useVarargs, types.noWarnings); currentResolutionContext.addApplicableCandidate(sym, mt); } catch (InapplicableMethodException ex) { currentResolutionContext.addInapplicableCandidate(sym, ex.getDiagnostic()); switch (bestSoFar.kind) { case ABSENT_MTH: return new InapplicableSymbolError(currentResolutionContext); case WRONG_MTH: bestSoFar = new InapplicableSymbolsError(currentResolutionContext); default: return bestSoFar; } } if (!isAccessible(env, site, sym)) { return (bestSoFar.kind == ABSENT_MTH) ? new AccessError(env, site, sym) : bestSoFar; } return (bestSoFar.kind.isResolutionError() && bestSoFar.kind != AMBIGUOUS) ? sym : mostSpecific(argtypes, sym, bestSoFar, env, site, useVarargs); } /* Return the most specific of the two methods for a call, * given that both are accessible and applicable. * @param m1 A new candidate for most specific. * @param m2 The previous most specific candidate. * @param env The current environment. * @param site The original type from where the selection * takes place. * @param allowBoxing Allow boxing conversions of arguments. * @param useVarargs Box trailing arguments into an array for varargs. */ Symbol mostSpecific(List argtypes, Symbol m1, Symbol m2, Env env, final Type site, boolean useVarargs) { switch (m2.kind) { case MTH: if (m1 == m2) return m1; boolean m1SignatureMoreSpecific = signatureMoreSpecific(argtypes, env, site, m1, m2, useVarargs); boolean m2SignatureMoreSpecific = signatureMoreSpecific(argtypes, env, site, m2, m1, useVarargs); if (m1SignatureMoreSpecific && m2SignatureMoreSpecific) { Type mt1 = types.memberType(site, m1); Type mt2 = types.memberType(site, m2); if (!types.overrideEquivalent(mt1, mt2)) return ambiguityError(m1, m2); // same signature; select (a) the non-bridge method, or // (b) the one that overrides the other, or (c) the concrete // one, or (d) merge both abstract signatures if ((m1.flags() & BRIDGE) != (m2.flags() & BRIDGE)) return ((m1.flags() & BRIDGE) != 0) ? m2 : m1; // if one overrides or hides the other, use it TypeSymbol m1Owner = (TypeSymbol)m1.owner; TypeSymbol m2Owner = (TypeSymbol)m2.owner; if (types.asSuper(m1Owner.type, m2Owner) != null && ((m1.owner.flags_field & INTERFACE) == 0 || (m2.owner.flags_field & INTERFACE) != 0) && m1.overrides(m2, m1Owner, types, false)) return m1; if (types.asSuper(m2Owner.type, m1Owner) != null && ((m2.owner.flags_field & INTERFACE) == 0 || (m1.owner.flags_field & INTERFACE) != 0) && m2.overrides(m1, m2Owner, types, false)) return m2; boolean m1Abstract = (m1.flags() & ABSTRACT) != 0; boolean m2Abstract = (m2.flags() & ABSTRACT) != 0; if (m1Abstract && !m2Abstract) return m2; if (m2Abstract && !m1Abstract) return m1; // both abstract or both concrete return ambiguityError(m1, m2); } if (m1SignatureMoreSpecific) return m1; if (m2SignatureMoreSpecific) return m2; return ambiguityError(m1, m2); case AMBIGUOUS: //compare m1 to ambiguous methods in m2 AmbiguityError e = (AmbiguityError)m2.baseSymbol(); boolean m1MoreSpecificThanAnyAmbiguous = true; boolean allAmbiguousMoreSpecificThanM1 = true; for (Symbol s : e.ambiguousSyms) { Symbol moreSpecific = mostSpecific(argtypes, m1, s, env, site, useVarargs); m1MoreSpecificThanAnyAmbiguous &= moreSpecific == m1; allAmbiguousMoreSpecificThanM1 &= moreSpecific == s; } if (m1MoreSpecificThanAnyAmbiguous) return m1; //if m1 is more specific than some ambiguous methods, but other ambiguous methods are //more specific than m1, add it as a new ambiguous method: if (!allAmbiguousMoreSpecificThanM1) e.addAmbiguousSymbol(m1); return e; default: throw new AssertionError(); } } //where private boolean signatureMoreSpecific(List actuals, Env env, Type site, Symbol m1, Symbol m2, boolean useVarargs) { noteWarner.clear(); int maxLength = Math.max( Math.max(m1.type.getParameterTypes().length(), actuals.length()), m2.type.getParameterTypes().length()); MethodResolutionContext prevResolutionContext = currentResolutionContext; try { currentResolutionContext = new MethodResolutionContext(); currentResolutionContext.step = prevResolutionContext.step; currentResolutionContext.methodCheck = prevResolutionContext.methodCheck.mostSpecificCheck(actuals); Type mst = instantiate(env, site, m2, null, adjustArgs(types.cvarLowerBounds(types.memberType(site, m1).getParameterTypes()), m1, maxLength, useVarargs), null, false, useVarargs, noteWarner); return mst != null && !noteWarner.hasLint(Lint.LintCategory.UNCHECKED); } finally { currentResolutionContext = prevResolutionContext; } } 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; } } //where Symbol ambiguityError(Symbol m1, Symbol m2) { if (((m1.flags() | m2.flags()) & CLASH) != 0) { return (m1.flags() & CLASH) == 0 ? m1 : m2; } else { return new AmbiguityError(m1, m2); } } Symbol findMethodInScope(Env env, Type site, Name name, List argtypes, List typeargtypes, Scope sc, Symbol bestSoFar, boolean allowBoxing, boolean useVarargs, boolean abstractok) { for (Symbol s : sc.getSymbolsByName(name, new LookupFilter(abstractok))) { bestSoFar = selectBest(env, site, argtypes, typeargtypes, s, bestSoFar, allowBoxing, useVarargs); } return bestSoFar; } //where class LookupFilter implements Filter { boolean abstractOk; LookupFilter(boolean abstractOk) { this.abstractOk = abstractOk; } public boolean accepts(Symbol s) { long flags = s.flags(); return s.kind == MTH && (flags & SYNTHETIC) == 0 && (abstractOk || (flags & DEFAULT) != 0 || (flags & ABSTRACT) == 0); } } /** Find best qualified method matching given name, type and value * arguments. * @param env The current environment. * @param site The original type from where the selection * takes place. * @param name The method's name. * @param argtypes The method's value arguments. * @param typeargtypes The method's type arguments * @param allowBoxing Allow boxing conversions of arguments. * @param useVarargs Box trailing arguments into an array for varargs. */ Symbol findMethod(Env env, Type site, Name name, List argtypes, List typeargtypes, boolean allowBoxing, boolean useVarargs) { Symbol bestSoFar = methodNotFound; bestSoFar = findMethod(env, site, name, argtypes, typeargtypes, site.tsym.type, bestSoFar, allowBoxing, useVarargs); return bestSoFar; } // where private Symbol findMethod(Env env, Type site, Name name, List argtypes, List typeargtypes, Type intype, Symbol bestSoFar, boolean allowBoxing, boolean useVarargs) { @SuppressWarnings({"unchecked","rawtypes"}) List[] itypes = (List[])new List[] { List.nil(), List.nil() }; InterfaceLookupPhase iphase = InterfaceLookupPhase.ABSTRACT_OK; for (TypeSymbol s : superclasses(intype)) { bestSoFar = findMethodInScope(env, site, name, argtypes, typeargtypes, s.members(), bestSoFar, allowBoxing, useVarargs, true); if (name == names.init) return bestSoFar; iphase = (iphase == null) ? null : iphase.update(s, this); if (iphase != null) { for (Type itype : types.interfaces(s.type)) { itypes[iphase.ordinal()] = types.union(types.closure(itype), itypes[iphase.ordinal()]); } } } Symbol concrete = bestSoFar.kind.isValid() && (bestSoFar.flags() & ABSTRACT) == 0 ? bestSoFar : methodNotFound; for (InterfaceLookupPhase iphase2 : InterfaceLookupPhase.values()) { //keep searching for abstract methods for (Type itype : itypes[iphase2.ordinal()]) { if (!itype.isInterface()) continue; //skip j.l.Object (included by Types.closure()) if (iphase2 == InterfaceLookupPhase.DEFAULT_OK && (itype.tsym.flags() & DEFAULT) == 0) continue; bestSoFar = findMethodInScope(env, site, name, argtypes, typeargtypes, itype.tsym.members(), bestSoFar, allowBoxing, useVarargs, true); if (concrete != bestSoFar && concrete.kind.isValid() && bestSoFar.kind.isValid() && types.isSubSignature(concrete.type, bestSoFar.type)) { //this is an hack - as javac does not do full membership checks //most specific ends up comparing abstract methods that might have //been implemented by some concrete method in a subclass and, //because of raw override, it is possible for an abstract method //to be more specific than the concrete method - so we need //to explicitly call that out (see CR 6178365) bestSoFar = concrete; } } } return bestSoFar; } enum InterfaceLookupPhase { ABSTRACT_OK() { @Override InterfaceLookupPhase update(Symbol s, Resolve rs) { //We should not look for abstract methods if receiver is a concrete class //(as concrete classes are expected to implement all abstracts coming //from superinterfaces) if ((s.flags() & (ABSTRACT | INTERFACE | ENUM)) != 0) { return this; } else { return DEFAULT_OK; } } }, DEFAULT_OK() { @Override InterfaceLookupPhase update(Symbol s, Resolve rs) { return this; } }; abstract InterfaceLookupPhase update(Symbol s, Resolve rs); } /** * Return an Iterable object to scan the superclasses of a given type. * It's crucial that the scan is done lazily, as we don't want to accidentally * access more supertypes than strictly needed (as this could trigger completion * errors if some of the not-needed supertypes are missing/ill-formed). */ Iterable superclasses(final Type intype) { return () -> new Iterator() { List seen = List.nil(); TypeSymbol currentSym = symbolFor(intype); TypeSymbol prevSym = null; public boolean hasNext() { if (currentSym == syms.noSymbol) { currentSym = symbolFor(types.supertype(prevSym.type)); } return currentSym != null; } public TypeSymbol next() { prevSym = currentSym; currentSym = syms.noSymbol; Assert.check(prevSym != null || prevSym != syms.noSymbol); return prevSym; } public void remove() { throw new UnsupportedOperationException(); } TypeSymbol symbolFor(Type t) { if (!t.hasTag(CLASS) && !t.hasTag(TYPEVAR)) { return null; } t = types.skipTypeVars(t, false); if (seen.contains(t.tsym)) { //degenerate case in which we have a circular //class hierarchy - because of ill-formed classfiles return null; } seen = seen.prepend(t.tsym); return t.tsym; } }; } /** Find unqualified method matching given name, type and value arguments. * @param env The current environment. * @param name The method's name. * @param argtypes The method's value arguments. * @param typeargtypes The method's type arguments. * @param allowBoxing Allow boxing conversions of arguments. * @param useVarargs Box trailing arguments into an array for varargs. */ Symbol findFun(Env env, Name name, List argtypes, List typeargtypes, boolean allowBoxing, boolean useVarargs) { Symbol bestSoFar = methodNotFound; Env env1 = env; boolean staticOnly = false; while (env1.outer != null) { if (isStatic(env1)) staticOnly = true; Assert.check(env1.info.preferredTreeForDiagnostics == null); env1.info.preferredTreeForDiagnostics = env.tree; try { Symbol sym = findMethod( env1, env1.enclClass.sym.type, name, argtypes, typeargtypes, allowBoxing, useVarargs); if (sym.exists()) { if (staticOnly && sym.kind == MTH && sym.owner.kind == TYP && (sym.flags() & STATIC) == 0) return new StaticError(sym); else return sym; } else { bestSoFar = bestOf(bestSoFar, sym); } } finally { env1.info.preferredTreeForDiagnostics = null; } if ((env1.enclClass.sym.flags() & STATIC) != 0) staticOnly = true; env1 = env1.outer; } Symbol sym = findMethod(env, syms.predefClass.type, name, argtypes, typeargtypes, allowBoxing, useVarargs); if (sym.exists()) return sym; for (Symbol currentSym : env.toplevel.namedImportScope.getSymbolsByName(name)) { Symbol origin = env.toplevel.namedImportScope.getOrigin(currentSym).owner; if (currentSym.kind == MTH) { if (currentSym.owner.type != origin.type) currentSym = currentSym.clone(origin); if (!isAccessible(env, origin.type, currentSym)) currentSym = new AccessError(env, origin.type, currentSym); bestSoFar = selectBest(env, origin.type, argtypes, typeargtypes, currentSym, bestSoFar, allowBoxing, useVarargs); } } if (bestSoFar.exists()) return bestSoFar; for (Symbol currentSym : env.toplevel.starImportScope.getSymbolsByName(name)) { Symbol origin = env.toplevel.starImportScope.getOrigin(currentSym).owner; if (currentSym.kind == MTH) { if (currentSym.owner.type != origin.type) currentSym = currentSym.clone(origin); if (!isAccessible(env, origin.type, currentSym)) currentSym = new AccessError(env, origin.type, currentSym); bestSoFar = selectBest(env, origin.type, argtypes, typeargtypes, currentSym, bestSoFar, allowBoxing, useVarargs); } } return bestSoFar; } /** Load toplevel or member class with given fully qualified name and * verify that it is accessible. * @param env The current environment. * @param name The fully qualified name of the class to be loaded. */ Symbol loadClass(Env env, Name name, RecoveryLoadClass recoveryLoadClass) { try { ClassSymbol c = finder.loadClass(env.toplevel.modle, name); return isAccessible(env, c) ? c : new AccessError(env, null, c); } catch (ClassFinder.BadClassFile err) { throw err; } catch (CompletionFailure ex) { Symbol candidate = recoveryLoadClass.loadClass(env, name); if (candidate != null) { return candidate; } return typeNotFound; } } public interface RecoveryLoadClass { Symbol loadClass(Env env, Name name); } private final RecoveryLoadClass noRecovery = (env, name) -> null; private final RecoveryLoadClass doRecoveryLoadClass = new RecoveryLoadClass() { @Override public Symbol loadClass(Env env, Name name) { List candidates = Convert.classCandidates(name); return lookupInvisibleSymbol(env, name, n -> () -> createCompoundIterator(candidates, c -> syms.getClassesForName(c) .iterator()), (ms, n) -> { for (Name candidate : candidates) { try { return finder.loadClass(ms, candidate); } catch (CompletionFailure cf) { //ignore } } return null; }, sym -> sym.kind == Kind.TYP, false, typeNotFound); } }; private final RecoveryLoadClass namedImportScopeRecovery = (env, name) -> { Scope importScope = env.toplevel.namedImportScope; Symbol existing = importScope.findFirst(Convert.shortName(name), sym -> sym.kind == TYP && sym.flatName() == name); if (existing != null) { return new InvisibleSymbolError(env, true, existing); } return null; }; private final RecoveryLoadClass starImportScopeRecovery = (env, name) -> { Scope importScope = env.toplevel.starImportScope; Symbol existing = importScope.findFirst(Convert.shortName(name), sym -> sym.kind == TYP && sym.flatName() == name); if (existing != null) { try { existing = finder.loadClass(existing.packge().modle, name); return new InvisibleSymbolError(env, true, existing); } catch (CompletionFailure cf) { //ignore } } return null; }; Symbol lookupPackage(Env env, Name name) { PackageSymbol pack = syms.lookupPackage(env.toplevel.modle, name); if (allowModules && isImportOnDemand(env, name)) { pack.complete(); if (!pack.exists()) { Name nameAndDot = name.append('.', names.empty); boolean prefixOfKnown = env.toplevel.modle.visiblePackages.values() .stream() .anyMatch(p -> p.fullname.startsWith(nameAndDot)); return lookupInvisibleSymbol(env, name, syms::getPackagesForName, syms::enterPackage, sym -> { sym.complete(); return sym.exists(); }, prefixOfKnown, pack); } } return pack; } private boolean isImportOnDemand(Env env, Name name) { if (!env.tree.hasTag(IMPORT)) return false; JCTree qualid = ((JCImport) env.tree).qualid; if (!qualid.hasTag(SELECT)) return false; if (TreeInfo.name(qualid) != names.asterisk) return false; return TreeInfo.fullName(((JCFieldAccess) qualid).selected) == name; } private Symbol lookupInvisibleSymbol(Env env, Name name, Function> get, BiFunction load, Predicate validate, boolean suppressError, Symbol defaultResult) { //even if a class/package cannot be found in the current module and among packages in modules //it depends on that are exported for any or this module, the class/package may exist internally //in some of these modules, or may exist in a module on which this module does not depend. //Provide better diagnostic in such cases by looking for the class in any module: Iterable candidates = get.apply(name); for (S sym : candidates) { if (validate.test(sym)) return createInvisibleSymbolError(env, suppressError, sym); } Set recoverableModules = new HashSet<>(syms.getAllModules()); recoverableModules.remove(env.toplevel.modle); for (ModuleSymbol ms : recoverableModules) { //avoid overly eager completing classes from source-based modules, as those //may not be completable with the current compiler settings: if (ms.sourceLocation == null) { if (ms.classLocation == null) { ms = moduleFinder.findModule(ms); } if (ms.kind != ERR) { S sym = load.apply(ms, name); if (sym != null && validate.test(sym)) { return createInvisibleSymbolError(env, suppressError, sym); } } } } return defaultResult; } private Symbol createInvisibleSymbolError(Env env, boolean suppressError, Symbol sym) { if (symbolPackageVisible(env, sym)) { return new AccessError(env, null, sym); } else { return new InvisibleSymbolError(env, suppressError, sym); } } private boolean symbolPackageVisible(Env env, Symbol sym) { ModuleSymbol envMod = env.toplevel.modle; PackageSymbol symPack = sym.packge(); return envMod == symPack.modle || envMod.visiblePackages.containsKey(symPack.fullname); } /** * Find a type declared in a scope (not inherited). Return null * if none is found. * @param env The current environment. * @param site The original type from where the selection takes * place. * @param name The type's name. * @param c The class to search for the member type. This is * always a superclass or implemented interface of * site's class. */ Symbol findImmediateMemberType(Env env, Type site, Name name, TypeSymbol c) { for (Symbol sym : c.members().getSymbolsByName(name)) { if (sym.kind == TYP) { return isAccessible(env, site, sym) ? sym : new AccessError(env, site, sym); } } return typeNotFound; } /** Find a member type inherited from a superclass or interface. * @param env The current environment. * @param site The original type from where the selection takes * place. * @param name The type's name. * @param c The class to search for the member type. This is * always a superclass or implemented interface of * site's class. */ Symbol findInheritedMemberType(Env env, Type site, Name name, TypeSymbol c) { Symbol bestSoFar = typeNotFound; Symbol sym; Type st = types.supertype(c.type); if (st != null && st.hasTag(CLASS)) { sym = findMemberType(env, site, name, st.tsym); bestSoFar = bestOf(bestSoFar, sym); } for (List l = types.interfaces(c.type); bestSoFar.kind != AMBIGUOUS && l.nonEmpty(); l = l.tail) { sym = findMemberType(env, site, name, l.head.tsym); if (!bestSoFar.kind.isResolutionError() && !sym.kind.isResolutionError() && sym.owner != bestSoFar.owner) bestSoFar = new AmbiguityError(bestSoFar, sym); else bestSoFar = bestOf(bestSoFar, sym); } return bestSoFar; } /** Find qualified member type. * @param env The current environment. * @param site The original type from where the selection takes * place. * @param name The type's name. * @param c The class to search for the member type. This is * always a superclass or implemented interface of * site's class. */ Symbol findMemberType(Env env, Type site, Name name, TypeSymbol c) { Symbol sym = findImmediateMemberType(env, site, name, c); if (sym != typeNotFound) return sym; return findInheritedMemberType(env, site, name, c); } /** Find a global type in given scope and load corresponding class. * @param env The current environment. * @param scope The scope in which to look for the type. * @param name The type's name. */ Symbol findGlobalType(Env env, Scope scope, Name name, RecoveryLoadClass recoveryLoadClass) { Symbol bestSoFar = typeNotFound; for (Symbol s : scope.getSymbolsByName(name)) { Symbol sym = loadClass(env, s.flatName(), recoveryLoadClass); if (bestSoFar.kind == TYP && sym.kind == TYP && bestSoFar != sym) return new AmbiguityError(bestSoFar, sym); else bestSoFar = bestOf(bestSoFar, sym); } return bestSoFar; } Symbol findTypeVar(Env env, Name name, boolean staticOnly) { for (Symbol sym : env.info.scope.getSymbolsByName(name)) { if (sym.kind == TYP) { if (staticOnly && sym.type.hasTag(TYPEVAR) && sym.owner.kind == TYP) return new StaticError(sym); return sym; } } return typeNotFound; } /** Find an unqualified type symbol. * @param env The current environment. * @param name The type's name. */ Symbol findType(Env env, Name name) { if (name == names.empty) return typeNotFound; // do not allow inadvertent "lookup" of anonymous types Symbol bestSoFar = typeNotFound; Symbol sym; boolean staticOnly = false; for (Env env1 = env; env1.outer != null; env1 = env1.outer) { if (isStatic(env1)) staticOnly = true; // First, look for a type variable and the first member type final Symbol tyvar = findTypeVar(env1, name, staticOnly); sym = findImmediateMemberType(env1, env1.enclClass.sym.type, name, env1.enclClass.sym); // Return the type variable if we have it, and have no // immediate member, OR the type variable is for a method. if (tyvar != typeNotFound) { if (env.baseClause || sym == typeNotFound || (tyvar.kind == TYP && tyvar.exists() && tyvar.owner.kind == MTH)) { return tyvar; } } // If the environment is a class def, finish up, // otherwise, do the entire findMemberType if (sym == typeNotFound) sym = findInheritedMemberType(env1, env1.enclClass.sym.type, name, env1.enclClass.sym); if (staticOnly && sym.kind == TYP && sym.type.hasTag(CLASS) && sym.type.getEnclosingType().hasTag(CLASS) && env1.enclClass.sym.type.isParameterized() && sym.type.getEnclosingType().isParameterized()) return new StaticError(sym); else if (sym.exists()) return sym; else bestSoFar = bestOf(bestSoFar, sym); JCClassDecl encl = env1.baseClause ? (JCClassDecl)env1.tree : env1.enclClass; if ((encl.sym.flags() & STATIC) != 0) staticOnly = true; } if (!env.tree.hasTag(IMPORT)) { sym = findGlobalType(env, env.toplevel.namedImportScope, name, namedImportScopeRecovery); if (sym.exists()) return sym; else bestSoFar = bestOf(bestSoFar, sym); sym = findGlobalType(env, env.toplevel.packge.members(), name, noRecovery); if (sym.exists()) return sym; else bestSoFar = bestOf(bestSoFar, sym); sym = findGlobalType(env, env.toplevel.starImportScope, name, starImportScopeRecovery); if (sym.exists()) return sym; else bestSoFar = bestOf(bestSoFar, sym); } return bestSoFar; } /** Find an unqualified identifier which matches a specified kind set. * @param env The current environment. * @param name The identifier's name. * @param kind Indicates the possible symbol kinds * (a subset of VAL, TYP, PCK). */ Symbol findIdent(Env env, Name name, KindSelector kind) { Symbol bestSoFar = typeNotFound; Symbol sym; if (kind.contains(KindSelector.VAL)) { sym = findVar(env, name); if (sym.exists()) return sym; else bestSoFar = bestOf(bestSoFar, sym); } if (kind.contains(KindSelector.TYP)) { sym = findType(env, name); if (sym.exists()) return sym; else bestSoFar = bestOf(bestSoFar, sym); } if (kind.contains(KindSelector.PCK)) return lookupPackage(env, name); else return bestSoFar; } /** Find an identifier in a package which matches a specified kind set. * @param env The current environment. * @param name The identifier's name. * @param kind Indicates the possible symbol kinds * (a nonempty subset of TYP, PCK). */ Symbol findIdentInPackage(Env env, TypeSymbol pck, Name name, KindSelector kind) { Name fullname = TypeSymbol.formFullName(name, pck); Symbol bestSoFar = typeNotFound; if (kind.contains(KindSelector.TYP)) { RecoveryLoadClass recoveryLoadClass = allowModules && !kind.contains(KindSelector.PCK) && !pck.exists() && !env.info.isSpeculative ? doRecoveryLoadClass : noRecovery; Symbol sym = loadClass(env, fullname, recoveryLoadClass); if (sym.exists()) { // don't allow programs to use flatnames if (name == sym.name) return sym; } else bestSoFar = bestOf(bestSoFar, sym); } if (kind.contains(KindSelector.PCK)) { return lookupPackage(env, fullname); } return bestSoFar; } /** Find an identifier among the members of a given type `site'. * @param env The current environment. * @param site The type containing the symbol to be found. * @param name The identifier's name. * @param kind Indicates the possible symbol kinds * (a subset of VAL, TYP). */ Symbol findIdentInType(Env env, Type site, Name name, KindSelector kind) { Symbol bestSoFar = typeNotFound; Symbol sym; if (kind.contains(KindSelector.VAL)) { sym = findField(env, site, name, site.tsym); if (sym.exists()) return sym; else bestSoFar = bestOf(bestSoFar, sym); } if (kind.contains(KindSelector.TYP)) { sym = findMemberType(env, site, name, site.tsym); if (sym.exists()) return sym; else bestSoFar = bestOf(bestSoFar, sym); } return bestSoFar; } /* *************************************************************************** * Access checking * The following methods convert ResolveErrors to ErrorSymbols, issuing * an error message in the process ****************************************************************************/ /** If `sym' is a bad symbol: report error and return errSymbol * else pass through unchanged, * additional arguments duplicate what has been used in trying to find the * symbol {@literal (--> flyweight pattern)}. This improves performance since we * expect misses to happen frequently. * * @param sym The symbol that was found, or a ResolveError. * @param pos The position to use for error reporting. * @param location The symbol the served as a context for this lookup * @param site The original type from where the selection took place. * @param name The symbol's name. * @param qualified Did we get here through a qualified expression resolution? * @param argtypes The invocation's value arguments, * if we looked for a method. * @param typeargtypes The invocation's type arguments, * if we looked for a method. * @param logResolveHelper helper class used to log resolve errors */ Symbol accessInternal(Symbol sym, DiagnosticPosition pos, Symbol location, Type site, Name name, boolean qualified, List argtypes, List typeargtypes, LogResolveHelper logResolveHelper) { if (sym.kind.isResolutionError()) { ResolveError errSym = (ResolveError)sym.baseSymbol(); sym = errSym.access(name, qualified ? site.tsym : syms.noSymbol); argtypes = logResolveHelper.getArgumentTypes(errSym, sym, name, argtypes); if (logResolveHelper.resolveDiagnosticNeeded(site, argtypes, typeargtypes)) { logResolveError(errSym, pos, location, site, name, argtypes, typeargtypes); } } return sym; } /** * Variant of the generalized access routine, to be used for generating method * resolution diagnostics */ Symbol accessMethod(Symbol sym, DiagnosticPosition pos, Symbol location, Type site, Name name, boolean qualified, List argtypes, List typeargtypes) { return accessInternal(sym, pos, location, site, name, qualified, argtypes, typeargtypes, methodLogResolveHelper); } /** Same as original accessMethod(), but without location. */ Symbol accessMethod(Symbol sym, DiagnosticPosition pos, Type site, Name name, boolean qualified, List argtypes, List typeargtypes) { return accessMethod(sym, pos, site.tsym, site, name, qualified, argtypes, typeargtypes); } /** * Variant of the generalized access routine, to be used for generating variable, * type resolution diagnostics */ Symbol accessBase(Symbol sym, DiagnosticPosition pos, Symbol location, Type site, Name name, boolean qualified) { return accessInternal(sym, pos, location, site, name, qualified, List.nil(), null, basicLogResolveHelper); } /** Same as original accessBase(), but without location. */ Symbol accessBase(Symbol sym, DiagnosticPosition pos, Type site, Name name, boolean qualified) { return accessBase(sym, pos, site.tsym, site, name, qualified); } interface LogResolveHelper { boolean resolveDiagnosticNeeded(Type site, List argtypes, List typeargtypes); List getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List argtypes); } LogResolveHelper basicLogResolveHelper = new LogResolveHelper() { public boolean resolveDiagnosticNeeded(Type site, List argtypes, List typeargtypes) { return !site.isErroneous(); } public List getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List argtypes) { return argtypes; } }; LogResolveHelper methodLogResolveHelper = new LogResolveHelper() { public boolean resolveDiagnosticNeeded(Type site, List argtypes, List typeargtypes) { return !site.isErroneous() && !Type.isErroneous(argtypes) && (typeargtypes == null || !Type.isErroneous(typeargtypes)); } public List getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List argtypes) { return argtypes.map(new ResolveDeferredRecoveryMap(AttrMode.SPECULATIVE, accessedSym, currentResolutionContext.step)); } }; class ResolveDeferredRecoveryMap extends DeferredAttr.RecoveryDeferredTypeMap { public ResolveDeferredRecoveryMap(AttrMode mode, Symbol msym, MethodResolutionPhase step) { deferredAttr.super(mode, msym, step); } @Override protected Type typeOf(DeferredType dt) { Type res = super.typeOf(dt); if (!res.isErroneous()) { switch (TreeInfo.skipParens(dt.tree).getTag()) { case LAMBDA: case REFERENCE: return dt; case CONDEXPR: return res == Type.recoveryType ? dt : res; } } return res; } } /** Check that sym is not an abstract method. */ void checkNonAbstract(DiagnosticPosition pos, Symbol sym) { if ((sym.flags() & ABSTRACT) != 0 && (sym.flags() & DEFAULT) == 0) log.error(pos, "abstract.cant.be.accessed.directly", kindName(sym), sym, sym.location()); } /* *************************************************************************** * Name resolution * Naming conventions are as for symbol lookup * Unlike the find... methods these methods will report access errors ****************************************************************************/ /** Resolve an unqualified (non-method) identifier. * @param pos The position to use for error reporting. * @param env The environment current at the identifier use. * @param name The identifier's name. * @param kind The set of admissible symbol kinds for the identifier. */ Symbol resolveIdent(DiagnosticPosition pos, Env env, Name name, KindSelector kind) { return accessBase( findIdent(env, name, kind), pos, env.enclClass.sym.type, name, false); } /** Resolve an unqualified method identifier. * @param pos The position to use for error reporting. * @param env The environment current at the method invocation. * @param name The identifier's name. * @param argtypes The types of the invocation's value arguments. * @param typeargtypes The types of the invocation's type arguments. */ Symbol resolveMethod(DiagnosticPosition pos, Env env, Name name, List argtypes, List typeargtypes) { return lookupMethod(env, pos, env.enclClass.sym, resolveMethodCheck, new BasicLookupHelper(name, env.enclClass.sym.type, argtypes, typeargtypes) { @Override Symbol doLookup(Env env, MethodResolutionPhase phase) { return findFun(env, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()); }}); } /** Resolve a qualified method identifier * @param pos The position to use for error reporting. * @param env The environment current at the method invocation. * @param site The type of the qualifying expression, in which * identifier is searched. * @param name The identifier's name. * @param argtypes The types of the invocation's value arguments. * @param typeargtypes The types of the invocation's type arguments. */ Symbol resolveQualifiedMethod(DiagnosticPosition pos, Env env, Type site, Name name, List argtypes, List typeargtypes) { return resolveQualifiedMethod(pos, env, site.tsym, site, name, argtypes, typeargtypes); } Symbol resolveQualifiedMethod(DiagnosticPosition pos, Env env, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { return resolveQualifiedMethod(new MethodResolutionContext(), pos, env, location, site, name, argtypes, typeargtypes); } private Symbol resolveQualifiedMethod(MethodResolutionContext resolveContext, DiagnosticPosition pos, Env env, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { return lookupMethod(env, pos, location, resolveContext, new BasicLookupHelper(name, site, argtypes, typeargtypes) { @Override Symbol doLookup(Env env, MethodResolutionPhase phase) { return findMethod(env, site, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()); } @Override Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) { if (sym.kind.isResolutionError()) { sym = super.access(env, pos, location, sym); } else if (allowMethodHandles) { MethodSymbol msym = (MethodSymbol)sym; if ((msym.flags() & SIGNATURE_POLYMORPHIC) != 0) { return findPolymorphicSignatureInstance(env, sym, argtypes); } } return sym; } }); } /** Find or create an implicit method of exactly the given type (after erasure). * Searches in a side table, not the main scope of the site. * This emulates the lookup process required by JSR 292 in JVM. * @param env Attribution environment * @param spMethod signature polymorphic method - i.e. MH.invokeExact * @param argtypes The required argument types */ Symbol findPolymorphicSignatureInstance(Env env, final Symbol spMethod, List argtypes) { Type mtype = infer.instantiatePolymorphicSignatureInstance(env, (MethodSymbol)spMethod, currentResolutionContext, argtypes); for (Symbol sym : polymorphicSignatureScope.getSymbolsByName(spMethod.name)) { // Check that there is already a method symbol for the method // type and owner if (types.isSameType(mtype, sym.type) && spMethod.owner == sym.owner) { return sym; } } // Create the desired method // Retain static modifier is to support invocations to // MethodHandle.linkTo* methods long flags = ABSTRACT | HYPOTHETICAL | spMethod.flags() & (Flags.AccessFlags | Flags.STATIC); Symbol msym = new MethodSymbol(flags, spMethod.name, mtype, spMethod.owner) { @Override public Symbol baseSymbol() { return spMethod; } }; if (!mtype.isErroneous()) { // Cache only if kosher. polymorphicSignatureScope.enter(msym); } return msym; } /** Resolve a qualified method identifier, throw a fatal error if not * found. * @param pos The position to use for error reporting. * @param env The environment current at the method invocation. * @param site The type of the qualifying expression, in which * identifier is searched. * @param name The identifier's name. * @param argtypes The types of the invocation's value arguments. * @param typeargtypes The types of the invocation's type arguments. */ public MethodSymbol resolveInternalMethod(DiagnosticPosition pos, Env env, Type site, Name name, List argtypes, List typeargtypes) { MethodResolutionContext resolveContext = new MethodResolutionContext(); resolveContext.internalResolution = true; Symbol sym = resolveQualifiedMethod(resolveContext, pos, env, site.tsym, site, name, argtypes, typeargtypes); if (sym.kind == MTH) return (MethodSymbol)sym; else throw new FatalError( diags.fragment("fatal.err.cant.locate.meth", name)); } /** Resolve constructor. * @param pos The position to use for error reporting. * @param env The environment current at the constructor invocation. * @param site The type of class for which a constructor is searched. * @param argtypes The types of the constructor invocation's value * arguments. * @param typeargtypes The types of the constructor invocation's type * arguments. */ Symbol resolveConstructor(DiagnosticPosition pos, Env env, Type site, List argtypes, List typeargtypes) { return resolveConstructor(new MethodResolutionContext(), pos, env, site, argtypes, typeargtypes); } private Symbol resolveConstructor(MethodResolutionContext resolveContext, final DiagnosticPosition pos, Env env, Type site, List argtypes, List typeargtypes) { return lookupMethod(env, pos, site.tsym, resolveContext, new BasicLookupHelper(names.init, site, argtypes, typeargtypes) { @Override Symbol doLookup(Env env, MethodResolutionPhase phase) { return findConstructor(pos, env, site, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()); } }); } /** Resolve a constructor, throw a fatal error if not found. * @param pos The position to use for error reporting. * @param env The environment current at the method invocation. * @param site The type to be constructed. * @param argtypes The types of the invocation's value arguments. * @param typeargtypes The types of the invocation's type arguments. */ public MethodSymbol resolveInternalConstructor(DiagnosticPosition pos, Env env, Type site, List argtypes, List typeargtypes) { MethodResolutionContext resolveContext = new MethodResolutionContext(); resolveContext.internalResolution = true; Symbol sym = resolveConstructor(resolveContext, pos, env, site, argtypes, typeargtypes); if (sym.kind == MTH) return (MethodSymbol)sym; else throw new FatalError( diags.fragment("fatal.err.cant.locate.ctor", site)); } Symbol findConstructor(DiagnosticPosition pos, Env env, Type site, List argtypes, List typeargtypes, boolean allowBoxing, boolean useVarargs) { Symbol sym = findMethod(env, site, names.init, argtypes, typeargtypes, allowBoxing, useVarargs); chk.checkDeprecated(pos, env.info.scope.owner, sym); return sym; } /** Resolve constructor using diamond inference. * @param pos The position to use for error reporting. * @param env The environment current at the constructor invocation. * @param site The type of class for which a constructor is searched. * The scope of this class has been touched in attribution. * @param argtypes The types of the constructor invocation's value * arguments. * @param typeargtypes The types of the constructor invocation's type * arguments. */ Symbol resolveDiamond(DiagnosticPosition pos, Env env, Type site, List argtypes, List typeargtypes) { return lookupMethod(env, pos, site.tsym, resolveMethodCheck, new BasicLookupHelper(names.init, site, argtypes, typeargtypes) { @Override Symbol doLookup(Env env, MethodResolutionPhase phase) { return findDiamond(env, site, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()); } @Override Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) { if (sym.kind.isResolutionError()) { if (sym.kind != WRONG_MTH && sym.kind != WRONG_MTHS) { sym = super.access(env, pos, location, sym); } else { final JCDiagnostic details = sym.kind == WRONG_MTH ? ((InapplicableSymbolError)sym.baseSymbol()).errCandidate().snd : null; sym = new DiamondError(sym, currentResolutionContext); sym = accessMethod(sym, pos, site, names.init, true, argtypes, typeargtypes); env.info.pendingResolutionPhase = currentResolutionContext.step; } } return sym; }}); } /** This method scans all the constructor symbol in a given class scope - * assuming that the original scope contains a constructor of the kind: * {@code Foo(X x, Y y)}, where X,Y are class type-variables declared in Foo, * a method check is executed against the modified constructor type: * {@code Foo(X x, Y y)}. This is crucial in order to enable diamond * inference. The inferred return type of the synthetic constructor IS * the inferred type for the diamond operator. */ private Symbol findDiamond(Env env, Type site, List argtypes, List typeargtypes, boolean allowBoxing, boolean useVarargs) { Symbol bestSoFar = methodNotFound; TypeSymbol tsym = site.tsym.isInterface() ? syms.objectType.tsym : site.tsym; for (final Symbol sym : tsym.members().getSymbolsByName(names.init)) { //- System.out.println(" e " + e.sym); if (sym.kind == MTH && (sym.flags_field & SYNTHETIC) == 0) { List oldParams = sym.type.hasTag(FORALL) ? ((ForAll)sym.type).tvars : List.nil(); Type constrType = new ForAll(site.tsym.type.getTypeArguments().appendList(oldParams), types.createMethodTypeWithReturn(sym.type.asMethodType(), site)); MethodSymbol newConstr = new MethodSymbol(sym.flags(), names.init, constrType, site.tsym) { @Override public Symbol baseSymbol() { return sym; } }; bestSoFar = selectBest(env, site, argtypes, typeargtypes, newConstr, bestSoFar, allowBoxing, useVarargs); } } return bestSoFar; } Symbol getMemberReference(DiagnosticPosition pos, Env env, JCMemberReference referenceTree, Type site, Name name) { site = types.capture(site); ReferenceLookupHelper lookupHelper = makeReferenceLookupHelper( referenceTree, site, name, List.nil(), null, VARARITY); Env newEnv = env.dup(env.tree, env.info.dup()); Symbol sym = lookupMethod(newEnv, env.tree.pos(), site.tsym, nilMethodCheck, lookupHelper); env.info.pendingResolutionPhase = newEnv.info.pendingResolutionPhase; return sym; } ReferenceLookupHelper makeReferenceLookupHelper(JCMemberReference referenceTree, Type site, Name name, List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) { if (!name.equals(names.init)) { //method reference return new MethodReferenceLookupHelper(referenceTree, name, site, argtypes, typeargtypes, maxPhase); } else if (site.hasTag(ARRAY)) { //array constructor reference return new ArrayConstructorReferenceLookupHelper(referenceTree, site, argtypes, typeargtypes, maxPhase); } else { //class constructor reference return new ConstructorReferenceLookupHelper(referenceTree, site, argtypes, typeargtypes, maxPhase); } } /** * 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, the process of picking * the resulting symbol is delegated to an helper class {@link com.sun.tools.javac.comp.Resolve.ReferenceChooser}. * * 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(Env env, JCMemberReference referenceTree, Type site, Name name, List argtypes, List typeargtypes, MethodCheck methodCheck, InferenceContext inferenceContext, ReferenceChooser referenceChooser) { //step 1 - bound lookup ReferenceLookupHelper boundLookupHelper = makeReferenceLookupHelper( referenceTree, site, name, argtypes, typeargtypes, VARARITY); Env boundEnv = env.dup(env.tree, env.info.dup()); MethodResolutionContext boundSearchResolveContext = new MethodResolutionContext(); boundSearchResolveContext.methodCheck = methodCheck; Symbol boundSym = lookupMethod(boundEnv, env.tree.pos(), site.tsym, boundSearchResolveContext, boundLookupHelper); ReferenceLookupResult boundRes = new ReferenceLookupResult(boundSym, boundSearchResolveContext); //step 2 - unbound lookup Symbol unboundSym = methodNotFound; Env unboundEnv = env.dup(env.tree, env.info.dup()); ReferenceLookupHelper unboundLookupHelper = boundLookupHelper.unboundLookup(inferenceContext); ReferenceLookupResult unboundRes = referenceNotFound; if (unboundLookupHelper != null) { MethodResolutionContext unboundSearchResolveContext = new MethodResolutionContext(); unboundSearchResolveContext.methodCheck = methodCheck; unboundSym = lookupMethod(unboundEnv, env.tree.pos(), site.tsym, unboundSearchResolveContext, unboundLookupHelper); unboundRes = new ReferenceLookupResult(unboundSym, unboundSearchResolveContext); } //merge results Pair res; Symbol bestSym = referenceChooser.result(boundRes, unboundRes); res = new Pair<>(bestSym, bestSym == unboundSym ? unboundLookupHelper : boundLookupHelper); env.info.pendingResolutionPhase = bestSym == unboundSym ? unboundEnv.info.pendingResolutionPhase : boundEnv.info.pendingResolutionPhase; return res; } /** * This class is used to represent a method reference lookup result. It keeps track of two * things: (i) the symbol found during a method reference lookup and (ii) the static kind * of the lookup (see {@link com.sun.tools.javac.comp.Resolve.ReferenceLookupResult.StaticKind}). */ static class ReferenceLookupResult { /** * Static kind associated with a method reference lookup. Erroneous lookups end up with * the UNDEFINED kind; successful lookups will end up with either STATIC, NON_STATIC, * depending on whether all applicable candidates are static or non-static methods, * respectively. If a successful lookup has both static and non-static applicable methods, * its kind is set to BOTH. */ enum StaticKind { STATIC, NON_STATIC, BOTH, UNDEFINED; /** * Retrieve the static kind associated with a given (method) symbol. */ static StaticKind from(Symbol s) { return s.isStatic() ? STATIC : NON_STATIC; } /** * Merge two static kinds together. */ static StaticKind reduce(StaticKind sk1, StaticKind sk2) { if (sk1 == UNDEFINED) { return sk2; } else if (sk2 == UNDEFINED) { return sk1; } else { return sk1 == sk2 ? sk1 : BOTH; } } } /** The static kind. */ StaticKind staticKind; /** The lookup result. */ Symbol sym; ReferenceLookupResult(Symbol sym, MethodResolutionContext resolutionContext) { this.staticKind = staticKind(sym, resolutionContext); this.sym = sym; } private StaticKind staticKind(Symbol sym, MethodResolutionContext resolutionContext) { switch (sym.kind) { case MTH: case AMBIGUOUS: return resolutionContext.candidates.stream() .filter(c -> c.isApplicable() && c.step == resolutionContext.step) .map(c -> StaticKind.from(c.sym)) .reduce(StaticKind::reduce) .orElse(StaticKind.UNDEFINED); default: return StaticKind.UNDEFINED; } } /** * Does this result corresponds to a successful lookup (i.e. one where a method has been found?) */ boolean isSuccess() { return staticKind != StaticKind.UNDEFINED; } /** * Does this result have given static kind? */ boolean hasKind(StaticKind sk) { return this.staticKind == sk; } /** * Error recovery helper: can this lookup result be ignored (for the purpose of returning * some 'better' result) ? */ boolean canIgnore() { switch (sym.kind) { case ABSENT_MTH: return true; case WRONG_MTH: InapplicableSymbolError errSym = (InapplicableSymbolError)sym.baseSymbol(); return new Template(MethodCheckDiag.ARITY_MISMATCH.regex()) .matches(errSym.errCandidate().snd); case WRONG_MTHS: InapplicableSymbolsError errSyms = (InapplicableSymbolsError)sym.baseSymbol(); return errSyms.filterCandidates(errSyms.mapCandidates()).isEmpty(); default: return false; } } } /** * This abstract class embodies the logic that converts one (bound lookup) or two (unbound lookup) * {@code ReferenceLookupResult} objects into a (@code Symbol), which is then regarded as the * result of method reference resolution. */ abstract class ReferenceChooser { /** * Generate a result from a pair of lookup result objects. This method delegates to the * appropriate result generation routine. */ Symbol result(ReferenceLookupResult boundRes, ReferenceLookupResult unboundRes) { return unboundRes != referenceNotFound ? unboundResult(boundRes, unboundRes) : boundResult(boundRes); } /** * Generate a symbol from a given bound lookup result. */ abstract Symbol boundResult(ReferenceLookupResult boundRes); /** * Generate a symbol from a pair of bound/unbound lookup results. */ abstract Symbol unboundResult(ReferenceLookupResult boundRes, ReferenceLookupResult unboundRes); } /** * This chooser implements the selection strategy used during a full lookup; this logic * is described in JLS SE 8 (15.3.2). */ ReferenceChooser basicReferenceChooser = new ReferenceChooser() { @Override Symbol boundResult(ReferenceLookupResult boundRes) { return !boundRes.isSuccess() || boundRes.hasKind(StaticKind.NON_STATIC) ? boundRes.sym : //the search produces a non-static method new BadMethodReferenceError(boundRes.sym, false); } @Override Symbol unboundResult(ReferenceLookupResult boundRes, ReferenceLookupResult unboundRes) { if (boundRes.hasKind(StaticKind.STATIC) && (!unboundRes.isSuccess() || unboundRes.hasKind(StaticKind.STATIC))) { //the first search produces a static method and no non-static method is applicable //during the second search return boundRes.sym; } else if (unboundRes.hasKind(StaticKind.NON_STATIC) && (!boundRes.isSuccess() || boundRes.hasKind(StaticKind.NON_STATIC))) { //the second search produces a non-static method and no static method is applicable //during the first search return unboundRes.sym; } else if (boundRes.isSuccess() && unboundRes.isSuccess()) { //both searches produce some result; ambiguity (error recovery) return ambiguityError(boundRes.sym, unboundRes.sym); } else if (boundRes.isSuccess() || unboundRes.isSuccess()) { //Both searches failed to produce a result with correct staticness (i.e. first search //produces an non-static method). Alternatively, a given search produced a result //with the right staticness, but the other search has applicable methods with wrong //staticness (error recovery) return new BadMethodReferenceError(boundRes.isSuccess() ? boundRes.sym : unboundRes.sym, true); } else { //both searches fail to produce a result - pick 'better' error using heuristics (error recovery) return (boundRes.canIgnore() && !unboundRes.canIgnore()) ? unboundRes.sym : boundRes.sym; } } }; /** * This chooser implements the selection strategy used during an arity-based lookup; this logic * is described in JLS SE 8 (15.12.2.1). */ ReferenceChooser structuralReferenceChooser = new ReferenceChooser() { @Override Symbol boundResult(ReferenceLookupResult boundRes) { return (!boundRes.isSuccess() || !boundRes.hasKind(StaticKind.STATIC)) ? boundRes.sym : //the search has at least one applicable non-static method new BadMethodReferenceError(boundRes.sym, false); } @Override Symbol unboundResult(ReferenceLookupResult boundRes, ReferenceLookupResult unboundRes) { if (boundRes.isSuccess() && !boundRes.hasKind(StaticKind.NON_STATIC)) { //the first serach has at least one applicable static method return boundRes.sym; } else if (unboundRes.isSuccess() && !unboundRes.hasKind(StaticKind.STATIC)) { //the second search has at least one applicable non-static method return unboundRes.sym; } else if (boundRes.isSuccess() || unboundRes.isSuccess()) { //either the first search produces a non-static method, or second search produces //a non-static method (error recovery) return new BadMethodReferenceError(boundRes.isSuccess() ? boundRes.sym : unboundRes.sym, true); } else { //both searches fail to produce a result - pick 'better' error using heuristics (error recovery) return (boundRes.canIgnore() && !unboundRes.canIgnore()) ? unboundRes.sym : boundRes.sym; } } }; /** * 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; /** Max overload resolution phase handled by this helper */ MethodResolutionPhase maxPhase; LookupHelper(Name name, Type site, List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) { this.name = name; this.site = site; this.argtypes = argtypes; this.typeargtypes = typeargtypes; this.maxPhase = maxPhase; } /** * Should lookup stop at given phase with given result */ final boolean shouldStop(Symbol sym, MethodResolutionPhase phase) { return phase.ordinal() > maxPhase.ordinal() || !sym.kind.isResolutionError() || sym.kind == AMBIGUOUS; } /** * 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); /** * Dump overload resolution info */ void debug(DiagnosticPosition pos, Symbol sym) { //do nothing } /** * Validate the result of the lookup */ abstract Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym); } abstract class BasicLookupHelper extends LookupHelper { BasicLookupHelper(Name name, Type site, List argtypes, List typeargtypes) { this(name, site, argtypes, typeargtypes, MethodResolutionPhase.VARARITY); } BasicLookupHelper(Name name, Type site, List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) { super(name, site, argtypes, typeargtypes, maxPhase); } @Override final Symbol lookup(Env env, MethodResolutionPhase phase) { Symbol sym = doLookup(env, phase); if (sym.kind == AMBIGUOUS) { AmbiguityError a_err = (AmbiguityError)sym.baseSymbol(); sym = a_err.mergeAbstracts(site); } return sym; } abstract Symbol doLookup(Env env, MethodResolutionPhase phase); @Override Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) { if (sym.kind.isResolutionError()) { //if nothing is found return the 'first' error sym = accessMethod(sym, pos, location, site, name, true, argtypes, typeargtypes); } return sym; } @Override void debug(DiagnosticPosition pos, Symbol sym) { reportVerboseResolutionDiagnostic(pos, name, site, argtypes, typeargtypes, sym); } } /** * 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; ReferenceLookupHelper(JCMemberReference referenceTree, Name name, Type site, List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) { super(name, site, argtypes, typeargtypes, maxPhase); this.referenceTree = referenceTree; } /** * Returns an unbound version of this lookup helper. By default, this * method returns an dummy lookup helper. */ ReferenceLookupHelper unboundLookup(InferenceContext inferenceContext) { return null; } /** * Get the kind of the member reference */ abstract JCMemberReference.ReferenceKind referenceKind(Symbol sym); Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) { if (sym.kind == AMBIGUOUS) { AmbiguityError a_err = (AmbiguityError)sym.baseSymbol(); sym = a_err.mergeAbstracts(site); } //skip error reporting 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 { /** The original method reference lookup site. */ Type originalSite; MethodReferenceLookupHelper(JCMemberReference referenceTree, Name name, Type site, List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) { super(referenceTree, name, types.skipTypeVars(site, true), argtypes, typeargtypes, maxPhase); this.originalSite = site; } @Override final Symbol lookup(Env env, MethodResolutionPhase phase) { return findMethod(env, site, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()); } @Override ReferenceLookupHelper unboundLookup(InferenceContext inferenceContext) { if (TreeInfo.isStaticSelector(referenceTree.expr, names)) { if (argtypes.nonEmpty() && (argtypes.head.hasTag(NONE) || types.isSubtypeUnchecked(inferenceContext.asUndetVar(argtypes.head), originalSite))) { return new UnboundMethodReferenceLookupHelper(referenceTree, name, originalSite, argtypes, typeargtypes, maxPhase); } else { return new ReferenceLookupHelper(referenceTree, name, site, argtypes, typeargtypes, maxPhase) { @Override ReferenceLookupHelper unboundLookup(InferenceContext inferenceContext) { return this; } @Override Symbol lookup(Env env, MethodResolutionPhase phase) { return methodNotFound; } @Override ReferenceKind referenceKind(Symbol sym) { Assert.error(); return null; } }; } } else { return super.unboundLookup(inferenceContext); } } @Override ReferenceKind referenceKind(Symbol sym) { if (sym.isStatic()) { return ReferenceKind.STATIC; } 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, MethodResolutionPhase maxPhase) { super(referenceTree, name, site, argtypes.tail, typeargtypes, maxPhase); if (site.isRaw() && !argtypes.head.hasTag(NONE)) { Type asSuperSite = types.asSuper(argtypes.head, site.tsym); this.site = types.skipTypeVars(asSuperSite, true); } } @Override ReferenceLookupHelper unboundLookup(InferenceContext inferenceContext) { return this; } @Override ReferenceKind referenceKind(Symbol sym) { return ReferenceKind.UNBOUND; } } /** * Helper class for array constructor lookup; an array constructor lookup * is simulated by looking up a method that returns the array type specified * as qualifier, and that accepts a single int parameter (size of the array). */ class ArrayConstructorReferenceLookupHelper extends ReferenceLookupHelper { ArrayConstructorReferenceLookupHelper(JCMemberReference referenceTree, Type site, List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) { super(referenceTree, names.init, site, argtypes, typeargtypes, maxPhase); } @Override protected Symbol lookup(Env env, MethodResolutionPhase phase) { WriteableScope sc = WriteableScope.create(syms.arrayClass); MethodSymbol arrayConstr = new MethodSymbol(PUBLIC, name, null, site.tsym); arrayConstr.type = new MethodType(List.of(syms.intType), site, List.nil(), syms.methodClass); sc.enter(arrayConstr); return findMethodInScope(env, site, name, argtypes, typeargtypes, sc, methodNotFound, phase.isBoxingRequired(), phase.isVarargsRequired(), false); } @Override ReferenceKind referenceKind(Symbol sym) { return ReferenceKind.ARRAY_CTOR; } } /** * 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, MethodResolutionPhase maxPhase) { super(referenceTree, names.init, site, argtypes, typeargtypes, maxPhase); if (site.isRaw()) { this.site = new ClassType(site.getEnclosingType(), site.tsym.type.getTypeArguments(), site.tsym, site.getMetadata()); needsInference = true; } } @Override protected Symbol lookup(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()); return enclosingInstanceMissing(env, site) ? new BadConstructorReferenceError(sym) : sym; } @Override ReferenceKind referenceKind(Symbol sym) { return site.getEnclosingType().hasTag(NONE) ? ReferenceKind.TOPLEVEL : ReferenceKind.IMPLICIT_INNER; } } /** * Main overload resolution routine. On each overload resolution step, a * lookup helper class is used to perform the method/constructor lookup; * at the end of the lookup, the helper is used to validate the results * (this last step might trigger overload resolution diagnostics). */ Symbol lookupMethod(Env env, DiagnosticPosition pos, Symbol location, MethodCheck methodCheck, LookupHelper lookupHelper) { MethodResolutionContext resolveContext = new MethodResolutionContext(); resolveContext.methodCheck = methodCheck; return lookupMethod(env, pos, location, resolveContext, lookupHelper); } Symbol lookupMethod(Env env, DiagnosticPosition pos, Symbol location, MethodResolutionContext resolveContext, LookupHelper lookupHelper) { MethodResolutionContext prevResolutionContext = currentResolutionContext; try { Symbol bestSoFar = methodNotFound; currentResolutionContext = resolveContext; for (MethodResolutionPhase phase : methodResolutionSteps) { if (lookupHelper.shouldStop(bestSoFar, phase)) break; MethodResolutionPhase prevPhase = currentResolutionContext.step; Symbol prevBest = bestSoFar; currentResolutionContext.step = phase; Symbol sym = lookupHelper.lookup(env, phase); lookupHelper.debug(pos, sym); bestSoFar = phase.mergeResults(bestSoFar, sym); env.info.pendingResolutionPhase = (prevBest == bestSoFar) ? prevPhase : phase; } return lookupHelper.access(env, pos, location, bestSoFar); } finally { currentResolutionContext = prevResolutionContext; } } /** * Resolve `c.name' where name == this or name == super. * @param pos The position to use for error reporting. * @param env The environment current at the expression. * @param c The qualifier. * @param name The identifier's name. */ Symbol resolveSelf(DiagnosticPosition pos, Env env, TypeSymbol c, Name name) { Env env1 = env; boolean staticOnly = false; while (env1.outer != null) { if (isStatic(env1)) staticOnly = true; if (env1.enclClass.sym == c) { Symbol sym = env1.info.scope.findFirst(name); if (sym != null) { if (staticOnly) sym = new StaticError(sym); return accessBase(sym, pos, env.enclClass.sym.type, name, true); } } if ((env1.enclClass.sym.flags() & STATIC) != 0) staticOnly = true; env1 = env1.outer; } if (c.isInterface() && name == names._super && !isStatic(env) && types.isDirectSuperInterface(c, env.enclClass.sym)) { //this might be a default super call if one of the superinterfaces is 'c' for (Type t : pruneInterfaces(env.enclClass.type)) { if (t.tsym == c) { env.info.defaultSuperCallSite = t; return new VarSymbol(0, names._super, types.asSuper(env.enclClass.type, c), env.enclClass.sym); } } //find a direct super type that is a subtype of 'c' for (Type i : types.directSupertypes(env.enclClass.type)) { if (i.tsym.isSubClass(c, types) && i.tsym != c) { log.error(pos, "illegal.default.super.call", c, diags.fragment("redundant.supertype", c, i)); return syms.errSymbol; } } Assert.error(); } log.error(pos, "not.encl.class", c); return syms.errSymbol; } //where private List pruneInterfaces(Type t) { ListBuffer result = new ListBuffer<>(); for (Type t1 : types.interfaces(t)) { boolean shouldAdd = true; for (Type t2 : types.directSupertypes(t)) { if (t1 != t2 && types.isSubtypeNoCapture(t2, t1)) { shouldAdd = false; } } if (shouldAdd) { result.append(t1); } } return result.toList(); } /** * Resolve `c.this' for an enclosing class c that contains the * named member. * @param pos The position to use for error reporting. * @param env The environment current at the expression. * @param member The member that must be contained in the result. */ Symbol resolveSelfContaining(DiagnosticPosition pos, 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 enclosingInstanceMissing(Env env, Type type) { if (type.hasTag(CLASS) && type.getEnclosingType().hasTag(CLASS)) { Symbol encl = resolveSelfContainingInternal(env, type.tsym, false); return encl == null || encl.kind.isResolutionError(); } return false; } private Symbol resolveSelfContainingInternal(Env env, Symbol member, boolean isSuperCall) { Name name = names._this; Env env1 = isSuperCall ? env.outer : env; boolean staticOnly = false; if (env1 != null) { while (env1 != null && env1.outer != null) { if (isStatic(env1)) staticOnly = true; if (env1.enclClass.sym.isSubClass(member.owner.enclClass(), types)) { Symbol sym = env1.info.scope.findFirst(name); if (sym != null) { if (staticOnly) sym = new StaticError(sym); return sym; } } if ((env1.enclClass.sym.flags() & STATIC) != 0) staticOnly = true; env1 = env1.outer; } } return null; } /** * Resolve an appropriate implicit this instance for t's container. * JLS 8.8.5.1 and 15.9.2 */ Type resolveImplicitThis(DiagnosticPosition pos, Env env, Type t) { return resolveImplicitThis(pos, env, t, false); } Type resolveImplicitThis(DiagnosticPosition pos, Env env, Type t, boolean isSuperCall) { Type thisType = (t.tsym.owner.kind.matches(KindSelector.VAL_MTH) ? resolveSelf(pos, env, t.getEnclosingType().tsym, names._this) : resolveSelfContaining(pos, env, t.tsym, isSuperCall)).type; if (env.info.isSelfCall && thisType.tsym == env.enclClass.sym) log.error(pos, "cant.ref.before.ctor.called", "this"); return thisType; } /* *************************************************************************** * ResolveError classes, indicating error situations when accessing symbols ****************************************************************************/ //used by TransTypes when checking target type of synthetic cast public void logAccessErrorInternal(Env env, JCTree tree, Type type) { AccessError error = new AccessError(env, env.enclClass.type, type.tsym); logResolveError(error, tree.pos(), env.enclClass.sym, env.enclClass.type, null, null, null); } //where private void logResolveError(ResolveError error, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { JCDiagnostic d = error.getDiagnostic(JCDiagnostic.DiagnosticType.ERROR, pos, location, site, name, argtypes, typeargtypes); if (d != null) { d.setFlag(DiagnosticFlag.RESOLVE_ERROR); log.report(d); } } private final LocalizedString noArgs = new LocalizedString("compiler.misc.no.args"); public Object methodArguments(List argtypes) { if (argtypes == null || argtypes.isEmpty()) { return noArgs; } else { ListBuffer diagArgs = new ListBuffer<>(); for (Type t : argtypes) { if (t.hasTag(DEFERRED)) { diagArgs.append(((DeferredAttr.DeferredType)t).tree); } else { diagArgs.append(t); } } return diagArgs; } } /** * Root class for resolution errors. Subclass of ResolveError * represent a different kinds of resolution error - as such they must * specify how they map into concrete compiler diagnostics. */ abstract class ResolveError extends Symbol { /** The name of the kind of error, for debugging only. */ final String debugName; ResolveError(Kind kind, String debugName) { super(kind, 0, null, null, null); this.debugName = debugName; } @Override @DefinedBy(Api.LANGUAGE_MODEL) public R accept(ElementVisitor v, P p) { throw new AssertionError(); } @Override public String toString() { return debugName; } @Override public boolean exists() { return false; } @Override public boolean isStatic() { return false; } /** * Create an external representation for this erroneous symbol to be * used during attribution - by default this returns the symbol of a * brand new error type which stores the original type found * during resolution. * * @param name the name used during resolution * @param location the location from which the symbol is accessed */ protected Symbol access(Name name, TypeSymbol location) { return types.createErrorType(name, location, syms.errSymbol.type).tsym; } /** * Create a diagnostic representing this resolution error. * * @param dkind The kind of the diagnostic to be created (e.g error). * @param pos The position to be used for error reporting. * @param site The original type from where the selection took place. * @param name The name of the symbol to be resolved. * @param argtypes The invocation's value arguments, * if we looked for a method. * @param typeargtypes The invocation's type arguments, * if we looked for a method. */ abstract JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes); } /** * This class is the root class of all resolution errors caused by * an invalid symbol being found during resolution. */ abstract class InvalidSymbolError extends ResolveError { /** The invalid symbol found during resolution */ Symbol sym; InvalidSymbolError(Kind kind, Symbol sym, String debugName) { super(kind, debugName); this.sym = sym; } @Override public boolean exists() { return true; } @Override public String toString() { return super.toString() + " wrongSym=" + sym; } @Override public Symbol access(Name name, TypeSymbol location) { if (!sym.kind.isResolutionError() && sym.kind.matches(KindSelector.TYP)) return types.createErrorType(name, location, sym.type).tsym; else return sym; } } /** * InvalidSymbolError error class indicating that a symbol matching a * given name does not exists in a given site. */ class SymbolNotFoundError extends ResolveError { SymbolNotFoundError(Kind kind) { this(kind, "symbol not found error"); } SymbolNotFoundError(Kind kind, String debugName) { super(kind, debugName); } @Override JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { argtypes = argtypes == null ? List.nil() : argtypes; typeargtypes = typeargtypes == null ? List.nil() : typeargtypes; if (name == names.error) return null; boolean hasLocation = false; if (location == null) { location = site.tsym; } if (!location.name.isEmpty()) { if (location.kind == PCK && !site.tsym.exists()) { return diags.create(dkind, log.currentSource(), pos, "doesnt.exist", location); } hasLocation = !location.name.equals(names._this) && !location.name.equals(names._super); } boolean isConstructor = name == names.init; KindName kindname = isConstructor ? KindName.CONSTRUCTOR : kind.absentKind(); Name idname = isConstructor ? site.tsym.name : name; String errKey = getErrorKey(kindname, typeargtypes.nonEmpty(), hasLocation); if (hasLocation) { return diags.create(dkind, log.currentSource(), pos, errKey, kindname, idname, //symbol kindname, name typeargtypes, args(argtypes), //type parameters and arguments (if any) getLocationDiag(location, site)); //location kindname, type } else { return diags.create(dkind, log.currentSource(), pos, errKey, kindname, idname, //symbol kindname, name typeargtypes, args(argtypes)); //type parameters and arguments (if any) } } //where private Object args(List args) { return args.isEmpty() ? args : methodArguments(args); } private String getErrorKey(KindName kindname, boolean hasTypeArgs, boolean hasLocation) { String key = "cant.resolve"; String suffix = hasLocation ? ".location" : ""; switch (kindname) { case METHOD: case CONSTRUCTOR: { suffix += ".args"; suffix += hasTypeArgs ? ".params" : ""; } } return key + suffix; } private JCDiagnostic getLocationDiag(Symbol location, Type site) { if (location.kind == VAR) { return diags.fragment("location.1", kindName(location), location, location.type); } else { return diags.fragment("location", typeKindName(site), site, null); } } } /** * InvalidSymbolError error class indicating that a given symbol * (either a method, a constructor or an operand) is not applicable * given an actual arguments/type argument list. */ class InapplicableSymbolError extends ResolveError { protected MethodResolutionContext resolveContext; InapplicableSymbolError(MethodResolutionContext context) { this(WRONG_MTH, "inapplicable symbol error", context); } protected InapplicableSymbolError(Kind kind, String debugName, MethodResolutionContext context) { super(kind, debugName); this.resolveContext = context; } @Override public String toString() { return super.toString(); } @Override public boolean exists() { return true; } @Override JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { if (name == names.error) return null; Pair c = errCandidate(); if (compactMethodDiags) { JCDiagnostic simpleDiag = MethodResolutionDiagHelper.rewrite(diags, pos, log.currentSource(), dkind, c.snd); if (simpleDiag != null) { return simpleDiag; } } Symbol ws = c.fst.asMemberOf(site, types); return diags.create(dkind, log.currentSource(), pos, "cant.apply.symbol", kindName(ws), ws.name == names.init ? ws.owner.name : ws.name, methodArguments(ws.type.getParameterTypes()), methodArguments(argtypes), kindName(ws.owner), ws.owner.type, c.snd); } @Override public Symbol access(Name name, TypeSymbol location) { return types.createErrorType(name, location, syms.errSymbol.type).tsym; } protected Pair errCandidate() { Candidate bestSoFar = null; for (Candidate c : resolveContext.candidates) { if (c.isApplicable()) continue; bestSoFar = c; } Assert.checkNonNull(bestSoFar); return new Pair<>(bestSoFar.sym, bestSoFar.details); } } /** * ResolveError error class indicating that a symbol (either methods, constructors or operand) * is not applicable given an actual arguments/type argument list. */ class InapplicableSymbolsError extends InapplicableSymbolError { InapplicableSymbolsError(MethodResolutionContext context) { super(WRONG_MTHS, "inapplicable symbols", context); } @Override JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { Map candidatesMap = mapCandidates(); Map filteredCandidates = compactMethodDiags ? filterCandidates(candidatesMap) : mapCandidates(); if (filteredCandidates.isEmpty()) { filteredCandidates = candidatesMap; } boolean truncatedDiag = candidatesMap.size() != filteredCandidates.size(); if (filteredCandidates.size() > 1) { JCDiagnostic err = diags.create(dkind, null, truncatedDiag ? EnumSet.of(DiagnosticFlag.COMPRESSED) : EnumSet.noneOf(DiagnosticFlag.class), log.currentSource(), pos, "cant.apply.symbols", name == names.init ? KindName.CONSTRUCTOR : kind.absentKind(), name == names.init ? site.tsym.name : name, methodArguments(argtypes)); return new JCDiagnostic.MultilineDiagnostic(err, candidateDetails(filteredCandidates, site)); } else if (filteredCandidates.size() == 1) { Map.Entry _e = filteredCandidates.entrySet().iterator().next(); final Pair p = new Pair<>(_e.getKey(), _e.getValue()); JCDiagnostic d = new InapplicableSymbolError(resolveContext) { @Override protected Pair errCandidate() { return p; } }.getDiagnostic(dkind, pos, location, site, name, argtypes, typeargtypes); if (truncatedDiag) { d.setFlag(DiagnosticFlag.COMPRESSED); } return d; } else { return new SymbolNotFoundError(ABSENT_MTH).getDiagnostic(dkind, pos, location, site, name, argtypes, typeargtypes); } } //where private Map mapCandidates() { Map candidates = new LinkedHashMap<>(); for (Candidate c : resolveContext.candidates) { if (c.isApplicable()) continue; candidates.put(c.sym, c.details); } return candidates; } Map filterCandidates(Map candidatesMap) { Map candidates = new LinkedHashMap<>(); for (Map.Entry _entry : candidatesMap.entrySet()) { JCDiagnostic d = _entry.getValue(); if (!new Template(MethodCheckDiag.ARITY_MISMATCH.regex()).matches(d)) { candidates.put(_entry.getKey(), d); } } return candidates; } private List candidateDetails(Map candidatesMap, Type site) { List details = List.nil(); for (Map.Entry _entry : candidatesMap.entrySet()) { Symbol sym = _entry.getKey(); JCDiagnostic detailDiag = diags.fragment("inapplicable.method", Kinds.kindName(sym), sym.location(site, types), sym.asMemberOf(site, types), _entry.getValue()); details = details.prepend(detailDiag); } //typically members are visited in reverse order (see Scope) //so we need to reverse the candidate list so that candidates //conform to source order return details; } } /** * DiamondError error class indicating that a constructor symbol is not applicable * given an actual arguments/type argument list using diamond inference. */ class DiamondError extends InapplicableSymbolError { Symbol sym; public DiamondError(Symbol sym, MethodResolutionContext context) { super(sym.kind, "diamondError", context); this.sym = sym; } JCDiagnostic getDetails() { return (sym.kind == WRONG_MTH) ? ((InapplicableSymbolError)sym.baseSymbol()).errCandidate().snd : null; } @Override JCDiagnostic getDiagnostic(DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { JCDiagnostic details = getDetails(); if (details != null && compactMethodDiags) { JCDiagnostic simpleDiag = MethodResolutionDiagHelper.rewrite(diags, pos, log.currentSource(), dkind, details); if (simpleDiag != null) { return simpleDiag; } } String key = details == null ? "cant.apply.diamond" : "cant.apply.diamond.1"; return diags.create(dkind, log.currentSource(), pos, key, diags.fragment("diamond", site.tsym), details); } } /** * An InvalidSymbolError error class indicating that a symbol is not * accessible from a given site */ class AccessError extends InvalidSymbolError { private Env env; private Type site; AccessError(Env env, Type site, Symbol sym) { super(HIDDEN, sym, "access error"); this.env = env; this.site = site; } @Override public boolean exists() { return false; } @Override JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { if (sym.owner.type.hasTag(ERROR)) return null; if (sym.name == names.init && sym.owner != site.tsym) { return new SymbolNotFoundError(ABSENT_MTH).getDiagnostic(dkind, pos, location, site, name, argtypes, typeargtypes); } else if ((sym.flags() & PUBLIC) != 0 || (env != null && this.site != null && !isAccessible(env, this.site))) { if (sym.owner.kind == PCK) { return diags.create(dkind, log.currentSource(), pos, "not.def.access.package.cant.access", sym, sym.location(), inaccessiblePackageReason(env, sym.packge())); } else if ( sym.packge() != syms.rootPackage && !symbolPackageVisible(env, sym)) { return diags.create(dkind, log.currentSource(), pos, "not.def.access.class.intf.cant.access.reason", sym, sym.location(), sym.location().packge(), inaccessiblePackageReason(env, sym.packge())); } else { return diags.create(dkind, log.currentSource(), pos, "not.def.access.class.intf.cant.access", sym, sym.location()); } } else if ((sym.flags() & (PRIVATE | PROTECTED)) != 0) { return diags.create(dkind, log.currentSource(), pos, "report.access", sym, asFlagSet(sym.flags() & (PRIVATE | PROTECTED)), sym.location()); } else { return diags.create(dkind, log.currentSource(), pos, "not.def.public.cant.access", sym, sym.location()); } } private String toString(Type type) { StringBuilder sb = new StringBuilder(); sb.append(type); if (type != null) { sb.append("[tsym:").append(type.tsym); if (type.tsym != null) sb.append("packge:").append(type.tsym.packge()); sb.append("]"); } return sb.toString(); } } class InvisibleSymbolError extends InvalidSymbolError { private final Env env; private final boolean suppressError; InvisibleSymbolError(Env env, boolean suppressError, Symbol sym) { super(HIDDEN, sym, "invisible class error"); this.env = env; this.suppressError = suppressError; this.name = sym.name; } @Override JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { if (suppressError) return null; if (sym.kind == PCK) { JCDiagnostic details = inaccessiblePackageReason(env, sym.packge()); return diags.create(dkind, log.currentSource(), pos, "package.not.visible", sym, details); } JCDiagnostic details = inaccessiblePackageReason(env, sym.packge()); if (pos.getTree() != null) { Symbol o = sym; JCTree tree = pos.getTree(); while (o.kind != PCK && tree.hasTag(SELECT)) { o = o.owner; tree = ((JCFieldAccess) tree).selected; } if (o.kind == PCK) { pos = tree.pos(); return diags.create(dkind, log.currentSource(), pos, "package.not.visible", o, details); } } return diags.create(dkind, log.currentSource(), pos, "not.def.access.package.cant.access", sym, sym.packge(), details); } } JCDiagnostic inaccessiblePackageReason(Env env, PackageSymbol sym) { //no dependency: if (!env.toplevel.modle.readModules.contains(sym.modle)) { //does not read: if (sym.modle != syms.unnamedModule) { if (env.toplevel.modle != syms.unnamedModule) { return diags.fragment(Fragments.NotDefAccessDoesNotRead(env.toplevel.modle, sym, sym.modle)); } else { return diags.fragment(Fragments.NotDefAccessDoesNotReadFromUnnamed(sym, sym.modle)); } } else { return diags.fragment(Fragments.NotDefAccessDoesNotReadUnnamed(sym, env.toplevel.modle)); } } else { if (sym.packge().modle.exports.stream().anyMatch(e -> e.packge == sym)) { //not exported to this module: if (env.toplevel.modle != syms.unnamedModule) { return diags.fragment(Fragments.NotDefAccessNotExportedToModule(sym, sym.modle, env.toplevel.modle)); } else { return diags.fragment(Fragments.NotDefAccessNotExportedToModuleFromUnnamed(sym, sym.modle)); } } else { //not exported: if (env.toplevel.modle != syms.unnamedModule) { return diags.fragment(Fragments.NotDefAccessNotExported(sym, sym.modle)); } else { return diags.fragment(Fragments.NotDefAccessNotExportedFromUnnamed(sym, sym.modle)); } } } } /** * InvalidSymbolError error class indicating that an instance member * has erroneously been accessed from a static context. */ class StaticError extends InvalidSymbolError { StaticError(Symbol sym) { super(STATICERR, sym, "static error"); } @Override JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { Symbol errSym = ((sym.kind == TYP && sym.type.hasTag(CLASS)) ? types.erasure(sym.type).tsym : sym); return diags.create(dkind, log.currentSource(), pos, "non-static.cant.be.ref", kindName(sym), errSym); } } /** * InvalidSymbolError error class indicating that a pair of symbols * (either methods, constructors or operands) are ambiguous * given an actual arguments/type argument list. */ class AmbiguityError extends ResolveError { /** The other maximally specific symbol */ List ambiguousSyms = List.nil(); @Override public boolean exists() { return true; } AmbiguityError(Symbol sym1, Symbol sym2) { super(AMBIGUOUS, "ambiguity error"); ambiguousSyms = flatten(sym2).appendList(flatten(sym1)); } private List flatten(Symbol sym) { if (sym.kind == AMBIGUOUS) { return ((AmbiguityError)sym.baseSymbol()).ambiguousSyms; } else { return List.of(sym); } } AmbiguityError addAmbiguousSymbol(Symbol s) { ambiguousSyms = ambiguousSyms.prepend(s); return this; } @Override JCDiagnostic getDiagnostic(JCDiagnostic.DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { List diagSyms = ambiguousSyms.reverse(); Symbol s1 = diagSyms.head; Symbol s2 = diagSyms.tail.head; Name sname = s1.name; if (sname == names.init) sname = s1.owner.name; return diags.create(dkind, log.currentSource(), pos, "ref.ambiguous", sname, kindName(s1), s1, s1.location(site, types), kindName(s2), s2, s2.location(site, types)); } /** * If multiple applicable methods are found during overload and none of them * is more specific than the others, attempt to merge their signatures. */ Symbol mergeAbstracts(Type site) { List ambiguousInOrder = ambiguousSyms.reverse(); return types.mergeAbstracts(ambiguousInOrder, site, true).orElse(this); } @Override protected Symbol access(Name name, TypeSymbol location) { Symbol firstAmbiguity = ambiguousSyms.last(); return firstAmbiguity.kind == TYP ? types.createErrorType(name, location, firstAmbiguity.type).tsym : firstAmbiguity; } } class BadVarargsMethod extends ResolveError { ResolveError delegatedError; BadVarargsMethod(ResolveError delegatedError) { super(delegatedError.kind, "badVarargs"); this.delegatedError = delegatedError; } @Override public Symbol baseSymbol() { return delegatedError.baseSymbol(); } @Override protected Symbol access(Name name, TypeSymbol location) { return delegatedError.access(name, location); } @Override public boolean exists() { return true; } @Override JCDiagnostic getDiagnostic(DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { return delegatedError.getDiagnostic(dkind, pos, location, site, name, argtypes, typeargtypes); } } /** * BadMethodReferenceError error class indicating that a method reference symbol has been found, * but with the wrong staticness. */ class BadMethodReferenceError extends StaticError { boolean unboundLookup; public BadMethodReferenceError(Symbol sym, boolean unboundLookup) { super(sym); this.unboundLookup = unboundLookup; } @Override JCDiagnostic getDiagnostic(DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) { final String key; if (!unboundLookup) { key = "bad.static.method.in.bound.lookup"; } else if (sym.isStatic()) { key = "bad.static.method.in.unbound.lookup"; } else { key = "bad.instance.method.in.unbound.lookup"; } return sym.kind.isResolutionError() ? ((ResolveError)sym).getDiagnostic(dkind, pos, location, site, name, argtypes, typeargtypes) : diags.create(dkind, log.currentSource(), pos, key, Kinds.kindName(sym), sym); } } /** * BadConstructorReferenceError error class indicating that a constructor reference symbol has been found, * but pointing to a class for which an enclosing instance is not available. */ class BadConstructorReferenceError extends InvalidSymbolError { public BadConstructorReferenceError(Symbol sym) { super(MISSING_ENCL, sym, "BadConstructorReferenceError"); } @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()); } } /** * Helper class for method resolution diagnostic simplification. * Certain resolution diagnostic are rewritten as simpler diagnostic * where the enclosing resolution diagnostic (i.e. 'inapplicable method') * is stripped away, as it doesn't carry additional info. The logic * for matching a given diagnostic is given in terms of a template * hierarchy: a diagnostic template can be specified programmatically, * so that only certain diagnostics are matched. Each templete is then * associated with a rewriter object that carries out the task of rewtiting * the diagnostic to a simpler one. */ static class MethodResolutionDiagHelper { /** * A diagnostic rewriter transforms a method resolution diagnostic * into a simpler one */ interface DiagnosticRewriter { JCDiagnostic rewriteDiagnostic(JCDiagnostic.Factory diags, DiagnosticPosition preferedPos, DiagnosticSource preferredSource, DiagnosticType preferredKind, JCDiagnostic d); } /** * A diagnostic template is made up of two ingredients: (i) a regular * expression for matching a diagnostic key and (ii) a list of sub-templates * for matching diagnostic arguments. */ static class Template { /** regex used to match diag key */ String regex; /** templates used to match diagnostic args */ Template[] subTemplates; Template(String key, Template... subTemplates) { this.regex = key; this.subTemplates = subTemplates; } /** * Returns true if the regex matches the diagnostic key and if * all diagnostic arguments are matches by corresponding sub-templates. */ boolean matches(Object o) { JCDiagnostic d = (JCDiagnostic)o; Object[] args = d.getArgs(); if (!d.getCode().matches(regex) || subTemplates.length != d.getArgs().length) { return false; } for (int i = 0; i < args.length ; i++) { if (!subTemplates[i].matches(args[i])) { return false; } } return true; } } /** * Common rewriter for all argument mismatch simplifications. */ static class ArgMismatchRewriter implements DiagnosticRewriter { /** the index of the subdiagnostic to be used as primary. */ int causeIndex; public ArgMismatchRewriter(int causeIndex) { this.causeIndex = causeIndex; } @Override public JCDiagnostic rewriteDiagnostic(JCDiagnostic.Factory diags, DiagnosticPosition preferedPos, DiagnosticSource preferredSource, DiagnosticType preferredKind, JCDiagnostic d) { JCDiagnostic cause = (JCDiagnostic)d.getArgs()[causeIndex]; DiagnosticPosition pos = d.getDiagnosticPosition(); if (pos == null) { pos = preferedPos; } return diags.create(preferredKind, preferredSource, pos, "prob.found.req", cause); } } /** a dummy template that match any diagnostic argument */ static final Template skip = new Template("") { @Override boolean matches(Object d) { return true; } }; /** template for matching inference-free arguments mismatch failures */ static final Template argMismatchTemplate = new Template(MethodCheckDiag.ARG_MISMATCH.regex(), skip); /** template for matching inference related arguments mismatch failures */ static final Template inferArgMismatchTemplate = new Template(MethodCheckDiag.ARG_MISMATCH.regex(), skip, skip) { @Override boolean matches(Object o) { if (!super.matches(o)) { return false; } JCDiagnostic d = (JCDiagnostic)o; @SuppressWarnings("unchecked") List tvars = (List)d.getArgs()[0]; return !containsAny(d, tvars); } BiPredicate> containsPredicate = (o, ts) -> { if (o instanceof Type) { return ((Type)o).containsAny(ts); } else if (o instanceof JCDiagnostic) { return containsAny((JCDiagnostic)o, ts); } else { return false; } }; boolean containsAny(JCDiagnostic d, List ts) { return Stream.of(d.getArgs()) .anyMatch(o -> containsPredicate.test(o, ts)); } }; /** rewriter map used for method resolution simplification */ static final Map rewriters = new LinkedHashMap<>(); static { rewriters.put(argMismatchTemplate, new ArgMismatchRewriter(0)); rewriters.put(inferArgMismatchTemplate, new ArgMismatchRewriter(1)); } /** * Main entry point for diagnostic rewriting - given a diagnostic, see if any templates matches it, * and rewrite it accordingly. */ static JCDiagnostic rewrite(JCDiagnostic.Factory diags, DiagnosticPosition pos, DiagnosticSource source, DiagnosticType dkind, JCDiagnostic d) { for (Map.Entry _entry : rewriters.entrySet()) { if (_entry.getKey().matches(d)) { JCDiagnostic simpleDiag = _entry.getValue().rewriteDiagnostic(diags, pos, source, dkind, d); simpleDiag.setFlag(DiagnosticFlag.COMPRESSED); return simpleDiag; } } return null; } } enum MethodResolutionPhase { BASIC(false, false), BOX(true, false), VARARITY(true, true) { @Override public Symbol mergeResults(Symbol bestSoFar, Symbol sym) { //Check invariants (see {@code LookupHelper.shouldStop}) Assert.check(bestSoFar.kind.isResolutionError() && bestSoFar.kind != AMBIGUOUS); if (!sym.kind.isResolutionError()) { //varargs resolution successful return sym; } else { //pick best error switch (bestSoFar.kind) { case WRONG_MTH: case WRONG_MTHS: //Override previous errors if they were caused by argument mismatch. //This generally means preferring current symbols - but we need to pay //attention to the fact that the varargs lookup returns 'less' candidates //than the previous rounds, and adjust that accordingly. switch (sym.kind) { case WRONG_MTH: //if the previous round matched more than one method, return that //result instead return bestSoFar.kind == WRONG_MTHS ? bestSoFar : sym; case ABSENT_MTH: //do not override erroneous symbol if the arity lookup did not //match any method return bestSoFar; case WRONG_MTHS: default: //safe to override return sym; } default: //otherwise, return first error return bestSoFar; } } } }; final boolean isBoxingRequired; final boolean isVarargsRequired; MethodResolutionPhase(boolean isBoxingRequired, boolean isVarargsRequired) { this.isBoxingRequired = isBoxingRequired; this.isVarargsRequired = isVarargsRequired; } public boolean isBoxingRequired() { return isBoxingRequired; } public boolean isVarargsRequired() { return isVarargsRequired; } public Symbol mergeResults(Symbol prev, Symbol sym) { return sym; } } final List methodResolutionSteps = List.of(BASIC, BOX, VARARITY); /** * A resolution context is used to keep track of intermediate results of * overload resolution, such as list of method that are not applicable * (used to generate more precise diagnostics) and so on. Resolution contexts * can be nested - this means that when each overload resolution routine should * work within the resolution context it created. */ class MethodResolutionContext { private List candidates = List.nil(); MethodResolutionPhase step = null; MethodCheck methodCheck = resolveMethodCheck; private boolean internalResolution = false; private DeferredAttr.AttrMode attrMode = DeferredAttr.AttrMode.SPECULATIVE; void addInapplicableCandidate(Symbol sym, JCDiagnostic details) { Candidate c = new Candidate(currentResolutionContext.step, sym, details, null); candidates = candidates.append(c); } void addApplicableCandidate(Symbol sym, Type mtype) { Candidate c = new Candidate(currentResolutionContext.step, sym, null, mtype); candidates = candidates.append(c); } DeferredAttrContext deferredAttrContext(Symbol sym, InferenceContext inferenceContext, ResultInfo pendingResult, Warner warn) { DeferredAttrContext parent = (pendingResult == null) ? deferredAttr.emptyDeferredAttrContext : pendingResult.checkContext.deferredAttrContext(); return deferredAttr.new DeferredAttrContext(attrMode, sym, step, inferenceContext, parent, warn); } /** * This class represents an overload resolution candidate. There are two * kinds of candidates: applicable methods and inapplicable methods; * applicable methods have a pointer to the instantiated method type, * while inapplicable candidates contain further details about the * reason why the method has been considered inapplicable. */ @SuppressWarnings("overrides") class Candidate { final MethodResolutionPhase step; final Symbol sym; final JCDiagnostic details; final Type mtype; private Candidate(MethodResolutionPhase step, Symbol sym, JCDiagnostic details, Type mtype) { this.step = step; this.sym = sym; this.details = details; this.mtype = mtype; } @Override public boolean equals(Object o) { if (o instanceof Candidate) { 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)) return true; } return false; } boolean isApplicable() { return mtype != null; } } DeferredAttr.AttrMode attrMode() { return attrMode; } boolean internal() { return internalResolution; } } MethodResolutionContext currentResolutionContext = null; }