mcimadamore@1347: /* mcimadamore@1674: * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. mcimadamore@1347: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. mcimadamore@1347: * mcimadamore@1347: * This code is free software; you can redistribute it and/or modify it mcimadamore@1347: * under the terms of the GNU General Public License version 2 only, as mcimadamore@1347: * published by the Free Software Foundation. Oracle designates this mcimadamore@1347: * particular file as subject to the "Classpath" exception as provided mcimadamore@1347: * by Oracle in the LICENSE file that accompanied this code. mcimadamore@1347: * mcimadamore@1347: * This code is distributed in the hope that it will be useful, but WITHOUT mcimadamore@1347: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or mcimadamore@1347: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License mcimadamore@1347: * version 2 for more details (a copy is included in the LICENSE file that mcimadamore@1347: * accompanied this code). mcimadamore@1347: * mcimadamore@1347: * You should have received a copy of the GNU General Public License version mcimadamore@1347: * 2 along with this work; if not, write to the Free Software Foundation, mcimadamore@1347: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. mcimadamore@1347: * mcimadamore@1347: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA mcimadamore@1347: * or visit www.oracle.com if you need additional information or have any mcimadamore@1347: * questions. mcimadamore@1347: */ mcimadamore@1347: mcimadamore@1347: package com.sun.tools.javac.comp; mcimadamore@1347: vromero@2000: import com.sun.source.tree.MemberReferenceTree; mcimadamore@1347: import com.sun.tools.javac.code.*; mcimadamore@1347: import com.sun.tools.javac.tree.*; mcimadamore@1347: import com.sun.tools.javac.util.*; mcimadamore@1674: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; mcimadamore@1347: import com.sun.tools.javac.code.Symbol.*; mcimadamore@1347: import com.sun.tools.javac.code.Type.*; mcimadamore@1347: import com.sun.tools.javac.comp.Attr.ResultInfo; mcimadamore@1347: import com.sun.tools.javac.comp.Infer.InferenceContext; mcimadamore@1347: import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; mcimadamore@1897: import com.sun.tools.javac.comp.Resolve.ReferenceLookupHelper; mcimadamore@1347: import com.sun.tools.javac.tree.JCTree.*; mcimadamore@1347: mcimadamore@1347: mcimadamore@1347: import java.util.ArrayList; vromero@2000: import java.util.Collections; mcimadamore@1481: import java.util.EnumSet; vromero@2000: import java.util.LinkedHashMap; mcimadamore@1415: import java.util.LinkedHashSet; mcimadamore@1347: import java.util.Map; mcimadamore@1347: import java.util.Set; mcimadamore@1347: import java.util.WeakHashMap; mcimadamore@1347: mcimadamore@1415: import static com.sun.tools.javac.code.TypeTag.*; mcimadamore@1347: import static com.sun.tools.javac.tree.JCTree.Tag.*; mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * This is an helper class that is used to perform deferred type-analysis. mcimadamore@1347: * Each time a poly expression occurs in argument position, javac attributes it mcimadamore@1347: * with a temporary 'deferred type' that is checked (possibly multiple times) mcimadamore@1347: * against an expected formal type. mcimadamore@1347: * mcimadamore@1347: *

This is NOT part of any supported API. mcimadamore@1347: * If you write code that depends on this, you do so at your own risk. mcimadamore@1347: * This code and its internal interfaces are subject to change or mcimadamore@1347: * deletion without notice. mcimadamore@1347: */ mcimadamore@1347: public class DeferredAttr extends JCTree.Visitor { mcimadamore@1347: protected static final Context.Key deferredAttrKey = mcimadamore@1347: new Context.Key(); mcimadamore@1347: mcimadamore@1347: final Attr attr; mcimadamore@1347: final Check chk; mcimadamore@1510: final JCDiagnostic.Factory diags; mcimadamore@1347: final Enter enter; mcimadamore@1347: final Infer infer; mcimadamore@1581: final Resolve rs; mcimadamore@1347: final Log log; mcimadamore@1347: final Symtab syms; mcimadamore@1347: final TreeMaker make; mcimadamore@1347: final Types types; mcimadamore@1347: mcimadamore@1347: public static DeferredAttr instance(Context context) { mcimadamore@1347: DeferredAttr instance = context.get(deferredAttrKey); mcimadamore@1347: if (instance == null) mcimadamore@1347: instance = new DeferredAttr(context); mcimadamore@1347: return instance; mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: protected DeferredAttr(Context context) { mcimadamore@1347: context.put(deferredAttrKey, this); mcimadamore@1347: attr = Attr.instance(context); mcimadamore@1347: chk = Check.instance(context); mcimadamore@1510: diags = JCDiagnostic.Factory.instance(context); mcimadamore@1347: enter = Enter.instance(context); mcimadamore@1347: infer = Infer.instance(context); mcimadamore@1581: rs = Resolve.instance(context); mcimadamore@1347: log = Log.instance(context); mcimadamore@1347: syms = Symtab.instance(context); mcimadamore@1347: make = TreeMaker.instance(context); mcimadamore@1347: types = Types.instance(context); mcimadamore@1510: Names names = Names.instance(context); mcimadamore@1889: stuckTree = make.Ident(names.empty).setType(Type.stuckType); mcimadamore@1897: emptyDeferredAttrContext = mcimadamore@1897: new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) { mcimadamore@1897: @Override vromero@2000: void addDeferredAttrNode(DeferredType dt, ResultInfo ri, DeferredStuckPolicy deferredStuckPolicy) { mcimadamore@1897: Assert.error("Empty deferred context!"); mcimadamore@1897: } mcimadamore@1897: @Override mcimadamore@1897: void complete() { mcimadamore@1897: Assert.error("Empty deferred context!"); mcimadamore@1897: } mcimadamore@1897: }; mcimadamore@1347: } mcimadamore@1347: mcimadamore@1510: /** shared tree for stuck expressions */ mcimadamore@1510: final JCTree stuckTree; mcimadamore@1510: mcimadamore@1347: /** mcimadamore@1347: * This type represents a deferred type. A deferred type starts off with mcimadamore@1347: * no information on the underlying expression type. Such info needs to be mcimadamore@1347: * discovered through type-checking the deferred type against a target-type. mcimadamore@1347: * Every deferred type keeps a pointer to the AST node from which it originated. mcimadamore@1347: */ mcimadamore@1347: public class DeferredType extends Type { mcimadamore@1347: mcimadamore@1347: public JCExpression tree; mcimadamore@1347: Env env; mcimadamore@1347: AttrMode mode; mcimadamore@1347: SpeculativeCache speculativeCache; mcimadamore@1347: mcimadamore@1347: DeferredType(JCExpression tree, Env env) { vromero@1853: super(null); mcimadamore@1347: this.tree = tree; mcimadamore@1899: this.env = attr.copyEnv(env); mcimadamore@1347: this.speculativeCache = new SpeculativeCache(); mcimadamore@1347: } mcimadamore@1347: vromero@1853: @Override vromero@1853: public TypeTag getTag() { vromero@1853: return DEFERRED; vromero@1853: } vromero@1853: mcimadamore@1347: /** mcimadamore@1347: * A speculative cache is used to keep track of all overload resolution rounds mcimadamore@1347: * that triggered speculative attribution on a given deferred type. Each entry mcimadamore@1347: * stores a pointer to the speculative tree and the resolution phase in which the entry mcimadamore@1347: * has been added. mcimadamore@1347: */ mcimadamore@1347: class SpeculativeCache { mcimadamore@1347: mcimadamore@1347: private Map> cache = mcimadamore@1347: new WeakHashMap>(); mcimadamore@1347: mcimadamore@1347: class Entry { mcimadamore@1347: JCTree speculativeTree; vromero@2000: ResultInfo resultInfo; mcimadamore@1347: vromero@2000: public Entry(JCTree speculativeTree, ResultInfo resultInfo) { mcimadamore@1347: this.speculativeTree = speculativeTree; vromero@2000: this.resultInfo = resultInfo; mcimadamore@1347: } mcimadamore@1347: vromero@2000: boolean matches(MethodResolutionPhase phase) { vromero@2000: return resultInfo.checkContext.deferredAttrContext().phase == phase; mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Retrieve a speculative cache entry corresponding to given symbol mcimadamore@1347: * and resolution phase mcimadamore@1347: */ mcimadamore@1347: Entry get(Symbol msym, MethodResolutionPhase phase) { mcimadamore@1347: List entries = cache.get(msym); mcimadamore@1347: if (entries == null) return null; mcimadamore@1347: for (Entry e : entries) { mcimadamore@1347: if (e.matches(phase)) return e; mcimadamore@1347: } mcimadamore@1347: return null; mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Stores a speculative cache entry corresponding to given symbol mcimadamore@1347: * and resolution phase mcimadamore@1347: */ vromero@2000: void put(JCTree speculativeTree, ResultInfo resultInfo) { vromero@2000: Symbol msym = resultInfo.checkContext.deferredAttrContext().msym; mcimadamore@1347: List entries = cache.get(msym); mcimadamore@1347: if (entries == null) { mcimadamore@1347: entries = List.nil(); mcimadamore@1347: } vromero@2000: cache.put(msym, entries.prepend(new Entry(speculativeTree, resultInfo))); mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Get the type that has been computed during a speculative attribution round mcimadamore@1347: */ mcimadamore@1347: Type speculativeType(Symbol msym, MethodResolutionPhase phase) { mcimadamore@1347: SpeculativeCache.Entry e = speculativeCache.get(msym, phase); mcimadamore@1347: return e != null ? e.speculativeTree.type : Type.noType; mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Check a deferred type against a potential target-type. Depending on mcimadamore@1347: * the current attribution mode, a normal vs. speculative attribution mcimadamore@1347: * round is performed on the underlying AST node. There can be only one mcimadamore@1347: * speculative round for a given target method symbol; moreover, a normal mcimadamore@1347: * attribution round must follow one or more speculative rounds. mcimadamore@1347: */ mcimadamore@1347: Type check(ResultInfo resultInfo) { vromero@2000: DeferredStuckPolicy deferredStuckPolicy; vromero@2000: if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) { vromero@2000: deferredStuckPolicy = dummyStuckPolicy; vromero@2000: } else if (resultInfo.checkContext.deferredAttrContext().mode == AttrMode.SPECULATIVE) { vromero@2000: deferredStuckPolicy = new OverloadStuckPolicy(resultInfo, this); vromero@2000: } else { vromero@2000: deferredStuckPolicy = new CheckStuckPolicy(resultInfo, this); vromero@2000: } vromero@2000: return check(resultInfo, deferredStuckPolicy, basicCompleter); mcimadamore@1481: } mcimadamore@1481: vromero@2000: private Type check(ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy, vromero@2000: DeferredTypeCompleter deferredTypeCompleter) { mcimadamore@1347: DeferredAttrContext deferredAttrContext = mcimadamore@1347: resultInfo.checkContext.deferredAttrContext(); mcimadamore@1347: Assert.check(deferredAttrContext != emptyDeferredAttrContext); vromero@2000: if (deferredStuckPolicy.isStuck()) { vromero@2000: deferredAttrContext.addDeferredAttrNode(this, resultInfo, deferredStuckPolicy); mcimadamore@1347: return Type.noType; mcimadamore@1347: } else { mcimadamore@1347: try { mcimadamore@1481: return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext); mcimadamore@1347: } finally { mcimadamore@1347: mode = deferredAttrContext.mode; mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1481: * A completer for deferred types. Defines an entry point for type-checking mcimadamore@1481: * a deferred type. mcimadamore@1481: */ mcimadamore@1481: interface DeferredTypeCompleter { mcimadamore@1481: /** mcimadamore@1481: * Entry point for type-checking a deferred type. Depending on the mcimadamore@1481: * circumstances, type-checking could amount to full attribution mcimadamore@1481: * or partial structural check (aka potential applicability). mcimadamore@1481: */ mcimadamore@1481: Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext); mcimadamore@1481: } mcimadamore@1481: vromero@2000: mcimadamore@1481: /** mcimadamore@1481: * A basic completer for deferred types. This completer type-checks a deferred type mcimadamore@1481: * using attribution; depending on the attribution mode, this could be either standard mcimadamore@1481: * or speculative attribution. mcimadamore@1481: */ mcimadamore@1481: DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() { mcimadamore@1481: public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { mcimadamore@1481: switch (deferredAttrContext.mode) { mcimadamore@1481: case SPECULATIVE: mcimadamore@1654: //Note: if a symbol is imported twice we might do two identical mcimadamore@1654: //speculative rounds... mcimadamore@1654: Assert.check(dt.mode == null || dt.mode == AttrMode.SPECULATIVE); mcimadamore@1481: JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo); vromero@2000: dt.speculativeCache.put(speculativeTree, resultInfo); mcimadamore@1481: return speculativeTree.type; mcimadamore@1481: case CHECK: mcimadamore@1562: Assert.check(dt.mode != null); mcimadamore@1481: return attr.attribTree(dt.tree, dt.env, resultInfo); mcimadamore@1481: } mcimadamore@1481: Assert.error(); mcimadamore@1481: return null; mcimadamore@1481: } mcimadamore@1481: }; mcimadamore@1481: mcimadamore@1562: DeferredTypeCompleter dummyCompleter = new DeferredTypeCompleter() { mcimadamore@1562: public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { mcimadamore@1562: Assert.check(deferredAttrContext.mode == AttrMode.CHECK); mcimadamore@1899: return dt.tree.type = Type.stuckType; mcimadamore@1562: } mcimadamore@1562: }; mcimadamore@1562: mcimadamore@1481: /** vromero@2000: * Policy for detecting stuck expressions. Different criteria might cause vromero@2000: * an expression to be judged as stuck, depending on whether the check vromero@2000: * is performed during overload resolution or after most specific. vromero@2000: */ vromero@2000: interface DeferredStuckPolicy { vromero@2000: /** vromero@2000: * Has the policy detected that a given expression should be considered stuck? vromero@2000: */ vromero@2000: boolean isStuck(); vromero@2000: /** vromero@2000: * Get the set of inference variables a given expression depends upon. vromero@2000: */ vromero@2000: Set stuckVars(); vromero@2000: /** vromero@2000: * Get the set of inference variables which might get new constraints vromero@2000: * if a given expression is being type-checked. vromero@2000: */ vromero@2000: Set depVars(); vromero@2000: } vromero@2000: vromero@2000: /** vromero@2000: * Basic stuck policy; an expression is never considered to be stuck. vromero@2000: */ vromero@2000: DeferredStuckPolicy dummyStuckPolicy = new DeferredStuckPolicy() { vromero@2000: @Override vromero@2000: public boolean isStuck() { vromero@2000: return false; vromero@2000: } vromero@2000: @Override vromero@2000: public Set stuckVars() { vromero@2000: return Collections.emptySet(); vromero@2000: } vromero@2000: @Override vromero@2000: public Set depVars() { vromero@2000: return Collections.emptySet(); vromero@2000: } vromero@2000: }; vromero@2000: vromero@2000: /** mcimadamore@1347: * The 'mode' in which the deferred type is to be type-checked mcimadamore@1347: */ mcimadamore@1347: public enum AttrMode { mcimadamore@1347: /** mcimadamore@1347: * A speculative type-checking round is used during overload resolution mcimadamore@1347: * mainly to generate constraints on inference variables. Side-effects mcimadamore@1347: * arising from type-checking the expression associated with the deferred mcimadamore@1347: * type are reversed after the speculative round finishes. This means the mcimadamore@1347: * expression tree will be left in a blank state. mcimadamore@1347: */ mcimadamore@1347: SPECULATIVE, mcimadamore@1347: /** mcimadamore@1347: * This is the plain type-checking mode. Produces side-effects on the underlying AST node mcimadamore@1347: */ mcimadamore@1347: CHECK; mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Routine that performs speculative type-checking; the input AST node is mcimadamore@1347: * cloned (to avoid side-effects cause by Attr) and compiler state is mcimadamore@1347: * restored after type-checking. All diagnostics (but critical ones) are mcimadamore@1347: * disabled during speculative type-checking. mcimadamore@1347: */ mcimadamore@1347: JCTree attribSpeculative(JCTree tree, Env env, ResultInfo resultInfo) { mcimadamore@1596: final JCTree newTree = new TreeCopier(make).copy(tree); mcimadamore@1347: Env speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared())); mcimadamore@1347: speculativeEnv.info.scope.owner = env.info.scope.owner; jjg@1406: Log.DeferredDiagnosticHandler deferredDiagnosticHandler = jjg@1406: new Log.DeferredDiagnosticHandler(log, new Filter() { mcimadamore@1596: public boolean accepts(final JCDiagnostic d) { mcimadamore@1596: class PosScanner extends TreeScanner { mcimadamore@1596: boolean found = false; mcimadamore@1596: mcimadamore@1596: @Override mcimadamore@1596: public void scan(JCTree tree) { mcimadamore@1596: if (tree != null && mcimadamore@1596: tree.pos() == d.getDiagnosticPosition()) { mcimadamore@1596: found = true; mcimadamore@1596: } mcimadamore@1596: super.scan(tree); mcimadamore@1596: } mcimadamore@1596: }; mcimadamore@1596: PosScanner posScanner = new PosScanner(); mcimadamore@1596: posScanner.scan(newTree); mcimadamore@1596: return posScanner.found; jjg@1406: } jjg@1406: }); mcimadamore@1347: try { mcimadamore@1347: attr.attribTree(newTree, speculativeEnv, resultInfo); mcimadamore@1347: unenterScanner.scan(newTree); mcimadamore@1347: return newTree; mcimadamore@1347: } finally { mcimadamore@1347: unenterScanner.scan(newTree); jjg@1406: log.popDiagnosticHandler(deferredDiagnosticHandler); mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: //where mcimadamore@1347: protected TreeScanner unenterScanner = new TreeScanner() { mcimadamore@1347: @Override mcimadamore@1347: public void visitClassDef(JCClassDecl tree) { mcimadamore@1347: ClassSymbol csym = tree.sym; mcimadamore@1415: //if something went wrong during method applicability check mcimadamore@1415: //it is possible that nested expressions inside argument expression mcimadamore@1415: //are left unchecked - in such cases there's nothing to clean up. mcimadamore@1415: if (csym == null) return; mcimadamore@1347: enter.typeEnvs.remove(csym); mcimadamore@1347: chk.compiled.remove(csym.flatname); mcimadamore@1347: syms.classes.remove(csym.flatname); mcimadamore@1347: super.visitClassDef(tree); mcimadamore@1347: } mcimadamore@1347: }; mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * A deferred context is created on each method check. A deferred context is mcimadamore@1347: * used to keep track of information associated with the method check, such as mcimadamore@1347: * the symbol of the method being checked, the overload resolution phase, mcimadamore@1347: * the kind of attribution mode to be applied to deferred types and so forth. mcimadamore@1347: * As deferred types are processed (by the method check routine) stuck AST nodes mcimadamore@1347: * are added (as new deferred attribution nodes) to this context. The complete() mcimadamore@1347: * routine makes sure that all pending nodes are properly processed, by mcimadamore@1347: * progressively instantiating all inference variables on which one or more mcimadamore@1347: * deferred attribution node is stuck. mcimadamore@1347: */ mcimadamore@1347: class DeferredAttrContext { mcimadamore@1347: mcimadamore@1347: /** attribution mode */ mcimadamore@1347: final AttrMode mode; mcimadamore@1347: mcimadamore@1347: /** symbol of the method being checked */ mcimadamore@1347: final Symbol msym; mcimadamore@1347: mcimadamore@1347: /** method resolution step */ mcimadamore@1347: final Resolve.MethodResolutionPhase phase; mcimadamore@1347: mcimadamore@1347: /** inference context */ mcimadamore@1347: final InferenceContext inferenceContext; mcimadamore@1347: mcimadamore@1551: /** parent deferred context */ mcimadamore@1551: final DeferredAttrContext parent; mcimadamore@1551: mcimadamore@1551: /** Warner object to report warnings */ mcimadamore@1551: final Warner warn; mcimadamore@1551: mcimadamore@1347: /** list of deferred attribution nodes to be processed */ mcimadamore@1347: ArrayList deferredAttrNodes = new ArrayList(); mcimadamore@1347: mcimadamore@1551: DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, mcimadamore@1551: InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) { mcimadamore@1347: this.mode = mode; mcimadamore@1347: this.msym = msym; mcimadamore@1347: this.phase = phase; mcimadamore@1551: this.parent = parent; mcimadamore@1551: this.warn = warn; mcimadamore@1347: this.inferenceContext = inferenceContext; mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable mcimadamore@1347: * Nodes added this way act as 'roots' for the out-of-order method checking process. mcimadamore@1347: */ vromero@2000: void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, vromero@2000: DeferredStuckPolicy deferredStuckPolicy) { vromero@2000: deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, deferredStuckPolicy)); mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Incrementally process all nodes, by skipping 'stuck' nodes and attributing mcimadamore@1347: * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes) mcimadamore@1347: * some inference variable might get eagerly instantiated so that all nodes mcimadamore@1347: * can be type-checked. mcimadamore@1347: */ mcimadamore@1347: void complete() { mcimadamore@1347: while (!deferredAttrNodes.isEmpty()) { vromero@2000: Map> depVarsMap = new LinkedHashMap>(); vromero@2000: List stuckVars = List.nil(); mcimadamore@1347: boolean progress = false; mcimadamore@1347: //scan a defensive copy of the node list - this is because a deferred mcimadamore@1347: //attribution round can add new nodes to the list mcimadamore@1347: for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) { mcimadamore@1551: if (!deferredAttrNode.process(this)) { vromero@2000: List restStuckVars = vromero@2000: List.from(deferredAttrNode.deferredStuckPolicy.stuckVars()) vromero@2000: .intersect(inferenceContext.restvars()); vromero@2000: stuckVars = stuckVars.prependList(restStuckVars); vromero@2000: //update dependency map vromero@2000: for (Type t : List.from(deferredAttrNode.deferredStuckPolicy.depVars()) vromero@2000: .intersect(inferenceContext.restvars())) { vromero@2000: Set prevDeps = depVarsMap.get(t); vromero@2000: if (prevDeps == null) { vromero@2000: prevDeps = new LinkedHashSet(); vromero@2000: depVarsMap.put(t, prevDeps); vromero@2000: } vromero@2000: prevDeps.addAll(restStuckVars); vromero@2000: } mcimadamore@1510: } else { mcimadamore@1347: deferredAttrNodes.remove(deferredAttrNode); mcimadamore@1347: progress = true; mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: if (!progress) { vromero@2000: DeferredAttrContext dac = this; vromero@2000: while (dac != emptyDeferredAttrContext) { vromero@2000: if (dac.mode == AttrMode.SPECULATIVE) { vromero@2000: //unsticking does not take place during overload vromero@2000: break; vromero@2000: } vromero@2000: dac = dac.parent; vromero@2000: } mcimadamore@1347: //remove all variables that have already been instantiated mcimadamore@1347: //from the list of stuck variables vromero@2000: try { vromero@2000: inferenceContext.solveAny(stuckVars, depVarsMap, warn); vromero@2000: inferenceContext.notifyChange(); vromero@2000: } catch (Infer.GraphStrategy.NodeNotFoundException ex) { vromero@2000: //this means that we are in speculative mode and the vromero@2000: //set of contraints are too tight for progess to be made. vromero@2000: //Just leave the remaining expressions as stuck. vromero@2000: break; vromero@2000: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1551: } mcimadamore@1551: mcimadamore@1551: /** mcimadamore@1551: * Class representing a deferred attribution node. It keeps track of mcimadamore@1551: * a deferred type, along with the expected target type information. mcimadamore@1551: */ vromero@2000: class DeferredAttrNode { mcimadamore@1551: mcimadamore@1551: /** underlying deferred type */ mcimadamore@1551: DeferredType dt; mcimadamore@1551: mcimadamore@1551: /** underlying target type information */ mcimadamore@1551: ResultInfo resultInfo; mcimadamore@1551: vromero@2000: /** stuck policy associated with this node */ vromero@2000: DeferredStuckPolicy deferredStuckPolicy; mcimadamore@1551: vromero@2000: DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy) { mcimadamore@1551: this.dt = dt; mcimadamore@1551: this.resultInfo = resultInfo; vromero@2000: this.deferredStuckPolicy = deferredStuckPolicy; mcimadamore@1551: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1551: * Process a deferred attribution node. mcimadamore@1551: * Invariant: a stuck node cannot be processed. mcimadamore@1347: */ mcimadamore@1551: @SuppressWarnings("fallthrough") vromero@2000: boolean process(final DeferredAttrContext deferredAttrContext) { mcimadamore@1551: switch (deferredAttrContext.mode) { mcimadamore@1551: case SPECULATIVE: vromero@2000: if (deferredStuckPolicy.isStuck()) { vromero@2000: dt.check(resultInfo, dummyStuckPolicy, new StructuralStuckChecker()); vromero@2000: return true; vromero@2000: } else { vromero@2000: Assert.error("Cannot get here"); vromero@2000: } mcimadamore@1551: case CHECK: vromero@2000: if (deferredStuckPolicy.isStuck()) { mcimadamore@1562: //stuck expression - see if we can propagate mcimadamore@1562: if (deferredAttrContext.parent != emptyDeferredAttrContext && vromero@2000: Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, vromero@2000: List.from(deferredStuckPolicy.stuckVars()))) { vromero@2000: deferredAttrContext.parent.addDeferredAttrNode(dt, vromero@2000: resultInfo.dup(new Check.NestedCheckContext(resultInfo.checkContext) { vromero@2000: @Override vromero@2000: public InferenceContext inferenceContext() { vromero@2000: return deferredAttrContext.parent.inferenceContext; vromero@2000: } vromero@2000: @Override vromero@2000: public DeferredAttrContext deferredAttrContext() { vromero@2000: return deferredAttrContext.parent; vromero@2000: } vromero@2000: }), deferredStuckPolicy); vromero@2000: dt.tree.type = Type.stuckType; mcimadamore@1562: return true; mcimadamore@1562: } else { mcimadamore@1562: return false; mcimadamore@1562: } mcimadamore@1551: } else { vromero@2000: ResultInfo instResultInfo = vromero@2000: resultInfo.dup(deferredAttrContext.inferenceContext.asInstType(resultInfo.pt)); vromero@2000: dt.check(instResultInfo, dummyStuckPolicy, basicCompleter); mcimadamore@1551: return true; mcimadamore@1551: } mcimadamore@1551: default: mcimadamore@1551: throw new AssertionError("Bad mode"); mcimadamore@1551: } mcimadamore@1551: } mcimadamore@1347: mcimadamore@1551: /** mcimadamore@1551: * Structural checker for stuck expressions mcimadamore@1551: */ mcimadamore@1551: class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter { mcimadamore@1347: mcimadamore@1347: ResultInfo resultInfo; mcimadamore@1551: InferenceContext inferenceContext; vromero@2000: Env env; mcimadamore@1347: mcimadamore@1551: public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { mcimadamore@1551: this.resultInfo = resultInfo; mcimadamore@1551: this.inferenceContext = deferredAttrContext.inferenceContext; vromero@2000: this.env = dt.env; mcimadamore@1551: dt.tree.accept(this); vromero@2000: dt.speculativeCache.put(stuckTree, resultInfo); mcimadamore@1551: return Type.noType; mcimadamore@1551: } mcimadamore@1347: mcimadamore@1551: @Override mcimadamore@1551: public void visitLambda(JCLambda tree) { mcimadamore@1551: Check.CheckContext checkContext = resultInfo.checkContext; mcimadamore@1551: Type pt = resultInfo.pt; mcimadamore@1551: if (inferenceContext.inferencevars.contains(pt)) { mcimadamore@1551: //ok mcimadamore@1551: return; mcimadamore@1551: } else { mcimadamore@1551: //must be a functional descriptor mcimadamore@1551: try { mcimadamore@1551: Type desc = types.findDescriptorType(pt); mcimadamore@1551: if (desc.getParameterTypes().length() != tree.params.length()) { mcimadamore@1551: checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda")); mcimadamore@1551: } mcimadamore@1551: } catch (Types.FunctionDescriptorLookupError ex) { mcimadamore@1551: checkContext.report(null, ex.getDiagnostic()); mcimadamore@1551: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: @Override mcimadamore@1551: public void visitNewClass(JCNewClass tree) { mcimadamore@1551: //do nothing mcimadamore@1347: } mcimadamore@1347: mcimadamore@1551: @Override mcimadamore@1551: public void visitApply(JCMethodInvocation tree) { mcimadamore@1551: //do nothing mcimadamore@1347: } mcimadamore@1347: mcimadamore@1551: @Override mcimadamore@1551: public void visitReference(JCMemberReference tree) { mcimadamore@1551: Check.CheckContext checkContext = resultInfo.checkContext; mcimadamore@1551: Type pt = resultInfo.pt; mcimadamore@1551: if (inferenceContext.inferencevars.contains(pt)) { mcimadamore@1551: //ok mcimadamore@1551: return; mcimadamore@1551: } else { mcimadamore@1551: try { mcimadamore@1551: types.findDescriptorType(pt); mcimadamore@1551: } catch (Types.FunctionDescriptorLookupError ex) { mcimadamore@1551: checkContext.report(null, ex.getDiagnostic()); mcimadamore@1510: } vromero@2000: Env localEnv = env.dup(tree); vromero@2000: JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv, vromero@2000: attr.memberReferenceQualifierResult(tree)); alundblad@2047: ListBuffer argtypes = new ListBuffer<>(); vromero@2000: for (Type t : types.findDescriptorType(pt).getParameterTypes()) { vromero@2000: argtypes.append(Type.noType); vromero@2000: } vromero@2000: JCMemberReference mref2 = new TreeCopier(make).copy(tree); vromero@2000: mref2.expr = exprTree; vromero@2193: Symbol lookupSym = vromero@2193: rs.resolveMemberReferenceByArity(localEnv, mref2, exprTree.type, vromero@2193: tree.name, argtypes.toList(), inferenceContext); vromero@2193: switch (lookupSym.kind) { mcimadamore@1581: //note: as argtypes are erroneous types, type-errors must mcimadamore@1581: //have been caused by arity mismatch mcimadamore@1581: case Kinds.ABSENT_MTH: mcimadamore@1581: case Kinds.WRONG_MTH: mcimadamore@1581: case Kinds.WRONG_MTHS: vromero@2193: case Kinds.WRONG_STATICNESS: vromero@2000: checkContext.report(tree, diags.fragment("incompatible.arg.types.in.mref")); mcimadamore@1581: } mcimadamore@1510: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** an empty deferred attribution context - all methods throw exceptions */ mcimadamore@1897: final DeferredAttrContext emptyDeferredAttrContext; mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Map a list of types possibly containing one or more deferred types mcimadamore@1347: * into a list of ordinary types. Each deferred type D is mapped into a type T, mcimadamore@1347: * where T is computed by retrieving the type that has already been mcimadamore@1347: * computed for D during a previous deferred attribution round of the given kind. mcimadamore@1347: */ mcimadamore@1347: class DeferredTypeMap extends Type.Mapping { mcimadamore@1347: mcimadamore@1347: DeferredAttrContext deferredAttrContext; mcimadamore@1347: mcimadamore@1347: protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) { mcimadamore@1347: super(String.format("deferredTypeMap[%s]", mode)); mcimadamore@1551: this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, mcimadamore@1551: infer.emptyContext, emptyDeferredAttrContext, types.noWarnings); mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: @Override mcimadamore@1347: public Type apply(Type t) { jjg@1374: if (!t.hasTag(DEFERRED)) { mcimadamore@1347: return t.map(this); mcimadamore@1347: } else { mcimadamore@1347: DeferredType dt = (DeferredType)t; mcimadamore@1347: return typeOf(dt); mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: protected Type typeOf(DeferredType dt) { mcimadamore@1347: switch (deferredAttrContext.mode) { mcimadamore@1347: case CHECK: mcimadamore@1347: return dt.tree.type == null ? Type.noType : dt.tree.type; mcimadamore@1347: case SPECULATIVE: mcimadamore@1347: return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase); mcimadamore@1347: } mcimadamore@1347: Assert.error(); mcimadamore@1347: return null; mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Specialized recovery deferred mapping. mcimadamore@1347: * Each deferred type D is mapped into a type T, where T is computed either by mcimadamore@1347: * (i) retrieving the type that has already been computed for D during a previous mcimadamore@1347: * attribution round (as before), or (ii) by synthesizing a new type R for D mcimadamore@1347: * (the latter step is useful in a recovery scenario). mcimadamore@1347: */ mcimadamore@1347: public class RecoveryDeferredTypeMap extends DeferredTypeMap { mcimadamore@1347: mcimadamore@1347: public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) { mcimadamore@1415: super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX); mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: @Override mcimadamore@1347: protected Type typeOf(DeferredType dt) { mcimadamore@1347: Type owntype = super.typeOf(dt); mcimadamore@1415: return owntype == Type.noType ? mcimadamore@1347: recover(dt) : owntype; mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Synthesize a type for a deferred type that hasn't been previously mcimadamore@1347: * reduced to an ordinary type. Functional deferred types and conditionals mcimadamore@1347: * are mapped to themselves, in order to have a richer diagnostic mcimadamore@1347: * representation. Remaining deferred types are attributed using mcimadamore@1347: * a default expected type (j.l.Object). mcimadamore@1347: */ mcimadamore@1347: private Type recover(DeferredType dt) { mcimadamore@1889: dt.check(attr.new RecoveryInfo(deferredAttrContext) { mcimadamore@1889: @Override mcimadamore@1889: protected Type check(DiagnosticPosition pos, Type found) { mcimadamore@1889: return chk.checkNonVoid(pos, super.check(pos, found)); mcimadamore@1889: } mcimadamore@1889: }); mcimadamore@1415: return super.apply(dt); mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1481: * A special tree scanner that would only visit portions of a given tree. mcimadamore@1481: * The set of nodes visited by the scanner can be customized at construction-time. mcimadamore@1481: */ mcimadamore@1481: abstract static class FilterScanner extends TreeScanner { mcimadamore@1481: mcimadamore@1481: final Filter treeFilter; mcimadamore@1481: mcimadamore@1481: FilterScanner(final Set validTags) { mcimadamore@1481: this.treeFilter = new Filter() { mcimadamore@1481: public boolean accepts(JCTree t) { mcimadamore@1481: return validTags.contains(t.getTag()); mcimadamore@1481: } mcimadamore@1481: }; mcimadamore@1481: } mcimadamore@1481: mcimadamore@1481: @Override mcimadamore@1481: public void scan(JCTree tree) { mcimadamore@1481: if (tree != null) { mcimadamore@1481: if (treeFilter.accepts(tree)) { mcimadamore@1481: super.scan(tree); mcimadamore@1481: } else { mcimadamore@1481: skip(tree); mcimadamore@1481: } mcimadamore@1481: } mcimadamore@1481: } mcimadamore@1481: mcimadamore@1481: /** mcimadamore@1481: * handler that is executed when a node has been discarded mcimadamore@1481: */ mcimadamore@1481: abstract void skip(JCTree tree); mcimadamore@1481: } mcimadamore@1481: mcimadamore@1481: /** mcimadamore@1481: * A tree scanner suitable for visiting the target-type dependent nodes of mcimadamore@1481: * a given argument expression. mcimadamore@1481: */ mcimadamore@1481: static class PolyScanner extends FilterScanner { mcimadamore@1481: mcimadamore@1481: PolyScanner() { mcimadamore@1481: super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE)); mcimadamore@1481: } mcimadamore@1481: mcimadamore@1481: @Override mcimadamore@1481: void skip(JCTree tree) { mcimadamore@1481: //do nothing mcimadamore@1481: } mcimadamore@1481: } mcimadamore@1481: mcimadamore@1481: /** mcimadamore@1481: * A tree scanner suitable for visiting the target-type dependent nodes nested mcimadamore@1481: * within a lambda expression body. mcimadamore@1481: */ mcimadamore@1481: static class LambdaReturnScanner extends FilterScanner { mcimadamore@1481: mcimadamore@1481: LambdaReturnScanner() { mcimadamore@1481: super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP, mcimadamore@1481: FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP)); mcimadamore@1481: } mcimadamore@1481: mcimadamore@1481: @Override mcimadamore@1481: void skip(JCTree tree) { mcimadamore@1481: //do nothing mcimadamore@1481: } mcimadamore@1348: } mcimadamore@1348: mcimadamore@1348: /** mcimadamore@1348: * This visitor is used to check that structural expressions conform mcimadamore@1348: * to their target - this step is required as inference could end up mcimadamore@1348: * inferring types that make some of the nested expressions incompatible mcimadamore@1348: * with their corresponding instantiated target mcimadamore@1348: */ vromero@2000: class CheckStuckPolicy extends PolyScanner implements DeferredStuckPolicy, Infer.FreeTypeListener { mcimadamore@1348: mcimadamore@1348: Type pt; mcimadamore@1348: Infer.InferenceContext inferenceContext; mcimadamore@1415: Set stuckVars = new LinkedHashSet(); vromero@2000: Set depVars = new LinkedHashSet(); mcimadamore@1348: vromero@2000: @Override vromero@2000: public boolean isStuck() { vromero@2000: return !stuckVars.isEmpty(); vromero@2000: } vromero@2000: vromero@2000: @Override vromero@2000: public Set stuckVars() { vromero@2000: return stuckVars; vromero@2000: } vromero@2000: vromero@2000: @Override vromero@2000: public Set depVars() { vromero@2000: return depVars; vromero@2000: } vromero@2000: vromero@2000: public CheckStuckPolicy(ResultInfo resultInfo, DeferredType dt) { vromero@2000: this.pt = resultInfo.pt; vromero@2000: this.inferenceContext = resultInfo.checkContext.inferenceContext(); vromero@2000: scan(dt.tree); vromero@2000: if (!stuckVars.isEmpty()) { vromero@2000: resultInfo.checkContext.inferenceContext() vromero@2000: .addFreeTypeListener(List.from(stuckVars), this); vromero@2000: } vromero@2000: } vromero@2000: vromero@2000: @Override vromero@2000: public void typesInferred(InferenceContext inferenceContext) { vromero@2000: stuckVars.clear(); mcimadamore@1348: } mcimadamore@1348: mcimadamore@1348: @Override mcimadamore@1348: public void visitLambda(JCLambda tree) { mcimadamore@1481: if (inferenceContext.inferenceVars().contains(pt)) { mcimadamore@1481: stuckVars.add(pt); mcimadamore@1348: } mcimadamore@1510: if (!types.isFunctionalInterface(pt)) { mcimadamore@1481: return; mcimadamore@1481: } mcimadamore@1481: Type descType = types.findDescriptorType(pt); mcimadamore@1481: List freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); mcimadamore@1510: if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT && mcimadamore@1481: freeArgVars.nonEmpty()) { mcimadamore@1481: stuckVars.addAll(freeArgVars); vromero@2000: depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); mcimadamore@1481: } mcimadamore@1481: scanLambdaBody(tree, descType.getReturnType()); mcimadamore@1348: } mcimadamore@1348: mcimadamore@1348: @Override mcimadamore@1348: public void visitReference(JCMemberReference tree) { mcimadamore@1348: scan(tree.expr); mcimadamore@1348: if (inferenceContext.inferenceVars().contains(pt)) { mcimadamore@1348: stuckVars.add(pt); mcimadamore@1348: return; mcimadamore@1348: } mcimadamore@1510: if (!types.isFunctionalInterface(pt)) { mcimadamore@1348: return; mcimadamore@1348: } mcimadamore@1415: mcimadamore@1348: Type descType = types.findDescriptorType(pt); mcimadamore@1348: List freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); vromero@2000: if (freeArgVars.nonEmpty() && vromero@2000: tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) { vromero@2000: stuckVars.addAll(freeArgVars); vromero@2000: depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); mcimadamore@1897: } mcimadamore@1348: } mcimadamore@1348: mcimadamore@1481: void scanLambdaBody(JCLambda lambda, final Type pt) { mcimadamore@1481: if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { vromero@2000: Type prevPt = this.pt; vromero@2000: try { vromero@2000: this.pt = pt; vromero@2000: scan(lambda.body); vromero@2000: } finally { vromero@2000: this.pt = prevPt; vromero@2000: } mcimadamore@1481: } else { mcimadamore@1481: LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() { mcimadamore@1481: @Override mcimadamore@1481: public void visitReturn(JCReturn tree) { mcimadamore@1481: if (tree.expr != null) { vromero@2000: Type prevPt = CheckStuckPolicy.this.pt; vromero@2000: try { vromero@2000: CheckStuckPolicy.this.pt = pt; vromero@2000: CheckStuckPolicy.this.scan(tree.expr); vromero@2000: } finally { vromero@2000: CheckStuckPolicy.this.pt = prevPt; vromero@2000: } mcimadamore@1481: } mcimadamore@1481: } mcimadamore@1481: }; mcimadamore@1481: lambdaScanner.scan(lambda.body); mcimadamore@1348: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1697: mcimadamore@1697: /** vromero@2000: * This visitor is used to check that structural expressions conform vromero@2000: * to their target - this step is required as inference could end up vromero@2000: * inferring types that make some of the nested expressions incompatible vromero@2000: * with their corresponding instantiated target vromero@2000: */ vromero@2000: class OverloadStuckPolicy extends CheckStuckPolicy implements DeferredStuckPolicy { vromero@2000: vromero@2000: boolean stuck; vromero@2000: vromero@2000: @Override vromero@2000: public boolean isStuck() { vromero@2000: return super.isStuck() || stuck; vromero@2000: } vromero@2000: vromero@2000: public OverloadStuckPolicy(ResultInfo resultInfo, DeferredType dt) { vromero@2000: super(resultInfo, dt); vromero@2000: } vromero@2000: vromero@2000: @Override vromero@2000: public void visitLambda(JCLambda tree) { vromero@2000: super.visitLambda(tree); vromero@2000: if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT) { vromero@2000: stuck = true; vromero@2000: } vromero@2000: } vromero@2000: vromero@2000: @Override vromero@2000: public void visitReference(JCMemberReference tree) { vromero@2000: super.visitReference(tree); vromero@2000: if (tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) { vromero@2000: stuck = true; vromero@2000: } vromero@2000: } vromero@2000: } vromero@2000: vromero@2000: /** mcimadamore@1697: * Does the argument expression {@code expr} need speculative type-checking? mcimadamore@1697: */ mcimadamore@1697: boolean isDeferred(Env env, JCExpression expr) { mcimadamore@1697: DeferredChecker dc = new DeferredChecker(env); mcimadamore@1697: dc.scan(expr); mcimadamore@1697: return dc.result.isPoly(); mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: /** mcimadamore@1697: * The kind of an argument expression. This is used by the analysis that mcimadamore@1697: * determines as to whether speculative attribution is necessary. mcimadamore@1697: */ mcimadamore@1697: enum ArgumentExpressionKind { mcimadamore@1697: mcimadamore@1697: /** kind that denotes poly argument expression */ mcimadamore@1697: POLY, mcimadamore@1697: /** kind that denotes a standalone expression */ mcimadamore@1697: NO_POLY, mcimadamore@1697: /** kind that denotes a primitive/boxed standalone expression */ mcimadamore@1697: PRIMITIVE; mcimadamore@1697: mcimadamore@1697: /** mcimadamore@1697: * Does this kind denote a poly argument expression mcimadamore@1697: */ mcimadamore@1697: public final boolean isPoly() { mcimadamore@1697: return this == POLY; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: /** mcimadamore@1697: * Does this kind denote a primitive standalone expression mcimadamore@1697: */ mcimadamore@1697: public final boolean isPrimitive() { mcimadamore@1697: return this == PRIMITIVE; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: /** mcimadamore@1697: * Compute the kind of a standalone expression of a given type mcimadamore@1697: */ mcimadamore@1697: static ArgumentExpressionKind standaloneKind(Type type, Types types) { mcimadamore@1697: return types.unboxedTypeOrType(type).isPrimitive() ? mcimadamore@1697: ArgumentExpressionKind.PRIMITIVE : mcimadamore@1697: ArgumentExpressionKind.NO_POLY; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: /** mcimadamore@1697: * Compute the kind of a method argument expression given its symbol mcimadamore@1697: */ mcimadamore@1697: static ArgumentExpressionKind methodKind(Symbol sym, Types types) { mcimadamore@1697: Type restype = sym.type.getReturnType(); mcimadamore@1697: if (sym.type.hasTag(FORALL) && mcimadamore@1697: restype.containsAny(((ForAll)sym.type).tvars)) { mcimadamore@1697: return ArgumentExpressionKind.POLY; mcimadamore@1697: } else { mcimadamore@1697: return ArgumentExpressionKind.standaloneKind(restype, types); mcimadamore@1697: } mcimadamore@1697: } mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: /** mcimadamore@1697: * Tree scanner used for checking as to whether an argument expression mcimadamore@1697: * requires speculative attribution mcimadamore@1697: */ mcimadamore@1697: final class DeferredChecker extends FilterScanner { mcimadamore@1697: mcimadamore@1697: Env env; mcimadamore@1697: ArgumentExpressionKind result; mcimadamore@1697: mcimadamore@1697: public DeferredChecker(Env env) { mcimadamore@1697: super(deferredCheckerTags); mcimadamore@1697: this.env = env; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: @Override mcimadamore@1697: public void visitLambda(JCLambda tree) { mcimadamore@1697: //a lambda is always a poly expression mcimadamore@1697: result = ArgumentExpressionKind.POLY; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: @Override mcimadamore@1697: public void visitReference(JCMemberReference tree) { vromero@2000: //perform arity-based check vromero@2000: Env localEnv = env.dup(tree); vromero@2000: JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv, vromero@2000: attr.memberReferenceQualifierResult(tree)); vromero@2000: JCMemberReference mref2 = new TreeCopier(make).copy(tree); vromero@2000: mref2.expr = exprTree; vromero@2193: Symbol res = vromero@2193: rs.getMemberReference(tree, localEnv, mref2, vromero@2193: exprTree.type, tree.name); vromero@2193: tree.sym = res; vromero@2000: if (res.kind >= Kinds.ERRONEOUS || vromero@2000: res.type.hasTag(FORALL) || vromero@2000: (res.flags() & Flags.VARARGS) != 0 || vromero@2000: (TreeInfo.isStaticSelector(exprTree, tree.name.table.names) && vromero@2000: exprTree.type.isRaw())) { vromero@2000: tree.overloadKind = JCMemberReference.OverloadKind.OVERLOADED; vromero@2000: } else { vromero@2000: tree.overloadKind = JCMemberReference.OverloadKind.UNOVERLOADED; vromero@2000: } mcimadamore@1697: //a method reference is always a poly expression mcimadamore@1697: result = ArgumentExpressionKind.POLY; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: @Override mcimadamore@1697: public void visitTypeCast(JCTypeCast tree) { mcimadamore@1697: //a cast is always a standalone expression mcimadamore@1697: result = ArgumentExpressionKind.NO_POLY; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: @Override mcimadamore@1697: public void visitConditional(JCConditional tree) { mcimadamore@1697: scan(tree.truepart); mcimadamore@1697: if (!result.isPrimitive()) { mcimadamore@1697: result = ArgumentExpressionKind.POLY; mcimadamore@1697: return; mcimadamore@1697: } mcimadamore@1697: scan(tree.falsepart); mcimadamore@1697: result = reduce(ArgumentExpressionKind.PRIMITIVE); mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: @Override mcimadamore@1697: public void visitNewClass(JCNewClass tree) { mcimadamore@1697: result = (TreeInfo.isDiamond(tree) || attr.findDiamonds) ? mcimadamore@1697: ArgumentExpressionKind.POLY : ArgumentExpressionKind.NO_POLY; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: @Override mcimadamore@1697: public void visitApply(JCMethodInvocation tree) { mcimadamore@1697: Name name = TreeInfo.name(tree.meth); mcimadamore@1697: mcimadamore@1697: //fast path mcimadamore@1697: if (tree.typeargs.nonEmpty() || mcimadamore@1697: name == name.table.names._this || mcimadamore@1697: name == name.table.names._super) { mcimadamore@1697: result = ArgumentExpressionKind.NO_POLY; mcimadamore@1697: return; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: //slow path mcimadamore@1697: final JCExpression rec = tree.meth.hasTag(SELECT) ? mcimadamore@1697: ((JCFieldAccess)tree.meth).selected : mcimadamore@1697: null; mcimadamore@1697: mcimadamore@1697: if (rec != null && !isSimpleReceiver(rec)) { mcimadamore@1697: //give up if receiver is too complex (to cut down analysis time) mcimadamore@1697: result = ArgumentExpressionKind.POLY; mcimadamore@1697: return; mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: Type site = rec != null ? mcimadamore@1697: attribSpeculative(rec, env, attr.unknownTypeExprInfo).type : mcimadamore@1697: env.enclClass.sym.type; mcimadamore@1697: mcimadamore@1888: while (site.hasTag(TYPEVAR)) { mcimadamore@1888: site = site.getUpperBound(); mcimadamore@1888: } mcimadamore@1888: mcimadamore@1897: List args = rs.dummyArgs(tree.args.length()); mcimadamore@1697: mcimadamore@1897: Resolve.LookupHelper lh = rs.new LookupHelper(name, site, args, List.nil(), MethodResolutionPhase.VARARITY) { mcimadamore@1697: @Override mcimadamore@1697: Symbol lookup(Env env, MethodResolutionPhase phase) { mcimadamore@1697: return rec == null ? mcimadamore@1697: rs.findFun(env, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()) : mcimadamore@1697: rs.findMethod(env, site, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired(), false); mcimadamore@1697: } mcimadamore@1697: @Override mcimadamore@1697: Symbol access(Env env, DiagnosticPosition pos, Symbol location, Symbol sym) { mcimadamore@1697: return sym; mcimadamore@1697: } mcimadamore@1697: }; mcimadamore@1697: mcimadamore@1697: Symbol sym = rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh); mcimadamore@1697: mcimadamore@1697: if (sym.kind == Kinds.AMBIGUOUS) { mcimadamore@1697: Resolve.AmbiguityError err = (Resolve.AmbiguityError)sym.baseSymbol(); mcimadamore@1697: result = ArgumentExpressionKind.PRIMITIVE; vromero@1850: for (Symbol s : err.ambiguousSyms) { vromero@1850: if (result.isPoly()) break; mcimadamore@1697: if (s.kind == Kinds.MTH) { mcimadamore@1697: result = reduce(ArgumentExpressionKind.methodKind(s, types)); mcimadamore@1697: } mcimadamore@1697: } mcimadamore@1697: } else { mcimadamore@1697: result = (sym.kind == Kinds.MTH) ? mcimadamore@1697: ArgumentExpressionKind.methodKind(sym, types) : mcimadamore@1697: ArgumentExpressionKind.NO_POLY; mcimadamore@1697: } mcimadamore@1697: } mcimadamore@1697: //where mcimadamore@1697: private boolean isSimpleReceiver(JCTree rec) { mcimadamore@1697: switch (rec.getTag()) { mcimadamore@1697: case IDENT: mcimadamore@1697: return true; mcimadamore@1697: case SELECT: mcimadamore@1697: return isSimpleReceiver(((JCFieldAccess)rec).selected); mcimadamore@1697: case TYPEAPPLY: mcimadamore@1697: case TYPEARRAY: mcimadamore@1697: return true; mcimadamore@1697: case ANNOTATED_TYPE: mcimadamore@1697: return isSimpleReceiver(((JCAnnotatedType)rec).underlyingType); mcimadamore@1697: default: mcimadamore@1697: return false; mcimadamore@1697: } mcimadamore@1697: } mcimadamore@1697: private ArgumentExpressionKind reduce(ArgumentExpressionKind kind) { mcimadamore@1697: switch (result) { mcimadamore@1697: case PRIMITIVE: return kind; mcimadamore@1697: case NO_POLY: return kind.isPoly() ? kind : result; mcimadamore@1697: case POLY: return result; mcimadamore@1697: default: mcimadamore@1697: Assert.error(); mcimadamore@1697: return null; mcimadamore@1697: } mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: @Override mcimadamore@1697: public void visitLiteral(JCLiteral tree) { mcimadamore@1697: Type litType = attr.litType(tree.typetag); mcimadamore@1697: result = ArgumentExpressionKind.standaloneKind(litType, types); mcimadamore@1697: } mcimadamore@1697: mcimadamore@1697: @Override mcimadamore@1697: void skip(JCTree tree) { mcimadamore@1697: result = ArgumentExpressionKind.NO_POLY; mcimadamore@1697: } mcimadamore@1697: } mcimadamore@1697: //where mcimadamore@1697: private EnumSet deferredCheckerTags = mcimadamore@1697: EnumSet.of(LAMBDA, REFERENCE, PARENS, TYPECAST, mcimadamore@1697: CONDEXPR, NEWCLASS, APPLY, LITERAL); mcimadamore@1347: }