aoqi@0: /*
aoqi@0: * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
aoqi@0: *
aoqi@0: * This code is free software; you can redistribute it and/or modify it
aoqi@0: * under the terms of the GNU General Public License version 2 only, as
aoqi@0: * published by the Free Software Foundation. Oracle designates this
aoqi@0: * particular file as subject to the "Classpath" exception as provided
aoqi@0: * by Oracle in the LICENSE file that accompanied this code.
aoqi@0: *
aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT
aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that
aoqi@0: * accompanied this code).
aoqi@0: *
aoqi@0: * You should have received a copy of the GNU General Public License version
aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation,
aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
aoqi@0: *
aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
aoqi@0: * or visit www.oracle.com if you need additional information or have any
aoqi@0: * questions.
aoqi@0: */
aoqi@0:
aoqi@0: package com.sun.tools.javac.comp;
aoqi@0:
aoqi@0: import com.sun.source.tree.LambdaExpressionTree.BodyKind;
aoqi@0: import com.sun.tools.javac.code.*;
aoqi@0: import com.sun.tools.javac.tree.*;
aoqi@0: import com.sun.tools.javac.util.*;
aoqi@0: import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
aoqi@0: import com.sun.tools.javac.code.Symbol.*;
aoqi@0: import com.sun.tools.javac.code.Type.*;
aoqi@0: import com.sun.tools.javac.comp.Attr.ResultInfo;
aoqi@0: import com.sun.tools.javac.comp.Infer.InferenceContext;
aoqi@0: import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
aoqi@0: import com.sun.tools.javac.tree.JCTree.*;
aoqi@0:
aoqi@0: import java.util.ArrayList;
aoqi@0: import java.util.Collections;
aoqi@0: import java.util.EnumSet;
aoqi@0: import java.util.LinkedHashMap;
aoqi@0: import java.util.LinkedHashSet;
aoqi@0: import java.util.Map;
aoqi@0: import java.util.Set;
aoqi@0: import java.util.WeakHashMap;
aoqi@0:
aoqi@0: import static com.sun.tools.javac.code.Kinds.VAL;
aoqi@0: import static com.sun.tools.javac.code.TypeTag.*;
aoqi@0: import static com.sun.tools.javac.tree.JCTree.Tag.*;
aoqi@0:
aoqi@0: /**
aoqi@0: * This is an helper class that is used to perform deferred type-analysis.
aoqi@0: * Each time a poly expression occurs in argument position, javac attributes it
aoqi@0: * with a temporary 'deferred type' that is checked (possibly multiple times)
aoqi@0: * against an expected formal type.
aoqi@0: *
aoqi@0: *
This is NOT part of any supported API.
aoqi@0: * If you write code that depends on this, you do so at your own risk.
aoqi@0: * This code and its internal interfaces are subject to change or
aoqi@0: * deletion without notice.
aoqi@0: */
aoqi@0: public class DeferredAttr extends JCTree.Visitor {
aoqi@0: protected static final Context.Key deferredAttrKey =
aoqi@0: new Context.Key();
aoqi@0:
aoqi@0: final Attr attr;
aoqi@0: final Check chk;
aoqi@0: final JCDiagnostic.Factory diags;
aoqi@0: final Enter enter;
aoqi@0: final Infer infer;
aoqi@0: final Resolve rs;
aoqi@0: final Log log;
aoqi@0: final Symtab syms;
aoqi@0: final TreeMaker make;
aoqi@0: final Types types;
aoqi@0: final Flow flow;
aoqi@0: final Names names;
aoqi@0: final TypeEnvs typeEnvs;
aoqi@0:
aoqi@0: public static DeferredAttr instance(Context context) {
aoqi@0: DeferredAttr instance = context.get(deferredAttrKey);
aoqi@0: if (instance == null)
aoqi@0: instance = new DeferredAttr(context);
aoqi@0: return instance;
aoqi@0: }
aoqi@0:
aoqi@0: protected DeferredAttr(Context context) {
aoqi@0: context.put(deferredAttrKey, this);
aoqi@0: attr = Attr.instance(context);
aoqi@0: chk = Check.instance(context);
aoqi@0: diags = JCDiagnostic.Factory.instance(context);
aoqi@0: enter = Enter.instance(context);
aoqi@0: infer = Infer.instance(context);
aoqi@0: rs = Resolve.instance(context);
aoqi@0: log = Log.instance(context);
aoqi@0: syms = Symtab.instance(context);
aoqi@0: make = TreeMaker.instance(context);
aoqi@0: types = Types.instance(context);
aoqi@0: flow = Flow.instance(context);
aoqi@0: names = Names.instance(context);
aoqi@0: stuckTree = make.Ident(names.empty).setType(Type.stuckType);
aoqi@0: typeEnvs = TypeEnvs.instance(context);
aoqi@0: emptyDeferredAttrContext =
aoqi@0: new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) {
aoqi@0: @Override
aoqi@0: void addDeferredAttrNode(DeferredType dt, ResultInfo ri, DeferredStuckPolicy deferredStuckPolicy) {
aoqi@0: Assert.error("Empty deferred context!");
aoqi@0: }
aoqi@0: @Override
aoqi@0: void complete() {
aoqi@0: Assert.error("Empty deferred context!");
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public String toString() {
aoqi@0: return "Empty deferred context!";
aoqi@0: }
aoqi@0: };
aoqi@0: }
aoqi@0:
aoqi@0: /** shared tree for stuck expressions */
aoqi@0: final JCTree stuckTree;
aoqi@0:
aoqi@0: /**
aoqi@0: * This type represents a deferred type. A deferred type starts off with
aoqi@0: * no information on the underlying expression type. Such info needs to be
aoqi@0: * discovered through type-checking the deferred type against a target-type.
aoqi@0: * Every deferred type keeps a pointer to the AST node from which it originated.
aoqi@0: */
aoqi@0: public class DeferredType extends Type {
aoqi@0:
aoqi@0: public JCExpression tree;
aoqi@0: Env env;
aoqi@0: AttrMode mode;
aoqi@0: SpeculativeCache speculativeCache;
aoqi@0:
aoqi@0: DeferredType(JCExpression tree, Env env) {
aoqi@0: super(null);
aoqi@0: this.tree = tree;
aoqi@0: this.env = attr.copyEnv(env);
aoqi@0: this.speculativeCache = new SpeculativeCache();
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public TypeTag getTag() {
aoqi@0: return DEFERRED;
aoqi@0: }
aoqi@0:
aoqi@0: @Override
aoqi@0: public String toString() {
aoqi@0: return "DeferredType";
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * A speculative cache is used to keep track of all overload resolution rounds
aoqi@0: * that triggered speculative attribution on a given deferred type. Each entry
aoqi@0: * stores a pointer to the speculative tree and the resolution phase in which the entry
aoqi@0: * has been added.
aoqi@0: */
aoqi@0: class SpeculativeCache {
aoqi@0:
aoqi@0: private Map> cache =
aoqi@0: new WeakHashMap>();
aoqi@0:
aoqi@0: class Entry {
aoqi@0: JCTree speculativeTree;
aoqi@0: ResultInfo resultInfo;
aoqi@0:
aoqi@0: public Entry(JCTree speculativeTree, ResultInfo resultInfo) {
aoqi@0: this.speculativeTree = speculativeTree;
aoqi@0: this.resultInfo = resultInfo;
aoqi@0: }
aoqi@0:
aoqi@0: boolean matches(MethodResolutionPhase phase) {
aoqi@0: return resultInfo.checkContext.deferredAttrContext().phase == phase;
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Retrieve a speculative cache entry corresponding to given symbol
aoqi@0: * and resolution phase
aoqi@0: */
aoqi@0: Entry get(Symbol msym, MethodResolutionPhase phase) {
aoqi@0: List entries = cache.get(msym);
aoqi@0: if (entries == null) return null;
aoqi@0: for (Entry e : entries) {
aoqi@0: if (e.matches(phase)) return e;
aoqi@0: }
aoqi@0: return null;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Stores a speculative cache entry corresponding to given symbol
aoqi@0: * and resolution phase
aoqi@0: */
aoqi@0: void put(JCTree speculativeTree, ResultInfo resultInfo) {
aoqi@0: Symbol msym = resultInfo.checkContext.deferredAttrContext().msym;
aoqi@0: List entries = cache.get(msym);
aoqi@0: if (entries == null) {
aoqi@0: entries = List.nil();
aoqi@0: }
aoqi@0: cache.put(msym, entries.prepend(new Entry(speculativeTree, resultInfo)));
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Get the type that has been computed during a speculative attribution round
aoqi@0: */
aoqi@0: Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
aoqi@0: SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
aoqi@0: return e != null ? e.speculativeTree.type : Type.noType;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Check a deferred type against a potential target-type. Depending on
aoqi@0: * the current attribution mode, a normal vs. speculative attribution
aoqi@0: * round is performed on the underlying AST node. There can be only one
aoqi@0: * speculative round for a given target method symbol; moreover, a normal
aoqi@0: * attribution round must follow one or more speculative rounds.
aoqi@0: */
aoqi@0: Type check(ResultInfo resultInfo) {
aoqi@0: DeferredStuckPolicy deferredStuckPolicy;
aoqi@0: if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
aoqi@0: deferredStuckPolicy = dummyStuckPolicy;
aoqi@0: } else if (resultInfo.checkContext.deferredAttrContext().mode == AttrMode.SPECULATIVE) {
aoqi@0: deferredStuckPolicy = new OverloadStuckPolicy(resultInfo, this);
aoqi@0: } else {
aoqi@0: deferredStuckPolicy = new CheckStuckPolicy(resultInfo, this);
aoqi@0: }
aoqi@0: return check(resultInfo, deferredStuckPolicy, basicCompleter);
aoqi@0: }
aoqi@0:
aoqi@0: private Type check(ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy,
aoqi@0: DeferredTypeCompleter deferredTypeCompleter) {
aoqi@0: DeferredAttrContext deferredAttrContext =
aoqi@0: resultInfo.checkContext.deferredAttrContext();
aoqi@0: Assert.check(deferredAttrContext != emptyDeferredAttrContext);
aoqi@0: if (deferredStuckPolicy.isStuck()) {
aoqi@0: deferredAttrContext.addDeferredAttrNode(this, resultInfo, deferredStuckPolicy);
aoqi@0: return Type.noType;
aoqi@0: } else {
aoqi@0: try {
aoqi@0: return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
aoqi@0: } finally {
aoqi@0: mode = deferredAttrContext.mode;
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * A completer for deferred types. Defines an entry point for type-checking
aoqi@0: * a deferred type.
aoqi@0: */
aoqi@0: interface DeferredTypeCompleter {
aoqi@0: /**
aoqi@0: * Entry point for type-checking a deferred type. Depending on the
aoqi@0: * circumstances, type-checking could amount to full attribution
aoqi@0: * or partial structural check (aka potential applicability).
aoqi@0: */
aoqi@0: Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
aoqi@0: }
aoqi@0:
aoqi@0:
aoqi@0: /**
aoqi@0: * A basic completer for deferred types. This completer type-checks a deferred type
aoqi@0: * using attribution; depending on the attribution mode, this could be either standard
aoqi@0: * or speculative attribution.
aoqi@0: */
aoqi@0: DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
aoqi@0: public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
aoqi@0: switch (deferredAttrContext.mode) {
aoqi@0: case SPECULATIVE:
aoqi@0: //Note: if a symbol is imported twice we might do two identical
aoqi@0: //speculative rounds...
aoqi@0: Assert.check(dt.mode == null || dt.mode == AttrMode.SPECULATIVE);
aoqi@0: JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
aoqi@0: dt.speculativeCache.put(speculativeTree, resultInfo);
aoqi@0: return speculativeTree.type;
aoqi@0: case CHECK:
aoqi@0: Assert.check(dt.mode != null);
aoqi@0: return attr.attribTree(dt.tree, dt.env, resultInfo);
aoqi@0: }
aoqi@0: Assert.error();
aoqi@0: return null;
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: DeferredTypeCompleter dummyCompleter = new DeferredTypeCompleter() {
aoqi@0: public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
aoqi@0: Assert.check(deferredAttrContext.mode == AttrMode.CHECK);
aoqi@0: return dt.tree.type = Type.stuckType;
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: /**
aoqi@0: * Policy for detecting stuck expressions. Different criteria might cause
aoqi@0: * an expression to be judged as stuck, depending on whether the check
aoqi@0: * is performed during overload resolution or after most specific.
aoqi@0: */
aoqi@0: interface DeferredStuckPolicy {
aoqi@0: /**
aoqi@0: * Has the policy detected that a given expression should be considered stuck?
aoqi@0: */
aoqi@0: boolean isStuck();
aoqi@0: /**
aoqi@0: * Get the set of inference variables a given expression depends upon.
aoqi@0: */
aoqi@0: Set stuckVars();
aoqi@0: /**
aoqi@0: * Get the set of inference variables which might get new constraints
aoqi@0: * if a given expression is being type-checked.
aoqi@0: */
aoqi@0: Set depVars();
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Basic stuck policy; an expression is never considered to be stuck.
aoqi@0: */
aoqi@0: DeferredStuckPolicy dummyStuckPolicy = new DeferredStuckPolicy() {
aoqi@0: @Override
aoqi@0: public boolean isStuck() {
aoqi@0: return false;
aoqi@0: }
aoqi@0: @Override
aoqi@0: public Set stuckVars() {
aoqi@0: return Collections.emptySet();
aoqi@0: }
aoqi@0: @Override
aoqi@0: public Set depVars() {
aoqi@0: return Collections.emptySet();
aoqi@0: }
aoqi@0: };
aoqi@0:
aoqi@0: /**
aoqi@0: * The 'mode' in which the deferred type is to be type-checked
aoqi@0: */
aoqi@0: public enum AttrMode {
aoqi@0: /**
aoqi@0: * A speculative type-checking round is used during overload resolution
aoqi@0: * mainly to generate constraints on inference variables. Side-effects
aoqi@0: * arising from type-checking the expression associated with the deferred
aoqi@0: * type are reversed after the speculative round finishes. This means the
aoqi@0: * expression tree will be left in a blank state.
aoqi@0: */
aoqi@0: SPECULATIVE,
aoqi@0: /**
aoqi@0: * This is the plain type-checking mode. Produces side-effects on the underlying AST node
aoqi@0: */
aoqi@0: CHECK;
aoqi@0: }
aoqi@0:
aoqi@0: /**
aoqi@0: * Routine that performs speculative type-checking; the input AST node is
aoqi@0: * cloned (to avoid side-effects cause by Attr) and compiler state is
aoqi@0: * restored after type-checking. All diagnostics (but critical ones) are
aoqi@0: * disabled during speculative type-checking.
aoqi@0: */
aoqi@0: JCTree attribSpeculative(JCTree tree, Env env, ResultInfo resultInfo) {
aoqi@0: final JCTree newTree = new TreeCopier