src/share/classes/com/sun/tools/javac/comp/DeferredAttr.java

Tue, 09 Oct 2012 19:10:00 -0700

author
jjg
date
Tue, 09 Oct 2012 19:10:00 -0700
changeset 1357
c75be5bc5283
parent 1348
573ceb23beeb
child 1374
c002fdee76fd
permissions
-rw-r--r--

8000663: clean up langtools imports
Reviewed-by: darcy

     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  */
    26 package com.sun.tools.javac.comp;
    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.*;
    38 import javax.tools.JavaFileObject;
    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;
    47 import static com.sun.tools.javac.code.TypeTags.*;
    48 import static com.sun.tools.javac.tree.JCTree.Tag.*;
    50 /**
    51  * This is an helper class that is used to perform deferred type-analysis.
    52  * Each time a poly expression occurs in argument position, javac attributes it
    53  * with a temporary 'deferred type' that is checked (possibly multiple times)
    54  * against an expected formal type.
    55  *
    56  *  <p><b>This is NOT part of any supported API.
    57  *  If you write code that depends on this, you do so at your own risk.
    58  *  This code and its internal interfaces are subject to change or
    59  *  deletion without notice.</b>
    60  */
    61 public class DeferredAttr extends JCTree.Visitor {
    62     protected static final Context.Key<DeferredAttr> deferredAttrKey =
    63         new Context.Key<DeferredAttr>();
    65     final Attr attr;
    66     final Check chk;
    67     final Enter enter;
    68     final Infer infer;
    69     final Log log;
    70     final Symtab syms;
    71     final TreeMaker make;
    72     final Types types;
    74     public static DeferredAttr instance(Context context) {
    75         DeferredAttr instance = context.get(deferredAttrKey);
    76         if (instance == null)
    77             instance = new DeferredAttr(context);
    78         return instance;
    79     }
    81     protected DeferredAttr(Context context) {
    82         context.put(deferredAttrKey, this);
    83         attr = Attr.instance(context);
    84         chk = Check.instance(context);
    85         enter = Enter.instance(context);
    86         infer = Infer.instance(context);
    87         log = Log.instance(context);
    88         syms = Symtab.instance(context);
    89         make = TreeMaker.instance(context);
    90         types = Types.instance(context);
    91     }
    93     /**
    94      * This type represents a deferred type. A deferred type starts off with
    95      * no information on the underlying expression type. Such info needs to be
    96      * discovered through type-checking the deferred type against a target-type.
    97      * Every deferred type keeps a pointer to the AST node from which it originated.
    98      */
    99     public class DeferredType extends Type {
   101         public JCExpression tree;
   102         Env<AttrContext> env;
   103         AttrMode mode;
   104         SpeculativeCache speculativeCache;
   106         DeferredType(JCExpression tree, Env<AttrContext> env) {
   107             super(DEFERRED, null);
   108             this.tree = tree;
   109             this.env = env.dup(tree, env.info.dup());
   110             this.speculativeCache = new SpeculativeCache();
   111         }
   113         /**
   114          * A speculative cache is used to keep track of all overload resolution rounds
   115          * that triggered speculative attribution on a given deferred type. Each entry
   116          * stores a pointer to the speculative tree and the resolution phase in which the entry
   117          * has been added.
   118          */
   119         class SpeculativeCache {
   121             private Map<Symbol, List<Entry>> cache =
   122                     new WeakHashMap<Symbol, List<Entry>>();
   124             class Entry {
   125                 JCTree speculativeTree;
   126                 Resolve.MethodResolutionPhase phase;
   128                 public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
   129                     this.speculativeTree = speculativeTree;
   130                     this.phase = phase;
   131                 }
   133                 boolean matches(Resolve.MethodResolutionPhase phase) {
   134                     return this.phase == phase;
   135                 }
   136             }
   138             /**
   139              * Clone a speculative cache entry as a fresh entry associated
   140              * with a new method (this maybe required to fixup speculative cache
   141              * misses after Resolve.access())
   142              */
   143             void dupAllTo(Symbol from, Symbol to) {
   144                 Assert.check(cache.get(to) == null);
   145                 List<Entry> entries = cache.get(from);
   146                 if (entries != null) {
   147                     cache.put(to, entries);
   148                 }
   149             }
   151             /**
   152              * Retrieve a speculative cache entry corresponding to given symbol
   153              * and resolution phase
   154              */
   155             Entry get(Symbol msym, MethodResolutionPhase phase) {
   156                 List<Entry> entries = cache.get(msym);
   157                 if (entries == null) return null;
   158                 for (Entry e : entries) {
   159                     if (e.matches(phase)) return e;
   160                 }
   161                 return null;
   162             }
   164             /**
   165              * Stores a speculative cache entry corresponding to given symbol
   166              * and resolution phase
   167              */
   168             void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
   169                 List<Entry> entries = cache.get(msym);
   170                 if (entries == null) {
   171                     entries = List.nil();
   172                 }
   173                 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
   174             }
   175         }
   177         /**
   178          * Get the type that has been computed during a speculative attribution round
   179          */
   180         Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
   181             SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
   182             return e != null ? e.speculativeTree.type : Type.noType;
   183         }
   185         /**
   186          * Check a deferred type against a potential target-type. Depending on
   187          * the current attribution mode, a normal vs. speculative attribution
   188          * round is performed on the underlying AST node. There can be only one
   189          * speculative round for a given target method symbol; moreover, a normal
   190          * attribution round must follow one or more speculative rounds.
   191          */
   192         Type check(ResultInfo resultInfo) {
   193             DeferredAttrContext deferredAttrContext =
   194                     resultInfo.checkContext.deferredAttrContext();
   195             Assert.check(deferredAttrContext != emptyDeferredAttrContext);
   196             List<Type> stuckVars = stuckVars(tree, resultInfo);
   197             if (stuckVars.nonEmpty()) {
   198                 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
   199                 return Type.noType;
   200             } else {
   201                 try {
   202                     switch (deferredAttrContext.mode) {
   203                         case SPECULATIVE:
   204                             Assert.check(mode == null ||
   205                                     (mode == AttrMode.SPECULATIVE &&
   206                                     speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).tag == NONE));
   207                             JCTree speculativeTree = attribSpeculative(tree, env, resultInfo);
   208                             speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
   209                             return speculativeTree.type;
   210                         case CHECK:
   211                             Assert.check(mode == AttrMode.SPECULATIVE);
   212                             return attr.attribTree(tree, env, resultInfo);
   213                     }
   214                     Assert.error();
   215                     return null;
   216                 } finally {
   217                     mode = deferredAttrContext.mode;
   218                 }
   219             }
   220         }
   221     }
   223     /**
   224      * The 'mode' in which the deferred type is to be type-checked
   225      */
   226     public enum AttrMode {
   227         /**
   228          * A speculative type-checking round is used during overload resolution
   229          * mainly to generate constraints on inference variables. Side-effects
   230          * arising from type-checking the expression associated with the deferred
   231          * type are reversed after the speculative round finishes. This means the
   232          * expression tree will be left in a blank state.
   233          */
   234         SPECULATIVE,
   235         /**
   236          * This is the plain type-checking mode. Produces side-effects on the underlying AST node
   237          */
   238         CHECK;
   239     }
   241     /**
   242      * Routine that performs speculative type-checking; the input AST node is
   243      * cloned (to avoid side-effects cause by Attr) and compiler state is
   244      * restored after type-checking. All diagnostics (but critical ones) are
   245      * disabled during speculative type-checking.
   246      */
   247     JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   248         JCTree newTree = new TreeCopier<Object>(make).copy(tree);
   249         Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
   250         speculativeEnv.info.scope.owner = env.info.scope.owner;
   251         Filter<JCDiagnostic> prevDeferDiagsFilter = log.deferredDiagFilter;
   252         Queue<JCDiagnostic> prevDeferredDiags = log.deferredDiagnostics;
   253         final JavaFileObject currentSource = log.currentSourceFile();
   254         try {
   255             log.deferredDiagnostics = new ListBuffer<JCDiagnostic>();
   256             log.deferredDiagFilter = new Filter<JCDiagnostic>() {
   257                 public boolean accepts(JCDiagnostic t) {
   258                     return t.getDiagnosticSource().getFile().equals(currentSource);
   259                 }
   260             };
   261             attr.attribTree(newTree, speculativeEnv, resultInfo);
   262             unenterScanner.scan(newTree);
   263             return newTree;
   264         } catch (Abort ex) {
   265             //if some very bad condition occurred during deferred attribution
   266             //we should dump all errors before killing javac
   267             log.reportDeferredDiagnostics();
   268             throw ex;
   269         } finally {
   270             unenterScanner.scan(newTree);
   271             log.deferredDiagFilter = prevDeferDiagsFilter;
   272             log.deferredDiagnostics = prevDeferredDiags;
   273         }
   274     }
   275     //where
   276         protected TreeScanner unenterScanner = new TreeScanner() {
   277             @Override
   278             public void visitClassDef(JCClassDecl tree) {
   279                 ClassSymbol csym = tree.sym;
   280                 enter.typeEnvs.remove(csym);
   281                 chk.compiled.remove(csym.flatname);
   282                 syms.classes.remove(csym.flatname);
   283                 super.visitClassDef(tree);
   284             }
   285         };
   287     /**
   288      * A deferred context is created on each method check. A deferred context is
   289      * used to keep track of information associated with the method check, such as
   290      * the symbol of the method being checked, the overload resolution phase,
   291      * the kind of attribution mode to be applied to deferred types and so forth.
   292      * As deferred types are processed (by the method check routine) stuck AST nodes
   293      * are added (as new deferred attribution nodes) to this context. The complete()
   294      * routine makes sure that all pending nodes are properly processed, by
   295      * progressively instantiating all inference variables on which one or more
   296      * deferred attribution node is stuck.
   297      */
   298     class DeferredAttrContext {
   300         /** attribution mode */
   301         final AttrMode mode;
   303         /** symbol of the method being checked */
   304         final Symbol msym;
   306         /** method resolution step */
   307         final Resolve.MethodResolutionPhase phase;
   309         /** inference context */
   310         final InferenceContext inferenceContext;
   312         /** list of deferred attribution nodes to be processed */
   313         ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
   315         DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, InferenceContext inferenceContext) {
   316             this.mode = mode;
   317             this.msym = msym;
   318             this.phase = phase;
   319             this.inferenceContext = inferenceContext;
   320         }
   322         /**
   323          * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
   324          * Nodes added this way act as 'roots' for the out-of-order method checking process.
   325          */
   326         void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   327             deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
   328         }
   330         /**
   331          * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
   332          * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
   333          * some inference variable might get eagerly instantiated so that all nodes
   334          * can be type-checked.
   335          */
   336         void complete() {
   337             while (!deferredAttrNodes.isEmpty()) {
   338                 Set<Type> stuckVars = new HashSet<Type>();
   339                 boolean progress = false;
   340                 //scan a defensive copy of the node list - this is because a deferred
   341                 //attribution round can add new nodes to the list
   342                 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
   343                     if (!deferredAttrNode.isStuck()) {
   344                         deferredAttrNode.process();
   345                         deferredAttrNodes.remove(deferredAttrNode);
   346                         progress = true;
   347                     } else {
   348                         stuckVars.addAll(deferredAttrNode.stuckVars);
   349                     }
   350                 }
   351                 if (!progress) {
   352                     //remove all variables that have already been instantiated
   353                     //from the list of stuck variables
   354                     inferenceContext.solveAny(inferenceContext.freeVarsIn(List.from(stuckVars)), types, infer);
   355                     inferenceContext.notifyChange(types);
   356                 }
   357             }
   358         }
   360         /**
   361          * Class representing a deferred attribution node. It keeps track of
   362          * a deferred type, along with the expected target type information.
   363          */
   364         class DeferredAttrNode implements Infer.InferenceContext.FreeTypeListener {
   366             /** underlying deferred type */
   367             DeferredType dt;
   369             /** underlying target type information */
   370             ResultInfo resultInfo;
   372             /** list of uninferred inference variables causing this node to be stuck */
   373             List<Type> stuckVars;
   375             DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   376                 this.dt = dt;
   377                 this.resultInfo = resultInfo;
   378                 this.stuckVars = stuckVars;
   379                 if (!stuckVars.isEmpty()) {
   380                     resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
   381                 }
   382             }
   384             @Override
   385             public void typesInferred(InferenceContext inferenceContext) {
   386                 stuckVars = List.nil();
   387                 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types));
   388             }
   390             /**
   391              * is this node stuck?
   392              */
   393             boolean isStuck() {
   394                 return stuckVars.nonEmpty();
   395             }
   397             /**
   398              * Process a deferred attribution node.
   399              * Invariant: a stuck node cannot be processed.
   400              */
   401             void process() {
   402                 if (isStuck()) {
   403                     throw new IllegalStateException("Cannot process a stuck deferred node");
   404                 }
   405                 dt.check(resultInfo);
   406             }
   407         }
   408     }
   410     /** an empty deferred attribution context - all methods throw exceptions */
   411     final DeferredAttrContext emptyDeferredAttrContext =
   412             new DeferredAttrContext(null, null, null, null) {
   413                 @Override
   414                 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
   415                     Assert.error("Empty deferred context!");
   416                 }
   417                 @Override
   418                 void complete() {
   419                     Assert.error("Empty deferred context!");
   420                 }
   421             };
   423     /**
   424      * Map a list of types possibly containing one or more deferred types
   425      * into a list of ordinary types. Each deferred type D is mapped into a type T,
   426      * where T is computed by retrieving the type that has already been
   427      * computed for D during a previous deferred attribution round of the given kind.
   428      */
   429     class DeferredTypeMap extends Type.Mapping {
   431         DeferredAttrContext deferredAttrContext;
   433         protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   434             super(String.format("deferredTypeMap[%s]", mode));
   435             this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, infer.emptyContext);
   436         }
   438         protected boolean validState(DeferredType dt) {
   439             return dt.mode != null &&
   440                     deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
   441         }
   443         @Override
   444         public Type apply(Type t) {
   445             if (t.tag != DEFERRED) {
   446                 return t.map(this);
   447             } else {
   448                 DeferredType dt = (DeferredType)t;
   449                 Assert.check(validState(dt));
   450                 return typeOf(dt);
   451             }
   452         }
   454         protected Type typeOf(DeferredType dt) {
   455             switch (deferredAttrContext.mode) {
   456                 case CHECK:
   457                     return dt.tree.type == null ? Type.noType : dt.tree.type;
   458                 case SPECULATIVE:
   459                     return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
   460             }
   461             Assert.error();
   462             return null;
   463         }
   464     }
   466     /**
   467      * Specialized recovery deferred mapping.
   468      * Each deferred type D is mapped into a type T, where T is computed either by
   469      * (i) retrieving the type that has already been computed for D during a previous
   470      * attribution round (as before), or (ii) by synthesizing a new type R for D
   471      * (the latter step is useful in a recovery scenario).
   472      */
   473     public class RecoveryDeferredTypeMap extends DeferredTypeMap {
   475         public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   476             super(mode, msym, phase);
   477         }
   479         @Override
   480         protected Type typeOf(DeferredType dt) {
   481             Type owntype = super.typeOf(dt);
   482             return owntype.tag == NONE ?
   483                         recover(dt) : owntype;
   484         }
   486         @Override
   487         protected boolean validState(DeferredType dt) {
   488             return true;
   489         }
   491         /**
   492          * Synthesize a type for a deferred type that hasn't been previously
   493          * reduced to an ordinary type. Functional deferred types and conditionals
   494          * are mapped to themselves, in order to have a richer diagnostic
   495          * representation. Remaining deferred types are attributed using
   496          * a default expected type (j.l.Object).
   497          */
   498         private Type recover(DeferredType dt) {
   499             dt.check(attr.new RecoveryInfo(deferredAttrContext));
   500             switch (TreeInfo.skipParens(dt.tree).getTag()) {
   501                 case LAMBDA:
   502                 case REFERENCE:
   503                 case CONDEXPR:
   504                     //propagate those deferred types to the
   505                     //diagnostic formatter
   506                     return dt;
   507                 default:
   508                     return super.apply(dt);
   509             }
   510         }
   511     }
   513     /**
   514      * Retrieves the list of inference variables that need to be inferred before
   515      * an AST node can be type-checked
   516      */
   517     @SuppressWarnings("fallthrough")
   518     List<Type> stuckVars(JCTree tree, ResultInfo resultInfo) {
   519         if (resultInfo.pt.tag == NONE || resultInfo.pt.isErroneous()) {
   520             return List.nil();
   521         } else {
   522             StuckChecker sc = new StuckChecker(resultInfo);
   523             sc.scan(tree);
   524             return List.from(sc.stuckVars);
   525         }
   526     }
   528     /**
   529      * This visitor is used to check that structural expressions conform
   530      * to their target - this step is required as inference could end up
   531      * inferring types that make some of the nested expressions incompatible
   532      * with their corresponding instantiated target
   533      */
   534     class StuckChecker extends TreeScanner {
   536         Type pt;
   537         Filter<JCTree> treeFilter;
   538         Infer.InferenceContext inferenceContext;
   539         Set<Type> stuckVars = new HashSet<Type>();
   541         final Filter<JCTree> argsFilter = new Filter<JCTree>() {
   542             public boolean accepts(JCTree t) {
   543                 switch (t.getTag()) {
   544                     case CONDEXPR:
   545                     case LAMBDA:
   546                     case PARENS:
   547                     case REFERENCE:
   548                         return true;
   549                     default:
   550                         return false;
   551                 }
   552             }
   553         };
   555         final Filter<JCTree> lambdaBodyFilter = new Filter<JCTree>() {
   556             public boolean accepts(JCTree t) {
   557                 switch (t.getTag()) {
   558                     case BLOCK: case CASE: case CATCH: case DOLOOP:
   559                     case FOREACHLOOP: case FORLOOP: case RETURN:
   560                     case SYNCHRONIZED: case SWITCH: case TRY: case WHILELOOP:
   561                         return true;
   562                     default:
   563                         return false;
   564                 }
   565             }
   566         };
   568         StuckChecker(ResultInfo resultInfo) {
   569             this.pt = resultInfo.pt;
   570             this.inferenceContext = resultInfo.checkContext.inferenceContext();
   571             this.treeFilter = argsFilter;
   572         }
   574         @Override
   575         public void scan(JCTree tree) {
   576             if (tree != null && treeFilter.accepts(tree)) {
   577                 super.scan(tree);
   578             }
   579         }
   581         @Override
   582         public void visitLambda(JCLambda tree) {
   583             Type prevPt = pt;
   584             Filter<JCTree> prevFilter = treeFilter;
   585             try {
   586                 if (inferenceContext.inferenceVars().contains(pt)) {
   587                     stuckVars.add(pt);
   588                 }
   589                 if (!types.isFunctionalInterface(pt.tsym)) {
   590                     return;
   591                 }
   592                 Type descType = types.findDescriptorType(pt);
   593                 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   594                 if (!TreeInfo.isExplicitLambda(tree) &&
   595                         freeArgVars.nonEmpty()) {
   596                     stuckVars.addAll(freeArgVars);
   597                 }
   598                 pt = descType.getReturnType();
   599                 if (tree.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
   600                     scan(tree.getBody());
   601                 } else {
   602                     treeFilter = lambdaBodyFilter;
   603                     super.visitLambda(tree);
   604                 }
   605             } finally {
   606                 pt = prevPt;
   607                 treeFilter = prevFilter;
   608             }
   609         }
   611         @Override
   612         public void visitReference(JCMemberReference tree) {
   613             scan(tree.expr);
   614             if (inferenceContext.inferenceVars().contains(pt)) {
   615                 stuckVars.add(pt);
   616                 return;
   617             }
   618             if (!types.isFunctionalInterface(pt.tsym)) {
   619                 return;
   620             }
   621             Type descType = types.findDescriptorType(pt);
   622             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   623             stuckVars.addAll(freeArgVars);
   624         }
   626         @Override
   627         public void visitReturn(JCReturn tree) {
   628             Filter<JCTree> prevFilter = treeFilter;
   629             try {
   630                 treeFilter = argsFilter;
   631                 if (tree.expr != null) {
   632                     scan(tree.expr);
   633                 }
   634             } finally {
   635                 treeFilter = prevFilter;
   636             }
   637         }
   638     }
   639 }

mercurial