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

Mon, 21 Jan 2013 20:13:56 +0000

author
mcimadamore
date
Mon, 21 Jan 2013 20:13:56 +0000
changeset 1510
7873d37f5b37
parent 1481
d07340b61e6a
child 1550
1df20330f6bd
permissions
-rw-r--r--

8005244: Implement overload resolution as per latest spec EDR
Summary: Add support for stuck expressions and provisional applicability
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 == AttrMode.SPECULATIVE);
   238                     return attr.attribTree(dt.tree, dt.env, resultInfo);
   239             }
   240             Assert.error();
   241             return null;
   242         }
   243     };
   245     /**
   246      * The 'mode' in which the deferred type is to be type-checked
   247      */
   248     public enum AttrMode {
   249         /**
   250          * A speculative type-checking round is used during overload resolution
   251          * mainly to generate constraints on inference variables. Side-effects
   252          * arising from type-checking the expression associated with the deferred
   253          * type are reversed after the speculative round finishes. This means the
   254          * expression tree will be left in a blank state.
   255          */
   256         SPECULATIVE,
   257         /**
   258          * This is the plain type-checking mode. Produces side-effects on the underlying AST node
   259          */
   260         CHECK;
   261     }
   263     /**
   264      * Routine that performs speculative type-checking; the input AST node is
   265      * cloned (to avoid side-effects cause by Attr) and compiler state is
   266      * restored after type-checking. All diagnostics (but critical ones) are
   267      * disabled during speculative type-checking.
   268      */
   269     JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   270         JCTree newTree = new TreeCopier<Object>(make).copy(tree);
   271         Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
   272         speculativeEnv.info.scope.owner = env.info.scope.owner;
   273         final JavaFileObject currentSource = log.currentSourceFile();
   274         Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
   275                 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
   276             public boolean accepts(JCDiagnostic t) {
   277                 return t.getDiagnosticSource().getFile().equals(currentSource);
   278             }
   279         });
   280         try {
   281             attr.attribTree(newTree, speculativeEnv, resultInfo);
   282             unenterScanner.scan(newTree);
   283             return newTree;
   284         } catch (Abort ex) {
   285             //if some very bad condition occurred during deferred attribution
   286             //we should dump all errors before killing javac
   287             deferredDiagnosticHandler.reportDeferredDiagnostics();
   288             throw ex;
   289         } finally {
   290             unenterScanner.scan(newTree);
   291             log.popDiagnosticHandler(deferredDiagnosticHandler);
   292         }
   293     }
   294     //where
   295         protected TreeScanner unenterScanner = new TreeScanner() {
   296             @Override
   297             public void visitClassDef(JCClassDecl tree) {
   298                 ClassSymbol csym = tree.sym;
   299                 //if something went wrong during method applicability check
   300                 //it is possible that nested expressions inside argument expression
   301                 //are left unchecked - in such cases there's nothing to clean up.
   302                 if (csym == null) return;
   303                 enter.typeEnvs.remove(csym);
   304                 chk.compiled.remove(csym.flatname);
   305                 syms.classes.remove(csym.flatname);
   306                 super.visitClassDef(tree);
   307             }
   308         };
   310     /**
   311      * A deferred context is created on each method check. A deferred context is
   312      * used to keep track of information associated with the method check, such as
   313      * the symbol of the method being checked, the overload resolution phase,
   314      * the kind of attribution mode to be applied to deferred types and so forth.
   315      * As deferred types are processed (by the method check routine) stuck AST nodes
   316      * are added (as new deferred attribution nodes) to this context. The complete()
   317      * routine makes sure that all pending nodes are properly processed, by
   318      * progressively instantiating all inference variables on which one or more
   319      * deferred attribution node is stuck.
   320      */
   321     class DeferredAttrContext {
   323         /** attribution mode */
   324         final AttrMode mode;
   326         /** symbol of the method being checked */
   327         final Symbol msym;
   329         /** method resolution step */
   330         final Resolve.MethodResolutionPhase phase;
   332         /** inference context */
   333         final InferenceContext inferenceContext;
   335         /** list of deferred attribution nodes to be processed */
   336         ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
   338         DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, InferenceContext inferenceContext) {
   339             this.mode = mode;
   340             this.msym = msym;
   341             this.phase = phase;
   342             this.inferenceContext = inferenceContext;
   343         }
   345         /**
   346          * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
   347          * Nodes added this way act as 'roots' for the out-of-order method checking process.
   348          */
   349         void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   350             deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
   351         }
   353         /**
   354          * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
   355          * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
   356          * some inference variable might get eagerly instantiated so that all nodes
   357          * can be type-checked.
   358          */
   359         void complete() {
   360             while (!deferredAttrNodes.isEmpty()) {
   361                 Set<Type> stuckVars = new LinkedHashSet<Type>();
   362                 boolean progress = false;
   363                 //scan a defensive copy of the node list - this is because a deferred
   364                 //attribution round can add new nodes to the list
   365                 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
   366                     if (!deferredAttrNode.process()) {
   367                         stuckVars.addAll(deferredAttrNode.stuckVars);
   368                     } else {
   369                         deferredAttrNodes.remove(deferredAttrNode);
   370                         progress = true;
   371                     }
   372                 }
   373                 if (!progress) {
   374                     //remove all variables that have already been instantiated
   375                     //from the list of stuck variables
   376                     inferenceContext.solveAny(inferenceContext.freeVarsIn(List.from(stuckVars)), types, infer);
   377                     inferenceContext.notifyChange(types);
   378                 }
   379             }
   380         }
   382         /**
   383          * Class representing a deferred attribution node. It keeps track of
   384          * a deferred type, along with the expected target type information.
   385          */
   386         class DeferredAttrNode implements Infer.InferenceContext.FreeTypeListener {
   388             /** underlying deferred type */
   389             DeferredType dt;
   391             /** underlying target type information */
   392             ResultInfo resultInfo;
   394             /** list of uninferred inference variables causing this node to be stuck */
   395             List<Type> stuckVars;
   397             DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   398                 this.dt = dt;
   399                 this.resultInfo = resultInfo;
   400                 this.stuckVars = stuckVars;
   401                 if (!stuckVars.isEmpty()) {
   402                     resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
   403                 }
   404             }
   406             @Override
   407             public void typesInferred(InferenceContext inferenceContext) {
   408                 stuckVars = List.nil();
   409                 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types));
   410             }
   412             /**
   413              * Process a deferred attribution node.
   414              * Invariant: a stuck node cannot be processed.
   415              */
   416             @SuppressWarnings("fallthrough")
   417             boolean process() {
   418                 switch (mode) {
   419                     case SPECULATIVE:
   420                         dt.check(resultInfo, List.<Type>nil(), new StructuralStuckChecker());
   421                         return true;
   422                     case CHECK:
   423                         if (stuckVars.nonEmpty()) {
   424                             return false;
   425                         } else {
   426                             dt.check(resultInfo, stuckVars, basicCompleter);
   427                             return true;
   428                         }
   429                     default:
   430                         throw new AssertionError("Bad mode");
   431                 }
   432             }
   434             /**
   435              * Structural checker for stuck expressions
   436              */
   437             class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter {
   439                 ResultInfo resultInfo;
   441                 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
   442                     this.resultInfo = resultInfo;
   443                     dt.tree.accept(this);
   444                     dt.speculativeCache.put(msym, stuckTree, phase);
   445                     return Type.noType;
   446                 }
   448                 @Override
   449                 public void visitLambda(JCLambda tree) {
   450                     Check.CheckContext checkContext = resultInfo.checkContext;
   451                     Type pt = resultInfo.pt;
   452                     if (inferenceContext.inferencevars.contains(pt)) {
   453                         //ok
   454                         return;
   455                     } else {
   456                         //must be a functional descriptor
   457                         try {
   458                             Type desc = types.findDescriptorType(pt);
   459                             if (desc.getParameterTypes().length() != tree.params.length()) {
   460                                 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
   461                             }
   462                         } catch (Types.FunctionDescriptorLookupError ex) {
   463                             checkContext.report(null, ex.getDiagnostic());
   464                         }
   465                     }
   466                 }
   468                 @Override
   469                 public void visitNewClass(JCNewClass tree) {
   470                     //do nothing
   471                 }
   473                 @Override
   474                 public void visitApply(JCMethodInvocation tree) {
   475                     //do nothing
   476                 }
   478                 @Override
   479                 public void visitReference(JCMemberReference tree) {
   480                     Check.CheckContext checkContext = resultInfo.checkContext;
   481                     Type pt = resultInfo.pt;
   482                     if (inferenceContext.inferencevars.contains(pt)) {
   483                         //ok
   484                         return;
   485                     } else {
   486                         try {
   487                             //TODO: we should speculative determine if there's a match
   488                             //based on arity - if yes, method is applicable.
   489                             types.findDescriptorType(pt);
   490                         } catch (Types.FunctionDescriptorLookupError ex) {
   491                             checkContext.report(null, ex.getDiagnostic());
   492                         }
   493                     }
   494                 }
   495             }
   496         }
   497     }
   499     /** an empty deferred attribution context - all methods throw exceptions */
   500     final DeferredAttrContext emptyDeferredAttrContext =
   501             new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, null) {
   502                 @Override
   503                 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
   504                     Assert.error("Empty deferred context!");
   505                 }
   506                 @Override
   507                 void complete() {
   508                     Assert.error("Empty deferred context!");
   509                 }
   510             };
   512     /**
   513      * Map a list of types possibly containing one or more deferred types
   514      * into a list of ordinary types. Each deferred type D is mapped into a type T,
   515      * where T is computed by retrieving the type that has already been
   516      * computed for D during a previous deferred attribution round of the given kind.
   517      */
   518     class DeferredTypeMap extends Type.Mapping {
   520         DeferredAttrContext deferredAttrContext;
   522         protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   523             super(String.format("deferredTypeMap[%s]", mode));
   524             this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, infer.emptyContext);
   525         }
   527         protected boolean validState(DeferredType dt) {
   528             return dt.mode != null &&
   529                     deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
   530         }
   532         @Override
   533         public Type apply(Type t) {
   534             if (!t.hasTag(DEFERRED)) {
   535                 return t.map(this);
   536             } else {
   537                 DeferredType dt = (DeferredType)t;
   538                 Assert.check(validState(dt));
   539                 return typeOf(dt);
   540             }
   541         }
   543         protected Type typeOf(DeferredType dt) {
   544             switch (deferredAttrContext.mode) {
   545                 case CHECK:
   546                     return dt.tree.type == null ? Type.noType : dt.tree.type;
   547                 case SPECULATIVE:
   548                     return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
   549             }
   550             Assert.error();
   551             return null;
   552         }
   553     }
   555     /**
   556      * Specialized recovery deferred mapping.
   557      * Each deferred type D is mapped into a type T, where T is computed either by
   558      * (i) retrieving the type that has already been computed for D during a previous
   559      * attribution round (as before), or (ii) by synthesizing a new type R for D
   560      * (the latter step is useful in a recovery scenario).
   561      */
   562     public class RecoveryDeferredTypeMap extends DeferredTypeMap {
   564         public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   565             super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
   566         }
   568         @Override
   569         protected Type typeOf(DeferredType dt) {
   570             Type owntype = super.typeOf(dt);
   571             return owntype == Type.noType ?
   572                         recover(dt) : owntype;
   573         }
   575         @Override
   576         protected boolean validState(DeferredType dt) {
   577             return true;
   578         }
   580         /**
   581          * Synthesize a type for a deferred type that hasn't been previously
   582          * reduced to an ordinary type. Functional deferred types and conditionals
   583          * are mapped to themselves, in order to have a richer diagnostic
   584          * representation. Remaining deferred types are attributed using
   585          * a default expected type (j.l.Object).
   586          */
   587         private Type recover(DeferredType dt) {
   588             dt.check(attr.new RecoveryInfo(deferredAttrContext));
   589             return super.apply(dt);
   590         }
   591     }
   593     /**
   594      * Retrieves the list of inference variables that need to be inferred before
   595      * an AST node can be type-checked
   596      */
   597     @SuppressWarnings("fallthrough")
   598     List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   599                 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
   600             return List.nil();
   601         } else {
   602             return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext());
   603         }
   604     }
   605     //where
   606         private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) {
   607             StuckChecker sc = new StuckChecker(pt, inferenceContext);
   608             sc.scan(tree);
   609             return List.from(sc.stuckVars);
   610         }
   612     /**
   613      * A special tree scanner that would only visit portions of a given tree.
   614      * The set of nodes visited by the scanner can be customized at construction-time.
   615      */
   616     abstract static class FilterScanner extends TreeScanner {
   618         final Filter<JCTree> treeFilter;
   620         FilterScanner(final Set<JCTree.Tag> validTags) {
   621             this.treeFilter = new Filter<JCTree>() {
   622                 public boolean accepts(JCTree t) {
   623                     return validTags.contains(t.getTag());
   624                 }
   625             };
   626         }
   628         @Override
   629         public void scan(JCTree tree) {
   630             if (tree != null) {
   631                 if (treeFilter.accepts(tree)) {
   632                     super.scan(tree);
   633                 } else {
   634                     skip(tree);
   635                 }
   636             }
   637         }
   639         /**
   640          * handler that is executed when a node has been discarded
   641          */
   642         abstract void skip(JCTree tree);
   643     }
   645     /**
   646      * A tree scanner suitable for visiting the target-type dependent nodes of
   647      * a given argument expression.
   648      */
   649     static class PolyScanner extends FilterScanner {
   651         PolyScanner() {
   652             super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
   653         }
   655         @Override
   656         void skip(JCTree tree) {
   657             //do nothing
   658         }
   659     }
   661     /**
   662      * A tree scanner suitable for visiting the target-type dependent nodes nested
   663      * within a lambda expression body.
   664      */
   665     static class LambdaReturnScanner extends FilterScanner {
   667         LambdaReturnScanner() {
   668             super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
   669                     FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
   670         }
   672         @Override
   673         void skip(JCTree tree) {
   674             //do nothing
   675         }
   676     }
   678     /**
   679      * This visitor is used to check that structural expressions conform
   680      * to their target - this step is required as inference could end up
   681      * inferring types that make some of the nested expressions incompatible
   682      * with their corresponding instantiated target
   683      */
   684     class StuckChecker extends PolyScanner {
   686         Type pt;
   687         Infer.InferenceContext inferenceContext;
   688         Set<Type> stuckVars = new LinkedHashSet<Type>();
   690         StuckChecker(Type pt, Infer.InferenceContext inferenceContext) {
   691             this.pt = pt;
   692             this.inferenceContext = inferenceContext;
   693         }
   695         @Override
   696         public void visitLambda(JCLambda tree) {
   697             if (inferenceContext.inferenceVars().contains(pt)) {
   698                 stuckVars.add(pt);
   699             }
   700             if (!types.isFunctionalInterface(pt)) {
   701                 return;
   702             }
   703             Type descType = types.findDescriptorType(pt);
   704             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   705             if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
   706                     freeArgVars.nonEmpty()) {
   707                 stuckVars.addAll(freeArgVars);
   708             }
   709             scanLambdaBody(tree, descType.getReturnType());
   710         }
   712         @Override
   713         public void visitReference(JCMemberReference tree) {
   714             scan(tree.expr);
   715             if (inferenceContext.inferenceVars().contains(pt)) {
   716                 stuckVars.add(pt);
   717                 return;
   718             }
   719             if (!types.isFunctionalInterface(pt)) {
   720                 return;
   721             }
   723             Type descType = types.findDescriptorType(pt);
   724             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   725             stuckVars.addAll(freeArgVars);
   726         }
   728         void scanLambdaBody(JCLambda lambda, final Type pt) {
   729             if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
   730                 stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext));
   731             } else {
   732                 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
   733                     @Override
   734                     public void visitReturn(JCReturn tree) {
   735                         if (tree.expr != null) {
   736                             stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext));
   737                         }
   738                     }
   739                 };
   740                 lambdaScanner.scan(lambda.body);
   741             }
   742         }
   743     }
   744 }

mercurial