mcimadamore@1347: /* mcimadamore@1347: * Copyright (c) 2012, 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: 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@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@1347: import com.sun.tools.javac.tree.JCTree.*; mcimadamore@1347: mcimadamore@1347: import javax.tools.JavaFileObject; mcimadamore@1347: mcimadamore@1347: import java.util.ArrayList; mcimadamore@1347: import java.util.HashSet; mcimadamore@1347: import java.util.Map; mcimadamore@1347: import java.util.Queue; mcimadamore@1347: import java.util.Set; mcimadamore@1347: import java.util.WeakHashMap; mcimadamore@1347: mcimadamore@1347: import static com.sun.tools.javac.code.TypeTags.*; mcimadamore@1347: import static com.sun.tools.javac.tree.JCTree.Tag.*; mcimadamore@1347: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 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@1347: final Enter enter; mcimadamore@1347: final Infer infer; 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@1347: enter = Enter.instance(context); mcimadamore@1347: infer = Infer.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@1347: } mcimadamore@1347: 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) { mcimadamore@1347: super(DEFERRED, null); mcimadamore@1347: this.tree = tree; mcimadamore@1347: this.env = env.dup(tree, env.info.dup()); mcimadamore@1347: this.speculativeCache = new SpeculativeCache(); mcimadamore@1347: } mcimadamore@1347: 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; mcimadamore@1347: Resolve.MethodResolutionPhase phase; mcimadamore@1347: mcimadamore@1347: public Entry(JCTree speculativeTree, MethodResolutionPhase phase) { mcimadamore@1347: this.speculativeTree = speculativeTree; mcimadamore@1347: this.phase = phase; mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: boolean matches(Resolve.MethodResolutionPhase phase) { mcimadamore@1347: return this.phase == phase; mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Clone a speculative cache entry as a fresh entry associated mcimadamore@1347: * with a new method (this maybe required to fixup speculative cache mcimadamore@1347: * misses after Resolve.access()) mcimadamore@1347: */ mcimadamore@1347: void dupAllTo(Symbol from, Symbol to) { mcimadamore@1347: Assert.check(cache.get(to) == null); mcimadamore@1347: List entries = cache.get(from); mcimadamore@1347: if (entries != null) { mcimadamore@1347: cache.put(to, entries); 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: */ mcimadamore@1347: void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) { mcimadamore@1347: List entries = cache.get(msym); mcimadamore@1347: if (entries == null) { mcimadamore@1347: entries = List.nil(); mcimadamore@1347: } mcimadamore@1347: cache.put(msym, entries.prepend(new Entry(speculativeTree, phase))); 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) { mcimadamore@1347: DeferredAttrContext deferredAttrContext = mcimadamore@1347: resultInfo.checkContext.deferredAttrContext(); mcimadamore@1347: Assert.check(deferredAttrContext != emptyDeferredAttrContext); mcimadamore@1347: List stuckVars = stuckVars(tree, resultInfo); mcimadamore@1347: if (stuckVars.nonEmpty()) { mcimadamore@1347: deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars); mcimadamore@1347: return Type.noType; mcimadamore@1347: } else { mcimadamore@1347: try { mcimadamore@1347: switch (deferredAttrContext.mode) { mcimadamore@1347: case SPECULATIVE: mcimadamore@1347: Assert.check(mode == null || mcimadamore@1347: (mode == AttrMode.SPECULATIVE && mcimadamore@1347: speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).tag == NONE)); mcimadamore@1347: JCTree speculativeTree = attribSpeculative(tree, env, resultInfo); mcimadamore@1347: speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase); mcimadamore@1347: return speculativeTree.type; mcimadamore@1347: case CHECK: mcimadamore@1347: Assert.check(mode == AttrMode.SPECULATIVE); mcimadamore@1347: return attr.attribTree(tree, env, resultInfo); mcimadamore@1347: } mcimadamore@1347: Assert.error(); mcimadamore@1347: return null; mcimadamore@1347: } finally { mcimadamore@1347: mode = deferredAttrContext.mode; mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** 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@1347: 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; mcimadamore@1347: Filter prevDeferDiagsFilter = log.deferredDiagFilter; mcimadamore@1347: Queue prevDeferredDiags = log.deferredDiagnostics; mcimadamore@1347: final JavaFileObject currentSource = log.currentSourceFile(); mcimadamore@1347: try { mcimadamore@1347: log.deferredDiagnostics = new ListBuffer(); mcimadamore@1347: log.deferredDiagFilter = new Filter() { mcimadamore@1347: public boolean accepts(JCDiagnostic t) { mcimadamore@1347: return t.getDiagnosticSource().getFile().equals(currentSource); mcimadamore@1347: } mcimadamore@1347: }; mcimadamore@1347: attr.attribTree(newTree, speculativeEnv, resultInfo); mcimadamore@1347: unenterScanner.scan(newTree); mcimadamore@1347: return newTree; mcimadamore@1347: } catch (Abort ex) { mcimadamore@1347: //if some very bad condition occurred during deferred attribution mcimadamore@1347: //we should dump all errors before killing javac mcimadamore@1347: log.reportDeferredDiagnostics(); mcimadamore@1347: throw ex; mcimadamore@1347: } finally { mcimadamore@1347: unenterScanner.scan(newTree); mcimadamore@1347: log.deferredDiagFilter = prevDeferDiagsFilter; mcimadamore@1347: log.deferredDiagnostics = prevDeferredDiags; 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@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@1347: /** list of deferred attribution nodes to be processed */ mcimadamore@1347: ArrayList deferredAttrNodes = new ArrayList(); mcimadamore@1347: mcimadamore@1347: DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, InferenceContext inferenceContext) { mcimadamore@1347: this.mode = mode; mcimadamore@1347: this.msym = msym; mcimadamore@1347: this.phase = phase; 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: */ mcimadamore@1347: void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List stuckVars) { mcimadamore@1347: deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars)); 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()) { mcimadamore@1347: Set stuckVars = new HashSet(); 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@1347: if (!deferredAttrNode.isStuck()) { mcimadamore@1347: deferredAttrNode.process(); mcimadamore@1347: deferredAttrNodes.remove(deferredAttrNode); mcimadamore@1347: progress = true; mcimadamore@1347: } else { mcimadamore@1347: stuckVars.addAll(deferredAttrNode.stuckVars); mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: if (!progress) { mcimadamore@1347: //remove all variables that have already been instantiated mcimadamore@1347: //from the list of stuck variables mcimadamore@1347: inferenceContext.solveAny(inferenceContext.freeVarsIn(List.from(stuckVars)), types, infer); mcimadamore@1347: inferenceContext.notifyChange(types); mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Class representing a deferred attribution node. It keeps track of mcimadamore@1347: * a deferred type, along with the expected target type information. mcimadamore@1347: */ mcimadamore@1347: class DeferredAttrNode implements Infer.InferenceContext.FreeTypeListener { mcimadamore@1347: mcimadamore@1347: /** underlying deferred type */ mcimadamore@1347: DeferredType dt; mcimadamore@1347: mcimadamore@1347: /** underlying target type information */ mcimadamore@1347: ResultInfo resultInfo; mcimadamore@1347: mcimadamore@1347: /** list of uninferred inference variables causing this node to be stuck */ mcimadamore@1347: List stuckVars; mcimadamore@1347: mcimadamore@1347: DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List stuckVars) { mcimadamore@1347: this.dt = dt; mcimadamore@1347: this.resultInfo = resultInfo; mcimadamore@1347: this.stuckVars = stuckVars; mcimadamore@1347: if (!stuckVars.isEmpty()) { mcimadamore@1347: resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this); mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: @Override mcimadamore@1347: public void typesInferred(InferenceContext inferenceContext) { mcimadamore@1347: stuckVars = List.nil(); mcimadamore@1347: resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types)); mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * is this node stuck? mcimadamore@1347: */ mcimadamore@1347: boolean isStuck() { mcimadamore@1347: return stuckVars.nonEmpty(); mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Process a deferred attribution node. mcimadamore@1347: * Invariant: a stuck node cannot be processed. mcimadamore@1347: */ mcimadamore@1347: void process() { mcimadamore@1347: if (isStuck()) { mcimadamore@1347: throw new IllegalStateException("Cannot process a stuck deferred node"); mcimadamore@1347: } mcimadamore@1347: dt.check(resultInfo); mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** an empty deferred attribution context - all methods throw exceptions */ mcimadamore@1347: final DeferredAttrContext emptyDeferredAttrContext = mcimadamore@1347: new DeferredAttrContext(null, null, null, null) { mcimadamore@1347: @Override mcimadamore@1347: void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List stuckVars) { mcimadamore@1347: Assert.error("Empty deferred context!"); mcimadamore@1347: } mcimadamore@1347: @Override mcimadamore@1347: void complete() { mcimadamore@1347: Assert.error("Empty deferred context!"); mcimadamore@1347: } mcimadamore@1347: }; 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@1347: this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, infer.emptyContext); mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: protected boolean validState(DeferredType dt) { mcimadamore@1347: return dt.mode != null && mcimadamore@1347: deferredAttrContext.mode.ordinal() <= dt.mode.ordinal(); mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: @Override mcimadamore@1347: public Type apply(Type t) { mcimadamore@1347: if (t.tag != DEFERRED) { mcimadamore@1347: return t.map(this); mcimadamore@1347: } else { mcimadamore@1347: DeferredType dt = (DeferredType)t; mcimadamore@1347: Assert.check(validState(dt)); 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@1347: super(mode, msym, phase); mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: @Override mcimadamore@1347: protected Type typeOf(DeferredType dt) { mcimadamore@1347: Type owntype = super.typeOf(dt); mcimadamore@1347: return owntype.tag == NONE ? mcimadamore@1347: recover(dt) : owntype; mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: @Override mcimadamore@1347: protected boolean validState(DeferredType dt) { mcimadamore@1347: return true; 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@1348: dt.check(attr.new RecoveryInfo(deferredAttrContext)); mcimadamore@1347: switch (TreeInfo.skipParens(dt.tree).getTag()) { mcimadamore@1347: case LAMBDA: mcimadamore@1347: case REFERENCE: mcimadamore@1347: case CONDEXPR: mcimadamore@1347: //propagate those deferred types to the mcimadamore@1347: //diagnostic formatter mcimadamore@1347: return dt; mcimadamore@1347: default: mcimadamore@1347: return super.apply(dt); mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1347: * Retrieves the list of inference variables that need to be inferred before mcimadamore@1347: * an AST node can be type-checked mcimadamore@1347: */ mcimadamore@1347: @SuppressWarnings("fallthrough") mcimadamore@1348: List stuckVars(JCTree tree, ResultInfo resultInfo) { mcimadamore@1348: if (resultInfo.pt.tag == NONE || resultInfo.pt.isErroneous()) { mcimadamore@1348: return List.nil(); mcimadamore@1348: } else { mcimadamore@1348: StuckChecker sc = new StuckChecker(resultInfo); mcimadamore@1348: sc.scan(tree); mcimadamore@1348: return List.from(sc.stuckVars); mcimadamore@1348: } 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: */ mcimadamore@1348: class StuckChecker extends TreeScanner { mcimadamore@1348: mcimadamore@1348: Type pt; mcimadamore@1348: Filter treeFilter; mcimadamore@1348: Infer.InferenceContext inferenceContext; mcimadamore@1348: Set stuckVars = new HashSet(); mcimadamore@1348: mcimadamore@1348: final Filter argsFilter = new Filter() { mcimadamore@1348: public boolean accepts(JCTree t) { mcimadamore@1348: switch (t.getTag()) { mcimadamore@1348: case CONDEXPR: mcimadamore@1348: case LAMBDA: mcimadamore@1348: case PARENS: mcimadamore@1348: case REFERENCE: mcimadamore@1348: return true; mcimadamore@1348: default: mcimadamore@1348: return false; mcimadamore@1348: } mcimadamore@1348: } mcimadamore@1348: }; mcimadamore@1348: mcimadamore@1348: final Filter lambdaBodyFilter = new Filter() { mcimadamore@1348: public boolean accepts(JCTree t) { mcimadamore@1348: switch (t.getTag()) { mcimadamore@1348: case BLOCK: case CASE: case CATCH: case DOLOOP: mcimadamore@1348: case FOREACHLOOP: case FORLOOP: case RETURN: mcimadamore@1348: case SYNCHRONIZED: case SWITCH: case TRY: case WHILELOOP: mcimadamore@1348: return true; mcimadamore@1348: default: mcimadamore@1348: return false; mcimadamore@1348: } mcimadamore@1348: } mcimadamore@1348: }; mcimadamore@1348: mcimadamore@1348: StuckChecker(ResultInfo resultInfo) { mcimadamore@1348: this.pt = resultInfo.pt; mcimadamore@1348: this.inferenceContext = resultInfo.checkContext.inferenceContext(); mcimadamore@1348: this.treeFilter = argsFilter; mcimadamore@1348: } mcimadamore@1348: mcimadamore@1348: @Override mcimadamore@1348: public void scan(JCTree tree) { mcimadamore@1348: if (tree != null && treeFilter.accepts(tree)) { mcimadamore@1348: super.scan(tree); mcimadamore@1348: } mcimadamore@1348: } mcimadamore@1348: mcimadamore@1348: @Override mcimadamore@1348: public void visitLambda(JCLambda tree) { mcimadamore@1348: Type prevPt = pt; mcimadamore@1348: Filter prevFilter = treeFilter; mcimadamore@1348: try { mcimadamore@1348: if (inferenceContext.inferenceVars().contains(pt)) { mcimadamore@1348: stuckVars.add(pt); mcimadamore@1348: } mcimadamore@1348: if (!types.isFunctionalInterface(pt.tsym)) { mcimadamore@1348: return; mcimadamore@1348: } mcimadamore@1348: Type descType = types.findDescriptorType(pt); mcimadamore@1348: List freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); mcimadamore@1348: if (!TreeInfo.isExplicitLambda(tree) && mcimadamore@1348: freeArgVars.nonEmpty()) { mcimadamore@1348: stuckVars.addAll(freeArgVars); mcimadamore@1348: } mcimadamore@1348: pt = descType.getReturnType(); mcimadamore@1348: if (tree.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { mcimadamore@1348: scan(tree.getBody()); mcimadamore@1348: } else { mcimadamore@1348: treeFilter = lambdaBodyFilter; mcimadamore@1348: super.visitLambda(tree); mcimadamore@1348: } mcimadamore@1348: } finally { mcimadamore@1348: pt = prevPt; mcimadamore@1348: treeFilter = prevFilter; mcimadamore@1348: } 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@1348: if (!types.isFunctionalInterface(pt.tsym)) { mcimadamore@1348: return; mcimadamore@1348: } mcimadamore@1348: Type descType = types.findDescriptorType(pt); mcimadamore@1348: List freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); mcimadamore@1348: stuckVars.addAll(freeArgVars); mcimadamore@1348: } mcimadamore@1348: mcimadamore@1348: @Override mcimadamore@1348: public void visitReturn(JCReturn tree) { mcimadamore@1348: Filter prevFilter = treeFilter; mcimadamore@1348: try { mcimadamore@1348: treeFilter = argsFilter; mcimadamore@1348: if (tree.expr != null) { mcimadamore@1348: scan(tree.expr); mcimadamore@1348: } mcimadamore@1348: } finally { mcimadamore@1348: treeFilter = prevFilter; mcimadamore@1348: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: }