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

Tue, 12 Feb 2013 19:25:09 +0000

author
mcimadamore
date
Tue, 12 Feb 2013 19:25:09 +0000
changeset 1562
2154ed9ff6c8
parent 1551
8cdd96f2fdb9
child 1581
4ff468de829d
permissions
-rw-r--r--

8007464: Add graph inference support
Summary: Add support for more aggressive type-inference scheme
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.EnumSet;
    42 import java.util.LinkedHashSet;
    43 import java.util.Map;
    44 import java.util.Queue;
    45 import java.util.Set;
    46 import java.util.WeakHashMap;
    48 import static com.sun.tools.javac.code.TypeTag.*;
    49 import static com.sun.tools.javac.tree.JCTree.Tag.*;
    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>();
    66     final Attr attr;
    67     final Check chk;
    68     final JCDiagnostic.Factory diags;
    69     final Enter enter;
    70     final Infer infer;
    71     final Log log;
    72     final Symtab syms;
    73     final TreeMaker make;
    74     final Types types;
    76     public static DeferredAttr instance(Context context) {
    77         DeferredAttr instance = context.get(deferredAttrKey);
    78         if (instance == null)
    79             instance = new DeferredAttr(context);
    80         return instance;
    81     }
    83     protected DeferredAttr(Context context) {
    84         context.put(deferredAttrKey, this);
    85         attr = Attr.instance(context);
    86         chk = Check.instance(context);
    87         diags = JCDiagnostic.Factory.instance(context);
    88         enter = Enter.instance(context);
    89         infer = Infer.instance(context);
    90         log = Log.instance(context);
    91         syms = Symtab.instance(context);
    92         make = TreeMaker.instance(context);
    93         types = Types.instance(context);
    94         Names names = Names.instance(context);
    95         stuckTree = make.Ident(names.empty).setType(Type.noType);
    96     }
    98     /** shared tree for stuck expressions */
    99     final JCTree stuckTree;
   101     /**
   102      * This type represents a deferred type. A deferred type starts off with
   103      * no information on the underlying expression type. Such info needs to be
   104      * discovered through type-checking the deferred type against a target-type.
   105      * Every deferred type keeps a pointer to the AST node from which it originated.
   106      */
   107     public class DeferredType extends Type {
   109         public JCExpression tree;
   110         Env<AttrContext> env;
   111         AttrMode mode;
   112         SpeculativeCache speculativeCache;
   114         DeferredType(JCExpression tree, Env<AttrContext> env) {
   115             super(DEFERRED, null);
   116             this.tree = tree;
   117             this.env = env.dup(tree, env.info.dup());
   118             this.speculativeCache = new SpeculativeCache();
   119         }
   121         /**
   122          * A speculative cache is used to keep track of all overload resolution rounds
   123          * that triggered speculative attribution on a given deferred type. Each entry
   124          * stores a pointer to the speculative tree and the resolution phase in which the entry
   125          * has been added.
   126          */
   127         class SpeculativeCache {
   129             private Map<Symbol, List<Entry>> cache =
   130                     new WeakHashMap<Symbol, List<Entry>>();
   132             class Entry {
   133                 JCTree speculativeTree;
   134                 Resolve.MethodResolutionPhase phase;
   136                 public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
   137                     this.speculativeTree = speculativeTree;
   138                     this.phase = phase;
   139                 }
   141                 boolean matches(Resolve.MethodResolutionPhase phase) {
   142                     return this.phase == phase;
   143                 }
   144             }
   146             /**
   147              * Retrieve a speculative cache entry corresponding to given symbol
   148              * and resolution phase
   149              */
   150             Entry get(Symbol msym, MethodResolutionPhase phase) {
   151                 List<Entry> entries = cache.get(msym);
   152                 if (entries == null) return null;
   153                 for (Entry e : entries) {
   154                     if (e.matches(phase)) return e;
   155                 }
   156                 return null;
   157             }
   159             /**
   160              * Stores a speculative cache entry corresponding to given symbol
   161              * and resolution phase
   162              */
   163             void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
   164                 List<Entry> entries = cache.get(msym);
   165                 if (entries == null) {
   166                     entries = List.nil();
   167                 }
   168                 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
   169             }
   170         }
   172         /**
   173          * Get the type that has been computed during a speculative attribution round
   174          */
   175         Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
   176             SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
   177             return e != null ? e.speculativeTree.type : Type.noType;
   178         }
   180         /**
   181          * Check a deferred type against a potential target-type. Depending on
   182          * the current attribution mode, a normal vs. speculative attribution
   183          * round is performed on the underlying AST node. There can be only one
   184          * speculative round for a given target method symbol; moreover, a normal
   185          * attribution round must follow one or more speculative rounds.
   186          */
   187         Type check(ResultInfo resultInfo) {
   188             return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter);
   189         }
   191         Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) {
   192             DeferredAttrContext deferredAttrContext =
   193                     resultInfo.checkContext.deferredAttrContext();
   194             Assert.check(deferredAttrContext != emptyDeferredAttrContext);
   195             if (stuckVars.nonEmpty()) {
   196                 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
   197                 return Type.noType;
   198             } else {
   199                 try {
   200                     return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
   201                 } finally {
   202                     mode = deferredAttrContext.mode;
   203                 }
   204             }
   205         }
   206     }
   208     /**
   209      * A completer for deferred types. Defines an entry point for type-checking
   210      * a deferred type.
   211      */
   212     interface DeferredTypeCompleter {
   213         /**
   214          * Entry point for type-checking a deferred type. Depending on the
   215          * circumstances, type-checking could amount to full attribution
   216          * or partial structural check (aka potential applicability).
   217          */
   218         Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
   219     }
   221     /**
   222      * A basic completer for deferred types. This completer type-checks a deferred type
   223      * using attribution; depending on the attribution mode, this could be either standard
   224      * or speculative attribution.
   225      */
   226     DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
   227         public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
   228             switch (deferredAttrContext.mode) {
   229                 case SPECULATIVE:
   230                     Assert.check(dt.mode == null ||
   231                             (dt.mode == AttrMode.SPECULATIVE &&
   232                             dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE)));
   233                     JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
   234                     dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
   235                     return speculativeTree.type;
   236                 case CHECK:
   237                     Assert.check(dt.mode != null);
   238                     return attr.attribTree(dt.tree, dt.env, resultInfo);
   239             }
   240             Assert.error();
   241             return null;
   242         }
   243     };
   245     DeferredTypeCompleter dummyCompleter = new DeferredTypeCompleter() {
   246         public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
   247             Assert.check(deferredAttrContext.mode == AttrMode.CHECK);
   248             return dt.tree.type = Type.noType;
   249         }
   250     };
   252     /**
   253      * The 'mode' in which the deferred type is to be type-checked
   254      */
   255     public enum AttrMode {
   256         /**
   257          * A speculative type-checking round is used during overload resolution
   258          * mainly to generate constraints on inference variables. Side-effects
   259          * arising from type-checking the expression associated with the deferred
   260          * type are reversed after the speculative round finishes. This means the
   261          * expression tree will be left in a blank state.
   262          */
   263         SPECULATIVE,
   264         /**
   265          * This is the plain type-checking mode. Produces side-effects on the underlying AST node
   266          */
   267         CHECK;
   268     }
   270     /**
   271      * Routine that performs speculative type-checking; the input AST node is
   272      * cloned (to avoid side-effects cause by Attr) and compiler state is
   273      * restored after type-checking. All diagnostics (but critical ones) are
   274      * disabled during speculative type-checking.
   275      */
   276     JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   277         JCTree newTree = new TreeCopier<Object>(make).copy(tree);
   278         Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
   279         speculativeEnv.info.scope.owner = env.info.scope.owner;
   280         final JavaFileObject currentSource = log.currentSourceFile();
   281         Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
   282                 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
   283             public boolean accepts(JCDiagnostic t) {
   284                 return t.getDiagnosticSource().getFile().equals(currentSource);
   285             }
   286         });
   287         try {
   288             attr.attribTree(newTree, speculativeEnv, resultInfo);
   289             unenterScanner.scan(newTree);
   290             return newTree;
   291         } catch (Abort ex) {
   292             //if some very bad condition occurred during deferred attribution
   293             //we should dump all errors before killing javac
   294             deferredDiagnosticHandler.reportDeferredDiagnostics();
   295             throw ex;
   296         } finally {
   297             unenterScanner.scan(newTree);
   298             log.popDiagnosticHandler(deferredDiagnosticHandler);
   299         }
   300     }
   301     //where
   302         protected TreeScanner unenterScanner = new TreeScanner() {
   303             @Override
   304             public void visitClassDef(JCClassDecl tree) {
   305                 ClassSymbol csym = tree.sym;
   306                 //if something went wrong during method applicability check
   307                 //it is possible that nested expressions inside argument expression
   308                 //are left unchecked - in such cases there's nothing to clean up.
   309                 if (csym == null) return;
   310                 enter.typeEnvs.remove(csym);
   311                 chk.compiled.remove(csym.flatname);
   312                 syms.classes.remove(csym.flatname);
   313                 super.visitClassDef(tree);
   314             }
   315         };
   317     /**
   318      * A deferred context is created on each method check. A deferred context is
   319      * used to keep track of information associated with the method check, such as
   320      * the symbol of the method being checked, the overload resolution phase,
   321      * the kind of attribution mode to be applied to deferred types and so forth.
   322      * As deferred types are processed (by the method check routine) stuck AST nodes
   323      * are added (as new deferred attribution nodes) to this context. The complete()
   324      * routine makes sure that all pending nodes are properly processed, by
   325      * progressively instantiating all inference variables on which one or more
   326      * deferred attribution node is stuck.
   327      */
   328     class DeferredAttrContext {
   330         /** attribution mode */
   331         final AttrMode mode;
   333         /** symbol of the method being checked */
   334         final Symbol msym;
   336         /** method resolution step */
   337         final Resolve.MethodResolutionPhase phase;
   339         /** inference context */
   340         final InferenceContext inferenceContext;
   342         /** parent deferred context */
   343         final DeferredAttrContext parent;
   345         /** Warner object to report warnings */
   346         final Warner warn;
   348         /** list of deferred attribution nodes to be processed */
   349         ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
   351         DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase,
   352                 InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) {
   353             this.mode = mode;
   354             this.msym = msym;
   355             this.phase = phase;
   356             this.parent = parent;
   357             this.warn = warn;
   358             this.inferenceContext = inferenceContext;
   359         }
   361         /**
   362          * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
   363          * Nodes added this way act as 'roots' for the out-of-order method checking process.
   364          */
   365         void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   366             deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
   367         }
   369         /**
   370          * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
   371          * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
   372          * some inference variable might get eagerly instantiated so that all nodes
   373          * can be type-checked.
   374          */
   375         void complete() {
   376             while (!deferredAttrNodes.isEmpty()) {
   377                 Set<Type> stuckVars = new LinkedHashSet<Type>();
   378                 boolean progress = false;
   379                 //scan a defensive copy of the node list - this is because a deferred
   380                 //attribution round can add new nodes to the list
   381                 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
   382                     if (!deferredAttrNode.process(this)) {
   383                         stuckVars.addAll(deferredAttrNode.stuckVars);
   384                     } else {
   385                         deferredAttrNodes.remove(deferredAttrNode);
   386                         progress = true;
   387                     }
   388                 }
   389                 if (!progress) {
   390                     //remove all variables that have already been instantiated
   391                     //from the list of stuck variables
   392                     inferenceContext.solveAny(List.from(stuckVars), warn);
   393                     inferenceContext.notifyChange();
   394                 }
   395             }
   396         }
   397     }
   399     /**
   400      * Class representing a deferred attribution node. It keeps track of
   401      * a deferred type, along with the expected target type information.
   402      */
   403     class DeferredAttrNode implements Infer.FreeTypeListener {
   405         /** underlying deferred type */
   406         DeferredType dt;
   408         /** underlying target type information */
   409         ResultInfo resultInfo;
   411         /** list of uninferred inference variables causing this node to be stuck */
   412         List<Type> stuckVars;
   414         DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   415             this.dt = dt;
   416             this.resultInfo = resultInfo;
   417             this.stuckVars = stuckVars;
   418             if (!stuckVars.isEmpty()) {
   419                 resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
   420             }
   421         }
   423         @Override
   424         public void typesInferred(InferenceContext inferenceContext) {
   425             stuckVars = List.nil();
   426             resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt));
   427         }
   429         /**
   430          * Process a deferred attribution node.
   431          * Invariant: a stuck node cannot be processed.
   432          */
   433         @SuppressWarnings("fallthrough")
   434         boolean process(DeferredAttrContext deferredAttrContext) {
   435             switch (deferredAttrContext.mode) {
   436                 case SPECULATIVE:
   437                     dt.check(resultInfo, List.<Type>nil(), new StructuralStuckChecker());
   438                     return true;
   439                 case CHECK:
   440                     if (stuckVars.nonEmpty()) {
   441                         //stuck expression - see if we can propagate
   442                         if (deferredAttrContext.parent != emptyDeferredAttrContext &&
   443                                 Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, List.from(stuckVars))) {
   444                             deferredAttrContext.parent.deferredAttrNodes.add(this);
   445                             dt.check(resultInfo, List.<Type>nil(), dummyCompleter);
   446                             return true;
   447                         } else {
   448                             return false;
   449                         }
   450                     } else {
   451                         dt.check(resultInfo, stuckVars, basicCompleter);
   452                         return true;
   453                     }
   454                 default:
   455                     throw new AssertionError("Bad mode");
   456             }
   457         }
   459         /**
   460          * Structural checker for stuck expressions
   461          */
   462         class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter {
   464             ResultInfo resultInfo;
   465             InferenceContext inferenceContext;
   467             public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
   468                 this.resultInfo = resultInfo;
   469                 this.inferenceContext = deferredAttrContext.inferenceContext;
   470                 dt.tree.accept(this);
   471                 dt.speculativeCache.put(deferredAttrContext.msym, stuckTree, deferredAttrContext.phase);
   472                 return Type.noType;
   473             }
   475             @Override
   476             public void visitLambda(JCLambda tree) {
   477                 Check.CheckContext checkContext = resultInfo.checkContext;
   478                 Type pt = resultInfo.pt;
   479                 if (inferenceContext.inferencevars.contains(pt)) {
   480                     //ok
   481                     return;
   482                 } else {
   483                     //must be a functional descriptor
   484                     try {
   485                         Type desc = types.findDescriptorType(pt);
   486                         if (desc.getParameterTypes().length() != tree.params.length()) {
   487                             checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
   488                         }
   489                     } catch (Types.FunctionDescriptorLookupError ex) {
   490                         checkContext.report(null, ex.getDiagnostic());
   491                     }
   492                 }
   493             }
   495             @Override
   496             public void visitNewClass(JCNewClass tree) {
   497                 //do nothing
   498             }
   500             @Override
   501             public void visitApply(JCMethodInvocation tree) {
   502                 //do nothing
   503             }
   505             @Override
   506             public void visitReference(JCMemberReference tree) {
   507                 Check.CheckContext checkContext = resultInfo.checkContext;
   508                 Type pt = resultInfo.pt;
   509                 if (inferenceContext.inferencevars.contains(pt)) {
   510                     //ok
   511                     return;
   512                 } else {
   513                     try {
   514                         //TODO: we should speculative determine if there's a match
   515                         //based on arity - if yes, method is applicable.
   516                         types.findDescriptorType(pt);
   517                     } catch (Types.FunctionDescriptorLookupError ex) {
   518                         checkContext.report(null, ex.getDiagnostic());
   519                     }
   520                 }
   521             }
   522         }
   523     }
   525     /** an empty deferred attribution context - all methods throw exceptions */
   526     final DeferredAttrContext emptyDeferredAttrContext =
   527             new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, null, null, null) {
   528                 @Override
   529                 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
   530                     Assert.error("Empty deferred context!");
   531                 }
   532                 @Override
   533                 void complete() {
   534                     Assert.error("Empty deferred context!");
   535                 }
   536             };
   538     /**
   539      * Map a list of types possibly containing one or more deferred types
   540      * into a list of ordinary types. Each deferred type D is mapped into a type T,
   541      * where T is computed by retrieving the type that has already been
   542      * computed for D during a previous deferred attribution round of the given kind.
   543      */
   544     class DeferredTypeMap extends Type.Mapping {
   546         DeferredAttrContext deferredAttrContext;
   548         protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   549             super(String.format("deferredTypeMap[%s]", mode));
   550             this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase,
   551                     infer.emptyContext, emptyDeferredAttrContext, types.noWarnings);
   552         }
   554         protected boolean validState(DeferredType dt) {
   555             return dt.mode != null &&
   556                     deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
   557         }
   559         @Override
   560         public Type apply(Type t) {
   561             if (!t.hasTag(DEFERRED)) {
   562                 return t.map(this);
   563             } else {
   564                 DeferredType dt = (DeferredType)t;
   565                 Assert.check(validState(dt));
   566                 return typeOf(dt);
   567             }
   568         }
   570         protected Type typeOf(DeferredType dt) {
   571             switch (deferredAttrContext.mode) {
   572                 case CHECK:
   573                     return dt.tree.type == null ? Type.noType : dt.tree.type;
   574                 case SPECULATIVE:
   575                     return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
   576             }
   577             Assert.error();
   578             return null;
   579         }
   580     }
   582     /**
   583      * Specialized recovery deferred mapping.
   584      * Each deferred type D is mapped into a type T, where T is computed either by
   585      * (i) retrieving the type that has already been computed for D during a previous
   586      * attribution round (as before), or (ii) by synthesizing a new type R for D
   587      * (the latter step is useful in a recovery scenario).
   588      */
   589     public class RecoveryDeferredTypeMap extends DeferredTypeMap {
   591         public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   592             super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
   593         }
   595         @Override
   596         protected Type typeOf(DeferredType dt) {
   597             Type owntype = super.typeOf(dt);
   598             return owntype == Type.noType ?
   599                         recover(dt) : owntype;
   600         }
   602         @Override
   603         protected boolean validState(DeferredType dt) {
   604             return true;
   605         }
   607         /**
   608          * Synthesize a type for a deferred type that hasn't been previously
   609          * reduced to an ordinary type. Functional deferred types and conditionals
   610          * are mapped to themselves, in order to have a richer diagnostic
   611          * representation. Remaining deferred types are attributed using
   612          * a default expected type (j.l.Object).
   613          */
   614         private Type recover(DeferredType dt) {
   615             dt.check(attr.new RecoveryInfo(deferredAttrContext));
   616             return super.apply(dt);
   617         }
   618     }
   620     /**
   621      * Retrieves the list of inference variables that need to be inferred before
   622      * an AST node can be type-checked
   623      */
   624     @SuppressWarnings("fallthrough")
   625     List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   626                 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
   627             return List.nil();
   628         } else {
   629             return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext());
   630         }
   631     }
   632     //where
   633         private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) {
   634             StuckChecker sc = new StuckChecker(pt, inferenceContext);
   635             sc.scan(tree);
   636             return List.from(sc.stuckVars);
   637         }
   639     /**
   640      * A special tree scanner that would only visit portions of a given tree.
   641      * The set of nodes visited by the scanner can be customized at construction-time.
   642      */
   643     abstract static class FilterScanner extends TreeScanner {
   645         final Filter<JCTree> treeFilter;
   647         FilterScanner(final Set<JCTree.Tag> validTags) {
   648             this.treeFilter = new Filter<JCTree>() {
   649                 public boolean accepts(JCTree t) {
   650                     return validTags.contains(t.getTag());
   651                 }
   652             };
   653         }
   655         @Override
   656         public void scan(JCTree tree) {
   657             if (tree != null) {
   658                 if (treeFilter.accepts(tree)) {
   659                     super.scan(tree);
   660                 } else {
   661                     skip(tree);
   662                 }
   663             }
   664         }
   666         /**
   667          * handler that is executed when a node has been discarded
   668          */
   669         abstract void skip(JCTree tree);
   670     }
   672     /**
   673      * A tree scanner suitable for visiting the target-type dependent nodes of
   674      * a given argument expression.
   675      */
   676     static class PolyScanner extends FilterScanner {
   678         PolyScanner() {
   679             super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
   680         }
   682         @Override
   683         void skip(JCTree tree) {
   684             //do nothing
   685         }
   686     }
   688     /**
   689      * A tree scanner suitable for visiting the target-type dependent nodes nested
   690      * within a lambda expression body.
   691      */
   692     static class LambdaReturnScanner extends FilterScanner {
   694         LambdaReturnScanner() {
   695             super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
   696                     FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
   697         }
   699         @Override
   700         void skip(JCTree tree) {
   701             //do nothing
   702         }
   703     }
   705     /**
   706      * This visitor is used to check that structural expressions conform
   707      * to their target - this step is required as inference could end up
   708      * inferring types that make some of the nested expressions incompatible
   709      * with their corresponding instantiated target
   710      */
   711     class StuckChecker extends PolyScanner {
   713         Type pt;
   714         Infer.InferenceContext inferenceContext;
   715         Set<Type> stuckVars = new LinkedHashSet<Type>();
   717         StuckChecker(Type pt, Infer.InferenceContext inferenceContext) {
   718             this.pt = pt;
   719             this.inferenceContext = inferenceContext;
   720         }
   722         @Override
   723         public void visitLambda(JCLambda tree) {
   724             if (inferenceContext.inferenceVars().contains(pt)) {
   725                 stuckVars.add(pt);
   726             }
   727             if (!types.isFunctionalInterface(pt)) {
   728                 return;
   729             }
   730             Type descType = types.findDescriptorType(pt);
   731             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   732             if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
   733                     freeArgVars.nonEmpty()) {
   734                 stuckVars.addAll(freeArgVars);
   735             }
   736             scanLambdaBody(tree, descType.getReturnType());
   737         }
   739         @Override
   740         public void visitReference(JCMemberReference tree) {
   741             scan(tree.expr);
   742             if (inferenceContext.inferenceVars().contains(pt)) {
   743                 stuckVars.add(pt);
   744                 return;
   745             }
   746             if (!types.isFunctionalInterface(pt)) {
   747                 return;
   748             }
   750             Type descType = types.findDescriptorType(pt);
   751             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   752             stuckVars.addAll(freeArgVars);
   753         }
   755         void scanLambdaBody(JCLambda lambda, final Type pt) {
   756             if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
   757                 stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext));
   758             } else {
   759                 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
   760                     @Override
   761                     public void visitReturn(JCReturn tree) {
   762                         if (tree.expr != null) {
   763                             stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext));
   764                         }
   765                     }
   766                 };
   767                 lambdaScanner.scan(lambda.body);
   768             }
   769         }
   770     }
   771 }

mercurial