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@1481: import java.util.EnumSet; mcimadamore@1415: import java.util.LinkedHashSet; 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@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@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@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@1510: stuckTree = make.Ident(names.empty).setType(Type.noType); 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) { 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: * 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@1481: return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter); mcimadamore@1481: } mcimadamore@1481: mcimadamore@1481: Type check(ResultInfo resultInfo, List stuckVars, DeferredTypeCompleter deferredTypeCompleter) { mcimadamore@1347: DeferredAttrContext deferredAttrContext = mcimadamore@1347: resultInfo.checkContext.deferredAttrContext(); mcimadamore@1347: Assert.check(deferredAttrContext != emptyDeferredAttrContext); mcimadamore@1347: if (stuckVars.nonEmpty()) { mcimadamore@1347: deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars); 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: 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@1481: Assert.check(dt.mode == null || mcimadamore@1481: (dt.mode == AttrMode.SPECULATIVE && mcimadamore@1481: dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE))); mcimadamore@1481: JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo); mcimadamore@1481: dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase); mcimadamore@1481: return speculativeTree.type; mcimadamore@1481: case CHECK: mcimadamore@1481: Assert.check(dt.mode == AttrMode.SPECULATIVE); 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@1481: /** 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: final JavaFileObject currentSource = log.currentSourceFile(); jjg@1406: Log.DeferredDiagnosticHandler deferredDiagnosticHandler = jjg@1406: new Log.DeferredDiagnosticHandler(log, new Filter() { jjg@1406: public boolean accepts(JCDiagnostic t) { jjg@1406: return t.getDiagnosticSource().getFile().equals(currentSource); 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: } catch (Abort ex) { mcimadamore@1347: //if some very bad condition occurred during deferred attribution mcimadamore@1347: //we should dump all errors before killing javac jjg@1406: deferredDiagnosticHandler.reportDeferredDiagnostics(); mcimadamore@1347: throw ex; 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@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@1415: Set stuckVars = new LinkedHashSet(); 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@1510: if (!deferredAttrNode.process()) { mcimadamore@1510: stuckVars.addAll(deferredAttrNode.stuckVars); mcimadamore@1510: } else { mcimadamore@1347: deferredAttrNodes.remove(deferredAttrNode); mcimadamore@1347: progress = true; 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@1510: * Process a deferred attribution node. mcimadamore@1510: * Invariant: a stuck node cannot be processed. mcimadamore@1347: */ mcimadamore@1510: @SuppressWarnings("fallthrough") mcimadamore@1510: boolean process() { mcimadamore@1510: switch (mode) { mcimadamore@1510: case SPECULATIVE: mcimadamore@1510: dt.check(resultInfo, List.nil(), new StructuralStuckChecker()); mcimadamore@1510: return true; mcimadamore@1510: case CHECK: mcimadamore@1510: if (stuckVars.nonEmpty()) { mcimadamore@1510: return false; mcimadamore@1510: } else { mcimadamore@1510: dt.check(resultInfo, stuckVars, basicCompleter); mcimadamore@1510: return true; mcimadamore@1510: } mcimadamore@1510: default: mcimadamore@1510: throw new AssertionError("Bad mode"); mcimadamore@1510: } mcimadamore@1347: } mcimadamore@1347: mcimadamore@1347: /** mcimadamore@1510: * Structural checker for stuck expressions mcimadamore@1347: */ mcimadamore@1510: class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter { mcimadamore@1510: mcimadamore@1510: ResultInfo resultInfo; mcimadamore@1510: mcimadamore@1510: public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { mcimadamore@1510: this.resultInfo = resultInfo; mcimadamore@1510: dt.tree.accept(this); mcimadamore@1510: dt.speculativeCache.put(msym, stuckTree, phase); mcimadamore@1510: return Type.noType; mcimadamore@1347: } mcimadamore@1510: mcimadamore@1510: @Override mcimadamore@1510: public void visitLambda(JCLambda tree) { mcimadamore@1510: Check.CheckContext checkContext = resultInfo.checkContext; mcimadamore@1510: Type pt = resultInfo.pt; mcimadamore@1510: if (inferenceContext.inferencevars.contains(pt)) { mcimadamore@1510: //ok mcimadamore@1510: return; mcimadamore@1510: } else { mcimadamore@1510: //must be a functional descriptor mcimadamore@1510: try { mcimadamore@1510: Type desc = types.findDescriptorType(pt); mcimadamore@1510: if (desc.getParameterTypes().length() != tree.params.length()) { mcimadamore@1510: checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda")); mcimadamore@1510: } mcimadamore@1510: } catch (Types.FunctionDescriptorLookupError ex) { mcimadamore@1510: checkContext.report(null, ex.getDiagnostic()); mcimadamore@1510: } mcimadamore@1510: } mcimadamore@1510: } mcimadamore@1510: mcimadamore@1510: @Override mcimadamore@1510: public void visitNewClass(JCNewClass tree) { mcimadamore@1510: //do nothing mcimadamore@1510: } mcimadamore@1510: mcimadamore@1510: @Override mcimadamore@1510: public void visitApply(JCMethodInvocation tree) { mcimadamore@1510: //do nothing mcimadamore@1510: } mcimadamore@1510: mcimadamore@1510: @Override mcimadamore@1510: public void visitReference(JCMemberReference tree) { mcimadamore@1510: Check.CheckContext checkContext = resultInfo.checkContext; mcimadamore@1510: Type pt = resultInfo.pt; mcimadamore@1510: if (inferenceContext.inferencevars.contains(pt)) { mcimadamore@1510: //ok mcimadamore@1510: return; mcimadamore@1510: } else { mcimadamore@1510: try { mcimadamore@1510: //TODO: we should speculative determine if there's a match mcimadamore@1510: //based on arity - if yes, method is applicable. mcimadamore@1510: types.findDescriptorType(pt); mcimadamore@1510: } catch (Types.FunctionDescriptorLookupError ex) { mcimadamore@1510: checkContext.report(null, ex.getDiagnostic()); mcimadamore@1510: } mcimadamore@1510: } mcimadamore@1510: } 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@1415: new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, 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) { jjg@1374: if (!t.hasTag(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@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: @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@1415: return super.apply(dt); 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@1415: List stuckVars(JCTree tree, Env env, ResultInfo resultInfo) { mcimadamore@1415: if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) { mcimadamore@1348: return List.nil(); mcimadamore@1348: } else { mcimadamore@1481: return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext()); mcimadamore@1481: } mcimadamore@1481: } mcimadamore@1481: //where mcimadamore@1481: private List stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) { mcimadamore@1481: StuckChecker sc = new StuckChecker(pt, inferenceContext); mcimadamore@1348: sc.scan(tree); mcimadamore@1348: return List.from(sc.stuckVars); mcimadamore@1348: } mcimadamore@1481: mcimadamore@1481: /** 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: */ mcimadamore@1481: class StuckChecker extends PolyScanner { mcimadamore@1348: mcimadamore@1348: Type pt; mcimadamore@1348: Infer.InferenceContext inferenceContext; mcimadamore@1415: Set stuckVars = new LinkedHashSet(); mcimadamore@1348: mcimadamore@1481: StuckChecker(Type pt, Infer.InferenceContext inferenceContext) { mcimadamore@1481: this.pt = pt; mcimadamore@1481: this.inferenceContext = inferenceContext; 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); 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()); mcimadamore@1348: stuckVars.addAll(freeArgVars); mcimadamore@1348: } mcimadamore@1348: mcimadamore@1481: void scanLambdaBody(JCLambda lambda, final Type pt) { mcimadamore@1481: if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { mcimadamore@1481: stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext)); 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) { mcimadamore@1481: stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext)); mcimadamore@1481: } mcimadamore@1481: } mcimadamore@1481: }; mcimadamore@1481: lambdaScanner.scan(lambda.body); mcimadamore@1348: } mcimadamore@1347: } mcimadamore@1347: } mcimadamore@1347: }