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

Fri, 30 Nov 2012 15:14:25 +0000

author
mcimadamore
date
Fri, 30 Nov 2012 15:14:25 +0000
changeset 1434
34d1ebaf4645
parent 1415
01c9d4161882
child 1481
d07340b61e6a
permissions
-rw-r--r--

8004102: Add support for generic functional descriptors
Summary: Method references are allowed to have a generic functional interface descriptor target
Reviewed-by: jjg

     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.LinkedHashSet;
    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.TypeTag.*;
    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              * Retrieve a speculative cache entry corresponding to given symbol
   140              * and resolution phase
   141              */
   142             Entry get(Symbol msym, MethodResolutionPhase phase) {
   143                 List<Entry> entries = cache.get(msym);
   144                 if (entries == null) return null;
   145                 for (Entry e : entries) {
   146                     if (e.matches(phase)) return e;
   147                 }
   148                 return null;
   149             }
   151             /**
   152              * Stores a speculative cache entry corresponding to given symbol
   153              * and resolution phase
   154              */
   155             void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
   156                 List<Entry> entries = cache.get(msym);
   157                 if (entries == null) {
   158                     entries = List.nil();
   159                 }
   160                 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
   161             }
   162         }
   164         /**
   165          * Get the type that has been computed during a speculative attribution round
   166          */
   167         Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
   168             SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
   169             return e != null ? e.speculativeTree.type : Type.noType;
   170         }
   172         /**
   173          * Check a deferred type against a potential target-type. Depending on
   174          * the current attribution mode, a normal vs. speculative attribution
   175          * round is performed on the underlying AST node. There can be only one
   176          * speculative round for a given target method symbol; moreover, a normal
   177          * attribution round must follow one or more speculative rounds.
   178          */
   179         Type check(ResultInfo resultInfo) {
   180             DeferredAttrContext deferredAttrContext =
   181                     resultInfo.checkContext.deferredAttrContext();
   182             Assert.check(deferredAttrContext != emptyDeferredAttrContext);
   183             List<Type> stuckVars = stuckVars(tree, env, resultInfo);
   184             if (stuckVars.nonEmpty()) {
   185                 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
   186                 return Type.noType;
   187             } else {
   188                 try {
   189                     switch (deferredAttrContext.mode) {
   190                         case SPECULATIVE:
   191                             Assert.check(mode == null ||
   192                                     (mode == AttrMode.SPECULATIVE &&
   193                                     speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE)));
   194                             JCTree speculativeTree = attribSpeculative(tree, env, resultInfo);
   195                             speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
   196                             return speculativeTree.type;
   197                         case CHECK:
   198                             Assert.check(mode == AttrMode.SPECULATIVE);
   199                             return attr.attribTree(tree, env, resultInfo);
   200                     }
   201                     Assert.error();
   202                     return null;
   203                 } finally {
   204                     mode = deferredAttrContext.mode;
   205                 }
   206             }
   207         }
   208     }
   210     /**
   211      * The 'mode' in which the deferred type is to be type-checked
   212      */
   213     public enum AttrMode {
   214         /**
   215          * A speculative type-checking round is used during overload resolution
   216          * mainly to generate constraints on inference variables. Side-effects
   217          * arising from type-checking the expression associated with the deferred
   218          * type are reversed after the speculative round finishes. This means the
   219          * expression tree will be left in a blank state.
   220          */
   221         SPECULATIVE,
   222         /**
   223          * This is the plain type-checking mode. Produces side-effects on the underlying AST node
   224          */
   225         CHECK;
   226     }
   228     /**
   229      * Routine that performs speculative type-checking; the input AST node is
   230      * cloned (to avoid side-effects cause by Attr) and compiler state is
   231      * restored after type-checking. All diagnostics (but critical ones) are
   232      * disabled during speculative type-checking.
   233      */
   234     JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   235         JCTree newTree = new TreeCopier<Object>(make).copy(tree);
   236         Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
   237         speculativeEnv.info.scope.owner = env.info.scope.owner;
   238         final JavaFileObject currentSource = log.currentSourceFile();
   239         Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
   240                 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
   241             public boolean accepts(JCDiagnostic t) {
   242                 return t.getDiagnosticSource().getFile().equals(currentSource);
   243             }
   244         });
   245         try {
   246             attr.attribTree(newTree, speculativeEnv, resultInfo);
   247             unenterScanner.scan(newTree);
   248             return newTree;
   249         } catch (Abort ex) {
   250             //if some very bad condition occurred during deferred attribution
   251             //we should dump all errors before killing javac
   252             deferredDiagnosticHandler.reportDeferredDiagnostics();
   253             throw ex;
   254         } finally {
   255             unenterScanner.scan(newTree);
   256             log.popDiagnosticHandler(deferredDiagnosticHandler);
   257         }
   258     }
   259     //where
   260         protected TreeScanner unenterScanner = new TreeScanner() {
   261             @Override
   262             public void visitClassDef(JCClassDecl tree) {
   263                 ClassSymbol csym = tree.sym;
   264                 //if something went wrong during method applicability check
   265                 //it is possible that nested expressions inside argument expression
   266                 //are left unchecked - in such cases there's nothing to clean up.
   267                 if (csym == null) return;
   268                 enter.typeEnvs.remove(csym);
   269                 chk.compiled.remove(csym.flatname);
   270                 syms.classes.remove(csym.flatname);
   271                 super.visitClassDef(tree);
   272             }
   273         };
   275     /**
   276      * A deferred context is created on each method check. A deferred context is
   277      * used to keep track of information associated with the method check, such as
   278      * the symbol of the method being checked, the overload resolution phase,
   279      * the kind of attribution mode to be applied to deferred types and so forth.
   280      * As deferred types are processed (by the method check routine) stuck AST nodes
   281      * are added (as new deferred attribution nodes) to this context. The complete()
   282      * routine makes sure that all pending nodes are properly processed, by
   283      * progressively instantiating all inference variables on which one or more
   284      * deferred attribution node is stuck.
   285      */
   286     class DeferredAttrContext {
   288         /** attribution mode */
   289         final AttrMode mode;
   291         /** symbol of the method being checked */
   292         final Symbol msym;
   294         /** method resolution step */
   295         final Resolve.MethodResolutionPhase phase;
   297         /** inference context */
   298         final InferenceContext inferenceContext;
   300         /** list of deferred attribution nodes to be processed */
   301         ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
   303         DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, InferenceContext inferenceContext) {
   304             this.mode = mode;
   305             this.msym = msym;
   306             this.phase = phase;
   307             this.inferenceContext = inferenceContext;
   308         }
   310         /**
   311          * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
   312          * Nodes added this way act as 'roots' for the out-of-order method checking process.
   313          */
   314         void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   315             deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
   316         }
   318         /**
   319          * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
   320          * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
   321          * some inference variable might get eagerly instantiated so that all nodes
   322          * can be type-checked.
   323          */
   324         void complete() {
   325             while (!deferredAttrNodes.isEmpty()) {
   326                 Set<Type> stuckVars = new LinkedHashSet<Type>();
   327                 boolean progress = false;
   328                 //scan a defensive copy of the node list - this is because a deferred
   329                 //attribution round can add new nodes to the list
   330                 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
   331                     if (!deferredAttrNode.isStuck()) {
   332                         deferredAttrNode.process();
   333                         deferredAttrNodes.remove(deferredAttrNode);
   334                         progress = true;
   335                     } else {
   336                         stuckVars.addAll(deferredAttrNode.stuckVars);
   337                     }
   338                 }
   339                 if (!progress) {
   340                     //remove all variables that have already been instantiated
   341                     //from the list of stuck variables
   342                     inferenceContext.solveAny(inferenceContext.freeVarsIn(List.from(stuckVars)), types, infer);
   343                     inferenceContext.notifyChange(types);
   344                 }
   345             }
   346         }
   348         /**
   349          * Class representing a deferred attribution node. It keeps track of
   350          * a deferred type, along with the expected target type information.
   351          */
   352         class DeferredAttrNode implements Infer.InferenceContext.FreeTypeListener {
   354             /** underlying deferred type */
   355             DeferredType dt;
   357             /** underlying target type information */
   358             ResultInfo resultInfo;
   360             /** list of uninferred inference variables causing this node to be stuck */
   361             List<Type> stuckVars;
   363             DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   364                 this.dt = dt;
   365                 this.resultInfo = resultInfo;
   366                 this.stuckVars = stuckVars;
   367                 if (!stuckVars.isEmpty()) {
   368                     resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
   369                 }
   370             }
   372             @Override
   373             public void typesInferred(InferenceContext inferenceContext) {
   374                 stuckVars = List.nil();
   375                 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types));
   376             }
   378             /**
   379              * is this node stuck?
   380              */
   381             boolean isStuck() {
   382                 return stuckVars.nonEmpty();
   383             }
   385             /**
   386              * Process a deferred attribution node.
   387              * Invariant: a stuck node cannot be processed.
   388              */
   389             void process() {
   390                 if (isStuck()) {
   391                     throw new IllegalStateException("Cannot process a stuck deferred node");
   392                 }
   393                 dt.check(resultInfo);
   394             }
   395         }
   396     }
   398     /** an empty deferred attribution context - all methods throw exceptions */
   399     final DeferredAttrContext emptyDeferredAttrContext =
   400             new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, null) {
   401                 @Override
   402                 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
   403                     Assert.error("Empty deferred context!");
   404                 }
   405                 @Override
   406                 void complete() {
   407                     Assert.error("Empty deferred context!");
   408                 }
   409             };
   411     /**
   412      * Map a list of types possibly containing one or more deferred types
   413      * into a list of ordinary types. Each deferred type D is mapped into a type T,
   414      * where T is computed by retrieving the type that has already been
   415      * computed for D during a previous deferred attribution round of the given kind.
   416      */
   417     class DeferredTypeMap extends Type.Mapping {
   419         DeferredAttrContext deferredAttrContext;
   421         protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   422             super(String.format("deferredTypeMap[%s]", mode));
   423             this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, infer.emptyContext);
   424         }
   426         protected boolean validState(DeferredType dt) {
   427             return dt.mode != null &&
   428                     deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
   429         }
   431         @Override
   432         public Type apply(Type t) {
   433             if (!t.hasTag(DEFERRED)) {
   434                 return t.map(this);
   435             } else {
   436                 DeferredType dt = (DeferredType)t;
   437                 Assert.check(validState(dt));
   438                 return typeOf(dt);
   439             }
   440         }
   442         protected Type typeOf(DeferredType dt) {
   443             switch (deferredAttrContext.mode) {
   444                 case CHECK:
   445                     return dt.tree.type == null ? Type.noType : dt.tree.type;
   446                 case SPECULATIVE:
   447                     return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
   448             }
   449             Assert.error();
   450             return null;
   451         }
   452     }
   454     /**
   455      * Specialized recovery deferred mapping.
   456      * Each deferred type D is mapped into a type T, where T is computed either by
   457      * (i) retrieving the type that has already been computed for D during a previous
   458      * attribution round (as before), or (ii) by synthesizing a new type R for D
   459      * (the latter step is useful in a recovery scenario).
   460      */
   461     public class RecoveryDeferredTypeMap extends DeferredTypeMap {
   463         public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   464             super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
   465         }
   467         @Override
   468         protected Type typeOf(DeferredType dt) {
   469             Type owntype = super.typeOf(dt);
   470             return owntype == Type.noType ?
   471                         recover(dt) : owntype;
   472         }
   474         @Override
   475         protected boolean validState(DeferredType dt) {
   476             return true;
   477         }
   479         /**
   480          * Synthesize a type for a deferred type that hasn't been previously
   481          * reduced to an ordinary type. Functional deferred types and conditionals
   482          * are mapped to themselves, in order to have a richer diagnostic
   483          * representation. Remaining deferred types are attributed using
   484          * a default expected type (j.l.Object).
   485          */
   486         private Type recover(DeferredType dt) {
   487             dt.check(attr.new RecoveryInfo(deferredAttrContext));
   488             return super.apply(dt);
   489         }
   490     }
   492     /**
   493      * Retrieves the list of inference variables that need to be inferred before
   494      * an AST node can be type-checked
   495      */
   496     @SuppressWarnings("fallthrough")
   497     List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   498                 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
   499             return List.nil();
   500         } else {
   501             StuckChecker sc = new StuckChecker(resultInfo, env);
   502             sc.scan(tree);
   503             return List.from(sc.stuckVars);
   504         }
   505     }
   507     /**
   508      * This visitor is used to check that structural expressions conform
   509      * to their target - this step is required as inference could end up
   510      * inferring types that make some of the nested expressions incompatible
   511      * with their corresponding instantiated target
   512      */
   513     class StuckChecker extends TreeScanner {
   515         Type pt;
   516         Filter<JCTree> treeFilter;
   517         Infer.InferenceContext inferenceContext;
   518         Set<Type> stuckVars = new LinkedHashSet<Type>();
   519         Env<AttrContext> env;
   521         final Filter<JCTree> argsFilter = new Filter<JCTree>() {
   522             public boolean accepts(JCTree t) {
   523                 switch (t.getTag()) {
   524                     case CONDEXPR:
   525                     case LAMBDA:
   526                     case PARENS:
   527                     case REFERENCE:
   528                         return true;
   529                     default:
   530                         return false;
   531                 }
   532             }
   533         };
   535         final Filter<JCTree> lambdaBodyFilter = new Filter<JCTree>() {
   536             public boolean accepts(JCTree t) {
   537                 switch (t.getTag()) {
   538                     case BLOCK: case CASE: case CATCH: case DOLOOP:
   539                     case FOREACHLOOP: case FORLOOP: case RETURN:
   540                     case SYNCHRONIZED: case SWITCH: case TRY: case WHILELOOP:
   541                         return true;
   542                     default:
   543                         return false;
   544                 }
   545             }
   546         };
   548         StuckChecker(ResultInfo resultInfo, Env<AttrContext> env) {
   549             this.pt = resultInfo.pt;
   550             this.inferenceContext = resultInfo.checkContext.inferenceContext();
   551             this.treeFilter = argsFilter;
   552             this.env = env;
   553         }
   555         @Override
   556         public void scan(JCTree tree) {
   557             if (tree != null && treeFilter.accepts(tree)) {
   558                 super.scan(tree);
   559             }
   560         }
   562         @Override
   563         public void visitLambda(JCLambda tree) {
   564             Type prevPt = pt;
   565             Filter<JCTree> prevFilter = treeFilter;
   566             try {
   567                 if (inferenceContext.inferenceVars().contains(pt)) {
   568                     stuckVars.add(pt);
   569                 }
   570                 if (!types.isFunctionalInterface(pt.tsym)) {
   571                     return;
   572                 }
   573                 Type descType = types.findDescriptorType(pt);
   574                 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   575                 if (!TreeInfo.isExplicitLambda(tree) &&
   576                         freeArgVars.nonEmpty()) {
   577                     stuckVars.addAll(freeArgVars);
   578                 }
   579                 pt = descType.getReturnType();
   580                 if (tree.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
   581                     scan(tree.getBody());
   582                 } else {
   583                     treeFilter = lambdaBodyFilter;
   584                     super.visitLambda(tree);
   585                 }
   586             } finally {
   587                 pt = prevPt;
   588                 treeFilter = prevFilter;
   589             }
   590         }
   592         @Override
   593         public void visitReference(JCMemberReference tree) {
   594             scan(tree.expr);
   595             if (inferenceContext.inferenceVars().contains(pt)) {
   596                 stuckVars.add(pt);
   597                 return;
   598             }
   599             if (!types.isFunctionalInterface(pt.tsym)) {
   600                 return;
   601             }
   603             Type descType = types.findDescriptorType(pt);
   604             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   605             stuckVars.addAll(freeArgVars);
   606         }
   608         @Override
   609         public void visitReturn(JCReturn tree) {
   610             Filter<JCTree> prevFilter = treeFilter;
   611             try {
   612                 treeFilter = argsFilter;
   613                 if (tree.expr != null) {
   614                     scan(tree.expr);
   615                 }
   616             } finally {
   617                 treeFilter = prevFilter;
   618             }
   619         }
   620     }
   621 }

mercurial