aoqi@0: /*
rpatil@3092: * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0: *
aoqi@0: * This code is free software; you can redistribute it and/or modify it
aoqi@0: * under the terms of the GNU General Public License version 2 only, as
aoqi@0: * published by the Free Software Foundation. Oracle designates this
aoqi@0: * particular file as subject to the "Classpath" exception as provided
aoqi@0: * by Oracle in the LICENSE file that accompanied this code.
aoqi@0: *
aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0: * accompanied this code).
aoqi@0: *
aoqi@0: * You should have received a copy of the GNU General Public License version
aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0: *
aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0: * or visit www.oracle.com if you need additional information or have any
aoqi@0: * questions.
aoqi@0: */
aoqi@0:
aoqi@0: package com.sun.tools.javac.comp;
aoqi@0:
aoqi@0: import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
aoqi@0: import com.sun.tools.javac.api.Formattable.LocalizedString;
aoqi@0: import com.sun.tools.javac.code.*;
aoqi@0: import com.sun.tools.javac.code.Symbol.*;
aoqi@0: import com.sun.tools.javac.code.Type.*;
aoqi@0: import com.sun.tools.javac.comp.Attr.ResultInfo;
aoqi@0: import com.sun.tools.javac.comp.Check.CheckContext;
aoqi@0: import com.sun.tools.javac.comp.DeferredAttr.AttrMode;
aoqi@0: import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext;
aoqi@0: import com.sun.tools.javac.comp.DeferredAttr.DeferredType;
aoqi@0: import com.sun.tools.javac.comp.Infer.InferenceContext;
aoqi@0: import com.sun.tools.javac.comp.Infer.FreeTypeListener;
aoqi@0: import com.sun.tools.javac.comp.Resolve.MethodResolutionContext.Candidate;
aoqi@0: import com.sun.tools.javac.comp.Resolve.MethodResolutionDiagHelper.DiagnosticRewriter;
aoqi@0: import com.sun.tools.javac.comp.Resolve.MethodResolutionDiagHelper.Template;
aoqi@0: import com.sun.tools.javac.jvm.*;
aoqi@0: import com.sun.tools.javac.main.Option;
aoqi@0: import com.sun.tools.javac.tree.*;
aoqi@0: import com.sun.tools.javac.tree.JCTree.*;
aoqi@0: import com.sun.tools.javac.tree.JCTree.JCMemberReference.ReferenceKind;
aoqi@0: import com.sun.tools.javac.tree.JCTree.JCPolyExpression.*;
aoqi@0: import com.sun.tools.javac.util.*;
aoqi@0: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
aoqi@0: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
aoqi@0: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;
aoqi@0:
aoqi@0: import java.util.Arrays;
aoqi@0: import java.util.Collection;
aoqi@0: import java.util.EnumMap;
aoqi@0: import java.util.EnumSet;
aoqi@0: import java.util.Iterator;
aoqi@0: import java.util.LinkedHashMap;
aoqi@0: import java.util.LinkedHashSet;
aoqi@0: import java.util.Map;
aoqi@0:
aoqi@0: import javax.lang.model.element.ElementVisitor;
aoqi@0:
aoqi@0: import static com.sun.tools.javac.code.Flags.*;
aoqi@0: import static com.sun.tools.javac.code.Flags.BLOCK;
aoqi@0: import static com.sun.tools.javac.code.Kinds.*;
aoqi@0: import static com.sun.tools.javac.code.Kinds.ERRONEOUS;
aoqi@0: import static com.sun.tools.javac.code.TypeTag.*;
aoqi@0: import static com.sun.tools.javac.comp.Resolve.MethodResolutionPhase.*;
aoqi@0: import static com.sun.tools.javac.tree.JCTree.Tag.*;
aoqi@0:
aoqi@0: /** Helper class for name resolution, used mostly by the attribution phase.
aoqi@0: *
aoqi@0: *
This is NOT part of any supported API.
aoqi@0: * If you write code that depends on this, you do so at your own risk.
aoqi@0: * This code and its internal interfaces are subject to change or
aoqi@0: * deletion without notice.
aoqi@0: */
aoqi@0: public class Resolve {
aoqi@0: protected static final Context.Key resolveKey =
aoqi@0: new Context.Key();
aoqi@0:
aoqi@0: Names names;
aoqi@0: Log log;
aoqi@0: Symtab syms;
aoqi@0: Attr attr;
aoqi@0: DeferredAttr deferredAttr;
aoqi@0: Check chk;
aoqi@0: Infer infer;
aoqi@0: ClassReader reader;
aoqi@0: TreeInfo treeinfo;
aoqi@0: Types types;
aoqi@0: JCDiagnostic.Factory diags;
aoqi@0: public final boolean boxingEnabled;
aoqi@0: public final boolean varargsEnabled;
aoqi@0: public final boolean allowMethodHandles;
aoqi@0: public final boolean allowFunctionalInterfaceMostSpecific;
vromero@2535: public final boolean checkVarargsAccessAfterResolution;
aoqi@0: private final boolean debugResolve;
aoqi@0: private final boolean compactMethodDiags;
aoqi@0: final EnumSet verboseResolutionMode;
aoqi@0:
aoqi@0: Scope polymorphicSignatureScope;
aoqi@0:
aoqi@0: protected Resolve(Context context) {
aoqi@0: context.put(resolveKey, this);
aoqi@0: syms = Symtab.instance(context);
aoqi@0:
aoqi@0: varNotFound = new
aoqi@0: SymbolNotFoundError(ABSENT_VAR);
aoqi@0: methodNotFound = new
aoqi@0: SymbolNotFoundError(ABSENT_MTH);
aoqi@0: methodWithCorrectStaticnessNotFound = new
aoqi@0: SymbolNotFoundError(WRONG_STATICNESS,
aoqi@0: "method found has incorrect staticness");
aoqi@0: typeNotFound = new
aoqi@0: SymbolNotFoundError(ABSENT_TYP);
aoqi@0:
aoqi@0: names = Names.instance(context);
aoqi@0: log = Log.instance(context);
aoqi@0: attr = Attr.instance(context);
aoqi@0: deferredAttr = DeferredAttr.instance(context);
aoqi@0: chk = Check.instance(context);
aoqi@0: infer = Infer.instance(context);
aoqi@0: reader = ClassReader.instance(context);
aoqi@0: treeinfo = TreeInfo.instance(context);
aoqi@0: types = Types.instance(context);
aoqi@0: diags = JCDiagnostic.Factory.instance(context);
aoqi@0: Source source = Source.instance(context);
aoqi@0: boxingEnabled = source.allowBoxing();
aoqi@0: varargsEnabled = source.allowVarargs();
aoqi@0: Options options = Options.instance(context);
aoqi@0: debugResolve = options.isSet("debugresolve");
aoqi@0: compactMethodDiags = options.isSet(Option.XDIAGS, "compact") ||
aoqi@0: options.isUnset(Option.XDIAGS) && options.isUnset("rawDiagnostics");
aoqi@0: verboseResolutionMode = VerboseResolutionMode.getVerboseResolutionMode(options);
aoqi@0: Target target = Target.instance(context);
aoqi@0: allowMethodHandles = target.hasMethodHandles();
aoqi@0: allowFunctionalInterfaceMostSpecific = source.allowFunctionalInterfaceMostSpecific();
vromero@2535: checkVarargsAccessAfterResolution =
vromero@2531: source.allowPostApplicabilityVarargsAccessCheck();
aoqi@0: polymorphicSignatureScope = new Scope(syms.noSymbol);
aoqi@0:
aoqi@0: inapplicableMethodException = new InapplicableMethodException(diags);
aoqi@0: }
aoqi@0:
aoqi@0: /** error symbols, which are returned when resolution fails
aoqi@0: */
aoqi@0: private final SymbolNotFoundError varNotFound;
aoqi@0: private final SymbolNotFoundError methodNotFound;
aoqi@0: private final SymbolNotFoundError methodWithCorrectStaticnessNotFound;
aoqi@0: private final SymbolNotFoundError typeNotFound;
aoqi@0:
aoqi@0: public static Resolve instance(Context context) {
aoqi@0: Resolve instance = context.get(resolveKey);
aoqi@0: if (instance == null)
aoqi@0: instance = new Resolve(context);
aoqi@0: return instance;
aoqi@0: }
aoqi@0:
aoqi@0: //
aoqi@0: enum VerboseResolutionMode {
aoqi@0: SUCCESS("success"),
aoqi@0: FAILURE("failure"),
aoqi@0: APPLICABLE("applicable"),
aoqi@0: INAPPLICABLE("inapplicable"),
aoqi@0: DEFERRED_INST("deferred-inference"),
aoqi@0: PREDEF("predef"),
aoqi@0: OBJECT_INIT("object-init"),
aoqi@0: INTERNAL("internal");
aoqi@0:
aoqi@0: final String opt;
aoqi@0:
aoqi@0: private VerboseResolutionMode(String opt) {
aoqi@0: this.opt = opt;
aoqi@0: }
aoqi@0:
aoqi@0: static EnumSet getVerboseResolutionMode(Options opts) {
aoqi@0: String s = opts.get("verboseResolution");
aoqi@0: EnumSet res = EnumSet.noneOf(VerboseResolutionMode.class);
aoqi@0: if (s == null) return res;
aoqi@0: if (s.contains("all")) {
aoqi@0: res = EnumSet.allOf(VerboseResolutionMode.class);
aoqi@0: }
aoqi@0: Collection args = Arrays.asList(s.split(","));
aoqi@0: for (VerboseResolutionMode mode : values()) {
aoqi@0: if (args.contains(mode.opt)) {
aoqi@0: res.add(mode);
aoqi@0: } else if (args.contains("-" + mode.opt)) {
aoqi@0: res.remove(mode);
aoqi@0: }
aoqi@0: }
aoqi@0: return res;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: void reportVerboseResolutionDiagnostic(DiagnosticPosition dpos, Name name, Type site,
aoqi@0: List argtypes, List typeargtypes, Symbol bestSoFar) {
aoqi@0: boolean success = bestSoFar.kind < ERRONEOUS;
aoqi@0:
aoqi@0: if (success && !verboseResolutionMode.contains(VerboseResolutionMode.SUCCESS)) {
aoqi@0: return;
aoqi@0: } else if (!success && !verboseResolutionMode.contains(VerboseResolutionMode.FAILURE)) {
aoqi@0: return;
aoqi@0: }
aoqi@0:
aoqi@0: if (bestSoFar.name == names.init &&
aoqi@0: bestSoFar.owner == syms.objectType.tsym &&
aoqi@0: !verboseResolutionMode.contains(VerboseResolutionMode.OBJECT_INIT)) {
aoqi@0: return; //skip diags for Object constructor resolution
aoqi@0: } else if (site == syms.predefClass.type &&
aoqi@0: !verboseResolutionMode.contains(VerboseResolutionMode.PREDEF)) {
aoqi@0: return; //skip spurious diags for predef symbols (i.e. operators)
aoqi@0: } else if (currentResolutionContext.internalResolution &&
aoqi@0: !verboseResolutionMode.contains(VerboseResolutionMode.INTERNAL)) {
aoqi@0: return;
aoqi@0: }
aoqi@0:
aoqi@0: int pos = 0;
aoqi@0: int mostSpecificPos = -1;
aoqi@0: ListBuffer subDiags = new ListBuffer<>();
aoqi@0: for (Candidate c : currentResolutionContext.candidates) {
aoqi@0: if (currentResolutionContext.step != c.step ||
aoqi@0: (c.isApplicable() && !verboseResolutionMode.contains(VerboseResolutionMode.APPLICABLE)) ||
aoqi@0: (!c.isApplicable() && !verboseResolutionMode.contains(VerboseResolutionMode.INAPPLICABLE))) {
aoqi@0: continue;
aoqi@0: } else {
aoqi@0: subDiags.append(c.isApplicable() ?
aoqi@0: getVerboseApplicableCandidateDiag(pos, c.sym, c.mtype) :
aoqi@0: getVerboseInapplicableCandidateDiag(pos, c.sym, c.details));
aoqi@0: if (c.sym == bestSoFar)
aoqi@0: mostSpecificPos = pos;
aoqi@0: pos++;
aoqi@0: }
aoqi@0: }
aoqi@0: String key = success ? "verbose.resolve.multi" : "verbose.resolve.multi.1";
aoqi@0: List argtypes2 = Type.map(argtypes,
aoqi@0: deferredAttr.new RecoveryDeferredTypeMap(AttrMode.SPECULATIVE, bestSoFar, currentResolutionContext.step));
aoqi@0: JCDiagnostic main = diags.note(log.currentSource(), dpos, key, name,
aoqi@0: site.tsym, mostSpecificPos, currentResolutionContext.step,
aoqi@0: methodArguments(argtypes2),
aoqi@0: methodArguments(typeargtypes));
aoqi@0: JCDiagnostic d = new JCDiagnostic.MultilineDiagnostic(main, subDiags.toList());
aoqi@0: log.report(d);
aoqi@0: }
aoqi@0:
aoqi@0: JCDiagnostic getVerboseApplicableCandidateDiag(int pos, Symbol sym, Type inst) {
aoqi@0: JCDiagnostic subDiag = null;
aoqi@0: if (sym.type.hasTag(FORALL)) {
aoqi@0: subDiag = diags.fragment("partial.inst.sig", inst);
aoqi@0: }
aoqi@0:
aoqi@0: String key = subDiag == null ?
aoqi@0: "applicable.method.found" :
aoqi@0: "applicable.method.found.1";
aoqi@0:
aoqi@0: return diags.fragment(key, pos, sym, subDiag);
aoqi@0: }
aoqi@0:
aoqi@0: JCDiagnostic getVerboseInapplicableCandidateDiag(int pos, Symbol sym, JCDiagnostic subDiag) {
aoqi@0: return diags.fragment("not.applicable.method.found", pos, sym, subDiag);
aoqi@0: }
aoqi@0: //
aoqi@0:
aoqi@0: /* ************************************************************************
aoqi@0: * Identifier resolution
aoqi@0: *************************************************************************/
aoqi@0:
aoqi@0: /** An environment is "static" if its static level is greater than
aoqi@0: * the one of its outer environment
aoqi@0: */
aoqi@0: protected static boolean isStatic(Env env) {
alundblad@2814: return env.outer != null && env.info.staticLevel > env.outer.info.staticLevel;
aoqi@0: }
aoqi@0:
aoqi@0: /** An environment is an "initializer" if it is a constructor or
aoqi@0: * an instance initializer.
aoqi@0: */
aoqi@0: static boolean isInitializer(Env env) {
aoqi@0: Symbol owner = env.info.scope.owner;
aoqi@0: return owner.isConstructor() ||
aoqi@0: owner.owner.kind == TYP &&
aoqi@0: (owner.kind == VAR ||
aoqi@0: owner.kind == MTH && (owner.flags() & BLOCK) != 0) &&
aoqi@0: (owner.flags() & STATIC) == 0;
aoqi@0: }
aoqi@0:
aoqi@0: /** Is class accessible in given evironment?
aoqi@0: * @param env The current environment.
aoqi@0: * @param c The class whose accessibility is checked.
aoqi@0: */
aoqi@0: public boolean isAccessible(Env env, TypeSymbol c) {
aoqi@0: return isAccessible(env, c, false);
aoqi@0: }
aoqi@0:
aoqi@0: public boolean isAccessible(Env env, TypeSymbol c, boolean checkInner) {
aoqi@0: boolean isAccessible = false;
aoqi@0: switch ((short)(c.flags() & AccessFlags)) {
aoqi@0: case PRIVATE:
aoqi@0: isAccessible =
aoqi@0: env.enclClass.sym.outermostClass() ==
aoqi@0: c.owner.outermostClass();
aoqi@0: break;
aoqi@0: case 0:
aoqi@0: isAccessible =
aoqi@0: env.toplevel.packge == c.owner // fast special case
aoqi@0: ||
aoqi@0: env.toplevel.packge == c.packge()
aoqi@0: ||
aoqi@0: // Hack: this case is added since synthesized default constructors
aoqi@0: // of anonymous classes should be allowed to access
aoqi@0: // classes which would be inaccessible otherwise.
aoqi@0: env.enclMethod != null &&
aoqi@0: (env.enclMethod.mods.flags & ANONCONSTR) != 0;
aoqi@0: break;
aoqi@0: default: // error recovery
aoqi@0: case PUBLIC:
aoqi@0: isAccessible = true;
aoqi@0: break;
aoqi@0: case PROTECTED:
aoqi@0: isAccessible =
aoqi@0: env.toplevel.packge == c.owner // fast special case
aoqi@0: ||
aoqi@0: env.toplevel.packge == c.packge()
aoqi@0: ||
aoqi@0: isInnerSubClass(env.enclClass.sym, c.owner);
aoqi@0: break;
aoqi@0: }
aoqi@0: return (checkInner == false || c.type.getEnclosingType() == Type.noType) ?
aoqi@0: isAccessible :
aoqi@0: isAccessible && isAccessible(env, c.type.getEnclosingType(), checkInner);
aoqi@0: }
aoqi@0: //where
aoqi@0: /** Is given class a subclass of given base class, or an inner class
aoqi@0: * of a subclass?
aoqi@0: * Return null if no such class exists.
aoqi@0: * @param c The class which is the subclass or is contained in it.
aoqi@0: * @param base The base class
aoqi@0: */
aoqi@0: private boolean isInnerSubClass(ClassSymbol c, Symbol base) {
aoqi@0: while (c != null && !c.isSubClass(base, types)) {
aoqi@0: c = c.owner.enclClass();
aoqi@0: }
aoqi@0: return c != null;
aoqi@0: }
aoqi@0:
aoqi@0: boolean isAccessible(Env env, Type t) {
aoqi@0: return isAccessible(env, t, false);
aoqi@0: }
aoqi@0:
aoqi@0: boolean isAccessible(Env env, Type t, boolean checkInner) {
aoqi@0: return (t.hasTag(ARRAY))
aoqi@0: ? isAccessible(env, types.cvarUpperBound(types.elemtype(t)))
aoqi@0: : isAccessible(env, t.tsym, checkInner);
aoqi@0: }
aoqi@0:
aoqi@0: /** Is symbol accessible as a member of given type in given environment?
aoqi@0: * @param env The current environment.
aoqi@0: * @param site The type of which the tested symbol is regarded
aoqi@0: * as a member.
aoqi@0: * @param sym The symbol.
aoqi@0: */
aoqi@0: public boolean isAccessible(Env env, Type site, Symbol sym) {
aoqi@0: return isAccessible(env, site, sym, false);
aoqi@0: }
aoqi@0: public boolean isAccessible(Env env, Type site, Symbol sym, boolean checkInner) {
aoqi@0: if (sym.name == names.init && sym.owner != site.tsym) return false;
aoqi@0: switch ((short)(sym.flags() & AccessFlags)) {
aoqi@0: case PRIVATE:
aoqi@0: return
aoqi@0: (env.enclClass.sym == sym.owner // fast special case
aoqi@0: ||
aoqi@0: env.enclClass.sym.outermostClass() ==
aoqi@0: sym.owner.outermostClass())
aoqi@0: &&
aoqi@0: sym.isInheritedIn(site.tsym, types);
aoqi@0: case 0:
aoqi@0: return
aoqi@0: (env.toplevel.packge == sym.owner.owner // fast special case
aoqi@0: ||
aoqi@0: env.toplevel.packge == sym.packge())
aoqi@0: &&
aoqi@0: isAccessible(env, site, checkInner)
aoqi@0: &&
aoqi@0: sym.isInheritedIn(site.tsym, types)
aoqi@0: &&
aoqi@0: notOverriddenIn(site, sym);
aoqi@0: case PROTECTED:
aoqi@0: return
aoqi@0: (env.toplevel.packge == sym.owner.owner // fast special case
aoqi@0: ||
aoqi@0: env.toplevel.packge == sym.packge()
aoqi@0: ||
aoqi@0: isProtectedAccessible(sym, env.enclClass.sym, site)
aoqi@0: ||
aoqi@0: // OK to select instance method or field from 'super' or type name
aoqi@0: // (but type names should be disallowed elsewhere!)
aoqi@0: env.info.selectSuper && (sym.flags() & STATIC) == 0 && sym.kind != TYP)
aoqi@0: &&
aoqi@0: isAccessible(env, site, checkInner)
aoqi@0: &&
aoqi@0: notOverriddenIn(site, sym);
aoqi@0: default: // this case includes erroneous combinations as well
aoqi@0: return isAccessible(env, site, checkInner) && notOverriddenIn(site, sym);
aoqi@0: }
aoqi@0: }
aoqi@0: //where
aoqi@0: /* `sym' is accessible only if not overridden by
aoqi@0: * another symbol which is a member of `site'
aoqi@0: * (because, if it is overridden, `sym' is not strictly
aoqi@0: * speaking a member of `site'). A polymorphic signature method
aoqi@0: * cannot be overridden (e.g. MH.invokeExact(Object[])).
aoqi@0: */
aoqi@0: private boolean notOverriddenIn(Type site, Symbol sym) {
aoqi@0: if (sym.kind != MTH || sym.isConstructor() || sym.isStatic())
aoqi@0: return true;
aoqi@0: else {
aoqi@0: Symbol s2 = ((MethodSymbol)sym).implementation(site.tsym, types, true);
aoqi@0: return (s2 == null || s2 == sym || sym.owner == s2.owner ||
aoqi@0: !types.isSubSignature(types.memberType(site, s2), types.memberType(site, sym)));
aoqi@0: }
aoqi@0: }
aoqi@0: //where
aoqi@0: /** Is given protected symbol accessible if it is selected from given site
aoqi@0: * and the selection takes place in given class?
aoqi@0: * @param sym The symbol with protected access
aoqi@0: * @param c The class where the access takes place
aoqi@0: * @site The type of the qualifier
aoqi@0: */
aoqi@0: private
aoqi@0: boolean isProtectedAccessible(Symbol sym, ClassSymbol c, Type site) {
aoqi@0: Type newSite = site.hasTag(TYPEVAR) ? site.getUpperBound() : site;
aoqi@0: while (c != null &&
aoqi@0: !(c.isSubClass(sym.owner, types) &&
aoqi@0: (c.flags() & INTERFACE) == 0 &&
aoqi@0: // In JLS 2e 6.6.2.1, the subclass restriction applies
aoqi@0: // only to instance fields and methods -- types are excluded
aoqi@0: // regardless of whether they are declared 'static' or not.
aoqi@0: ((sym.flags() & STATIC) != 0 || sym.kind == TYP || newSite.tsym.isSubClass(c, types))))
aoqi@0: c = c.owner.enclClass();
aoqi@0: return c != null;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Performs a recursive scan of a type looking for accessibility problems
aoqi@0: * from current attribution environment
aoqi@0: */
aoqi@0: void checkAccessibleType(Env env, Type t) {
aoqi@0: accessibilityChecker.visit(t, env);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Accessibility type-visitor
aoqi@0: */
aoqi@0: Types.SimpleVisitor> accessibilityChecker =
aoqi@0: new Types.SimpleVisitor>() {
aoqi@0:
aoqi@0: void visit(List ts, Env env) {
aoqi@0: for (Type t : ts) {
aoqi@0: visit(t, env);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: public Void visitType(Type t, Env env) {
aoqi@0: return null;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public Void visitArrayType(ArrayType t, Env env) {
aoqi@0: visit(t.elemtype, env);
aoqi@0: return null;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public Void visitClassType(ClassType t, Env env) {
aoqi@0: visit(t.getTypeArguments(), env);
aoqi@0: if (!isAccessible(env, t, true)) {
aoqi@0: accessBase(new AccessError(t.tsym), env.tree.pos(), env.enclClass.sym, t, t.tsym.name, true);
aoqi@0: }
aoqi@0: return null;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public Void visitWildcardType(WildcardType t, Env env) {
aoqi@0: visit(t.type, env);
aoqi@0: return null;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public Void visitMethodType(MethodType t, Env env) {
aoqi@0: visit(t.getParameterTypes(), env);
aoqi@0: visit(t.getReturnType(), env);
aoqi@0: visit(t.getThrownTypes(), env);
aoqi@0: return null;
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: /** Try to instantiate the type of a method so that it fits
aoqi@0: * given type arguments and argument types. If successful, return
aoqi@0: * the method's instantiated type, else return null.
aoqi@0: * The instantiation will take into account an additional leading
aoqi@0: * formal parameter if the method is an instance method seen as a member
aoqi@0: * of an under determined site. In this case, we treat site as an additional
aoqi@0: * parameter and the parameters of the class containing the method as
aoqi@0: * additional type variables that get instantiated.
aoqi@0: *
aoqi@0: * @param env The current environment
aoqi@0: * @param site The type of which the method is a member.
aoqi@0: * @param m The method symbol.
aoqi@0: * @param argtypes The invocation's given value arguments.
aoqi@0: * @param typeargtypes The invocation's given type arguments.
aoqi@0: * @param allowBoxing Allow boxing conversions of arguments.
aoqi@0: * @param useVarargs Box trailing arguments into an array for varargs.
aoqi@0: */
aoqi@0: Type rawInstantiate(Env env,
aoqi@0: Type site,
aoqi@0: Symbol m,
aoqi@0: ResultInfo resultInfo,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: boolean allowBoxing,
aoqi@0: boolean useVarargs,
aoqi@0: Warner warn) throws Infer.InferenceException {
aoqi@0:
aoqi@0: Type mt = types.memberType(site, m);
aoqi@0: // tvars is the list of formal type variables for which type arguments
aoqi@0: // need to inferred.
aoqi@0: List tvars = List.nil();
aoqi@0: if (typeargtypes == null) typeargtypes = List.nil();
aoqi@0: if (!mt.hasTag(FORALL) && typeargtypes.nonEmpty()) {
aoqi@0: // This is not a polymorphic method, but typeargs are supplied
aoqi@0: // which is fine, see JLS 15.12.2.1
aoqi@0: } else if (mt.hasTag(FORALL) && typeargtypes.nonEmpty()) {
aoqi@0: ForAll pmt = (ForAll) mt;
aoqi@0: if (typeargtypes.length() != pmt.tvars.length())
aoqi@0: throw inapplicableMethodException.setMessage("arg.length.mismatch"); // not enough args
aoqi@0: // Check type arguments are within bounds
aoqi@0: List formals = pmt.tvars;
aoqi@0: List actuals = typeargtypes;
aoqi@0: while (formals.nonEmpty() && actuals.nonEmpty()) {
aoqi@0: List bounds = types.subst(types.getBounds((TypeVar)formals.head),
aoqi@0: pmt.tvars, typeargtypes);
aoqi@0: for (; bounds.nonEmpty(); bounds = bounds.tail)
aoqi@0: if (!types.isSubtypeUnchecked(actuals.head, bounds.head, warn))
aoqi@0: throw inapplicableMethodException.setMessage("explicit.param.do.not.conform.to.bounds",actuals.head, bounds);
aoqi@0: formals = formals.tail;
aoqi@0: actuals = actuals.tail;
aoqi@0: }
aoqi@0: mt = types.subst(pmt.qtype, pmt.tvars, typeargtypes);
aoqi@0: } else if (mt.hasTag(FORALL)) {
aoqi@0: ForAll pmt = (ForAll) mt;
aoqi@0: List tvars1 = types.newInstances(pmt.tvars);
aoqi@0: tvars = tvars.appendList(tvars1);
aoqi@0: mt = types.subst(pmt.qtype, pmt.tvars, tvars1);
aoqi@0: }
aoqi@0:
aoqi@0: // find out whether we need to go the slow route via infer
aoqi@0: boolean instNeeded = tvars.tail != null; /*inlined: tvars.nonEmpty()*/
aoqi@0: for (List l = argtypes;
aoqi@0: l.tail != null/*inlined: l.nonEmpty()*/ && !instNeeded;
aoqi@0: l = l.tail) {
aoqi@0: if (l.head.hasTag(FORALL)) instNeeded = true;
aoqi@0: }
aoqi@0:
aoqi@0: if (instNeeded)
aoqi@0: return infer.instantiateMethod(env,
aoqi@0: tvars,
aoqi@0: (MethodType)mt,
aoqi@0: resultInfo,
aoqi@0: (MethodSymbol)m,
aoqi@0: argtypes,
aoqi@0: allowBoxing,
aoqi@0: useVarargs,
aoqi@0: currentResolutionContext,
aoqi@0: warn);
aoqi@0:
aoqi@0: DeferredAttr.DeferredAttrContext dc = currentResolutionContext.deferredAttrContext(m, infer.emptyContext, resultInfo, warn);
aoqi@0: currentResolutionContext.methodCheck.argumentsAcceptable(env, dc,
aoqi@0: argtypes, mt.getParameterTypes(), warn);
aoqi@0: dc.complete();
aoqi@0: return mt;
aoqi@0: }
aoqi@0:
aoqi@0: Type checkMethod(Env env,
aoqi@0: Type site,
aoqi@0: Symbol m,
aoqi@0: ResultInfo resultInfo,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: Warner warn) {
aoqi@0: MethodResolutionContext prevContext = currentResolutionContext;
aoqi@0: try {
aoqi@0: currentResolutionContext = new MethodResolutionContext();
aoqi@0: currentResolutionContext.attrMode = DeferredAttr.AttrMode.CHECK;
aoqi@0: if (env.tree.hasTag(JCTree.Tag.REFERENCE)) {
aoqi@0: //method/constructor references need special check class
aoqi@0: //to handle inference variables in 'argtypes' (might happen
aoqi@0: //during an unsticking round)
aoqi@0: currentResolutionContext.methodCheck =
aoqi@0: new MethodReferenceCheck(resultInfo.checkContext.inferenceContext());
aoqi@0: }
aoqi@0: MethodResolutionPhase step = currentResolutionContext.step = env.info.pendingResolutionPhase;
aoqi@0: return rawInstantiate(env, site, m, resultInfo, argtypes, typeargtypes,
aoqi@0: step.isBoxingRequired(), step.isVarargsRequired(), warn);
aoqi@0: }
aoqi@0: finally {
aoqi@0: currentResolutionContext = prevContext;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /** Same but returns null instead throwing a NoInstanceException
aoqi@0: */
aoqi@0: Type instantiate(Env env,
aoqi@0: Type site,
aoqi@0: Symbol m,
aoqi@0: ResultInfo resultInfo,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: boolean allowBoxing,
aoqi@0: boolean useVarargs,
aoqi@0: Warner warn) {
aoqi@0: try {
aoqi@0: return rawInstantiate(env, site, m, resultInfo, argtypes, typeargtypes,
aoqi@0: allowBoxing, useVarargs, warn);
aoqi@0: } catch (InapplicableMethodException ex) {
aoqi@0: return null;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * This interface defines an entry point that should be used to perform a
aoqi@0: * method check. A method check usually consist in determining as to whether
aoqi@0: * a set of types (actuals) is compatible with another set of types (formals).
aoqi@0: * Since the notion of compatibility can vary depending on the circumstances,
aoqi@0: * this interfaces allows to easily add new pluggable method check routines.
aoqi@0: */
aoqi@0: interface MethodCheck {
aoqi@0: /**
aoqi@0: * Main method check routine. A method check usually consist in determining
aoqi@0: * as to whether a set of types (actuals) is compatible with another set of
aoqi@0: * types (formals). If an incompatibility is found, an unchecked exception
aoqi@0: * is assumed to be thrown.
aoqi@0: */
aoqi@0: void argumentsAcceptable(Env env,
aoqi@0: DeferredAttrContext deferredAttrContext,
aoqi@0: List argtypes,
aoqi@0: List formals,
aoqi@0: Warner warn);
aoqi@0:
aoqi@0: /**
aoqi@0: * Retrieve the method check object that will be used during a
aoqi@0: * most specific check.
aoqi@0: */
aoqi@0: MethodCheck mostSpecificCheck(List actuals, boolean strict);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Helper enum defining all method check diagnostics (used by resolveMethodCheck).
aoqi@0: */
aoqi@0: enum MethodCheckDiag {
aoqi@0: /**
aoqi@0: * Actuals and formals differs in length.
aoqi@0: */
aoqi@0: ARITY_MISMATCH("arg.length.mismatch", "infer.arg.length.mismatch"),
aoqi@0: /**
aoqi@0: * An actual is incompatible with a formal.
aoqi@0: */
aoqi@0: ARG_MISMATCH("no.conforming.assignment.exists", "infer.no.conforming.assignment.exists"),
aoqi@0: /**
aoqi@0: * An actual is incompatible with the varargs element type.
aoqi@0: */
aoqi@0: VARARG_MISMATCH("varargs.argument.mismatch", "infer.varargs.argument.mismatch"),
aoqi@0: /**
aoqi@0: * The varargs element type is inaccessible.
aoqi@0: */
aoqi@0: INACCESSIBLE_VARARGS("inaccessible.varargs.type", "inaccessible.varargs.type");
aoqi@0:
aoqi@0: final String basicKey;
aoqi@0: final String inferKey;
aoqi@0:
aoqi@0: MethodCheckDiag(String basicKey, String inferKey) {
aoqi@0: this.basicKey = basicKey;
aoqi@0: this.inferKey = inferKey;
aoqi@0: }
aoqi@0:
aoqi@0: String regex() {
aoqi@0: return String.format("([a-z]*\\.)*(%s|%s)", basicKey, inferKey);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Dummy method check object. All methods are deemed applicable, regardless
aoqi@0: * of their formal parameter types.
aoqi@0: */
aoqi@0: MethodCheck nilMethodCheck = new MethodCheck() {
aoqi@0: public void argumentsAcceptable(Env env, DeferredAttrContext deferredAttrContext, List argtypes, List formals, Warner warn) {
aoqi@0: //do nothing - method always applicable regardless of actuals
aoqi@0: }
aoqi@0:
aoqi@0: public MethodCheck mostSpecificCheck(List actuals, boolean strict) {
aoqi@0: return this;
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: /**
aoqi@0: * Base class for 'real' method checks. The class defines the logic for
aoqi@0: * iterating through formals and actuals and provides and entry point
aoqi@0: * that can be used by subclasses in order to define the actual check logic.
aoqi@0: */
aoqi@0: abstract class AbstractMethodCheck implements MethodCheck {
aoqi@0: @Override
aoqi@0: public void argumentsAcceptable(final Env env,
aoqi@0: DeferredAttrContext deferredAttrContext,
aoqi@0: List argtypes,
aoqi@0: List formals,
aoqi@0: Warner warn) {
aoqi@0: //should we expand formals?
aoqi@0: boolean useVarargs = deferredAttrContext.phase.isVarargsRequired();
sadayapalam@3005: JCTree callTree = treeForDiagnostics(env);
sadayapalam@3005: List trees = TreeInfo.args(callTree);
aoqi@0:
aoqi@0: //inference context used during this method check
aoqi@0: InferenceContext inferenceContext = deferredAttrContext.inferenceContext;
aoqi@0:
aoqi@0: Type varargsFormal = useVarargs ? formals.last() : null;
aoqi@0:
aoqi@0: if (varargsFormal == null &&
aoqi@0: argtypes.size() != formals.size()) {
sadayapalam@3005: reportMC(callTree, MethodCheckDiag.ARITY_MISMATCH, inferenceContext); // not enough args
aoqi@0: }
aoqi@0:
aoqi@0: while (argtypes.nonEmpty() && formals.head != varargsFormal) {
aoqi@0: DiagnosticPosition pos = trees != null ? trees.head : null;
aoqi@0: checkArg(pos, false, argtypes.head, formals.head, deferredAttrContext, warn);
aoqi@0: argtypes = argtypes.tail;
aoqi@0: formals = formals.tail;
aoqi@0: trees = trees != null ? trees.tail : trees;
aoqi@0: }
aoqi@0:
aoqi@0: if (formals.head != varargsFormal) {
sadayapalam@3005: reportMC(callTree, MethodCheckDiag.ARITY_MISMATCH, inferenceContext); // not enough args
aoqi@0: }
aoqi@0:
aoqi@0: if (useVarargs) {
aoqi@0: //note: if applicability check is triggered by most specific test,
aoqi@0: //the last argument of a varargs is _not_ an array type (see JLS 15.12.2.5)
aoqi@0: final Type elt = types.elemtype(varargsFormal);
aoqi@0: while (argtypes.nonEmpty()) {
aoqi@0: DiagnosticPosition pos = trees != null ? trees.head : null;
aoqi@0: checkArg(pos, true, argtypes.head, elt, deferredAttrContext, warn);
aoqi@0: argtypes = argtypes.tail;
aoqi@0: trees = trees != null ? trees.tail : trees;
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
sadayapalam@3005: // where
sadayapalam@3005: private JCTree treeForDiagnostics(Env env) {
sadayapalam@3005: return env.info.preferredTreeForDiagnostics != null ? env.info.preferredTreeForDiagnostics : env.tree;
sadayapalam@3005: }
sadayapalam@3005:
aoqi@0: /**
aoqi@0: * Does the actual argument conforms to the corresponding formal?
aoqi@0: */
aoqi@0: abstract void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn);
aoqi@0:
aoqi@0: protected void reportMC(DiagnosticPosition pos, MethodCheckDiag diag, InferenceContext inferenceContext, Object... args) {
aoqi@0: boolean inferDiag = inferenceContext != infer.emptyContext;
aoqi@0: InapplicableMethodException ex = inferDiag ?
aoqi@0: infer.inferenceException : inapplicableMethodException;
aoqi@0: if (inferDiag && (!diag.inferKey.equals(diag.basicKey))) {
aoqi@0: Object[] args2 = new Object[args.length + 1];
aoqi@0: System.arraycopy(args, 0, args2, 1, args.length);
aoqi@0: args2[0] = inferenceContext.inferenceVars();
aoqi@0: args = args2;
aoqi@0: }
aoqi@0: String key = inferDiag ? diag.inferKey : diag.basicKey;
aoqi@0: throw ex.setMessage(diags.create(DiagnosticType.FRAGMENT, log.currentSource(), pos, key, args));
aoqi@0: }
aoqi@0:
aoqi@0: public MethodCheck mostSpecificCheck(List actuals, boolean strict) {
aoqi@0: return nilMethodCheck;
aoqi@0: }
aoqi@0:
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Arity-based method check. A method is applicable if the number of actuals
aoqi@0: * supplied conforms to the method signature.
aoqi@0: */
aoqi@0: MethodCheck arityMethodCheck = new AbstractMethodCheck() {
aoqi@0: @Override
aoqi@0: void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) {
aoqi@0: //do nothing - actual always compatible to formals
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public String toString() {
aoqi@0: return "arityMethodCheck";
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: List dummyArgs(int length) {
aoqi@0: ListBuffer buf = new ListBuffer<>();
aoqi@0: for (int i = 0 ; i < length ; i++) {
aoqi@0: buf.append(Type.noType);
aoqi@0: }
aoqi@0: return buf.toList();
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Main method applicability routine. Given a list of actual types A,
aoqi@0: * a list of formal types F, determines whether the types in A are
aoqi@0: * compatible (by method invocation conversion) with the types in F.
aoqi@0: *
aoqi@0: * Since this routine is shared between overload resolution and method
aoqi@0: * type-inference, a (possibly empty) inference context is used to convert
aoqi@0: * formal types to the corresponding 'undet' form ahead of a compatibility
aoqi@0: * check so that constraints can be propagated and collected.
aoqi@0: *
aoqi@0: * Moreover, if one or more types in A is a deferred type, this routine uses
aoqi@0: * DeferredAttr in order to perform deferred attribution. If one or more actual
aoqi@0: * deferred types are stuck, they are placed in a queue and revisited later
aoqi@0: * after the remainder of the arguments have been seen. If this is not sufficient
aoqi@0: * to 'unstuck' the argument, a cyclic inference error is called out.
aoqi@0: *
aoqi@0: * A method check handler (see above) is used in order to report errors.
aoqi@0: */
aoqi@0: MethodCheck resolveMethodCheck = new AbstractMethodCheck() {
aoqi@0:
aoqi@0: @Override
aoqi@0: void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) {
aoqi@0: ResultInfo mresult = methodCheckResult(varargs, formal, deferredAttrContext, warn);
aoqi@0: mresult.check(pos, actual);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public void argumentsAcceptable(final Env env,
aoqi@0: DeferredAttrContext deferredAttrContext,
aoqi@0: List argtypes,
aoqi@0: List formals,
aoqi@0: Warner warn) {
aoqi@0: super.argumentsAcceptable(env, deferredAttrContext, argtypes, formals, warn);
dlsmith@2788: // should we check varargs element type accessibility?
aoqi@0: if (deferredAttrContext.phase.isVarargsRequired()) {
dlsmith@2788: if (deferredAttrContext.mode == AttrMode.CHECK || !checkVarargsAccessAfterResolution) {
dlsmith@2788: varargsAccessible(env, types.elemtype(formals.last()), deferredAttrContext.inferenceContext);
vromero@2535: }
aoqi@0: }
aoqi@0: }
aoqi@0:
dlsmith@2788: /**
dlsmith@2788: * Test that the runtime array element type corresponding to 't' is accessible. 't' should be the
dlsmith@2788: * varargs element type of either the method invocation type signature (after inference completes)
dlsmith@2788: * or the method declaration signature (before inference completes).
dlsmith@2788: */
aoqi@0: private void varargsAccessible(final Env env, final Type t, final InferenceContext inferenceContext) {
aoqi@0: if (inferenceContext.free(t)) {
aoqi@0: inferenceContext.addFreeTypeListener(List.of(t), new FreeTypeListener() {
aoqi@0: @Override
aoqi@0: public void typesInferred(InferenceContext inferenceContext) {
aoqi@0: varargsAccessible(env, inferenceContext.asInstType(t), inferenceContext);
aoqi@0: }
aoqi@0: });
aoqi@0: } else {
dlsmith@2788: if (!isAccessible(env, types.erasure(t))) {
aoqi@0: Symbol location = env.enclClass.sym;
aoqi@0: reportMC(env.tree, MethodCheckDiag.INACCESSIBLE_VARARGS, inferenceContext, t, Kinds.kindName(location), location);
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private ResultInfo methodCheckResult(final boolean varargsCheck, Type to,
aoqi@0: final DeferredAttr.DeferredAttrContext deferredAttrContext, Warner rsWarner) {
aoqi@0: CheckContext checkContext = new MethodCheckContext(!deferredAttrContext.phase.isBoxingRequired(), deferredAttrContext, rsWarner) {
aoqi@0: MethodCheckDiag methodDiag = varargsCheck ?
aoqi@0: MethodCheckDiag.VARARG_MISMATCH : MethodCheckDiag.ARG_MISMATCH;
aoqi@0:
aoqi@0: @Override
aoqi@0: public void report(DiagnosticPosition pos, JCDiagnostic details) {
aoqi@0: reportMC(pos, methodDiag, deferredAttrContext.inferenceContext, details);
aoqi@0: }
aoqi@0: };
aoqi@0: return new MethodResultInfo(to, checkContext);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public MethodCheck mostSpecificCheck(List actuals, boolean strict) {
aoqi@0: return new MostSpecificCheck(strict, actuals);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public String toString() {
aoqi@0: return "resolveMethodCheck";
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: /**
aoqi@0: * This class handles method reference applicability checks; since during
aoqi@0: * these checks it's sometime possible to have inference variables on
aoqi@0: * the actual argument types list, the method applicability check must be
aoqi@0: * extended so that inference variables are 'opened' as needed.
aoqi@0: */
aoqi@0: class MethodReferenceCheck extends AbstractMethodCheck {
aoqi@0:
aoqi@0: InferenceContext pendingInferenceContext;
aoqi@0:
aoqi@0: MethodReferenceCheck(InferenceContext pendingInferenceContext) {
aoqi@0: this.pendingInferenceContext = pendingInferenceContext;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: void checkArg(DiagnosticPosition pos, boolean varargs, Type actual, Type formal, DeferredAttrContext deferredAttrContext, Warner warn) {
aoqi@0: ResultInfo mresult = methodCheckResult(varargs, formal, deferredAttrContext, warn);
aoqi@0: mresult.check(pos, actual);
aoqi@0: }
aoqi@0:
aoqi@0: private ResultInfo methodCheckResult(final boolean varargsCheck, Type to,
aoqi@0: final DeferredAttr.DeferredAttrContext deferredAttrContext, Warner rsWarner) {
aoqi@0: CheckContext checkContext = new MethodCheckContext(!deferredAttrContext.phase.isBoxingRequired(), deferredAttrContext, rsWarner) {
aoqi@0: MethodCheckDiag methodDiag = varargsCheck ?
aoqi@0: MethodCheckDiag.VARARG_MISMATCH : MethodCheckDiag.ARG_MISMATCH;
aoqi@0:
aoqi@0: @Override
aoqi@0: public boolean compatible(Type found, Type req, Warner warn) {
aoqi@0: found = pendingInferenceContext.asUndetVar(found);
aoqi@0: if (found.hasTag(UNDETVAR) && req.isPrimitive()) {
aoqi@0: req = types.boxedClass(req).type;
aoqi@0: }
aoqi@0: return super.compatible(found, req, warn);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public void report(DiagnosticPosition pos, JCDiagnostic details) {
aoqi@0: reportMC(pos, methodDiag, deferredAttrContext.inferenceContext, details);
aoqi@0: }
aoqi@0: };
aoqi@0: return new MethodResultInfo(to, checkContext);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public MethodCheck mostSpecificCheck(List actuals, boolean strict) {
aoqi@0: return new MostSpecificCheck(strict, actuals);
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: /**
aoqi@0: * Check context to be used during method applicability checks. A method check
aoqi@0: * context might contain inference variables.
aoqi@0: */
aoqi@0: abstract class MethodCheckContext implements CheckContext {
aoqi@0:
aoqi@0: boolean strict;
aoqi@0: DeferredAttrContext deferredAttrContext;
aoqi@0: Warner rsWarner;
aoqi@0:
aoqi@0: public MethodCheckContext(boolean strict, DeferredAttrContext deferredAttrContext, Warner rsWarner) {
aoqi@0: this.strict = strict;
aoqi@0: this.deferredAttrContext = deferredAttrContext;
aoqi@0: this.rsWarner = rsWarner;
aoqi@0: }
aoqi@0:
aoqi@0: public boolean compatible(Type found, Type req, Warner warn) {
vromero@2543: InferenceContext inferenceContext = deferredAttrContext.inferenceContext;
aoqi@0: return strict ?
vromero@2543: types.isSubtypeUnchecked(inferenceContext.asUndetVar(found), inferenceContext.asUndetVar(req), warn) :
vromero@2543: types.isConvertible(inferenceContext.asUndetVar(found), inferenceContext.asUndetVar(req), warn);
aoqi@0: }
aoqi@0:
aoqi@0: public void report(DiagnosticPosition pos, JCDiagnostic details) {
aoqi@0: throw inapplicableMethodException.setMessage(details);
aoqi@0: }
aoqi@0:
aoqi@0: public Warner checkWarner(DiagnosticPosition pos, Type found, Type req) {
aoqi@0: return rsWarner;
aoqi@0: }
aoqi@0:
aoqi@0: public InferenceContext inferenceContext() {
aoqi@0: return deferredAttrContext.inferenceContext;
aoqi@0: }
aoqi@0:
aoqi@0: public DeferredAttrContext deferredAttrContext() {
aoqi@0: return deferredAttrContext;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public String toString() {
aoqi@0: return "MethodReferenceCheck";
aoqi@0: }
aoqi@0:
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * ResultInfo class to be used during method applicability checks. Check
aoqi@0: * for deferred types goes through special path.
aoqi@0: */
aoqi@0: class MethodResultInfo extends ResultInfo {
aoqi@0:
aoqi@0: public MethodResultInfo(Type pt, CheckContext checkContext) {
aoqi@0: attr.super(VAL, pt, checkContext);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: protected Type check(DiagnosticPosition pos, Type found) {
aoqi@0: if (found.hasTag(DEFERRED)) {
aoqi@0: DeferredType dt = (DeferredType)found;
aoqi@0: return dt.check(this);
aoqi@0: } else {
rpatil@3092: Type uResult = U(found);
aoqi@0: Type capturedType = pos == null || pos.getTree() == null ?
aoqi@0: types.capture(uResult) :
aoqi@0: checkContext.inferenceContext()
aoqi@0: .cachedCapture(pos.getTree(), uResult, true);
aoqi@0: return super.check(pos, chk.checkNonVoid(pos, capturedType));
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * javac has a long-standing 'simplification' (see 6391995):
aoqi@0: * given an actual argument type, the method check is performed
aoqi@0: * on its upper bound. This leads to inconsistencies when an
aoqi@0: * argument type is checked against itself. For example, given
aoqi@0: * a type-variable T, it is not true that {@code U(T) <: T},
aoqi@0: * so we need to guard against that.
aoqi@0: */
aoqi@0: private Type U(Type found) {
aoqi@0: return found == pt ?
aoqi@0: found : types.cvarUpperBound(found);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: protected MethodResultInfo dup(Type newPt) {
aoqi@0: return new MethodResultInfo(newPt, checkContext);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: protected ResultInfo dup(CheckContext newContext) {
aoqi@0: return new MethodResultInfo(pt, newContext);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Most specific method applicability routine. Given a list of actual types A,
aoqi@0: * a list of formal types F1, and a list of formal types F2, the routine determines
aoqi@0: * as to whether the types in F1 can be considered more specific than those in F2 w.r.t.
aoqi@0: * argument types A.
aoqi@0: */
aoqi@0: class MostSpecificCheck implements MethodCheck {
aoqi@0:
aoqi@0: boolean strict;
aoqi@0: List actuals;
aoqi@0:
aoqi@0: MostSpecificCheck(boolean strict, List actuals) {
aoqi@0: this.strict = strict;
aoqi@0: this.actuals = actuals;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public void argumentsAcceptable(final Env env,
aoqi@0: DeferredAttrContext deferredAttrContext,
aoqi@0: List formals1,
aoqi@0: List formals2,
aoqi@0: Warner warn) {
aoqi@0: formals2 = adjustArgs(formals2, deferredAttrContext.msym, formals1.length(), deferredAttrContext.phase.isVarargsRequired());
aoqi@0: while (formals2.nonEmpty()) {
aoqi@0: ResultInfo mresult = methodCheckResult(formals2.head, deferredAttrContext, warn, actuals.head);
aoqi@0: mresult.check(null, formals1.head);
aoqi@0: formals1 = formals1.tail;
aoqi@0: formals2 = formals2.tail;
aoqi@0: actuals = actuals.isEmpty() ? actuals : actuals.tail;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Create a method check context to be used during the most specific applicability check
aoqi@0: */
aoqi@0: ResultInfo methodCheckResult(Type to, DeferredAttr.DeferredAttrContext deferredAttrContext,
aoqi@0: Warner rsWarner, Type actual) {
aoqi@0: return attr.new ResultInfo(Kinds.VAL, to,
aoqi@0: new MostSpecificCheckContext(strict, deferredAttrContext, rsWarner, actual));
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Subclass of method check context class that implements most specific
aoqi@0: * method conversion. If the actual type under analysis is a deferred type
aoqi@0: * a full blown structural analysis is carried out.
aoqi@0: */
aoqi@0: class MostSpecificCheckContext extends MethodCheckContext {
aoqi@0:
aoqi@0: Type actual;
aoqi@0:
aoqi@0: public MostSpecificCheckContext(boolean strict, DeferredAttrContext deferredAttrContext, Warner rsWarner, Type actual) {
aoqi@0: super(strict, deferredAttrContext, rsWarner);
aoqi@0: this.actual = actual;
aoqi@0: }
aoqi@0:
aoqi@0: public boolean compatible(Type found, Type req, Warner warn) {
aoqi@0: if (allowFunctionalInterfaceMostSpecific &&
aoqi@0: unrelatedFunctionalInterfaces(found, req) &&
aoqi@0: (actual != null && actual.getTag() == DEFERRED)) {
aoqi@0: DeferredType dt = (DeferredType) actual;
aoqi@0: DeferredType.SpeculativeCache.Entry e =
aoqi@0: dt.speculativeCache.get(deferredAttrContext.msym, deferredAttrContext.phase);
aoqi@0: if (e != null && e.speculativeTree != deferredAttr.stuckTree) {
aoqi@0: return functionalInterfaceMostSpecific(found, req, e.speculativeTree, warn);
aoqi@0: }
aoqi@0: }
aoqi@0: return super.compatible(found, req, warn);
aoqi@0: }
aoqi@0:
aoqi@0: /** Whether {@code t} and {@code s} are unrelated functional interface types. */
aoqi@0: private boolean unrelatedFunctionalInterfaces(Type t, Type s) {
aoqi@0: return types.isFunctionalInterface(t.tsym) &&
aoqi@0: types.isFunctionalInterface(s.tsym) &&
aoqi@0: types.asSuper(t, s.tsym) == null &&
aoqi@0: types.asSuper(s, t.tsym) == null;
aoqi@0: }
aoqi@0:
aoqi@0: /** Parameters {@code t} and {@code s} are unrelated functional interface types. */
aoqi@0: private boolean functionalInterfaceMostSpecific(Type t, Type s, JCTree tree, Warner warn) {
aoqi@0: FunctionalInterfaceMostSpecificChecker msc = new FunctionalInterfaceMostSpecificChecker(t, s, warn);
aoqi@0: msc.scan(tree);
aoqi@0: return msc.result;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Tests whether one functional interface type can be considered more specific
aoqi@0: * than another unrelated functional interface type for the scanned expression.
aoqi@0: */
aoqi@0: class FunctionalInterfaceMostSpecificChecker extends DeferredAttr.PolyScanner {
aoqi@0:
aoqi@0: final Type t;
aoqi@0: final Type s;
aoqi@0: final Warner warn;
aoqi@0: boolean result;
aoqi@0:
aoqi@0: /** Parameters {@code t} and {@code s} are unrelated functional interface types. */
aoqi@0: FunctionalInterfaceMostSpecificChecker(Type t, Type s, Warner warn) {
aoqi@0: this.t = t;
aoqi@0: this.s = s;
aoqi@0: this.warn = warn;
aoqi@0: result = true;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: void skip(JCTree tree) {
aoqi@0: result &= false;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public void visitConditional(JCConditional tree) {
aoqi@0: scan(tree.truepart);
aoqi@0: scan(tree.falsepart);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public void visitReference(JCMemberReference tree) {
aoqi@0: Type desc_t = types.findDescriptorType(t);
aoqi@0: Type desc_s = types.findDescriptorType(s);
aoqi@0: // use inference variables here for more-specific inference (18.5.4)
aoqi@0: if (!types.isSameTypes(desc_t.getParameterTypes(),
aoqi@0: inferenceContext().asUndetVars(desc_s.getParameterTypes()))) {
aoqi@0: result &= false;
aoqi@0: } else {
aoqi@0: // compare return types
aoqi@0: Type ret_t = desc_t.getReturnType();
aoqi@0: Type ret_s = desc_s.getReturnType();
aoqi@0: if (ret_s.hasTag(VOID)) {
aoqi@0: result &= true;
aoqi@0: } else if (ret_t.hasTag(VOID)) {
aoqi@0: result &= false;
aoqi@0: } else if (ret_t.isPrimitive() != ret_s.isPrimitive()) {
aoqi@0: boolean retValIsPrimitive =
aoqi@0: tree.refPolyKind == PolyKind.STANDALONE &&
aoqi@0: tree.sym.type.getReturnType().isPrimitive();
aoqi@0: result &= (retValIsPrimitive == ret_t.isPrimitive()) &&
aoqi@0: (retValIsPrimitive != ret_s.isPrimitive());
aoqi@0: } else {
aoqi@0: result &= MostSpecificCheckContext.super.compatible(ret_t, ret_s, warn);
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public void visitLambda(JCLambda tree) {
aoqi@0: Type desc_t = types.findDescriptorType(t);
aoqi@0: Type desc_s = types.findDescriptorType(s);
aoqi@0: // use inference variables here for more-specific inference (18.5.4)
aoqi@0: if (!types.isSameTypes(desc_t.getParameterTypes(),
aoqi@0: inferenceContext().asUndetVars(desc_s.getParameterTypes()))) {
aoqi@0: result &= false;
aoqi@0: } else {
aoqi@0: // compare return types
aoqi@0: Type ret_t = desc_t.getReturnType();
aoqi@0: Type ret_s = desc_s.getReturnType();
aoqi@0: if (ret_s.hasTag(VOID)) {
aoqi@0: result &= true;
aoqi@0: } else if (ret_t.hasTag(VOID)) {
aoqi@0: result &= false;
aoqi@0: } else if (unrelatedFunctionalInterfaces(ret_t, ret_s)) {
aoqi@0: for (JCExpression expr : lambdaResults(tree)) {
aoqi@0: result &= functionalInterfaceMostSpecific(ret_t, ret_s, expr, warn);
aoqi@0: }
aoqi@0: } else if (ret_t.isPrimitive() != ret_s.isPrimitive()) {
aoqi@0: for (JCExpression expr : lambdaResults(tree)) {
aoqi@0: boolean retValIsPrimitive = expr.isStandalone() && expr.type.isPrimitive();
aoqi@0: result &= (retValIsPrimitive == ret_t.isPrimitive()) &&
aoqi@0: (retValIsPrimitive != ret_s.isPrimitive());
aoqi@0: }
aoqi@0: } else {
aoqi@0: result &= MostSpecificCheckContext.super.compatible(ret_t, ret_s, warn);
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0: //where
aoqi@0:
aoqi@0: private List lambdaResults(JCLambda lambda) {
aoqi@0: if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
aoqi@0: return List.of((JCExpression) lambda.body);
aoqi@0: } else {
aoqi@0: final ListBuffer buffer = new ListBuffer<>();
aoqi@0: DeferredAttr.LambdaReturnScanner lambdaScanner =
aoqi@0: new DeferredAttr.LambdaReturnScanner() {
aoqi@0: @Override
aoqi@0: public void visitReturn(JCReturn tree) {
aoqi@0: if (tree.expr != null) {
aoqi@0: buffer.append(tree.expr);
aoqi@0: }
aoqi@0: }
aoqi@0: };
aoqi@0: lambdaScanner.scan(lambda.body);
aoqi@0: return buffer.toList();
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: }
aoqi@0:
aoqi@0: public MethodCheck mostSpecificCheck(List actuals, boolean strict) {
aoqi@0: Assert.error("Cannot get here!");
aoqi@0: return null;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: public static class InapplicableMethodException extends RuntimeException {
aoqi@0: private static final long serialVersionUID = 0;
aoqi@0:
aoqi@0: JCDiagnostic diagnostic;
aoqi@0: JCDiagnostic.Factory diags;
aoqi@0:
aoqi@0: InapplicableMethodException(JCDiagnostic.Factory diags) {
aoqi@0: this.diagnostic = null;
aoqi@0: this.diags = diags;
aoqi@0: }
aoqi@0: InapplicableMethodException setMessage() {
aoqi@0: return setMessage((JCDiagnostic)null);
aoqi@0: }
aoqi@0: InapplicableMethodException setMessage(String key) {
aoqi@0: return setMessage(key != null ? diags.fragment(key) : null);
aoqi@0: }
aoqi@0: InapplicableMethodException setMessage(String key, Object... args) {
aoqi@0: return setMessage(key != null ? diags.fragment(key, args) : null);
aoqi@0: }
aoqi@0: InapplicableMethodException setMessage(JCDiagnostic diag) {
aoqi@0: this.diagnostic = diag;
aoqi@0: return this;
aoqi@0: }
aoqi@0:
aoqi@0: public JCDiagnostic getDiagnostic() {
aoqi@0: return diagnostic;
aoqi@0: }
aoqi@0: }
aoqi@0: private final InapplicableMethodException inapplicableMethodException;
aoqi@0:
aoqi@0: /* ***************************************************************************
aoqi@0: * Symbol lookup
aoqi@0: * the following naming conventions for arguments are used
aoqi@0: *
aoqi@0: * env is the environment where the symbol was mentioned
aoqi@0: * site is the type of which the symbol is a member
aoqi@0: * name is the symbol's name
aoqi@0: * if no arguments are given
aoqi@0: * argtypes are the value arguments, if we search for a method
aoqi@0: *
aoqi@0: * If no symbol was found, a ResolveError detailing the problem is returned.
aoqi@0: ****************************************************************************/
aoqi@0:
aoqi@0: /** Find field. Synthetic fields are always skipped.
aoqi@0: * @param env The current environment.
aoqi@0: * @param site The original type from where the selection takes place.
aoqi@0: * @param name The name of the field.
aoqi@0: * @param c The class to search for the field. This is always
aoqi@0: * a superclass or implemented interface of site's class.
aoqi@0: */
aoqi@0: Symbol findField(Env env,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: TypeSymbol c) {
aoqi@0: while (c.type.hasTag(TYPEVAR))
aoqi@0: c = c.type.getUpperBound().tsym;
aoqi@0: Symbol bestSoFar = varNotFound;
aoqi@0: Symbol sym;
aoqi@0: Scope.Entry e = c.members().lookup(name);
aoqi@0: while (e.scope != null) {
aoqi@0: if (e.sym.kind == VAR && (e.sym.flags_field & SYNTHETIC) == 0) {
aoqi@0: return isAccessible(env, site, e.sym)
aoqi@0: ? e.sym : new AccessError(env, site, e.sym);
aoqi@0: }
aoqi@0: e = e.next();
aoqi@0: }
aoqi@0: Type st = types.supertype(c.type);
aoqi@0: if (st != null && (st.hasTag(CLASS) || st.hasTag(TYPEVAR))) {
aoqi@0: sym = findField(env, site, name, st.tsym);
aoqi@0: if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0: }
aoqi@0: for (List l = types.interfaces(c.type);
aoqi@0: bestSoFar.kind != AMBIGUOUS && l.nonEmpty();
aoqi@0: l = l.tail) {
aoqi@0: sym = findField(env, site, name, l.head.tsym);
aoqi@0: if (bestSoFar.exists() && sym.exists() &&
aoqi@0: sym.owner != bestSoFar.owner)
aoqi@0: bestSoFar = new AmbiguityError(bestSoFar, sym);
aoqi@0: else if (sym.kind < bestSoFar.kind)
aoqi@0: bestSoFar = sym;
aoqi@0: }
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: /** Resolve a field identifier, throw a fatal error if not found.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the method invocation.
aoqi@0: * @param site The type of the qualifying expression, in which
aoqi@0: * identifier is searched.
aoqi@0: * @param name The identifier's name.
aoqi@0: */
aoqi@0: public VarSymbol resolveInternalField(DiagnosticPosition pos, Env env,
aoqi@0: Type site, Name name) {
aoqi@0: Symbol sym = findField(env, site, name, site.tsym);
aoqi@0: if (sym.kind == VAR) return (VarSymbol)sym;
aoqi@0: else throw new FatalError(
aoqi@0: diags.fragment("fatal.err.cant.locate.field",
aoqi@0: name));
aoqi@0: }
aoqi@0:
aoqi@0: /** Find unqualified variable or field with given name.
aoqi@0: * Synthetic fields always skipped.
aoqi@0: * @param env The current environment.
aoqi@0: * @param name The name of the variable or field.
aoqi@0: */
aoqi@0: Symbol findVar(Env env, Name name) {
aoqi@0: Symbol bestSoFar = varNotFound;
aoqi@0: Symbol sym;
aoqi@0: Env env1 = env;
aoqi@0: boolean staticOnly = false;
aoqi@0: while (env1.outer != null) {
aoqi@0: if (isStatic(env1)) staticOnly = true;
aoqi@0: Scope.Entry e = env1.info.scope.lookup(name);
aoqi@0: while (e.scope != null &&
aoqi@0: (e.sym.kind != VAR ||
aoqi@0: (e.sym.flags_field & SYNTHETIC) != 0))
aoqi@0: e = e.next();
aoqi@0: sym = (e.scope != null)
aoqi@0: ? e.sym
aoqi@0: : findField(
aoqi@0: env1, env1.enclClass.sym.type, name, env1.enclClass.sym);
aoqi@0: if (sym.exists()) {
aoqi@0: if (staticOnly &&
aoqi@0: sym.kind == VAR &&
aoqi@0: sym.owner.kind == TYP &&
aoqi@0: (sym.flags() & STATIC) == 0)
aoqi@0: return new StaticError(sym);
aoqi@0: else
aoqi@0: return sym;
aoqi@0: } else if (sym.kind < bestSoFar.kind) {
aoqi@0: bestSoFar = sym;
aoqi@0: }
aoqi@0:
aoqi@0: if ((env1.enclClass.sym.flags() & STATIC) != 0) staticOnly = true;
aoqi@0: env1 = env1.outer;
aoqi@0: }
aoqi@0:
aoqi@0: sym = findField(env, syms.predefClass.type, name, syms.predefClass);
aoqi@0: if (sym.exists())
aoqi@0: return sym;
aoqi@0: if (bestSoFar.exists())
aoqi@0: return bestSoFar;
aoqi@0:
aoqi@0: Symbol origin = null;
aoqi@0: for (Scope sc : new Scope[] { env.toplevel.namedImportScope, env.toplevel.starImportScope }) {
aoqi@0: Scope.Entry e = sc.lookup(name);
aoqi@0: for (; e.scope != null; e = e.next()) {
aoqi@0: sym = e.sym;
aoqi@0: if (sym.kind != VAR)
aoqi@0: continue;
aoqi@0: // invariant: sym.kind == VAR
aoqi@0: if (bestSoFar.kind < AMBIGUOUS && sym.owner != bestSoFar.owner)
aoqi@0: return new AmbiguityError(bestSoFar, sym);
aoqi@0: else if (bestSoFar.kind >= VAR) {
aoqi@0: origin = e.getOrigin().owner;
aoqi@0: bestSoFar = isAccessible(env, origin.type, sym)
aoqi@0: ? sym : new AccessError(env, origin.type, sym);
aoqi@0: }
aoqi@0: }
aoqi@0: if (bestSoFar.exists()) break;
aoqi@0: }
aoqi@0: if (bestSoFar.kind == VAR && bestSoFar.owner.type != origin.type)
aoqi@0: return bestSoFar.clone(origin);
aoqi@0: else
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: Warner noteWarner = new Warner();
aoqi@0:
aoqi@0: /** Select the best method for a call site among two choices.
aoqi@0: * @param env The current environment.
aoqi@0: * @param site The original type from where the
aoqi@0: * selection takes place.
aoqi@0: * @param argtypes The invocation's value arguments,
aoqi@0: * @param typeargtypes The invocation's type arguments,
aoqi@0: * @param sym Proposed new best match.
aoqi@0: * @param bestSoFar Previously found best match.
aoqi@0: * @param allowBoxing Allow boxing conversions of arguments.
aoqi@0: * @param useVarargs Box trailing arguments into an array for varargs.
aoqi@0: */
aoqi@0: @SuppressWarnings("fallthrough")
aoqi@0: Symbol selectBest(Env env,
aoqi@0: Type site,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: Symbol sym,
aoqi@0: Symbol bestSoFar,
aoqi@0: boolean allowBoxing,
aoqi@0: boolean useVarargs,
aoqi@0: boolean operator) {
aoqi@0: if (sym.kind == ERR ||
aoqi@0: !sym.isInheritedIn(site.tsym, types)) {
aoqi@0: return bestSoFar;
aoqi@0: } else if (useVarargs && (sym.flags() & VARARGS) == 0) {
aoqi@0: return bestSoFar.kind >= ERRONEOUS ?
aoqi@0: new BadVarargsMethod((ResolveError)bestSoFar.baseSymbol()) :
aoqi@0: bestSoFar;
aoqi@0: }
aoqi@0: Assert.check(sym.kind < AMBIGUOUS);
aoqi@0: try {
aoqi@0: Type mt = rawInstantiate(env, site, sym, null, argtypes, typeargtypes,
aoqi@0: allowBoxing, useVarargs, types.noWarnings);
aoqi@0: if (!operator || verboseResolutionMode.contains(VerboseResolutionMode.PREDEF))
aoqi@0: currentResolutionContext.addApplicableCandidate(sym, mt);
aoqi@0: } catch (InapplicableMethodException ex) {
aoqi@0: if (!operator)
aoqi@0: currentResolutionContext.addInapplicableCandidate(sym, ex.getDiagnostic());
aoqi@0: switch (bestSoFar.kind) {
aoqi@0: case ABSENT_MTH:
aoqi@0: return new InapplicableSymbolError(currentResolutionContext);
aoqi@0: case WRONG_MTH:
aoqi@0: if (operator) return bestSoFar;
aoqi@0: bestSoFar = new InapplicableSymbolsError(currentResolutionContext);
aoqi@0: default:
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0: }
aoqi@0: if (!isAccessible(env, site, sym)) {
aoqi@0: return (bestSoFar.kind == ABSENT_MTH)
aoqi@0: ? new AccessError(env, site, sym)
aoqi@0: : bestSoFar;
aoqi@0: }
aoqi@0: return (bestSoFar.kind > AMBIGUOUS)
aoqi@0: ? sym
aoqi@0: : mostSpecific(argtypes, sym, bestSoFar, env, site,
aoqi@0: allowBoxing && operator, useVarargs);
aoqi@0: }
aoqi@0:
aoqi@0: /* Return the most specific of the two methods for a call,
aoqi@0: * given that both are accessible and applicable.
aoqi@0: * @param m1 A new candidate for most specific.
aoqi@0: * @param m2 The previous most specific candidate.
aoqi@0: * @param env The current environment.
aoqi@0: * @param site The original type from where the selection
aoqi@0: * takes place.
aoqi@0: * @param allowBoxing Allow boxing conversions of arguments.
aoqi@0: * @param useVarargs Box trailing arguments into an array for varargs.
aoqi@0: */
aoqi@0: Symbol mostSpecific(List argtypes, Symbol m1,
aoqi@0: Symbol m2,
aoqi@0: Env env,
aoqi@0: final Type site,
aoqi@0: boolean allowBoxing,
aoqi@0: boolean useVarargs) {
aoqi@0: switch (m2.kind) {
aoqi@0: case MTH:
aoqi@0: if (m1 == m2) return m1;
aoqi@0: boolean m1SignatureMoreSpecific =
aoqi@0: signatureMoreSpecific(argtypes, env, site, m1, m2, allowBoxing, useVarargs);
aoqi@0: boolean m2SignatureMoreSpecific =
aoqi@0: signatureMoreSpecific(argtypes, env, site, m2, m1, allowBoxing, useVarargs);
aoqi@0: if (m1SignatureMoreSpecific && m2SignatureMoreSpecific) {
aoqi@0: Type mt1 = types.memberType(site, m1);
aoqi@0: Type mt2 = types.memberType(site, m2);
aoqi@0: if (!types.overrideEquivalent(mt1, mt2))
aoqi@0: return ambiguityError(m1, m2);
aoqi@0:
aoqi@0: // same signature; select (a) the non-bridge method, or
aoqi@0: // (b) the one that overrides the other, or (c) the concrete
aoqi@0: // one, or (d) merge both abstract signatures
aoqi@0: if ((m1.flags() & BRIDGE) != (m2.flags() & BRIDGE))
aoqi@0: return ((m1.flags() & BRIDGE) != 0) ? m2 : m1;
aoqi@0:
aoqi@0: // if one overrides or hides the other, use it
aoqi@0: TypeSymbol m1Owner = (TypeSymbol)m1.owner;
aoqi@0: TypeSymbol m2Owner = (TypeSymbol)m2.owner;
aoqi@0: if (types.asSuper(m1Owner.type, m2Owner) != null &&
aoqi@0: ((m1.owner.flags_field & INTERFACE) == 0 ||
aoqi@0: (m2.owner.flags_field & INTERFACE) != 0) &&
aoqi@0: m1.overrides(m2, m1Owner, types, false))
aoqi@0: return m1;
aoqi@0: if (types.asSuper(m2Owner.type, m1Owner) != null &&
aoqi@0: ((m2.owner.flags_field & INTERFACE) == 0 ||
aoqi@0: (m1.owner.flags_field & INTERFACE) != 0) &&
aoqi@0: m2.overrides(m1, m2Owner, types, false))
aoqi@0: return m2;
aoqi@0: boolean m1Abstract = (m1.flags() & ABSTRACT) != 0;
aoqi@0: boolean m2Abstract = (m2.flags() & ABSTRACT) != 0;
aoqi@0: if (m1Abstract && !m2Abstract) return m2;
aoqi@0: if (m2Abstract && !m1Abstract) return m1;
aoqi@0: // both abstract or both concrete
aoqi@0: return ambiguityError(m1, m2);
aoqi@0: }
aoqi@0: if (m1SignatureMoreSpecific) return m1;
aoqi@0: if (m2SignatureMoreSpecific) return m2;
aoqi@0: return ambiguityError(m1, m2);
aoqi@0: case AMBIGUOUS:
aoqi@0: //compare m1 to ambiguous methods in m2
aoqi@0: AmbiguityError e = (AmbiguityError)m2.baseSymbol();
aoqi@0: boolean m1MoreSpecificThanAnyAmbiguous = true;
aoqi@0: boolean allAmbiguousMoreSpecificThanM1 = true;
aoqi@0: for (Symbol s : e.ambiguousSyms) {
aoqi@0: Symbol moreSpecific = mostSpecific(argtypes, m1, s, env, site, allowBoxing, useVarargs);
aoqi@0: m1MoreSpecificThanAnyAmbiguous &= moreSpecific == m1;
aoqi@0: allAmbiguousMoreSpecificThanM1 &= moreSpecific == s;
aoqi@0: }
aoqi@0: if (m1MoreSpecificThanAnyAmbiguous)
aoqi@0: return m1;
aoqi@0: //if m1 is more specific than some ambiguous methods, but other ambiguous methods are
aoqi@0: //more specific than m1, add it as a new ambiguous method:
aoqi@0: if (!allAmbiguousMoreSpecificThanM1)
aoqi@0: e.addAmbiguousSymbol(m1);
aoqi@0: return e;
aoqi@0: default:
aoqi@0: throw new AssertionError();
aoqi@0: }
aoqi@0: }
aoqi@0: //where
aoqi@0: private boolean signatureMoreSpecific(List actuals, Env env, Type site, Symbol m1, Symbol m2, boolean allowBoxing, boolean useVarargs) {
aoqi@0: noteWarner.clear();
aoqi@0: int maxLength = Math.max(
aoqi@0: Math.max(m1.type.getParameterTypes().length(), actuals.length()),
aoqi@0: m2.type.getParameterTypes().length());
aoqi@0: MethodResolutionContext prevResolutionContext = currentResolutionContext;
aoqi@0: try {
aoqi@0: currentResolutionContext = new MethodResolutionContext();
aoqi@0: currentResolutionContext.step = prevResolutionContext.step;
aoqi@0: currentResolutionContext.methodCheck =
aoqi@0: prevResolutionContext.methodCheck.mostSpecificCheck(actuals, !allowBoxing);
aoqi@0: Type mst = instantiate(env, site, m2, null,
aoqi@0: adjustArgs(types.cvarLowerBounds(types.memberType(site, m1).getParameterTypes()), m1, maxLength, useVarargs), null,
aoqi@0: allowBoxing, useVarargs, noteWarner);
aoqi@0: return mst != null &&
aoqi@0: !noteWarner.hasLint(Lint.LintCategory.UNCHECKED);
aoqi@0: } finally {
aoqi@0: currentResolutionContext = prevResolutionContext;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: List adjustArgs(List args, Symbol msym, int length, boolean allowVarargs) {
aoqi@0: if ((msym.flags() & VARARGS) != 0 && allowVarargs) {
aoqi@0: Type varargsElem = types.elemtype(args.last());
aoqi@0: if (varargsElem == null) {
aoqi@0: Assert.error("Bad varargs = " + args.last() + " " + msym);
aoqi@0: }
aoqi@0: List newArgs = args.reverse().tail.prepend(varargsElem).reverse();
aoqi@0: while (newArgs.length() < length) {
aoqi@0: newArgs = newArgs.append(newArgs.last());
aoqi@0: }
aoqi@0: return newArgs;
aoqi@0: } else {
aoqi@0: return args;
aoqi@0: }
aoqi@0: }
aoqi@0: //where
aoqi@0: Type mostSpecificReturnType(Type mt1, Type mt2) {
aoqi@0: Type rt1 = mt1.getReturnType();
aoqi@0: Type rt2 = mt2.getReturnType();
aoqi@0:
aoqi@0: if (mt1.hasTag(FORALL) && mt2.hasTag(FORALL)) {
aoqi@0: //if both are generic methods, adjust return type ahead of subtyping check
aoqi@0: rt1 = types.subst(rt1, mt1.getTypeArguments(), mt2.getTypeArguments());
aoqi@0: }
aoqi@0: //first use subtyping, then return type substitutability
aoqi@0: if (types.isSubtype(rt1, rt2)) {
aoqi@0: return mt1;
aoqi@0: } else if (types.isSubtype(rt2, rt1)) {
aoqi@0: return mt2;
aoqi@0: } else if (types.returnTypeSubstitutable(mt1, mt2)) {
aoqi@0: return mt1;
aoqi@0: } else if (types.returnTypeSubstitutable(mt2, mt1)) {
aoqi@0: return mt2;
aoqi@0: } else {
aoqi@0: return null;
aoqi@0: }
aoqi@0: }
aoqi@0: //where
aoqi@0: Symbol ambiguityError(Symbol m1, Symbol m2) {
aoqi@0: if (((m1.flags() | m2.flags()) & CLASH) != 0) {
aoqi@0: return (m1.flags() & CLASH) == 0 ? m1 : m2;
aoqi@0: } else {
aoqi@0: return new AmbiguityError(m1, m2);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: Symbol findMethodInScope(Env env,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: Scope sc,
aoqi@0: Symbol bestSoFar,
aoqi@0: boolean allowBoxing,
aoqi@0: boolean useVarargs,
aoqi@0: boolean operator,
aoqi@0: boolean abstractok) {
aoqi@0: for (Symbol s : sc.getElementsByName(name, new LookupFilter(abstractok))) {
aoqi@0: bestSoFar = selectBest(env, site, argtypes, typeargtypes, s,
aoqi@0: bestSoFar, allowBoxing, useVarargs, operator);
aoqi@0: }
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0: //where
aoqi@0: class LookupFilter implements Filter {
aoqi@0:
aoqi@0: boolean abstractOk;
aoqi@0:
aoqi@0: LookupFilter(boolean abstractOk) {
aoqi@0: this.abstractOk = abstractOk;
aoqi@0: }
aoqi@0:
aoqi@0: public boolean accepts(Symbol s) {
aoqi@0: long flags = s.flags();
aoqi@0: return s.kind == MTH &&
aoqi@0: (flags & SYNTHETIC) == 0 &&
aoqi@0: (abstractOk ||
aoqi@0: (flags & DEFAULT) != 0 ||
aoqi@0: (flags & ABSTRACT) == 0);
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: /** Find best qualified method matching given name, type and value
aoqi@0: * arguments.
aoqi@0: * @param env The current environment.
aoqi@0: * @param site The original type from where the selection
aoqi@0: * takes place.
aoqi@0: * @param name The method's name.
aoqi@0: * @param argtypes The method's value arguments.
aoqi@0: * @param typeargtypes The method's type arguments
aoqi@0: * @param allowBoxing Allow boxing conversions of arguments.
aoqi@0: * @param useVarargs Box trailing arguments into an array for varargs.
aoqi@0: */
aoqi@0: Symbol findMethod(Env env,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: boolean allowBoxing,
aoqi@0: boolean useVarargs,
aoqi@0: boolean operator) {
aoqi@0: Symbol bestSoFar = methodNotFound;
aoqi@0: bestSoFar = findMethod(env,
aoqi@0: site,
aoqi@0: name,
aoqi@0: argtypes,
aoqi@0: typeargtypes,
aoqi@0: site.tsym.type,
aoqi@0: bestSoFar,
aoqi@0: allowBoxing,
aoqi@0: useVarargs,
aoqi@0: operator);
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0: // where
aoqi@0: private Symbol findMethod(Env env,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: Type intype,
aoqi@0: Symbol bestSoFar,
aoqi@0: boolean allowBoxing,
aoqi@0: boolean useVarargs,
aoqi@0: boolean operator) {
aoqi@0: @SuppressWarnings({"unchecked","rawtypes"})
aoqi@0: List[] itypes = (List[])new List[] { List.nil(), List.nil() };
aoqi@0: InterfaceLookupPhase iphase = InterfaceLookupPhase.ABSTRACT_OK;
aoqi@0: for (TypeSymbol s : superclasses(intype)) {
aoqi@0: bestSoFar = findMethodInScope(env, site, name, argtypes, typeargtypes,
aoqi@0: s.members(), bestSoFar, allowBoxing, useVarargs, operator, true);
aoqi@0: if (name == names.init) return bestSoFar;
aoqi@0: iphase = (iphase == null) ? null : iphase.update(s, this);
aoqi@0: if (iphase != null) {
aoqi@0: for (Type itype : types.interfaces(s.type)) {
aoqi@0: itypes[iphase.ordinal()] = types.union(types.closure(itype), itypes[iphase.ordinal()]);
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: Symbol concrete = bestSoFar.kind < ERR &&
aoqi@0: (bestSoFar.flags() & ABSTRACT) == 0 ?
aoqi@0: bestSoFar : methodNotFound;
aoqi@0:
aoqi@0: for (InterfaceLookupPhase iphase2 : InterfaceLookupPhase.values()) {
aoqi@0: //keep searching for abstract methods
aoqi@0: for (Type itype : itypes[iphase2.ordinal()]) {
aoqi@0: if (!itype.isInterface()) continue; //skip j.l.Object (included by Types.closure())
aoqi@0: if (iphase2 == InterfaceLookupPhase.DEFAULT_OK &&
aoqi@0: (itype.tsym.flags() & DEFAULT) == 0) continue;
aoqi@0: bestSoFar = findMethodInScope(env, site, name, argtypes, typeargtypes,
aoqi@0: itype.tsym.members(), bestSoFar, allowBoxing, useVarargs, operator, true);
aoqi@0: if (concrete != bestSoFar &&
aoqi@0: concrete.kind < ERR && bestSoFar.kind < ERR &&
aoqi@0: types.isSubSignature(concrete.type, bestSoFar.type)) {
aoqi@0: //this is an hack - as javac does not do full membership checks
aoqi@0: //most specific ends up comparing abstract methods that might have
aoqi@0: //been implemented by some concrete method in a subclass and,
aoqi@0: //because of raw override, it is possible for an abstract method
aoqi@0: //to be more specific than the concrete method - so we need
aoqi@0: //to explicitly call that out (see CR 6178365)
aoqi@0: bestSoFar = concrete;
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: enum InterfaceLookupPhase {
aoqi@0: ABSTRACT_OK() {
aoqi@0: @Override
aoqi@0: InterfaceLookupPhase update(Symbol s, Resolve rs) {
aoqi@0: //We should not look for abstract methods if receiver is a concrete class
aoqi@0: //(as concrete classes are expected to implement all abstracts coming
aoqi@0: //from superinterfaces)
aoqi@0: if ((s.flags() & (ABSTRACT | INTERFACE | ENUM)) != 0) {
aoqi@0: return this;
aoqi@0: } else {
aoqi@0: return DEFAULT_OK;
aoqi@0: }
aoqi@0: }
aoqi@0: },
aoqi@0: DEFAULT_OK() {
aoqi@0: @Override
aoqi@0: InterfaceLookupPhase update(Symbol s, Resolve rs) {
aoqi@0: return this;
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: abstract InterfaceLookupPhase update(Symbol s, Resolve rs);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Return an Iterable object to scan the superclasses of a given type.
aoqi@0: * It's crucial that the scan is done lazily, as we don't want to accidentally
aoqi@0: * access more supertypes than strictly needed (as this could trigger completion
aoqi@0: * errors if some of the not-needed supertypes are missing/ill-formed).
aoqi@0: */
aoqi@0: Iterable superclasses(final Type intype) {
aoqi@0: return new Iterable() {
aoqi@0: public Iterator iterator() {
aoqi@0: return new Iterator() {
aoqi@0:
aoqi@0: List seen = List.nil();
aoqi@0: TypeSymbol currentSym = symbolFor(intype);
aoqi@0: TypeSymbol prevSym = null;
aoqi@0:
aoqi@0: public boolean hasNext() {
aoqi@0: if (currentSym == syms.noSymbol) {
aoqi@0: currentSym = symbolFor(types.supertype(prevSym.type));
aoqi@0: }
aoqi@0: return currentSym != null;
aoqi@0: }
aoqi@0:
aoqi@0: public TypeSymbol next() {
aoqi@0: prevSym = currentSym;
aoqi@0: currentSym = syms.noSymbol;
aoqi@0: Assert.check(prevSym != null || prevSym != syms.noSymbol);
aoqi@0: return prevSym;
aoqi@0: }
aoqi@0:
aoqi@0: public void remove() {
aoqi@0: throw new UnsupportedOperationException();
aoqi@0: }
aoqi@0:
aoqi@0: TypeSymbol symbolFor(Type t) {
aoqi@0: if (!t.hasTag(CLASS) &&
aoqi@0: !t.hasTag(TYPEVAR)) {
aoqi@0: return null;
aoqi@0: }
aoqi@0: while (t.hasTag(TYPEVAR))
aoqi@0: t = t.getUpperBound();
aoqi@0: if (seen.contains(t.tsym)) {
aoqi@0: //degenerate case in which we have a circular
aoqi@0: //class hierarchy - because of ill-formed classfiles
aoqi@0: return null;
aoqi@0: }
aoqi@0: seen = seen.prepend(t.tsym);
aoqi@0: return t.tsym;
aoqi@0: }
aoqi@0: };
aoqi@0: }
aoqi@0: };
aoqi@0: }
aoqi@0:
aoqi@0: /** Find unqualified method matching given name, type and value arguments.
aoqi@0: * @param env The current environment.
aoqi@0: * @param name The method's name.
aoqi@0: * @param argtypes The method's value arguments.
aoqi@0: * @param typeargtypes The method's type arguments.
aoqi@0: * @param allowBoxing Allow boxing conversions of arguments.
aoqi@0: * @param useVarargs Box trailing arguments into an array for varargs.
aoqi@0: */
aoqi@0: Symbol findFun(Env env, Name name,
aoqi@0: List argtypes, List typeargtypes,
aoqi@0: boolean allowBoxing, boolean useVarargs) {
aoqi@0: Symbol bestSoFar = methodNotFound;
aoqi@0: Symbol sym;
aoqi@0: Env env1 = env;
aoqi@0: boolean staticOnly = false;
aoqi@0: while (env1.outer != null) {
aoqi@0: if (isStatic(env1)) staticOnly = true;
sadayapalam@3005: Assert.check(env1.info.preferredTreeForDiagnostics == null);
sadayapalam@3005: env1.info.preferredTreeForDiagnostics = env.tree;
sadayapalam@3005: try {
sadayapalam@3005: sym = findMethod(
sadayapalam@3005: env1, env1.enclClass.sym.type, name, argtypes, typeargtypes,
sadayapalam@3005: allowBoxing, useVarargs, false);
sadayapalam@3005: if (sym.exists()) {
sadayapalam@3005: if (staticOnly &&
sadayapalam@3005: sym.kind == MTH &&
sadayapalam@3005: sym.owner.kind == TYP &&
sadayapalam@3005: (sym.flags() & STATIC) == 0) return new StaticError(sym);
sadayapalam@3005: else return sym;
sadayapalam@3005: } else if (sym.kind < bestSoFar.kind) {
sadayapalam@3005: bestSoFar = sym;
sadayapalam@3005: }
sadayapalam@3005: } finally {
sadayapalam@3005: env1.info.preferredTreeForDiagnostics = null;
aoqi@0: }
aoqi@0: if ((env1.enclClass.sym.flags() & STATIC) != 0) staticOnly = true;
aoqi@0: env1 = env1.outer;
aoqi@0: }
aoqi@0:
aoqi@0: sym = findMethod(env, syms.predefClass.type, name, argtypes,
aoqi@0: typeargtypes, allowBoxing, useVarargs, false);
aoqi@0: if (sym.exists())
aoqi@0: return sym;
aoqi@0:
aoqi@0: Scope.Entry e = env.toplevel.namedImportScope.lookup(name);
aoqi@0: for (; e.scope != null; e = e.next()) {
aoqi@0: sym = e.sym;
aoqi@0: Type origin = e.getOrigin().owner.type;
aoqi@0: if (sym.kind == MTH) {
aoqi@0: if (e.sym.owner.type != origin)
aoqi@0: sym = sym.clone(e.getOrigin().owner);
aoqi@0: if (!isAccessible(env, origin, sym))
aoqi@0: sym = new AccessError(env, origin, sym);
aoqi@0: bestSoFar = selectBest(env, origin,
aoqi@0: argtypes, typeargtypes,
aoqi@0: sym, bestSoFar,
aoqi@0: allowBoxing, useVarargs, false);
aoqi@0: }
aoqi@0: }
aoqi@0: if (bestSoFar.exists())
aoqi@0: return bestSoFar;
aoqi@0:
aoqi@0: e = env.toplevel.starImportScope.lookup(name);
aoqi@0: for (; e.scope != null; e = e.next()) {
aoqi@0: sym = e.sym;
aoqi@0: Type origin = e.getOrigin().owner.type;
aoqi@0: if (sym.kind == MTH) {
aoqi@0: if (e.sym.owner.type != origin)
aoqi@0: sym = sym.clone(e.getOrigin().owner);
aoqi@0: if (!isAccessible(env, origin, sym))
aoqi@0: sym = new AccessError(env, origin, sym);
aoqi@0: bestSoFar = selectBest(env, origin,
aoqi@0: argtypes, typeargtypes,
aoqi@0: sym, bestSoFar,
aoqi@0: allowBoxing, useVarargs, false);
aoqi@0: }
aoqi@0: }
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: /** Load toplevel or member class with given fully qualified name and
aoqi@0: * verify that it is accessible.
aoqi@0: * @param env The current environment.
aoqi@0: * @param name The fully qualified name of the class to be loaded.
aoqi@0: */
aoqi@0: Symbol loadClass(Env env, Name name) {
aoqi@0: try {
aoqi@0: ClassSymbol c = reader.loadClass(name);
aoqi@0: return isAccessible(env, c) ? c : new AccessError(c);
aoqi@0: } catch (ClassReader.BadClassFile err) {
aoqi@0: throw err;
aoqi@0: } catch (CompletionFailure ex) {
aoqi@0: return typeNotFound;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Find a type declared in a scope (not inherited). Return null
aoqi@0: * if none is found.
aoqi@0: * @param env The current environment.
aoqi@0: * @param site The original type from where the selection takes
aoqi@0: * place.
aoqi@0: * @param name The type's name.
aoqi@0: * @param c The class to search for the member type. This is
aoqi@0: * always a superclass or implemented interface of
aoqi@0: * site's class.
aoqi@0: */
aoqi@0: Symbol findImmediateMemberType(Env env,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: TypeSymbol c) {
aoqi@0: Scope.Entry e = c.members().lookup(name);
aoqi@0: while (e.scope != null) {
aoqi@0: if (e.sym.kind == TYP) {
aoqi@0: return isAccessible(env, site, e.sym)
aoqi@0: ? e.sym
aoqi@0: : new AccessError(env, site, e.sym);
aoqi@0: }
aoqi@0: e = e.next();
aoqi@0: }
aoqi@0: return typeNotFound;
aoqi@0: }
aoqi@0:
aoqi@0: /** Find a member type inherited from a superclass or interface.
aoqi@0: * @param env The current environment.
aoqi@0: * @param site The original type from where the selection takes
aoqi@0: * place.
aoqi@0: * @param name The type's name.
aoqi@0: * @param c The class to search for the member type. This is
aoqi@0: * always a superclass or implemented interface of
aoqi@0: * site's class.
aoqi@0: */
aoqi@0: Symbol findInheritedMemberType(Env env,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: TypeSymbol c) {
aoqi@0: Symbol bestSoFar = typeNotFound;
aoqi@0: Symbol sym;
aoqi@0: Type st = types.supertype(c.type);
aoqi@0: if (st != null && st.hasTag(CLASS)) {
aoqi@0: sym = findMemberType(env, site, name, st.tsym);
aoqi@0: if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0: }
aoqi@0: for (List l = types.interfaces(c.type);
aoqi@0: bestSoFar.kind != AMBIGUOUS && l.nonEmpty();
aoqi@0: l = l.tail) {
aoqi@0: sym = findMemberType(env, site, name, l.head.tsym);
aoqi@0: if (bestSoFar.kind < AMBIGUOUS && sym.kind < AMBIGUOUS &&
aoqi@0: sym.owner != bestSoFar.owner)
aoqi@0: bestSoFar = new AmbiguityError(bestSoFar, sym);
aoqi@0: else if (sym.kind < bestSoFar.kind)
aoqi@0: bestSoFar = sym;
aoqi@0: }
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: /** Find qualified member type.
aoqi@0: * @param env The current environment.
aoqi@0: * @param site The original type from where the selection takes
aoqi@0: * place.
aoqi@0: * @param name The type's name.
aoqi@0: * @param c The class to search for the member type. This is
aoqi@0: * always a superclass or implemented interface of
aoqi@0: * site's class.
aoqi@0: */
aoqi@0: Symbol findMemberType(Env env,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: TypeSymbol c) {
aoqi@0: Symbol sym = findImmediateMemberType(env, site, name, c);
aoqi@0:
aoqi@0: if (sym != typeNotFound)
aoqi@0: return sym;
aoqi@0:
aoqi@0: return findInheritedMemberType(env, site, name, c);
aoqi@0:
aoqi@0: }
aoqi@0:
aoqi@0: /** Find a global type in given scope and load corresponding class.
aoqi@0: * @param env The current environment.
aoqi@0: * @param scope The scope in which to look for the type.
aoqi@0: * @param name The type's name.
aoqi@0: */
aoqi@0: Symbol findGlobalType(Env env, Scope scope, Name name) {
aoqi@0: Symbol bestSoFar = typeNotFound;
aoqi@0: for (Scope.Entry e = scope.lookup(name); e.scope != null; e = e.next()) {
aoqi@0: Symbol sym = loadClass(env, e.sym.flatName());
aoqi@0: if (bestSoFar.kind == TYP && sym.kind == TYP &&
aoqi@0: bestSoFar != sym)
aoqi@0: return new AmbiguityError(bestSoFar, sym);
aoqi@0: else if (sym.kind < bestSoFar.kind)
aoqi@0: bestSoFar = sym;
aoqi@0: }
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: Symbol findTypeVar(Env env, Name name, boolean staticOnly) {
aoqi@0: for (Scope.Entry e = env.info.scope.lookup(name);
aoqi@0: e.scope != null;
aoqi@0: e = e.next()) {
aoqi@0: if (e.sym.kind == TYP) {
aoqi@0: if (staticOnly &&
aoqi@0: e.sym.type.hasTag(TYPEVAR) &&
aoqi@0: e.sym.owner.kind == TYP)
aoqi@0: return new StaticError(e.sym);
aoqi@0: return e.sym;
aoqi@0: }
aoqi@0: }
aoqi@0: return typeNotFound;
aoqi@0: }
aoqi@0:
aoqi@0: /** Find an unqualified type symbol.
aoqi@0: * @param env The current environment.
aoqi@0: * @param name The type's name.
aoqi@0: */
aoqi@0: Symbol findType(Env env, Name name) {
aoqi@0: Symbol bestSoFar = typeNotFound;
aoqi@0: Symbol sym;
aoqi@0: boolean staticOnly = false;
aoqi@0: for (Env env1 = env; env1.outer != null; env1 = env1.outer) {
aoqi@0: if (isStatic(env1)) staticOnly = true;
aoqi@0: // First, look for a type variable and the first member type
aoqi@0: final Symbol tyvar = findTypeVar(env1, name, staticOnly);
aoqi@0: sym = findImmediateMemberType(env1, env1.enclClass.sym.type,
aoqi@0: name, env1.enclClass.sym);
aoqi@0:
aoqi@0: // Return the type variable if we have it, and have no
aoqi@0: // immediate member, OR the type variable is for a method.
aoqi@0: if (tyvar != typeNotFound) {
aoqi@0: if (sym == typeNotFound ||
aoqi@0: (tyvar.kind == TYP && tyvar.exists() &&
aoqi@0: tyvar.owner.kind == MTH))
aoqi@0: return tyvar;
aoqi@0: }
aoqi@0:
aoqi@0: // If the environment is a class def, finish up,
aoqi@0: // otherwise, do the entire findMemberType
aoqi@0: if (sym == typeNotFound)
aoqi@0: sym = findInheritedMemberType(env1, env1.enclClass.sym.type,
aoqi@0: name, env1.enclClass.sym);
aoqi@0:
aoqi@0: if (staticOnly && sym.kind == TYP &&
aoqi@0: sym.type.hasTag(CLASS) &&
aoqi@0: sym.type.getEnclosingType().hasTag(CLASS) &&
aoqi@0: env1.enclClass.sym.type.isParameterized() &&
aoqi@0: sym.type.getEnclosingType().isParameterized())
aoqi@0: return new StaticError(sym);
aoqi@0: else if (sym.exists()) return sym;
aoqi@0: else if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0:
aoqi@0: JCClassDecl encl = env1.baseClause ? (JCClassDecl)env1.tree : env1.enclClass;
aoqi@0: if ((encl.sym.flags() & STATIC) != 0)
aoqi@0: staticOnly = true;
aoqi@0: }
aoqi@0:
aoqi@0: if (!env.tree.hasTag(IMPORT)) {
aoqi@0: sym = findGlobalType(env, env.toplevel.namedImportScope, name);
aoqi@0: if (sym.exists()) return sym;
aoqi@0: else if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0:
aoqi@0: sym = findGlobalType(env, env.toplevel.packge.members(), name);
aoqi@0: if (sym.exists()) return sym;
aoqi@0: else if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0:
aoqi@0: sym = findGlobalType(env, env.toplevel.starImportScope, name);
aoqi@0: if (sym.exists()) return sym;
aoqi@0: else if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0: }
aoqi@0:
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: /** Find an unqualified identifier which matches a specified kind set.
aoqi@0: * @param env The current environment.
aoqi@0: * @param name The identifier's name.
aoqi@0: * @param kind Indicates the possible symbol kinds
aoqi@0: * (a subset of VAL, TYP, PCK).
aoqi@0: */
aoqi@0: Symbol findIdent(Env env, Name name, int kind) {
aoqi@0: Symbol bestSoFar = typeNotFound;
aoqi@0: Symbol sym;
aoqi@0:
aoqi@0: if ((kind & VAR) != 0) {
aoqi@0: sym = findVar(env, name);
aoqi@0: if (sym.exists()) return sym;
aoqi@0: else if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0: }
aoqi@0:
aoqi@0: if ((kind & TYP) != 0) {
aoqi@0: sym = findType(env, name);
aoqi@0: if (sym.kind==TYP) {
aoqi@0: reportDependence(env.enclClass.sym, sym);
aoqi@0: }
aoqi@0: if (sym.exists()) return sym;
aoqi@0: else if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0: }
aoqi@0:
aoqi@0: if ((kind & PCK) != 0) return reader.enterPackage(name);
aoqi@0: else return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: /** Report dependencies.
aoqi@0: * @param from The enclosing class sym
aoqi@0: * @param to The found identifier that the class depends on.
aoqi@0: */
aoqi@0: public void reportDependence(Symbol from, Symbol to) {
aoqi@0: // Override if you want to collect the reported dependencies.
aoqi@0: }
aoqi@0:
aoqi@0: /** Find an identifier in a package which matches a specified kind set.
aoqi@0: * @param env The current environment.
aoqi@0: * @param name The identifier's name.
aoqi@0: * @param kind Indicates the possible symbol kinds
aoqi@0: * (a nonempty subset of TYP, PCK).
aoqi@0: */
aoqi@0: Symbol findIdentInPackage(Env env, TypeSymbol pck,
aoqi@0: Name name, int kind) {
aoqi@0: Name fullname = TypeSymbol.formFullName(name, pck);
aoqi@0: Symbol bestSoFar = typeNotFound;
aoqi@0: PackageSymbol pack = null;
aoqi@0: if ((kind & PCK) != 0) {
aoqi@0: pack = reader.enterPackage(fullname);
aoqi@0: if (pack.exists()) return pack;
aoqi@0: }
aoqi@0: if ((kind & TYP) != 0) {
aoqi@0: Symbol sym = loadClass(env, fullname);
aoqi@0: if (sym.exists()) {
aoqi@0: // don't allow programs to use flatnames
aoqi@0: if (name == sym.name) return sym;
aoqi@0: }
aoqi@0: else if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0: }
aoqi@0: return (pack != null) ? pack : bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: /** Find an identifier among the members of a given type `site'.
aoqi@0: * @param env The current environment.
aoqi@0: * @param site The type containing the symbol to be found.
aoqi@0: * @param name The identifier's name.
aoqi@0: * @param kind Indicates the possible symbol kinds
aoqi@0: * (a subset of VAL, TYP).
aoqi@0: */
aoqi@0: Symbol findIdentInType(Env env, Type site,
aoqi@0: Name name, int kind) {
aoqi@0: Symbol bestSoFar = typeNotFound;
aoqi@0: Symbol sym;
aoqi@0: if ((kind & VAR) != 0) {
aoqi@0: sym = findField(env, site, name, site.tsym);
aoqi@0: if (sym.exists()) return sym;
aoqi@0: else if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0: }
aoqi@0:
aoqi@0: if ((kind & TYP) != 0) {
aoqi@0: sym = findMemberType(env, site, name, site.tsym);
aoqi@0: if (sym.exists()) return sym;
aoqi@0: else if (sym.kind < bestSoFar.kind) bestSoFar = sym;
aoqi@0: }
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0: /* ***************************************************************************
aoqi@0: * Access checking
aoqi@0: * The following methods convert ResolveErrors to ErrorSymbols, issuing
aoqi@0: * an error message in the process
aoqi@0: ****************************************************************************/
aoqi@0:
aoqi@0: /** If `sym' is a bad symbol: report error and return errSymbol
aoqi@0: * else pass through unchanged,
aoqi@0: * additional arguments duplicate what has been used in trying to find the
aoqi@0: * symbol {@literal (--> flyweight pattern)}. This improves performance since we
aoqi@0: * expect misses to happen frequently.
aoqi@0: *
aoqi@0: * @param sym The symbol that was found, or a ResolveError.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param location The symbol the served as a context for this lookup
aoqi@0: * @param site The original type from where the selection took place.
aoqi@0: * @param name The symbol's name.
aoqi@0: * @param qualified Did we get here through a qualified expression resolution?
aoqi@0: * @param argtypes The invocation's value arguments,
aoqi@0: * if we looked for a method.
aoqi@0: * @param typeargtypes The invocation's type arguments,
aoqi@0: * if we looked for a method.
aoqi@0: * @param logResolveHelper helper class used to log resolve errors
aoqi@0: */
aoqi@0: Symbol accessInternal(Symbol sym,
aoqi@0: DiagnosticPosition pos,
aoqi@0: Symbol location,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: boolean qualified,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: LogResolveHelper logResolveHelper) {
aoqi@0: if (sym.kind >= AMBIGUOUS) {
aoqi@0: ResolveError errSym = (ResolveError)sym.baseSymbol();
aoqi@0: sym = errSym.access(name, qualified ? site.tsym : syms.noSymbol);
aoqi@0: argtypes = logResolveHelper.getArgumentTypes(errSym, sym, name, argtypes);
aoqi@0: if (logResolveHelper.resolveDiagnosticNeeded(site, argtypes, typeargtypes)) {
aoqi@0: logResolveError(errSym, pos, location, site, name, argtypes, typeargtypes);
aoqi@0: }
aoqi@0: }
aoqi@0: return sym;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Variant of the generalized access routine, to be used for generating method
aoqi@0: * resolution diagnostics
aoqi@0: */
aoqi@0: Symbol accessMethod(Symbol sym,
aoqi@0: DiagnosticPosition pos,
aoqi@0: Symbol location,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: boolean qualified,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: return accessInternal(sym, pos, location, site, name, qualified, argtypes, typeargtypes, methodLogResolveHelper);
aoqi@0: }
aoqi@0:
aoqi@0: /** Same as original accessMethod(), but without location.
aoqi@0: */
aoqi@0: Symbol accessMethod(Symbol sym,
aoqi@0: DiagnosticPosition pos,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: boolean qualified,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: return accessMethod(sym, pos, site.tsym, site, name, qualified, argtypes, typeargtypes);
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Variant of the generalized access routine, to be used for generating variable,
aoqi@0: * type resolution diagnostics
aoqi@0: */
aoqi@0: Symbol accessBase(Symbol sym,
aoqi@0: DiagnosticPosition pos,
aoqi@0: Symbol location,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: boolean qualified) {
aoqi@0: return accessInternal(sym, pos, location, site, name, qualified, List.nil(), null, basicLogResolveHelper);
aoqi@0: }
aoqi@0:
aoqi@0: /** Same as original accessBase(), but without location.
aoqi@0: */
aoqi@0: Symbol accessBase(Symbol sym,
aoqi@0: DiagnosticPosition pos,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: boolean qualified) {
aoqi@0: return accessBase(sym, pos, site.tsym, site, name, qualified);
aoqi@0: }
aoqi@0:
aoqi@0: interface LogResolveHelper {
aoqi@0: boolean resolveDiagnosticNeeded(Type site, List argtypes, List typeargtypes);
aoqi@0: List getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List argtypes);
aoqi@0: }
aoqi@0:
aoqi@0: LogResolveHelper basicLogResolveHelper = new LogResolveHelper() {
aoqi@0: public boolean resolveDiagnosticNeeded(Type site, List argtypes, List typeargtypes) {
aoqi@0: return !site.isErroneous();
aoqi@0: }
aoqi@0: public List getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List argtypes) {
aoqi@0: return argtypes;
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: LogResolveHelper methodLogResolveHelper = new LogResolveHelper() {
aoqi@0: public boolean resolveDiagnosticNeeded(Type site, List argtypes, List typeargtypes) {
aoqi@0: return !site.isErroneous() &&
aoqi@0: !Type.isErroneous(argtypes) &&
aoqi@0: (typeargtypes == null || !Type.isErroneous(typeargtypes));
aoqi@0: }
aoqi@0: public List getArgumentTypes(ResolveError errSym, Symbol accessedSym, Name name, List argtypes) {
aoqi@0: return (syms.operatorNames.contains(name)) ?
aoqi@0: argtypes :
aoqi@0: Type.map(argtypes, new ResolveDeferredRecoveryMap(AttrMode.SPECULATIVE, accessedSym, currentResolutionContext.step));
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: class ResolveDeferredRecoveryMap extends DeferredAttr.RecoveryDeferredTypeMap {
aoqi@0:
aoqi@0: public ResolveDeferredRecoveryMap(AttrMode mode, Symbol msym, MethodResolutionPhase step) {
aoqi@0: deferredAttr.super(mode, msym, step);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: protected Type typeOf(DeferredType dt) {
aoqi@0: Type res = super.typeOf(dt);
aoqi@0: if (!res.isErroneous()) {
aoqi@0: switch (TreeInfo.skipParens(dt.tree).getTag()) {
aoqi@0: case LAMBDA:
aoqi@0: case REFERENCE:
aoqi@0: return dt;
aoqi@0: case CONDEXPR:
aoqi@0: return res == Type.recoveryType ?
aoqi@0: dt : res;
aoqi@0: }
aoqi@0: }
aoqi@0: return res;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /** Check that sym is not an abstract method.
aoqi@0: */
aoqi@0: void checkNonAbstract(DiagnosticPosition pos, Symbol sym) {
aoqi@0: if ((sym.flags() & ABSTRACT) != 0 && (sym.flags() & DEFAULT) == 0)
aoqi@0: log.error(pos, "abstract.cant.be.accessed.directly",
aoqi@0: kindName(sym), sym, sym.location());
aoqi@0: }
aoqi@0:
aoqi@0: /* ***************************************************************************
aoqi@0: * Debugging
aoqi@0: ****************************************************************************/
aoqi@0:
aoqi@0: /** print all scopes starting with scope s and proceeding outwards.
aoqi@0: * used for debugging.
aoqi@0: */
aoqi@0: public void printscopes(Scope s) {
aoqi@0: while (s != null) {
aoqi@0: if (s.owner != null)
aoqi@0: System.err.print(s.owner + ": ");
aoqi@0: for (Scope.Entry e = s.elems; e != null; e = e.sibling) {
aoqi@0: if ((e.sym.flags() & ABSTRACT) != 0)
aoqi@0: System.err.print("abstract ");
aoqi@0: System.err.print(e.sym + " ");
aoqi@0: }
aoqi@0: System.err.println();
aoqi@0: s = s.next;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: void printscopes(Env env) {
aoqi@0: while (env.outer != null) {
aoqi@0: System.err.println("------------------------------");
aoqi@0: printscopes(env.info.scope);
aoqi@0: env = env.outer;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: public void printscopes(Type t) {
aoqi@0: while (t.hasTag(CLASS)) {
aoqi@0: printscopes(t.tsym.members());
aoqi@0: t = types.supertype(t);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /* ***************************************************************************
aoqi@0: * Name resolution
aoqi@0: * Naming conventions are as for symbol lookup
aoqi@0: * Unlike the find... methods these methods will report access errors
aoqi@0: ****************************************************************************/
aoqi@0:
aoqi@0: /** Resolve an unqualified (non-method) identifier.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the identifier use.
aoqi@0: * @param name The identifier's name.
aoqi@0: * @param kind The set of admissible symbol kinds for the identifier.
aoqi@0: */
aoqi@0: Symbol resolveIdent(DiagnosticPosition pos, Env env,
aoqi@0: Name name, int kind) {
aoqi@0: return accessBase(
aoqi@0: findIdent(env, name, kind),
aoqi@0: pos, env.enclClass.sym.type, name, false);
aoqi@0: }
aoqi@0:
aoqi@0: /** Resolve an unqualified method identifier.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the method invocation.
aoqi@0: * @param name The identifier's name.
aoqi@0: * @param argtypes The types of the invocation's value arguments.
aoqi@0: * @param typeargtypes The types of the invocation's type arguments.
aoqi@0: */
aoqi@0: Symbol resolveMethod(DiagnosticPosition pos,
aoqi@0: Env env,
aoqi@0: Name name,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: return lookupMethod(env, pos, env.enclClass.sym, resolveMethodCheck,
aoqi@0: new BasicLookupHelper(name, env.enclClass.sym.type, argtypes, typeargtypes) {
aoqi@0: @Override
aoqi@0: Symbol doLookup(Env env, MethodResolutionPhase phase) {
aoqi@0: return findFun(env, name, argtypes, typeargtypes,
aoqi@0: phase.isBoxingRequired(),
aoqi@0: phase.isVarargsRequired());
aoqi@0: }});
aoqi@0: }
aoqi@0:
aoqi@0: /** Resolve a qualified method identifier
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the method invocation.
aoqi@0: * @param site The type of the qualifying expression, in which
aoqi@0: * identifier is searched.
aoqi@0: * @param name The identifier's name.
aoqi@0: * @param argtypes The types of the invocation's value arguments.
aoqi@0: * @param typeargtypes The types of the invocation's type arguments.
aoqi@0: */
aoqi@0: Symbol resolveQualifiedMethod(DiagnosticPosition pos, Env env,
aoqi@0: Type site, Name name, List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: return resolveQualifiedMethod(pos, env, site.tsym, site, name, argtypes, typeargtypes);
aoqi@0: }
aoqi@0: Symbol resolveQualifiedMethod(DiagnosticPosition pos, Env env,
aoqi@0: Symbol location, Type site, Name name, List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: return resolveQualifiedMethod(new MethodResolutionContext(), pos, env, location, site, name, argtypes, typeargtypes);
aoqi@0: }
aoqi@0: private Symbol resolveQualifiedMethod(MethodResolutionContext resolveContext,
aoqi@0: DiagnosticPosition pos, Env env,
aoqi@0: Symbol location, Type site, Name name, List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: return lookupMethod(env, pos, location, resolveContext, new BasicLookupHelper(name, site, argtypes, typeargtypes) {
aoqi@0: @Override
aoqi@0: Symbol doLookup(Env env, MethodResolutionPhase phase) {
aoqi@0: return findMethod(env, site, name, argtypes, typeargtypes,
aoqi@0: phase.isBoxingRequired(),
aoqi@0: phase.isVarargsRequired(), false);
aoqi@0: }
aoqi@0: @Override
aoqi@0: Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) {
aoqi@0: if (sym.kind >= AMBIGUOUS) {
aoqi@0: sym = super.access(env, pos, location, sym);
aoqi@0: } else if (allowMethodHandles) {
aoqi@0: MethodSymbol msym = (MethodSymbol)sym;
aoqi@0: if ((msym.flags() & SIGNATURE_POLYMORPHIC) != 0) {
aoqi@0: return findPolymorphicSignatureInstance(env, sym, argtypes);
aoqi@0: }
aoqi@0: }
aoqi@0: return sym;
aoqi@0: }
aoqi@0: });
aoqi@0: }
aoqi@0:
aoqi@0: /** Find or create an implicit method of exactly the given type (after erasure).
aoqi@0: * Searches in a side table, not the main scope of the site.
aoqi@0: * This emulates the lookup process required by JSR 292 in JVM.
aoqi@0: * @param env Attribution environment
aoqi@0: * @param spMethod signature polymorphic method - i.e. MH.invokeExact
aoqi@0: * @param argtypes The required argument types
aoqi@0: */
aoqi@0: Symbol findPolymorphicSignatureInstance(Env env,
aoqi@0: final Symbol spMethod,
aoqi@0: List argtypes) {
aoqi@0: Type mtype = infer.instantiatePolymorphicSignatureInstance(env,
aoqi@0: (MethodSymbol)spMethod, currentResolutionContext, argtypes);
aoqi@0: for (Symbol sym : polymorphicSignatureScope.getElementsByName(spMethod.name)) {
aoqi@0: if (types.isSameType(mtype, sym.type)) {
aoqi@0: return sym;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: // create the desired method
aoqi@0: long flags = ABSTRACT | HYPOTHETICAL | spMethod.flags() & Flags.AccessFlags;
aoqi@0: Symbol msym = new MethodSymbol(flags, spMethod.name, mtype, spMethod.owner) {
aoqi@0: @Override
aoqi@0: public Symbol baseSymbol() {
aoqi@0: return spMethod;
aoqi@0: }
aoqi@0: };
mcimadamore@3075: if (!mtype.isErroneous()) { // Cache only if kosher.
mcimadamore@3075: polymorphicSignatureScope.enter(msym);
mcimadamore@3075: }
aoqi@0: return msym;
aoqi@0: }
aoqi@0:
aoqi@0: /** Resolve a qualified method identifier, throw a fatal error if not
aoqi@0: * found.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the method invocation.
aoqi@0: * @param site The type of the qualifying expression, in which
aoqi@0: * identifier is searched.
aoqi@0: * @param name The identifier's name.
aoqi@0: * @param argtypes The types of the invocation's value arguments.
aoqi@0: * @param typeargtypes The types of the invocation's type arguments.
aoqi@0: */
aoqi@0: public MethodSymbol resolveInternalMethod(DiagnosticPosition pos, Env env,
aoqi@0: Type site, Name name,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: MethodResolutionContext resolveContext = new MethodResolutionContext();
aoqi@0: resolveContext.internalResolution = true;
aoqi@0: Symbol sym = resolveQualifiedMethod(resolveContext, pos, env, site.tsym,
aoqi@0: site, name, argtypes, typeargtypes);
aoqi@0: if (sym.kind == MTH) return (MethodSymbol)sym;
aoqi@0: else throw new FatalError(
aoqi@0: diags.fragment("fatal.err.cant.locate.meth",
aoqi@0: name));
aoqi@0: }
aoqi@0:
aoqi@0: /** Resolve constructor.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the constructor invocation.
aoqi@0: * @param site The type of class for which a constructor is searched.
aoqi@0: * @param argtypes The types of the constructor invocation's value
aoqi@0: * arguments.
aoqi@0: * @param typeargtypes The types of the constructor invocation's type
aoqi@0: * arguments.
aoqi@0: */
aoqi@0: Symbol resolveConstructor(DiagnosticPosition pos,
aoqi@0: Env env,
aoqi@0: Type site,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: return resolveConstructor(new MethodResolutionContext(), pos, env, site, argtypes, typeargtypes);
aoqi@0: }
aoqi@0:
aoqi@0: private Symbol resolveConstructor(MethodResolutionContext resolveContext,
aoqi@0: final DiagnosticPosition pos,
aoqi@0: Env env,
aoqi@0: Type site,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: return lookupMethod(env, pos, site.tsym, resolveContext, new BasicLookupHelper(names.init, site, argtypes, typeargtypes) {
aoqi@0: @Override
aoqi@0: Symbol doLookup(Env env, MethodResolutionPhase phase) {
aoqi@0: return findConstructor(pos, env, site, argtypes, typeargtypes,
aoqi@0: phase.isBoxingRequired(),
aoqi@0: phase.isVarargsRequired());
aoqi@0: }
aoqi@0: });
aoqi@0: }
aoqi@0:
aoqi@0: /** Resolve a constructor, throw a fatal error if not found.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the method invocation.
aoqi@0: * @param site The type to be constructed.
aoqi@0: * @param argtypes The types of the invocation's value arguments.
aoqi@0: * @param typeargtypes The types of the invocation's type arguments.
aoqi@0: */
aoqi@0: public MethodSymbol resolveInternalConstructor(DiagnosticPosition pos, Env env,
aoqi@0: Type site,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: MethodResolutionContext resolveContext = new MethodResolutionContext();
aoqi@0: resolveContext.internalResolution = true;
aoqi@0: Symbol sym = resolveConstructor(resolveContext, pos, env, site, argtypes, typeargtypes);
aoqi@0: if (sym.kind == MTH) return (MethodSymbol)sym;
aoqi@0: else throw new FatalError(
aoqi@0: diags.fragment("fatal.err.cant.locate.ctor", site));
aoqi@0: }
aoqi@0:
aoqi@0: Symbol findConstructor(DiagnosticPosition pos, Env env,
aoqi@0: Type site, List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: boolean allowBoxing,
aoqi@0: boolean useVarargs) {
aoqi@0: Symbol sym = findMethod(env, site,
aoqi@0: names.init, argtypes,
aoqi@0: typeargtypes, allowBoxing,
aoqi@0: useVarargs, false);
aoqi@0: chk.checkDeprecated(pos, env.info.scope.owner, sym);
aoqi@0: return sym;
aoqi@0: }
aoqi@0:
aoqi@0: /** Resolve constructor using diamond inference.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the constructor invocation.
aoqi@0: * @param site The type of class for which a constructor is searched.
aoqi@0: * The scope of this class has been touched in attribution.
aoqi@0: * @param argtypes The types of the constructor invocation's value
aoqi@0: * arguments.
aoqi@0: * @param typeargtypes The types of the constructor invocation's type
aoqi@0: * arguments.
aoqi@0: */
aoqi@0: Symbol resolveDiamond(DiagnosticPosition pos,
aoqi@0: Env env,
aoqi@0: Type site,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes) {
aoqi@0: return lookupMethod(env, pos, site.tsym, resolveMethodCheck,
aoqi@0: new BasicLookupHelper(names.init, site, argtypes, typeargtypes) {
aoqi@0: @Override
aoqi@0: Symbol doLookup(Env env, MethodResolutionPhase phase) {
aoqi@0: return findDiamond(env, site, argtypes, typeargtypes,
aoqi@0: phase.isBoxingRequired(),
aoqi@0: phase.isVarargsRequired());
aoqi@0: }
aoqi@0: @Override
aoqi@0: Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) {
aoqi@0: if (sym.kind >= AMBIGUOUS) {
aoqi@0: if (sym.kind != WRONG_MTH && sym.kind != WRONG_MTHS) {
aoqi@0: sym = super.access(env, pos, location, sym);
aoqi@0: } else {
aoqi@0: final JCDiagnostic details = sym.kind == WRONG_MTH ?
aoqi@0: ((InapplicableSymbolError)sym.baseSymbol()).errCandidate().snd :
aoqi@0: null;
aoqi@0: sym = new InapplicableSymbolError(sym.kind, "diamondError", currentResolutionContext) {
aoqi@0: @Override
aoqi@0: JCDiagnostic getDiagnostic(DiagnosticType dkind, DiagnosticPosition pos,
aoqi@0: Symbol location, Type site, Name name, List argtypes, List typeargtypes) {
aoqi@0: String key = details == null ?
aoqi@0: "cant.apply.diamond" :
aoqi@0: "cant.apply.diamond.1";
aoqi@0: return diags.create(dkind, log.currentSource(), pos, key,
aoqi@0: diags.fragment("diamond", site.tsym), details);
aoqi@0: }
aoqi@0: };
aoqi@0: sym = accessMethod(sym, pos, site, names.init, true, argtypes, typeargtypes);
aoqi@0: env.info.pendingResolutionPhase = currentResolutionContext.step;
aoqi@0: }
aoqi@0: }
aoqi@0: return sym;
aoqi@0: }});
aoqi@0: }
aoqi@0:
aoqi@0: /** This method scans all the constructor symbol in a given class scope -
aoqi@0: * assuming that the original scope contains a constructor of the kind:
aoqi@0: * {@code Foo(X x, Y y)}, where X,Y are class type-variables declared in Foo,
aoqi@0: * a method check is executed against the modified constructor type:
aoqi@0: * {@code Foo(X x, Y y)}. This is crucial in order to enable diamond
aoqi@0: * inference. The inferred return type of the synthetic constructor IS
aoqi@0: * the inferred type for the diamond operator.
aoqi@0: */
aoqi@0: private Symbol findDiamond(Env env,
aoqi@0: Type site,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: boolean allowBoxing,
aoqi@0: boolean useVarargs) {
aoqi@0: Symbol bestSoFar = methodNotFound;
aoqi@0: for (Scope.Entry e = site.tsym.members().lookup(names.init);
aoqi@0: e.scope != null;
aoqi@0: e = e.next()) {
aoqi@0: final Symbol sym = e.sym;
aoqi@0: //- System.out.println(" e " + e.sym);
aoqi@0: if (sym.kind == MTH &&
aoqi@0: (sym.flags_field & SYNTHETIC) == 0) {
aoqi@0: List oldParams = e.sym.type.hasTag(FORALL) ?
aoqi@0: ((ForAll)sym.type).tvars :
aoqi@0: List.nil();
aoqi@0: Type constrType = new ForAll(site.tsym.type.getTypeArguments().appendList(oldParams),
aoqi@0: types.createMethodTypeWithReturn(sym.type.asMethodType(), site));
aoqi@0: MethodSymbol newConstr = new MethodSymbol(sym.flags(), names.init, constrType, site.tsym) {
aoqi@0: @Override
aoqi@0: public Symbol baseSymbol() {
aoqi@0: return sym;
aoqi@0: }
aoqi@0: };
aoqi@0: bestSoFar = selectBest(env, site, argtypes, typeargtypes,
aoqi@0: newConstr,
aoqi@0: bestSoFar,
aoqi@0: allowBoxing,
aoqi@0: useVarargs,
aoqi@0: false);
aoqi@0: }
aoqi@0: }
aoqi@0: return bestSoFar;
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0:
aoqi@0: /** Resolve operator.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param optag The tag of the operation tree.
aoqi@0: * @param env The environment current at the operation.
aoqi@0: * @param argtypes The types of the operands.
aoqi@0: */
aoqi@0: Symbol resolveOperator(DiagnosticPosition pos, JCTree.Tag optag,
aoqi@0: Env env, List argtypes) {
aoqi@0: MethodResolutionContext prevResolutionContext = currentResolutionContext;
aoqi@0: try {
aoqi@0: currentResolutionContext = new MethodResolutionContext();
aoqi@0: Name name = treeinfo.operatorName(optag);
aoqi@0: return lookupMethod(env, pos, syms.predefClass, currentResolutionContext,
aoqi@0: new BasicLookupHelper(name, syms.predefClass.type, argtypes, null, BOX) {
aoqi@0: @Override
aoqi@0: Symbol doLookup(Env env, MethodResolutionPhase phase) {
aoqi@0: return findMethod(env, site, name, argtypes, typeargtypes,
aoqi@0: phase.isBoxingRequired(),
aoqi@0: phase.isVarargsRequired(), true);
aoqi@0: }
aoqi@0: @Override
aoqi@0: Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) {
aoqi@0: return accessMethod(sym, pos, env.enclClass.sym.type, name,
aoqi@0: false, argtypes, null);
aoqi@0: }
aoqi@0: });
aoqi@0: } finally {
aoqi@0: currentResolutionContext = prevResolutionContext;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /** Resolve operator.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param optag The tag of the operation tree.
aoqi@0: * @param env The environment current at the operation.
aoqi@0: * @param arg The type of the operand.
aoqi@0: */
aoqi@0: Symbol resolveUnaryOperator(DiagnosticPosition pos, JCTree.Tag optag, Env env, Type arg) {
aoqi@0: return resolveOperator(pos, optag, env, List.of(arg));
aoqi@0: }
aoqi@0:
aoqi@0: /** Resolve binary operator.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param optag The tag of the operation tree.
aoqi@0: * @param env The environment current at the operation.
aoqi@0: * @param left The types of the left operand.
aoqi@0: * @param right The types of the right operand.
aoqi@0: */
aoqi@0: Symbol resolveBinaryOperator(DiagnosticPosition pos,
aoqi@0: JCTree.Tag optag,
aoqi@0: Env env,
aoqi@0: Type left,
aoqi@0: Type right) {
aoqi@0: return resolveOperator(pos, optag, env, List.of(left, right));
aoqi@0: }
aoqi@0:
aoqi@0: Symbol getMemberReference(DiagnosticPosition pos,
aoqi@0: Env env,
aoqi@0: JCMemberReference referenceTree,
aoqi@0: Type site,
aoqi@0: Name name) {
aoqi@0:
aoqi@0: site = types.capture(site);
aoqi@0:
aoqi@0: ReferenceLookupHelper lookupHelper = makeReferenceLookupHelper(
aoqi@0: referenceTree, site, name, List.nil(), null, VARARITY);
aoqi@0:
aoqi@0: Env newEnv = env.dup(env.tree, env.info.dup());
aoqi@0: Symbol sym = lookupMethod(newEnv, env.tree.pos(), site.tsym,
aoqi@0: nilMethodCheck, lookupHelper);
aoqi@0:
aoqi@0: env.info.pendingResolutionPhase = newEnv.info.pendingResolutionPhase;
aoqi@0:
aoqi@0: return sym;
aoqi@0: }
aoqi@0:
aoqi@0: ReferenceLookupHelper makeReferenceLookupHelper(JCMemberReference referenceTree,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: MethodResolutionPhase maxPhase) {
aoqi@0: ReferenceLookupHelper result;
aoqi@0: if (!name.equals(names.init)) {
aoqi@0: //method reference
aoqi@0: result =
aoqi@0: new MethodReferenceLookupHelper(referenceTree, name, site, argtypes, typeargtypes, maxPhase);
aoqi@0: } else {
aoqi@0: if (site.hasTag(ARRAY)) {
aoqi@0: //array constructor reference
aoqi@0: result =
aoqi@0: new ArrayConstructorReferenceLookupHelper(referenceTree, site, argtypes, typeargtypes, maxPhase);
aoqi@0: } else {
aoqi@0: //class constructor reference
aoqi@0: result =
aoqi@0: new ConstructorReferenceLookupHelper(referenceTree, site, argtypes, typeargtypes, maxPhase);
aoqi@0: }
aoqi@0: }
aoqi@0: return result;
aoqi@0: }
aoqi@0:
aoqi@0: Symbol resolveMemberReferenceByArity(Env env,
aoqi@0: JCMemberReference referenceTree,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: List argtypes,
aoqi@0: InferenceContext inferenceContext) {
aoqi@0:
aoqi@0: boolean isStaticSelector = TreeInfo.isStaticSelector(referenceTree.expr, names);
aoqi@0: site = types.capture(site);
aoqi@0:
aoqi@0: ReferenceLookupHelper boundLookupHelper = makeReferenceLookupHelper(
aoqi@0: referenceTree, site, name, argtypes, null, VARARITY);
aoqi@0: //step 1 - bound lookup
aoqi@0: Env boundEnv = env.dup(env.tree, env.info.dup());
aoqi@0: Symbol boundSym = lookupMethod(boundEnv, env.tree.pos(), site.tsym,
aoqi@0: arityMethodCheck, boundLookupHelper);
aoqi@0: if (isStaticSelector &&
aoqi@0: !name.equals(names.init) &&
aoqi@0: !boundSym.isStatic() &&
aoqi@0: boundSym.kind < ERRONEOUS) {
aoqi@0: boundSym = methodNotFound;
aoqi@0: }
aoqi@0:
aoqi@0: //step 2 - unbound lookup
aoqi@0: Symbol unboundSym = methodNotFound;
aoqi@0: ReferenceLookupHelper unboundLookupHelper = null;
aoqi@0: Env unboundEnv = env.dup(env.tree, env.info.dup());
aoqi@0: if (isStaticSelector) {
aoqi@0: unboundLookupHelper = boundLookupHelper.unboundLookup(inferenceContext);
aoqi@0: unboundSym = lookupMethod(unboundEnv, env.tree.pos(), site.tsym,
aoqi@0: arityMethodCheck, unboundLookupHelper);
aoqi@0: if (unboundSym.isStatic() &&
aoqi@0: unboundSym.kind < ERRONEOUS) {
aoqi@0: unboundSym = methodNotFound;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: //merge results
aoqi@0: Symbol bestSym = choose(boundSym, unboundSym);
aoqi@0: env.info.pendingResolutionPhase = bestSym == unboundSym ?
aoqi@0: unboundEnv.info.pendingResolutionPhase :
aoqi@0: boundEnv.info.pendingResolutionPhase;
aoqi@0:
aoqi@0: return bestSym;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Resolution of member references is typically done as a single
aoqi@0: * overload resolution step, where the argument types A are inferred from
aoqi@0: * the target functional descriptor.
aoqi@0: *
aoqi@0: * If the member reference is a method reference with a type qualifier,
aoqi@0: * a two-step lookup process is performed. The first step uses the
aoqi@0: * expected argument list A, while the second step discards the first
aoqi@0: * type from A (which is treated as a receiver type).
aoqi@0: *
aoqi@0: * There are two cases in which inference is performed: (i) if the member
aoqi@0: * reference is a constructor reference and the qualifier type is raw - in
aoqi@0: * which case diamond inference is used to infer a parameterization for the
aoqi@0: * type qualifier; (ii) if the member reference is an unbound reference
aoqi@0: * where the type qualifier is raw - in that case, during the unbound lookup
aoqi@0: * the receiver argument type is used to infer an instantiation for the raw
aoqi@0: * qualifier type.
aoqi@0: *
aoqi@0: * When a multi-step resolution process is exploited, it is an error
aoqi@0: * if two candidates are found (ambiguity).
aoqi@0: *
aoqi@0: * This routine returns a pair (T,S), where S is the member reference symbol,
aoqi@0: * and T is the type of the class in which S is defined. This is necessary as
aoqi@0: * the type T might be dynamically inferred (i.e. if constructor reference
aoqi@0: * has a raw qualifier).
aoqi@0: */
aoqi@0: Pair resolveMemberReference(Env env,
aoqi@0: JCMemberReference referenceTree,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: List argtypes,
aoqi@0: List typeargtypes,
aoqi@0: MethodCheck methodCheck,
aoqi@0: InferenceContext inferenceContext,
aoqi@0: AttrMode mode) {
aoqi@0:
aoqi@0: site = types.capture(site);
aoqi@0: ReferenceLookupHelper boundLookupHelper = makeReferenceLookupHelper(
aoqi@0: referenceTree, site, name, argtypes, typeargtypes, VARARITY);
aoqi@0:
aoqi@0: //step 1 - bound lookup
aoqi@0: Env boundEnv = env.dup(env.tree, env.info.dup());
aoqi@0: Symbol origBoundSym;
aoqi@0: boolean staticErrorForBound = false;
aoqi@0: MethodResolutionContext boundSearchResolveContext = new MethodResolutionContext();
aoqi@0: boundSearchResolveContext.methodCheck = methodCheck;
aoqi@0: Symbol boundSym = origBoundSym = lookupMethod(boundEnv, env.tree.pos(),
aoqi@0: site.tsym, boundSearchResolveContext, boundLookupHelper);
aoqi@0: SearchResultKind boundSearchResultKind = SearchResultKind.NOT_APPLICABLE_MATCH;
aoqi@0: boolean isStaticSelector = TreeInfo.isStaticSelector(referenceTree.expr, names);
aoqi@0: boolean shouldCheckForStaticness = isStaticSelector &&
aoqi@0: referenceTree.getMode() == ReferenceMode.INVOKE;
aoqi@0: if (boundSym.kind != WRONG_MTHS && boundSym.kind != WRONG_MTH) {
aoqi@0: if (shouldCheckForStaticness) {
aoqi@0: if (!boundSym.isStatic()) {
aoqi@0: staticErrorForBound = true;
aoqi@0: if (hasAnotherApplicableMethod(
aoqi@0: boundSearchResolveContext, boundSym, true)) {
aoqi@0: boundSearchResultKind = SearchResultKind.BAD_MATCH_MORE_SPECIFIC;
aoqi@0: } else {
aoqi@0: boundSearchResultKind = SearchResultKind.BAD_MATCH;
aoqi@0: if (boundSym.kind < ERRONEOUS) {
aoqi@0: boundSym = methodWithCorrectStaticnessNotFound;
aoqi@0: }
aoqi@0: }
aoqi@0: } else if (boundSym.kind < ERRONEOUS) {
aoqi@0: boundSearchResultKind = SearchResultKind.GOOD_MATCH;
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: //step 2 - unbound lookup
aoqi@0: Symbol origUnboundSym = null;
aoqi@0: Symbol unboundSym = methodNotFound;
aoqi@0: ReferenceLookupHelper unboundLookupHelper = null;
aoqi@0: Env unboundEnv = env.dup(env.tree, env.info.dup());
aoqi@0: SearchResultKind unboundSearchResultKind = SearchResultKind.NOT_APPLICABLE_MATCH;
aoqi@0: boolean staticErrorForUnbound = false;
aoqi@0: if (isStaticSelector) {
aoqi@0: unboundLookupHelper = boundLookupHelper.unboundLookup(inferenceContext);
aoqi@0: MethodResolutionContext unboundSearchResolveContext =
aoqi@0: new MethodResolutionContext();
aoqi@0: unboundSearchResolveContext.methodCheck = methodCheck;
aoqi@0: unboundSym = origUnboundSym = lookupMethod(unboundEnv, env.tree.pos(),
aoqi@0: site.tsym, unboundSearchResolveContext, unboundLookupHelper);
aoqi@0:
aoqi@0: if (unboundSym.kind != WRONG_MTH && unboundSym.kind != WRONG_MTHS) {
aoqi@0: if (shouldCheckForStaticness) {
aoqi@0: if (unboundSym.isStatic()) {
aoqi@0: staticErrorForUnbound = true;
aoqi@0: if (hasAnotherApplicableMethod(
aoqi@0: unboundSearchResolveContext, unboundSym, false)) {
aoqi@0: unboundSearchResultKind = SearchResultKind.BAD_MATCH_MORE_SPECIFIC;
aoqi@0: } else {
aoqi@0: unboundSearchResultKind = SearchResultKind.BAD_MATCH;
aoqi@0: if (unboundSym.kind < ERRONEOUS) {
aoqi@0: unboundSym = methodWithCorrectStaticnessNotFound;
aoqi@0: }
aoqi@0: }
aoqi@0: } else if (unboundSym.kind < ERRONEOUS) {
aoqi@0: unboundSearchResultKind = SearchResultKind.GOOD_MATCH;
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: //merge results
aoqi@0: Pair res;
aoqi@0: Symbol bestSym = choose(boundSym, unboundSym);
aoqi@0: if (bestSym.kind < ERRONEOUS && (staticErrorForBound || staticErrorForUnbound)) {
aoqi@0: if (staticErrorForBound) {
aoqi@0: boundSym = methodWithCorrectStaticnessNotFound;
aoqi@0: }
aoqi@0: if (staticErrorForUnbound) {
aoqi@0: unboundSym = methodWithCorrectStaticnessNotFound;
aoqi@0: }
aoqi@0: bestSym = choose(boundSym, unboundSym);
aoqi@0: }
aoqi@0: if (bestSym == methodWithCorrectStaticnessNotFound && mode == AttrMode.CHECK) {
aoqi@0: Symbol symToPrint = origBoundSym;
aoqi@0: String errorFragmentToPrint = "non-static.cant.be.ref";
aoqi@0: if (staticErrorForBound && staticErrorForUnbound) {
aoqi@0: if (unboundSearchResultKind == SearchResultKind.BAD_MATCH_MORE_SPECIFIC) {
aoqi@0: symToPrint = origUnboundSym;
aoqi@0: errorFragmentToPrint = "static.method.in.unbound.lookup";
aoqi@0: }
aoqi@0: } else {
aoqi@0: if (!staticErrorForBound) {
aoqi@0: symToPrint = origUnboundSym;
aoqi@0: errorFragmentToPrint = "static.method.in.unbound.lookup";
aoqi@0: }
aoqi@0: }
aoqi@0: log.error(referenceTree.expr.pos(), "invalid.mref",
aoqi@0: Kinds.kindName(referenceTree.getMode()),
aoqi@0: diags.fragment(errorFragmentToPrint,
aoqi@0: Kinds.kindName(symToPrint), symToPrint));
aoqi@0: }
aoqi@0: res = new Pair<>(bestSym,
aoqi@0: bestSym == unboundSym ? unboundLookupHelper : boundLookupHelper);
aoqi@0: env.info.pendingResolutionPhase = bestSym == unboundSym ?
aoqi@0: unboundEnv.info.pendingResolutionPhase :
aoqi@0: boundEnv.info.pendingResolutionPhase;
aoqi@0:
aoqi@0: return res;
aoqi@0: }
aoqi@0:
aoqi@0: enum SearchResultKind {
aoqi@0: GOOD_MATCH, //type I
aoqi@0: BAD_MATCH_MORE_SPECIFIC, //type II
aoqi@0: BAD_MATCH, //type III
aoqi@0: NOT_APPLICABLE_MATCH //type IV
aoqi@0: }
aoqi@0:
aoqi@0: boolean hasAnotherApplicableMethod(MethodResolutionContext resolutionContext,
aoqi@0: Symbol bestSoFar, boolean staticMth) {
aoqi@0: for (Candidate c : resolutionContext.candidates) {
aoqi@0: if (resolutionContext.step != c.step ||
aoqi@0: !c.isApplicable() ||
aoqi@0: c.sym == bestSoFar) {
aoqi@0: continue;
aoqi@0: } else {
aoqi@0: if (c.sym.isStatic() == staticMth) {
aoqi@0: return true;
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0: return false;
aoqi@0: }
aoqi@0:
aoqi@0: //where
aoqi@0: private Symbol choose(Symbol boundSym, Symbol unboundSym) {
aoqi@0: if (lookupSuccess(boundSym) && lookupSuccess(unboundSym)) {
aoqi@0: return ambiguityError(boundSym, unboundSym);
aoqi@0: } else if (lookupSuccess(boundSym) ||
aoqi@0: (canIgnore(unboundSym) && !canIgnore(boundSym))) {
aoqi@0: return boundSym;
aoqi@0: } else if (lookupSuccess(unboundSym) ||
aoqi@0: (canIgnore(boundSym) && !canIgnore(unboundSym))) {
aoqi@0: return unboundSym;
aoqi@0: } else {
aoqi@0: return boundSym;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: private boolean lookupSuccess(Symbol s) {
aoqi@0: return s.kind == MTH || s.kind == AMBIGUOUS;
aoqi@0: }
aoqi@0:
aoqi@0: private boolean canIgnore(Symbol s) {
aoqi@0: switch (s.kind) {
aoqi@0: case ABSENT_MTH:
aoqi@0: return true;
aoqi@0: case WRONG_MTH:
aoqi@0: InapplicableSymbolError errSym =
aoqi@0: (InapplicableSymbolError)s.baseSymbol();
aoqi@0: return new Template(MethodCheckDiag.ARITY_MISMATCH.regex())
aoqi@0: .matches(errSym.errCandidate().snd);
aoqi@0: case WRONG_MTHS:
aoqi@0: InapplicableSymbolsError errSyms =
aoqi@0: (InapplicableSymbolsError)s.baseSymbol();
aoqi@0: return errSyms.filterCandidates(errSyms.mapCandidates()).isEmpty();
aoqi@0: case WRONG_STATICNESS:
aoqi@0: return false;
aoqi@0: default:
aoqi@0: return false;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Helper for defining custom method-like lookup logic; a lookup helper
aoqi@0: * provides hooks for (i) the actual lookup logic and (ii) accessing the
aoqi@0: * lookup result (this step might result in compiler diagnostics to be generated)
aoqi@0: */
aoqi@0: abstract class LookupHelper {
aoqi@0:
aoqi@0: /** name of the symbol to lookup */
aoqi@0: Name name;
aoqi@0:
aoqi@0: /** location in which the lookup takes place */
aoqi@0: Type site;
aoqi@0:
aoqi@0: /** actual types used during the lookup */
aoqi@0: List argtypes;
aoqi@0:
aoqi@0: /** type arguments used during the lookup */
aoqi@0: List typeargtypes;
aoqi@0:
aoqi@0: /** Max overload resolution phase handled by this helper */
aoqi@0: MethodResolutionPhase maxPhase;
aoqi@0:
aoqi@0: LookupHelper(Name name, Type site, List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) {
aoqi@0: this.name = name;
aoqi@0: this.site = site;
aoqi@0: this.argtypes = argtypes;
aoqi@0: this.typeargtypes = typeargtypes;
aoqi@0: this.maxPhase = maxPhase;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Should lookup stop at given phase with given result
aoqi@0: */
mcimadamore@2559: final boolean shouldStop(Symbol sym, MethodResolutionPhase phase) {
aoqi@0: return phase.ordinal() > maxPhase.ordinal() ||
aoqi@0: sym.kind < ERRONEOUS || sym.kind == AMBIGUOUS;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Search for a symbol under a given overload resolution phase - this method
aoqi@0: * is usually called several times, once per each overload resolution phase
aoqi@0: */
aoqi@0: abstract Symbol lookup(Env env, MethodResolutionPhase phase);
aoqi@0:
aoqi@0: /**
aoqi@0: * Dump overload resolution info
aoqi@0: */
aoqi@0: void debug(DiagnosticPosition pos, Symbol sym) {
aoqi@0: //do nothing
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Validate the result of the lookup
aoqi@0: */
aoqi@0: abstract Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym);
aoqi@0: }
aoqi@0:
aoqi@0: abstract class BasicLookupHelper extends LookupHelper {
aoqi@0:
aoqi@0: BasicLookupHelper(Name name, Type site, List argtypes, List typeargtypes) {
aoqi@0: this(name, site, argtypes, typeargtypes, MethodResolutionPhase.VARARITY);
aoqi@0: }
aoqi@0:
aoqi@0: BasicLookupHelper(Name name, Type site, List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) {
aoqi@0: super(name, site, argtypes, typeargtypes, maxPhase);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: final Symbol lookup(Env env, MethodResolutionPhase phase) {
aoqi@0: Symbol sym = doLookup(env, phase);
aoqi@0: if (sym.kind == AMBIGUOUS) {
aoqi@0: AmbiguityError a_err = (AmbiguityError)sym.baseSymbol();
aoqi@0: sym = a_err.mergeAbstracts(site);
aoqi@0: }
aoqi@0: return sym;
aoqi@0: }
aoqi@0:
aoqi@0: abstract Symbol doLookup(Env env, MethodResolutionPhase phase);
aoqi@0:
aoqi@0: @Override
aoqi@0: Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) {
aoqi@0: if (sym.kind >= AMBIGUOUS) {
aoqi@0: //if nothing is found return the 'first' error
aoqi@0: sym = accessMethod(sym, pos, location, site, name, true, argtypes, typeargtypes);
aoqi@0: }
aoqi@0: return sym;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: void debug(DiagnosticPosition pos, Symbol sym) {
aoqi@0: reportVerboseResolutionDiagnostic(pos, name, site, argtypes, typeargtypes, sym);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Helper class for member reference lookup. A reference lookup helper
aoqi@0: * defines the basic logic for member reference lookup; a method gives
aoqi@0: * access to an 'unbound' helper used to perform an unbound member
aoqi@0: * reference lookup.
aoqi@0: */
aoqi@0: abstract class ReferenceLookupHelper extends LookupHelper {
aoqi@0:
aoqi@0: /** The member reference tree */
aoqi@0: JCMemberReference referenceTree;
aoqi@0:
aoqi@0: ReferenceLookupHelper(JCMemberReference referenceTree, Name name, Type site,
aoqi@0: List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) {
aoqi@0: super(name, site, argtypes, typeargtypes, maxPhase);
aoqi@0: this.referenceTree = referenceTree;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Returns an unbound version of this lookup helper. By default, this
aoqi@0: * method returns an dummy lookup helper.
aoqi@0: */
aoqi@0: ReferenceLookupHelper unboundLookup(InferenceContext inferenceContext) {
aoqi@0: //dummy loopkup helper that always return 'methodNotFound'
aoqi@0: return new ReferenceLookupHelper(referenceTree, name, site, argtypes, typeargtypes, maxPhase) {
aoqi@0: @Override
aoqi@0: ReferenceLookupHelper unboundLookup(InferenceContext inferenceContext) {
aoqi@0: return this;
aoqi@0: }
aoqi@0: @Override
aoqi@0: Symbol lookup(Env env, MethodResolutionPhase phase) {
aoqi@0: return methodNotFound;
aoqi@0: }
aoqi@0: @Override
aoqi@0: ReferenceKind referenceKind(Symbol sym) {
aoqi@0: Assert.error();
aoqi@0: return null;
aoqi@0: }
aoqi@0: };
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Get the kind of the member reference
aoqi@0: */
aoqi@0: abstract JCMemberReference.ReferenceKind referenceKind(Symbol sym);
aoqi@0:
aoqi@0: Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) {
aoqi@0: if (sym.kind == AMBIGUOUS) {
aoqi@0: AmbiguityError a_err = (AmbiguityError)sym.baseSymbol();
aoqi@0: sym = a_err.mergeAbstracts(site);
aoqi@0: }
aoqi@0: //skip error reporting
aoqi@0: return sym;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Helper class for method reference lookup. The lookup logic is based
aoqi@0: * upon Resolve.findMethod; in certain cases, this helper class has a
aoqi@0: * corresponding unbound helper class (see UnboundMethodReferenceLookupHelper).
aoqi@0: * In such cases, non-static lookup results are thrown away.
aoqi@0: */
aoqi@0: class MethodReferenceLookupHelper extends ReferenceLookupHelper {
aoqi@0:
aoqi@0: MethodReferenceLookupHelper(JCMemberReference referenceTree, Name name, Type site,
aoqi@0: List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) {
aoqi@0: super(referenceTree, name, site, argtypes, typeargtypes, maxPhase);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: final Symbol lookup(Env env, MethodResolutionPhase phase) {
aoqi@0: return findMethod(env, site, name, argtypes, typeargtypes,
aoqi@0: phase.isBoxingRequired(), phase.isVarargsRequired(), syms.operatorNames.contains(name));
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: ReferenceLookupHelper unboundLookup(InferenceContext inferenceContext) {
aoqi@0: if (TreeInfo.isStaticSelector(referenceTree.expr, names) &&
aoqi@0: argtypes.nonEmpty() &&
aoqi@0: (argtypes.head.hasTag(NONE) ||
aoqi@0: types.isSubtypeUnchecked(inferenceContext.asUndetVar(argtypes.head), site))) {
aoqi@0: return new UnboundMethodReferenceLookupHelper(referenceTree, name,
aoqi@0: site, argtypes, typeargtypes, maxPhase);
aoqi@0: } else {
aoqi@0: return super.unboundLookup(inferenceContext);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: ReferenceKind referenceKind(Symbol sym) {
aoqi@0: if (sym.isStatic()) {
aoqi@0: return ReferenceKind.STATIC;
aoqi@0: } else {
aoqi@0: Name selName = TreeInfo.name(referenceTree.getQualifierExpression());
aoqi@0: return selName != null && selName == names._super ?
aoqi@0: ReferenceKind.SUPER :
aoqi@0: ReferenceKind.BOUND;
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Helper class for unbound method reference lookup. Essentially the same
aoqi@0: * as the basic method reference lookup helper; main difference is that static
aoqi@0: * lookup results are thrown away. If qualifier type is raw, an attempt to
aoqi@0: * infer a parameterized type is made using the first actual argument (that
aoqi@0: * would otherwise be ignored during the lookup).
aoqi@0: */
aoqi@0: class UnboundMethodReferenceLookupHelper extends MethodReferenceLookupHelper {
aoqi@0:
aoqi@0: UnboundMethodReferenceLookupHelper(JCMemberReference referenceTree, Name name, Type site,
aoqi@0: List argtypes, List typeargtypes, MethodResolutionPhase maxPhase) {
aoqi@0: super(referenceTree, name, site, argtypes.tail, typeargtypes, maxPhase);
aoqi@0: if (site.isRaw() && !argtypes.head.hasTag(NONE)) {
aoqi@0: Type asSuperSite = types.asSuper(argtypes.head, site.tsym);
vromero@2611: this.site = types.capture(asSuperSite);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: ReferenceLookupHelper unboundLookup(InferenceContext inferenceContext) {
aoqi@0: return this;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: ReferenceKind referenceKind(Symbol sym) {
aoqi@0: return ReferenceKind.UNBOUND;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Helper class for array constructor lookup; an array constructor lookup
aoqi@0: * is simulated by looking up a method that returns the array type specified
aoqi@0: * as qualifier, and that accepts a single int parameter (size of the array).
aoqi@0: */
aoqi@0: class ArrayConstructorReferenceLookupHelper extends ReferenceLookupHelper {
aoqi@0:
aoqi@0: ArrayConstructorReferenceLookupHelper(JCMemberReference referenceTree, Type site, List argtypes,
aoqi@0: List typeargtypes, MethodResolutionPhase maxPhase) {
aoqi@0: super(referenceTree, names.init, site, argtypes, typeargtypes, maxPhase);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: protected Symbol lookup(Env env, MethodResolutionPhase phase) {
aoqi@0: Scope sc = new Scope(syms.arrayClass);
aoqi@0: MethodSymbol arrayConstr = new MethodSymbol(PUBLIC, name, null, site.tsym);
aoqi@0: arrayConstr.type = new MethodType(List.of(syms.intType), site, List.nil(), syms.methodClass);
aoqi@0: sc.enter(arrayConstr);
aoqi@0: return findMethodInScope(env, site, name, argtypes, typeargtypes, sc, methodNotFound, phase.isBoxingRequired(), phase.isVarargsRequired(), false, false);
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: ReferenceKind referenceKind(Symbol sym) {
aoqi@0: return ReferenceKind.ARRAY_CTOR;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Helper class for constructor reference lookup. The lookup logic is based
aoqi@0: * upon either Resolve.findMethod or Resolve.findDiamond - depending on
aoqi@0: * whether the constructor reference needs diamond inference (this is the case
aoqi@0: * if the qualifier type is raw). A special erroneous symbol is returned
aoqi@0: * if the lookup returns the constructor of an inner class and there's no
aoqi@0: * enclosing instance in scope.
aoqi@0: */
aoqi@0: class ConstructorReferenceLookupHelper extends ReferenceLookupHelper {
aoqi@0:
aoqi@0: boolean needsInference;
aoqi@0:
aoqi@0: ConstructorReferenceLookupHelper(JCMemberReference referenceTree, Type site, List argtypes,
aoqi@0: List typeargtypes, MethodResolutionPhase maxPhase) {
aoqi@0: super(referenceTree, names.init, site, argtypes, typeargtypes, maxPhase);
aoqi@0: if (site.isRaw()) {
aoqi@0: this.site = new ClassType(site.getEnclosingType(), site.tsym.type.getTypeArguments(), site.tsym);
aoqi@0: needsInference = true;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: protected Symbol lookup(Env env, MethodResolutionPhase phase) {
aoqi@0: Symbol sym = needsInference ?
aoqi@0: findDiamond(env, site, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()) :
aoqi@0: findMethod(env, site, name, argtypes, typeargtypes,
aoqi@0: phase.isBoxingRequired(), phase.isVarargsRequired(), syms.operatorNames.contains(name));
aoqi@0: return sym.kind != MTH ||
aoqi@0: site.getEnclosingType().hasTag(NONE) ||
aoqi@0: hasEnclosingInstance(env, site) ?
aoqi@0: sym : new InvalidSymbolError(Kinds.MISSING_ENCL, sym, null) {
aoqi@0: @Override
aoqi@0: JCDiagnostic getDiagnostic(DiagnosticType dkind, DiagnosticPosition pos, Symbol location, Type site, Name name, List argtypes, List typeargtypes) {
aoqi@0: return diags.create(dkind, log.currentSource(), pos,
aoqi@0: "cant.access.inner.cls.constr", site.tsym.name, argtypes, site.getEnclosingType());
aoqi@0: }
aoqi@0: };
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: ReferenceKind referenceKind(Symbol sym) {
aoqi@0: return site.getEnclosingType().hasTag(NONE) ?
aoqi@0: ReferenceKind.TOPLEVEL : ReferenceKind.IMPLICIT_INNER;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Main overload resolution routine. On each overload resolution step, a
aoqi@0: * lookup helper class is used to perform the method/constructor lookup;
aoqi@0: * at the end of the lookup, the helper is used to validate the results
aoqi@0: * (this last step might trigger overload resolution diagnostics).
aoqi@0: */
aoqi@0: Symbol lookupMethod(Env env, DiagnosticPosition pos, Symbol location, MethodCheck methodCheck, LookupHelper lookupHelper) {
aoqi@0: MethodResolutionContext resolveContext = new MethodResolutionContext();
aoqi@0: resolveContext.methodCheck = methodCheck;
aoqi@0: return lookupMethod(env, pos, location, resolveContext, lookupHelper);
aoqi@0: }
aoqi@0:
aoqi@0: Symbol lookupMethod(Env env, DiagnosticPosition pos, Symbol location,
aoqi@0: MethodResolutionContext resolveContext, LookupHelper lookupHelper) {
aoqi@0: MethodResolutionContext prevResolutionContext = currentResolutionContext;
aoqi@0: try {
aoqi@0: Symbol bestSoFar = methodNotFound;
aoqi@0: currentResolutionContext = resolveContext;
aoqi@0: for (MethodResolutionPhase phase : methodResolutionSteps) {
aoqi@0: if (!phase.isApplicable(boxingEnabled, varargsEnabled) ||
aoqi@0: lookupHelper.shouldStop(bestSoFar, phase)) break;
aoqi@0: MethodResolutionPhase prevPhase = currentResolutionContext.step;
aoqi@0: Symbol prevBest = bestSoFar;
aoqi@0: currentResolutionContext.step = phase;
aoqi@0: Symbol sym = lookupHelper.lookup(env, phase);
aoqi@0: lookupHelper.debug(pos, sym);
aoqi@0: bestSoFar = phase.mergeResults(bestSoFar, sym);
aoqi@0: env.info.pendingResolutionPhase = (prevBest == bestSoFar) ? prevPhase : phase;
aoqi@0: }
aoqi@0: return lookupHelper.access(env, pos, location, bestSoFar);
aoqi@0: } finally {
aoqi@0: currentResolutionContext = prevResolutionContext;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Resolve `c.name' where name == this or name == super.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the expression.
aoqi@0: * @param c The qualifier.
aoqi@0: * @param name The identifier's name.
aoqi@0: */
aoqi@0: Symbol resolveSelf(DiagnosticPosition pos,
aoqi@0: Env env,
aoqi@0: TypeSymbol c,
aoqi@0: Name name) {
aoqi@0: Env env1 = env;
aoqi@0: boolean staticOnly = false;
aoqi@0: while (env1.outer != null) {
aoqi@0: if (isStatic(env1)) staticOnly = true;
aoqi@0: if (env1.enclClass.sym == c) {
aoqi@0: Symbol sym = env1.info.scope.lookup(name).sym;
aoqi@0: if (sym != null) {
aoqi@0: if (staticOnly) sym = new StaticError(sym);
aoqi@0: return accessBase(sym, pos, env.enclClass.sym.type,
aoqi@0: name, true);
aoqi@0: }
aoqi@0: }
aoqi@0: if ((env1.enclClass.sym.flags() & STATIC) != 0) staticOnly = true;
aoqi@0: env1 = env1.outer;
aoqi@0: }
aoqi@0: if (c.isInterface() &&
aoqi@0: name == names._super && !isStatic(env) &&
aoqi@0: types.isDirectSuperInterface(c, env.enclClass.sym)) {
aoqi@0: //this might be a default super call if one of the superinterfaces is 'c'
aoqi@0: for (Type t : pruneInterfaces(env.enclClass.type)) {
aoqi@0: if (t.tsym == c) {
aoqi@0: env.info.defaultSuperCallSite = t;
aoqi@0: return new VarSymbol(0, names._super,
aoqi@0: types.asSuper(env.enclClass.type, c), env.enclClass.sym);
aoqi@0: }
aoqi@0: }
aoqi@0: //find a direct superinterface that is a subtype of 'c'
aoqi@0: for (Type i : types.interfaces(env.enclClass.type)) {
aoqi@0: if (i.tsym.isSubClass(c, types) && i.tsym != c) {
aoqi@0: log.error(pos, "illegal.default.super.call", c,
aoqi@0: diags.fragment("redundant.supertype", c, i));
aoqi@0: return syms.errSymbol;
aoqi@0: }
aoqi@0: }
aoqi@0: Assert.error();
aoqi@0: }
aoqi@0: log.error(pos, "not.encl.class", c);
aoqi@0: return syms.errSymbol;
aoqi@0: }
aoqi@0: //where
aoqi@0: private List pruneInterfaces(Type t) {
aoqi@0: ListBuffer result = new ListBuffer<>();
aoqi@0: for (Type t1 : types.interfaces(t)) {
aoqi@0: boolean shouldAdd = true;
aoqi@0: for (Type t2 : types.interfaces(t)) {
aoqi@0: if (t1 != t2 && types.isSubtypeNoCapture(t2, t1)) {
aoqi@0: shouldAdd = false;
aoqi@0: }
aoqi@0: }
aoqi@0: if (shouldAdd) {
aoqi@0: result.append(t1);
aoqi@0: }
aoqi@0: }
aoqi@0: return result.toList();
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * Resolve `c.this' for an enclosing class c that contains the
aoqi@0: * named member.
aoqi@0: * @param pos The position to use for error reporting.
aoqi@0: * @param env The environment current at the expression.
aoqi@0: * @param member The member that must be contained in the result.
aoqi@0: */
aoqi@0: Symbol resolveSelfContaining(DiagnosticPosition pos,
aoqi@0: Env env,
aoqi@0: Symbol member,
aoqi@0: boolean isSuperCall) {
aoqi@0: Symbol sym = resolveSelfContainingInternal(env, member, isSuperCall);
aoqi@0: if (sym == null) {
aoqi@0: log.error(pos, "encl.class.required", member);
aoqi@0: return syms.errSymbol;
aoqi@0: } else {
aoqi@0: return accessBase(sym, pos, env.enclClass.sym.type, sym.name, true);
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: boolean hasEnclosingInstance(Env env, Type type) {
aoqi@0: Symbol encl = resolveSelfContainingInternal(env, type.tsym, false);
aoqi@0: return encl != null && encl.kind < ERRONEOUS;
aoqi@0: }
aoqi@0:
aoqi@0: private Symbol resolveSelfContainingInternal(Env env,
aoqi@0: Symbol member,
aoqi@0: boolean isSuperCall) {
aoqi@0: Name name = names._this;
aoqi@0: Env env1 = isSuperCall ? env.outer : env;
aoqi@0: boolean staticOnly = false;
aoqi@0: if (env1 != null) {
aoqi@0: while (env1 != null && env1.outer != null) {
aoqi@0: if (isStatic(env1)) staticOnly = true;
aoqi@0: if (env1.enclClass.sym.isSubClass(member.owner, types)) {
aoqi@0: Symbol sym = env1.info.scope.lookup(name).sym;
aoqi@0: if (sym != null) {
aoqi@0: if (staticOnly) sym = new StaticError(sym);
aoqi@0: return sym;
aoqi@0: }
aoqi@0: }
aoqi@0: if ((env1.enclClass.sym.flags() & STATIC) != 0)
aoqi@0: staticOnly = true;
aoqi@0: env1 = env1.outer;
aoqi@0: }
aoqi@0: }
aoqi@0: return null;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Resolve an appropriate implicit this instance for t's container.
aoqi@0: * JLS 8.8.5.1 and 15.9.2
aoqi@0: */
aoqi@0: Type resolveImplicitThis(DiagnosticPosition pos, Env env, Type t) {
aoqi@0: return resolveImplicitThis(pos, env, t, false);
aoqi@0: }
aoqi@0:
aoqi@0: Type resolveImplicitThis(DiagnosticPosition pos, Env env, Type t, boolean isSuperCall) {
aoqi@0: Type thisType = (((t.tsym.owner.kind & (MTH|VAR)) != 0)
aoqi@0: ? resolveSelf(pos, env, t.getEnclosingType().tsym, names._this)
aoqi@0: : resolveSelfContaining(pos, env, t.tsym, isSuperCall)).type;
aoqi@0: if (env.info.isSelfCall && thisType.tsym == env.enclClass.sym)
aoqi@0: log.error(pos, "cant.ref.before.ctor.called", "this");
aoqi@0: return thisType;
aoqi@0: }
aoqi@0:
aoqi@0: /* ***************************************************************************
aoqi@0: * ResolveError classes, indicating error situations when accessing symbols
aoqi@0: ****************************************************************************/
aoqi@0:
aoqi@0: //used by TransTypes when checking target type of synthetic cast
aoqi@0: public void logAccessErrorInternal(Env env, JCTree tree, Type type) {
aoqi@0: AccessError error = new AccessError(env, env.enclClass.type, type.tsym);
aoqi@0: logResolveError(error, tree.pos(), env.enclClass.sym, env.enclClass.type, null, null, null);
aoqi@0: }
aoqi@0: //where
aoqi@0: private void logResolveError(ResolveError error,
aoqi@0: DiagnosticPosition pos,
aoqi@0: Symbol location,
aoqi@0: Type site,
aoqi@0: Name name,
aoqi@0: List argtypes,
aoqi@0: List