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:
jjg@1374: import static com.sun.tools.javac.code.TypeTag.DEFERRED;
jjg@1374: import static com.sun.tools.javac.code.TypeTag.NONE;
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@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 &&
jjg@1374: speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(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