langtools/src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java
changeset 14057 b4b0377b8dba
child 14058 c7ec7facdd20
equal deleted inserted replaced
14056:0ea78d6e0b7b 14057:b4b0377b8dba
       
     1 /*
       
     2  * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.tools.javac.comp;
       
    27 
       
    28 import com.sun.tools.javac.code.*;
       
    29 import com.sun.tools.javac.tree.*;
       
    30 import com.sun.tools.javac.util.*;
       
    31 import com.sun.tools.javac.code.Symbol.*;
       
    32 import com.sun.tools.javac.code.Type.*;
       
    33 import com.sun.tools.javac.comp.Attr.ResultInfo;
       
    34 import com.sun.tools.javac.comp.Infer.InferenceContext;
       
    35 import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
       
    36 import com.sun.tools.javac.tree.JCTree.*;
       
    37 
       
    38 import javax.tools.JavaFileObject;
       
    39 
       
    40 import java.util.ArrayList;
       
    41 import java.util.HashSet;
       
    42 import java.util.Map;
       
    43 import java.util.Queue;
       
    44 import java.util.Set;
       
    45 import java.util.WeakHashMap;
       
    46 
       
    47 import static com.sun.tools.javac.code.TypeTags.*;
       
    48 import static com.sun.tools.javac.tree.JCTree.Tag.*;
       
    49 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
       
    50 
       
    51 /**
       
    52  * This is an helper class that is used to perform deferred type-analysis.
       
    53  * Each time a poly expression occurs in argument position, javac attributes it
       
    54  * with a temporary 'deferred type' that is checked (possibly multiple times)
       
    55  * against an expected formal type.
       
    56  *
       
    57  *  <p><b>This is NOT part of any supported API.
       
    58  *  If you write code that depends on this, you do so at your own risk.
       
    59  *  This code and its internal interfaces are subject to change or
       
    60  *  deletion without notice.</b>
       
    61  */
       
    62 public class DeferredAttr extends JCTree.Visitor {
       
    63     protected static final Context.Key<DeferredAttr> deferredAttrKey =
       
    64         new Context.Key<DeferredAttr>();
       
    65 
       
    66     final Attr attr;
       
    67     final Check chk;
       
    68     final Enter enter;
       
    69     final Infer infer;
       
    70     final Log log;
       
    71     final Symtab syms;
       
    72     final TreeMaker make;
       
    73     final Types types;
       
    74 
       
    75     public static DeferredAttr instance(Context context) {
       
    76         DeferredAttr instance = context.get(deferredAttrKey);
       
    77         if (instance == null)
       
    78             instance = new DeferredAttr(context);
       
    79         return instance;
       
    80     }
       
    81 
       
    82     protected DeferredAttr(Context context) {
       
    83         context.put(deferredAttrKey, this);
       
    84         attr = Attr.instance(context);
       
    85         chk = Check.instance(context);
       
    86         enter = Enter.instance(context);
       
    87         infer = Infer.instance(context);
       
    88         log = Log.instance(context);
       
    89         syms = Symtab.instance(context);
       
    90         make = TreeMaker.instance(context);
       
    91         types = Types.instance(context);
       
    92     }
       
    93 
       
    94     /**
       
    95      * This type represents a deferred type. A deferred type starts off with
       
    96      * no information on the underlying expression type. Such info needs to be
       
    97      * discovered through type-checking the deferred type against a target-type.
       
    98      * Every deferred type keeps a pointer to the AST node from which it originated.
       
    99      */
       
   100     public class DeferredType extends Type {
       
   101 
       
   102         public JCExpression tree;
       
   103         Env<AttrContext> env;
       
   104         AttrMode mode;
       
   105         SpeculativeCache speculativeCache;
       
   106 
       
   107         DeferredType(JCExpression tree, Env<AttrContext> env) {
       
   108             super(DEFERRED, null);
       
   109             this.tree = tree;
       
   110             this.env = env.dup(tree, env.info.dup());
       
   111             this.speculativeCache = new SpeculativeCache();
       
   112         }
       
   113 
       
   114         /**
       
   115          * A speculative cache is used to keep track of all overload resolution rounds
       
   116          * that triggered speculative attribution on a given deferred type. Each entry
       
   117          * stores a pointer to the speculative tree and the resolution phase in which the entry
       
   118          * has been added.
       
   119          */
       
   120         class SpeculativeCache {
       
   121 
       
   122             private Map<Symbol, List<Entry>> cache =
       
   123                     new WeakHashMap<Symbol, List<Entry>>();
       
   124 
       
   125             class Entry {
       
   126                 JCTree speculativeTree;
       
   127                 Resolve.MethodResolutionPhase phase;
       
   128 
       
   129                 public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
       
   130                     this.speculativeTree = speculativeTree;
       
   131                     this.phase = phase;
       
   132                 }
       
   133 
       
   134                 boolean matches(Resolve.MethodResolutionPhase phase) {
       
   135                     return this.phase == phase;
       
   136                 }
       
   137             }
       
   138 
       
   139             /**
       
   140              * Clone a speculative cache entry as a fresh entry associated
       
   141              * with a new method (this maybe required to fixup speculative cache
       
   142              * misses after Resolve.access())
       
   143              */
       
   144             void dupAllTo(Symbol from, Symbol to) {
       
   145                 Assert.check(cache.get(to) == null);
       
   146                 List<Entry> entries = cache.get(from);
       
   147                 if (entries != null) {
       
   148                     cache.put(to, entries);
       
   149                 }
       
   150             }
       
   151 
       
   152             /**
       
   153              * Retrieve a speculative cache entry corresponding to given symbol
       
   154              * and resolution phase
       
   155              */
       
   156             Entry get(Symbol msym, MethodResolutionPhase phase) {
       
   157                 List<Entry> entries = cache.get(msym);
       
   158                 if (entries == null) return null;
       
   159                 for (Entry e : entries) {
       
   160                     if (e.matches(phase)) return e;
       
   161                 }
       
   162                 return null;
       
   163             }
       
   164 
       
   165             /**
       
   166              * Stores a speculative cache entry corresponding to given symbol
       
   167              * and resolution phase
       
   168              */
       
   169             void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
       
   170                 List<Entry> entries = cache.get(msym);
       
   171                 if (entries == null) {
       
   172                     entries = List.nil();
       
   173                 }
       
   174                 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
       
   175             }
       
   176         }
       
   177 
       
   178         /**
       
   179          * Get the type that has been computed during a speculative attribution round
       
   180          */
       
   181         Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
       
   182             SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
       
   183             return e != null ? e.speculativeTree.type : Type.noType;
       
   184         }
       
   185 
       
   186         /**
       
   187          * Check a deferred type against a potential target-type. Depending on
       
   188          * the current attribution mode, a normal vs. speculative attribution
       
   189          * round is performed on the underlying AST node. There can be only one
       
   190          * speculative round for a given target method symbol; moreover, a normal
       
   191          * attribution round must follow one or more speculative rounds.
       
   192          */
       
   193         Type check(ResultInfo resultInfo) {
       
   194             DeferredAttrContext deferredAttrContext =
       
   195                     resultInfo.checkContext.deferredAttrContext();
       
   196             Assert.check(deferredAttrContext != emptyDeferredAttrContext);
       
   197             List<Type> stuckVars = stuckVars(tree, resultInfo);
       
   198             if (stuckVars.nonEmpty()) {
       
   199                 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
       
   200                 return Type.noType;
       
   201             } else {
       
   202                 try {
       
   203                     switch (deferredAttrContext.mode) {
       
   204                         case SPECULATIVE:
       
   205                             Assert.check(mode == null ||
       
   206                                     (mode == AttrMode.SPECULATIVE &&
       
   207                                     speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).tag == NONE));
       
   208                             JCTree speculativeTree = attribSpeculative(tree, env, resultInfo);
       
   209                             speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
       
   210                             return speculativeTree.type;
       
   211                         case CHECK:
       
   212                             Assert.check(mode == AttrMode.SPECULATIVE);
       
   213                             return attr.attribTree(tree, env, resultInfo);
       
   214                     }
       
   215                     Assert.error();
       
   216                     return null;
       
   217                 } finally {
       
   218                     mode = deferredAttrContext.mode;
       
   219                 }
       
   220             }
       
   221         }
       
   222     }
       
   223 
       
   224     /**
       
   225      * The 'mode' in which the deferred type is to be type-checked
       
   226      */
       
   227     public enum AttrMode {
       
   228         /**
       
   229          * A speculative type-checking round is used during overload resolution
       
   230          * mainly to generate constraints on inference variables. Side-effects
       
   231          * arising from type-checking the expression associated with the deferred
       
   232          * type are reversed after the speculative round finishes. This means the
       
   233          * expression tree will be left in a blank state.
       
   234          */
       
   235         SPECULATIVE,
       
   236         /**
       
   237          * This is the plain type-checking mode. Produces side-effects on the underlying AST node
       
   238          */
       
   239         CHECK;
       
   240     }
       
   241 
       
   242     /**
       
   243      * Routine that performs speculative type-checking; the input AST node is
       
   244      * cloned (to avoid side-effects cause by Attr) and compiler state is
       
   245      * restored after type-checking. All diagnostics (but critical ones) are
       
   246      * disabled during speculative type-checking.
       
   247      */
       
   248     JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
       
   249         JCTree newTree = new TreeCopier<Object>(make).copy(tree);
       
   250         Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
       
   251         speculativeEnv.info.scope.owner = env.info.scope.owner;
       
   252         Filter<JCDiagnostic> prevDeferDiagsFilter = log.deferredDiagFilter;
       
   253         Queue<JCDiagnostic> prevDeferredDiags = log.deferredDiagnostics;
       
   254         final JavaFileObject currentSource = log.currentSourceFile();
       
   255         try {
       
   256             log.deferredDiagnostics = new ListBuffer<JCDiagnostic>();
       
   257             log.deferredDiagFilter = new Filter<JCDiagnostic>() {
       
   258                 public boolean accepts(JCDiagnostic t) {
       
   259                     return t.getDiagnosticSource().getFile().equals(currentSource);
       
   260                 }
       
   261             };
       
   262             attr.attribTree(newTree, speculativeEnv, resultInfo);
       
   263             unenterScanner.scan(newTree);
       
   264             return newTree;
       
   265         } catch (Abort ex) {
       
   266             //if some very bad condition occurred during deferred attribution
       
   267             //we should dump all errors before killing javac
       
   268             log.reportDeferredDiagnostics();
       
   269             throw ex;
       
   270         } finally {
       
   271             unenterScanner.scan(newTree);
       
   272             log.deferredDiagFilter = prevDeferDiagsFilter;
       
   273             log.deferredDiagnostics = prevDeferredDiags;
       
   274         }
       
   275     }
       
   276     //where
       
   277         protected TreeScanner unenterScanner = new TreeScanner() {
       
   278             @Override
       
   279             public void visitClassDef(JCClassDecl tree) {
       
   280                 ClassSymbol csym = tree.sym;
       
   281                 enter.typeEnvs.remove(csym);
       
   282                 chk.compiled.remove(csym.flatname);
       
   283                 syms.classes.remove(csym.flatname);
       
   284                 super.visitClassDef(tree);
       
   285             }
       
   286         };
       
   287 
       
   288     /**
       
   289      * A deferred context is created on each method check. A deferred context is
       
   290      * used to keep track of information associated with the method check, such as
       
   291      * the symbol of the method being checked, the overload resolution phase,
       
   292      * the kind of attribution mode to be applied to deferred types and so forth.
       
   293      * As deferred types are processed (by the method check routine) stuck AST nodes
       
   294      * are added (as new deferred attribution nodes) to this context. The complete()
       
   295      * routine makes sure that all pending nodes are properly processed, by
       
   296      * progressively instantiating all inference variables on which one or more
       
   297      * deferred attribution node is stuck.
       
   298      */
       
   299     class DeferredAttrContext {
       
   300 
       
   301         /** attribution mode */
       
   302         final AttrMode mode;
       
   303 
       
   304         /** symbol of the method being checked */
       
   305         final Symbol msym;
       
   306 
       
   307         /** method resolution step */
       
   308         final Resolve.MethodResolutionPhase phase;
       
   309 
       
   310         /** inference context */
       
   311         final InferenceContext inferenceContext;
       
   312 
       
   313         /** list of deferred attribution nodes to be processed */
       
   314         ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
       
   315 
       
   316         DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, InferenceContext inferenceContext) {
       
   317             this.mode = mode;
       
   318             this.msym = msym;
       
   319             this.phase = phase;
       
   320             this.inferenceContext = inferenceContext;
       
   321         }
       
   322 
       
   323         /**
       
   324          * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
       
   325          * Nodes added this way act as 'roots' for the out-of-order method checking process.
       
   326          */
       
   327         void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
       
   328             deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
       
   329         }
       
   330 
       
   331         /**
       
   332          * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
       
   333          * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
       
   334          * some inference variable might get eagerly instantiated so that all nodes
       
   335          * can be type-checked.
       
   336          */
       
   337         void complete() {
       
   338             while (!deferredAttrNodes.isEmpty()) {
       
   339                 Set<Type> stuckVars = new HashSet<Type>();
       
   340                 boolean progress = false;
       
   341                 //scan a defensive copy of the node list - this is because a deferred
       
   342                 //attribution round can add new nodes to the list
       
   343                 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
       
   344                     if (!deferredAttrNode.isStuck()) {
       
   345                         deferredAttrNode.process();
       
   346                         deferredAttrNodes.remove(deferredAttrNode);
       
   347                         progress = true;
       
   348                     } else {
       
   349                         stuckVars.addAll(deferredAttrNode.stuckVars);
       
   350                     }
       
   351                 }
       
   352                 if (!progress) {
       
   353                     //remove all variables that have already been instantiated
       
   354                     //from the list of stuck variables
       
   355                     inferenceContext.solveAny(inferenceContext.freeVarsIn(List.from(stuckVars)), types, infer);
       
   356                     inferenceContext.notifyChange(types);
       
   357                 }
       
   358             }
       
   359         }
       
   360 
       
   361         /**
       
   362          * Class representing a deferred attribution node. It keeps track of
       
   363          * a deferred type, along with the expected target type information.
       
   364          */
       
   365         class DeferredAttrNode implements Infer.InferenceContext.FreeTypeListener {
       
   366 
       
   367             /** underlying deferred type */
       
   368             DeferredType dt;
       
   369 
       
   370             /** underlying target type information */
       
   371             ResultInfo resultInfo;
       
   372 
       
   373             /** list of uninferred inference variables causing this node to be stuck */
       
   374             List<Type> stuckVars;
       
   375 
       
   376             DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
       
   377                 this.dt = dt;
       
   378                 this.resultInfo = resultInfo;
       
   379                 this.stuckVars = stuckVars;
       
   380                 if (!stuckVars.isEmpty()) {
       
   381                     resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
       
   382                 }
       
   383             }
       
   384 
       
   385             @Override
       
   386             public void typesInferred(InferenceContext inferenceContext) {
       
   387                 stuckVars = List.nil();
       
   388                 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types));
       
   389             }
       
   390 
       
   391             /**
       
   392              * is this node stuck?
       
   393              */
       
   394             boolean isStuck() {
       
   395                 return stuckVars.nonEmpty();
       
   396             }
       
   397 
       
   398             /**
       
   399              * Process a deferred attribution node.
       
   400              * Invariant: a stuck node cannot be processed.
       
   401              */
       
   402             void process() {
       
   403                 if (isStuck()) {
       
   404                     throw new IllegalStateException("Cannot process a stuck deferred node");
       
   405                 }
       
   406                 dt.check(resultInfo);
       
   407             }
       
   408         }
       
   409     }
       
   410 
       
   411     /** an empty deferred attribution context - all methods throw exceptions */
       
   412     final DeferredAttrContext emptyDeferredAttrContext =
       
   413             new DeferredAttrContext(null, null, null, null) {
       
   414                 @Override
       
   415                 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
       
   416                     Assert.error("Empty deferred context!");
       
   417                 }
       
   418                 @Override
       
   419                 void complete() {
       
   420                     Assert.error("Empty deferred context!");
       
   421                 }
       
   422             };
       
   423 
       
   424     /**
       
   425      * Map a list of types possibly containing one or more deferred types
       
   426      * into a list of ordinary types. Each deferred type D is mapped into a type T,
       
   427      * where T is computed by retrieving the type that has already been
       
   428      * computed for D during a previous deferred attribution round of the given kind.
       
   429      */
       
   430     class DeferredTypeMap extends Type.Mapping {
       
   431 
       
   432         DeferredAttrContext deferredAttrContext;
       
   433 
       
   434         protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
       
   435             super(String.format("deferredTypeMap[%s]", mode));
       
   436             this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, infer.emptyContext);
       
   437         }
       
   438 
       
   439         protected boolean validState(DeferredType dt) {
       
   440             return dt.mode != null &&
       
   441                     deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
       
   442         }
       
   443 
       
   444         @Override
       
   445         public Type apply(Type t) {
       
   446             if (t.tag != DEFERRED) {
       
   447                 return t.map(this);
       
   448             } else {
       
   449                 DeferredType dt = (DeferredType)t;
       
   450                 Assert.check(validState(dt));
       
   451                 return typeOf(dt);
       
   452             }
       
   453         }
       
   454 
       
   455         protected Type typeOf(DeferredType dt) {
       
   456             switch (deferredAttrContext.mode) {
       
   457                 case CHECK:
       
   458                     return dt.tree.type == null ? Type.noType : dt.tree.type;
       
   459                 case SPECULATIVE:
       
   460                     return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
       
   461             }
       
   462             Assert.error();
       
   463             return null;
       
   464         }
       
   465     }
       
   466 
       
   467     /**
       
   468      * Specialized recovery deferred mapping.
       
   469      * Each deferred type D is mapped into a type T, where T is computed either by
       
   470      * (i) retrieving the type that has already been computed for D during a previous
       
   471      * attribution round (as before), or (ii) by synthesizing a new type R for D
       
   472      * (the latter step is useful in a recovery scenario).
       
   473      */
       
   474     public class RecoveryDeferredTypeMap extends DeferredTypeMap {
       
   475 
       
   476         public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
       
   477             super(mode, msym, phase);
       
   478         }
       
   479 
       
   480         @Override
       
   481         protected Type typeOf(DeferredType dt) {
       
   482             Type owntype = super.typeOf(dt);
       
   483             return owntype.tag == NONE ?
       
   484                         recover(dt) : owntype;
       
   485         }
       
   486 
       
   487         @Override
       
   488         protected boolean validState(DeferredType dt) {
       
   489             return true;
       
   490         }
       
   491 
       
   492         /**
       
   493          * Synthesize a type for a deferred type that hasn't been previously
       
   494          * reduced to an ordinary type. Functional deferred types and conditionals
       
   495          * are mapped to themselves, in order to have a richer diagnostic
       
   496          * representation. Remaining deferred types are attributed using
       
   497          * a default expected type (j.l.Object).
       
   498          */
       
   499         private Type recover(DeferredType dt) {
       
   500             dt.check(new RecoveryInfo());
       
   501             switch (TreeInfo.skipParens(dt.tree).getTag()) {
       
   502                 case LAMBDA:
       
   503                 case REFERENCE:
       
   504                 case CONDEXPR:
       
   505                     //propagate those deferred types to the
       
   506                     //diagnostic formatter
       
   507                     return dt;
       
   508                 default:
       
   509                     return super.apply(dt);
       
   510             }
       
   511         }
       
   512 
       
   513         class RecoveryInfo extends ResultInfo {
       
   514 
       
   515             public RecoveryInfo() {
       
   516                 attr.super(Kinds.VAL, Type.recoveryType, new Check.NestedCheckContext(chk.basicHandler) {
       
   517                     @Override
       
   518                     public DeferredAttrContext deferredAttrContext() {
       
   519                         return deferredAttrContext;
       
   520                     }
       
   521                     @Override
       
   522                     public boolean compatible(Type found, Type req, Warner warn) {
       
   523                         return true;
       
   524                     }
       
   525                     @Override
       
   526                     public void report(DiagnosticPosition pos, JCDiagnostic details) {
       
   527                         //do nothing
       
   528                     }
       
   529                 });
       
   530             }
       
   531 
       
   532             @Override
       
   533             protected Type check(DiagnosticPosition pos, Type found) {
       
   534                 return chk.checkNonVoid(pos, super.check(pos, found));
       
   535             }
       
   536         }
       
   537     }
       
   538 
       
   539     /**
       
   540      * Retrieves the list of inference variables that need to be inferred before
       
   541      * an AST node can be type-checked
       
   542      */
       
   543     @SuppressWarnings("fallthrough")
       
   544     List<Type> stuckVars(JCExpression tree, ResultInfo resultInfo) {
       
   545         switch (tree.getTag()) {
       
   546             case LAMBDA:
       
   547             case REFERENCE:
       
   548                 Assert.error("not supported yet");
       
   549             default:
       
   550                 return List.nil();
       
   551         }
       
   552     }
       
   553 }