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

Tue, 08 Jan 2013 10:17:29 +0100

author
mcimadamore
date
Tue, 08 Jan 2013 10:17:29 +0100
changeset 1481
d07340b61e6a
parent 1415
01c9d4161882
child 1510
7873d37f5b37
permissions
-rw-r--r--

8005184: Restructure DeferredAttr to allow pluggable deferred type completers
Summary: Add hooks to generalize deferred type completion via custom helper objects
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 Enter enter;
    69     final Infer infer;
    70     final Log log;
    71     final Symtab syms;
    72     final TreeMaker make;
    73     final Types types;
    75     public static DeferredAttr instance(Context context) {
    76         DeferredAttr instance = context.get(deferredAttrKey);
    77         if (instance == null)
    78             instance = new DeferredAttr(context);
    79         return instance;
    80     }
    82     protected DeferredAttr(Context context) {
    83         context.put(deferredAttrKey, this);
    84         attr = Attr.instance(context);
    85         chk = Check.instance(context);
    86         enter = Enter.instance(context);
    87         infer = Infer.instance(context);
    88         log = Log.instance(context);
    89         syms = Symtab.instance(context);
    90         make = TreeMaker.instance(context);
    91         types = Types.instance(context);
    92     }
    94     /**
    95      * This type represents a deferred type. A deferred type starts off with
    96      * no information on the underlying expression type. Such info needs to be
    97      * discovered through type-checking the deferred type against a target-type.
    98      * Every deferred type keeps a pointer to the AST node from which it originated.
    99      */
   100     public class DeferredType extends Type {
   102         public JCExpression tree;
   103         Env<AttrContext> env;
   104         AttrMode mode;
   105         SpeculativeCache speculativeCache;
   107         DeferredType(JCExpression tree, Env<AttrContext> env) {
   108             super(DEFERRED, null);
   109             this.tree = tree;
   110             this.env = env.dup(tree, env.info.dup());
   111             this.speculativeCache = new SpeculativeCache();
   112         }
   114         /**
   115          * A speculative cache is used to keep track of all overload resolution rounds
   116          * that triggered speculative attribution on a given deferred type. Each entry
   117          * stores a pointer to the speculative tree and the resolution phase in which the entry
   118          * has been added.
   119          */
   120         class SpeculativeCache {
   122             private Map<Symbol, List<Entry>> cache =
   123                     new WeakHashMap<Symbol, List<Entry>>();
   125             class Entry {
   126                 JCTree speculativeTree;
   127                 Resolve.MethodResolutionPhase phase;
   129                 public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
   130                     this.speculativeTree = speculativeTree;
   131                     this.phase = phase;
   132                 }
   134                 boolean matches(Resolve.MethodResolutionPhase phase) {
   135                     return this.phase == phase;
   136                 }
   137             }
   139             /**
   140              * Retrieve a speculative cache entry corresponding to given symbol
   141              * and resolution phase
   142              */
   143             Entry get(Symbol msym, MethodResolutionPhase phase) {
   144                 List<Entry> entries = cache.get(msym);
   145                 if (entries == null) return null;
   146                 for (Entry e : entries) {
   147                     if (e.matches(phase)) return e;
   148                 }
   149                 return null;
   150             }
   152             /**
   153              * Stores a speculative cache entry corresponding to given symbol
   154              * and resolution phase
   155              */
   156             void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
   157                 List<Entry> entries = cache.get(msym);
   158                 if (entries == null) {
   159                     entries = List.nil();
   160                 }
   161                 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
   162             }
   163         }
   165         /**
   166          * Get the type that has been computed during a speculative attribution round
   167          */
   168         Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
   169             SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
   170             return e != null ? e.speculativeTree.type : Type.noType;
   171         }
   173         /**
   174          * Check a deferred type against a potential target-type. Depending on
   175          * the current attribution mode, a normal vs. speculative attribution
   176          * round is performed on the underlying AST node. There can be only one
   177          * speculative round for a given target method symbol; moreover, a normal
   178          * attribution round must follow one or more speculative rounds.
   179          */
   180         Type check(ResultInfo resultInfo) {
   181             return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter);
   182         }
   184         Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) {
   185             DeferredAttrContext deferredAttrContext =
   186                     resultInfo.checkContext.deferredAttrContext();
   187             Assert.check(deferredAttrContext != emptyDeferredAttrContext);
   188             if (stuckVars.nonEmpty()) {
   189                 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
   190                 return Type.noType;
   191             } else {
   192                 try {
   193                     return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
   194                 } finally {
   195                     mode = deferredAttrContext.mode;
   196                 }
   197             }
   198         }
   199     }
   201     /**
   202      * A completer for deferred types. Defines an entry point for type-checking
   203      * a deferred type.
   204      */
   205     interface DeferredTypeCompleter {
   206         /**
   207          * Entry point for type-checking a deferred type. Depending on the
   208          * circumstances, type-checking could amount to full attribution
   209          * or partial structural check (aka potential applicability).
   210          */
   211         Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
   212     }
   214     /**
   215      * A basic completer for deferred types. This completer type-checks a deferred type
   216      * using attribution; depending on the attribution mode, this could be either standard
   217      * or speculative attribution.
   218      */
   219     DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
   220         public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
   221             switch (deferredAttrContext.mode) {
   222                 case SPECULATIVE:
   223                     Assert.check(dt.mode == null ||
   224                             (dt.mode == AttrMode.SPECULATIVE &&
   225                             dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE)));
   226                     JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
   227                     dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
   228                     return speculativeTree.type;
   229                 case CHECK:
   230                     Assert.check(dt.mode == AttrMode.SPECULATIVE);
   231                     return attr.attribTree(dt.tree, dt.env, resultInfo);
   232             }
   233             Assert.error();
   234             return null;
   235         }
   236     };
   238     /**
   239      * The 'mode' in which the deferred type is to be type-checked
   240      */
   241     public enum AttrMode {
   242         /**
   243          * A speculative type-checking round is used during overload resolution
   244          * mainly to generate constraints on inference variables. Side-effects
   245          * arising from type-checking the expression associated with the deferred
   246          * type are reversed after the speculative round finishes. This means the
   247          * expression tree will be left in a blank state.
   248          */
   249         SPECULATIVE,
   250         /**
   251          * This is the plain type-checking mode. Produces side-effects on the underlying AST node
   252          */
   253         CHECK;
   254     }
   256     /**
   257      * Routine that performs speculative type-checking; the input AST node is
   258      * cloned (to avoid side-effects cause by Attr) and compiler state is
   259      * restored after type-checking. All diagnostics (but critical ones) are
   260      * disabled during speculative type-checking.
   261      */
   262     JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   263         JCTree newTree = new TreeCopier<Object>(make).copy(tree);
   264         Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
   265         speculativeEnv.info.scope.owner = env.info.scope.owner;
   266         final JavaFileObject currentSource = log.currentSourceFile();
   267         Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
   268                 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
   269             public boolean accepts(JCDiagnostic t) {
   270                 return t.getDiagnosticSource().getFile().equals(currentSource);
   271             }
   272         });
   273         try {
   274             attr.attribTree(newTree, speculativeEnv, resultInfo);
   275             unenterScanner.scan(newTree);
   276             return newTree;
   277         } catch (Abort ex) {
   278             //if some very bad condition occurred during deferred attribution
   279             //we should dump all errors before killing javac
   280             deferredDiagnosticHandler.reportDeferredDiagnostics();
   281             throw ex;
   282         } finally {
   283             unenterScanner.scan(newTree);
   284             log.popDiagnosticHandler(deferredDiagnosticHandler);
   285         }
   286     }
   287     //where
   288         protected TreeScanner unenterScanner = new TreeScanner() {
   289             @Override
   290             public void visitClassDef(JCClassDecl tree) {
   291                 ClassSymbol csym = tree.sym;
   292                 //if something went wrong during method applicability check
   293                 //it is possible that nested expressions inside argument expression
   294                 //are left unchecked - in such cases there's nothing to clean up.
   295                 if (csym == null) return;
   296                 enter.typeEnvs.remove(csym);
   297                 chk.compiled.remove(csym.flatname);
   298                 syms.classes.remove(csym.flatname);
   299                 super.visitClassDef(tree);
   300             }
   301         };
   303     /**
   304      * A deferred context is created on each method check. A deferred context is
   305      * used to keep track of information associated with the method check, such as
   306      * the symbol of the method being checked, the overload resolution phase,
   307      * the kind of attribution mode to be applied to deferred types and so forth.
   308      * As deferred types are processed (by the method check routine) stuck AST nodes
   309      * are added (as new deferred attribution nodes) to this context. The complete()
   310      * routine makes sure that all pending nodes are properly processed, by
   311      * progressively instantiating all inference variables on which one or more
   312      * deferred attribution node is stuck.
   313      */
   314     class DeferredAttrContext {
   316         /** attribution mode */
   317         final AttrMode mode;
   319         /** symbol of the method being checked */
   320         final Symbol msym;
   322         /** method resolution step */
   323         final Resolve.MethodResolutionPhase phase;
   325         /** inference context */
   326         final InferenceContext inferenceContext;
   328         /** list of deferred attribution nodes to be processed */
   329         ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
   331         DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, InferenceContext inferenceContext) {
   332             this.mode = mode;
   333             this.msym = msym;
   334             this.phase = phase;
   335             this.inferenceContext = inferenceContext;
   336         }
   338         /**
   339          * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
   340          * Nodes added this way act as 'roots' for the out-of-order method checking process.
   341          */
   342         void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   343             deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
   344         }
   346         /**
   347          * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
   348          * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
   349          * some inference variable might get eagerly instantiated so that all nodes
   350          * can be type-checked.
   351          */
   352         void complete() {
   353             while (!deferredAttrNodes.isEmpty()) {
   354                 Set<Type> stuckVars = new LinkedHashSet<Type>();
   355                 boolean progress = false;
   356                 //scan a defensive copy of the node list - this is because a deferred
   357                 //attribution round can add new nodes to the list
   358                 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
   359                     if (!deferredAttrNode.isStuck()) {
   360                         deferredAttrNode.process();
   361                         deferredAttrNodes.remove(deferredAttrNode);
   362                         progress = true;
   363                     } else {
   364                         stuckVars.addAll(deferredAttrNode.stuckVars);
   365                     }
   366                 }
   367                 if (!progress) {
   368                     //remove all variables that have already been instantiated
   369                     //from the list of stuck variables
   370                     inferenceContext.solveAny(inferenceContext.freeVarsIn(List.from(stuckVars)), types, infer);
   371                     inferenceContext.notifyChange(types);
   372                 }
   373             }
   374         }
   376         /**
   377          * Class representing a deferred attribution node. It keeps track of
   378          * a deferred type, along with the expected target type information.
   379          */
   380         class DeferredAttrNode implements Infer.InferenceContext.FreeTypeListener {
   382             /** underlying deferred type */
   383             DeferredType dt;
   385             /** underlying target type information */
   386             ResultInfo resultInfo;
   388             /** list of uninferred inference variables causing this node to be stuck */
   389             List<Type> stuckVars;
   391             DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   392                 this.dt = dt;
   393                 this.resultInfo = resultInfo;
   394                 this.stuckVars = stuckVars;
   395                 if (!stuckVars.isEmpty()) {
   396                     resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
   397                 }
   398             }
   400             @Override
   401             public void typesInferred(InferenceContext inferenceContext) {
   402                 stuckVars = List.nil();
   403                 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types));
   404             }
   406             /**
   407              * is this node stuck?
   408              */
   409             boolean isStuck() {
   410                 return stuckVars.nonEmpty();
   411             }
   413             /**
   414              * Process a deferred attribution node.
   415              * Invariant: a stuck node cannot be processed.
   416              */
   417             void process() {
   418                 if (isStuck()) {
   419                     throw new IllegalStateException("Cannot process a stuck deferred node");
   420                 }
   421                 dt.check(resultInfo);
   422             }
   423         }
   424     }
   426     /** an empty deferred attribution context - all methods throw exceptions */
   427     final DeferredAttrContext emptyDeferredAttrContext =
   428             new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, null) {
   429                 @Override
   430                 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
   431                     Assert.error("Empty deferred context!");
   432                 }
   433                 @Override
   434                 void complete() {
   435                     Assert.error("Empty deferred context!");
   436                 }
   437             };
   439     /**
   440      * Map a list of types possibly containing one or more deferred types
   441      * into a list of ordinary types. Each deferred type D is mapped into a type T,
   442      * where T is computed by retrieving the type that has already been
   443      * computed for D during a previous deferred attribution round of the given kind.
   444      */
   445     class DeferredTypeMap extends Type.Mapping {
   447         DeferredAttrContext deferredAttrContext;
   449         protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   450             super(String.format("deferredTypeMap[%s]", mode));
   451             this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, infer.emptyContext);
   452         }
   454         protected boolean validState(DeferredType dt) {
   455             return dt.mode != null &&
   456                     deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
   457         }
   459         @Override
   460         public Type apply(Type t) {
   461             if (!t.hasTag(DEFERRED)) {
   462                 return t.map(this);
   463             } else {
   464                 DeferredType dt = (DeferredType)t;
   465                 Assert.check(validState(dt));
   466                 return typeOf(dt);
   467             }
   468         }
   470         protected Type typeOf(DeferredType dt) {
   471             switch (deferredAttrContext.mode) {
   472                 case CHECK:
   473                     return dt.tree.type == null ? Type.noType : dt.tree.type;
   474                 case SPECULATIVE:
   475                     return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
   476             }
   477             Assert.error();
   478             return null;
   479         }
   480     }
   482     /**
   483      * Specialized recovery deferred mapping.
   484      * Each deferred type D is mapped into a type T, where T is computed either by
   485      * (i) retrieving the type that has already been computed for D during a previous
   486      * attribution round (as before), or (ii) by synthesizing a new type R for D
   487      * (the latter step is useful in a recovery scenario).
   488      */
   489     public class RecoveryDeferredTypeMap extends DeferredTypeMap {
   491         public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   492             super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
   493         }
   495         @Override
   496         protected Type typeOf(DeferredType dt) {
   497             Type owntype = super.typeOf(dt);
   498             return owntype == Type.noType ?
   499                         recover(dt) : owntype;
   500         }
   502         @Override
   503         protected boolean validState(DeferredType dt) {
   504             return true;
   505         }
   507         /**
   508          * Synthesize a type for a deferred type that hasn't been previously
   509          * reduced to an ordinary type. Functional deferred types and conditionals
   510          * are mapped to themselves, in order to have a richer diagnostic
   511          * representation. Remaining deferred types are attributed using
   512          * a default expected type (j.l.Object).
   513          */
   514         private Type recover(DeferredType dt) {
   515             dt.check(attr.new RecoveryInfo(deferredAttrContext));
   516             return super.apply(dt);
   517         }
   518     }
   520     /**
   521      * Retrieves the list of inference variables that need to be inferred before
   522      * an AST node can be type-checked
   523      */
   524     @SuppressWarnings("fallthrough")
   525     List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   526                 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
   527             return List.nil();
   528         } else {
   529             return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext());
   530         }
   531     }
   532     //where
   533         private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) {
   534             StuckChecker sc = new StuckChecker(pt, inferenceContext);
   535             sc.scan(tree);
   536             return List.from(sc.stuckVars);
   537         }
   539     /**
   540      * A special tree scanner that would only visit portions of a given tree.
   541      * The set of nodes visited by the scanner can be customized at construction-time.
   542      */
   543     abstract static class FilterScanner extends TreeScanner {
   545         final Filter<JCTree> treeFilter;
   547         FilterScanner(final Set<JCTree.Tag> validTags) {
   548             this.treeFilter = new Filter<JCTree>() {
   549                 public boolean accepts(JCTree t) {
   550                     return validTags.contains(t.getTag());
   551                 }
   552             };
   553         }
   555         @Override
   556         public void scan(JCTree tree) {
   557             if (tree != null) {
   558                 if (treeFilter.accepts(tree)) {
   559                     super.scan(tree);
   560                 } else {
   561                     skip(tree);
   562                 }
   563             }
   564         }
   566         /**
   567          * handler that is executed when a node has been discarded
   568          */
   569         abstract void skip(JCTree tree);
   570     }
   572     /**
   573      * A tree scanner suitable for visiting the target-type dependent nodes of
   574      * a given argument expression.
   575      */
   576     static class PolyScanner extends FilterScanner {
   578         PolyScanner() {
   579             super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
   580         }
   582         @Override
   583         void skip(JCTree tree) {
   584             //do nothing
   585         }
   586     }
   588     /**
   589      * A tree scanner suitable for visiting the target-type dependent nodes nested
   590      * within a lambda expression body.
   591      */
   592     static class LambdaReturnScanner extends FilterScanner {
   594         LambdaReturnScanner() {
   595             super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
   596                     FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
   597         }
   599         @Override
   600         void skip(JCTree tree) {
   601             //do nothing
   602         }
   603     }
   605     /**
   606      * This visitor is used to check that structural expressions conform
   607      * to their target - this step is required as inference could end up
   608      * inferring types that make some of the nested expressions incompatible
   609      * with their corresponding instantiated target
   610      */
   611     class StuckChecker extends PolyScanner {
   613         Type pt;
   614         Infer.InferenceContext inferenceContext;
   615         Set<Type> stuckVars = new LinkedHashSet<Type>();
   617         StuckChecker(Type pt, Infer.InferenceContext inferenceContext) {
   618             this.pt = pt;
   619             this.inferenceContext = inferenceContext;
   620         }
   622         @Override
   623         public void visitLambda(JCLambda tree) {
   624             if (inferenceContext.inferenceVars().contains(pt)) {
   625                 stuckVars.add(pt);
   626             }
   627             if (!types.isFunctionalInterface(pt.tsym)) {
   628                 return;
   629             }
   630             Type descType = types.findDescriptorType(pt);
   631             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   632             if (!TreeInfo.isExplicitLambda(tree) &&
   633                     freeArgVars.nonEmpty()) {
   634                 stuckVars.addAll(freeArgVars);
   635             }
   636             scanLambdaBody(tree, descType.getReturnType());
   637         }
   639         @Override
   640         public void visitReference(JCMemberReference tree) {
   641             scan(tree.expr);
   642             if (inferenceContext.inferenceVars().contains(pt)) {
   643                 stuckVars.add(pt);
   644                 return;
   645             }
   646             if (!types.isFunctionalInterface(pt.tsym)) {
   647                 return;
   648             }
   650             Type descType = types.findDescriptorType(pt);
   651             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   652             stuckVars.addAll(freeArgVars);
   653         }
   655         void scanLambdaBody(JCLambda lambda, final Type pt) {
   656             if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
   657                 stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext));
   658             } else {
   659                 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
   660                     @Override
   661                     public void visitReturn(JCReturn tree) {
   662                         if (tree.expr != null) {
   663                             stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext));
   664                         }
   665                     }
   666                 };
   667                 lambdaScanner.scan(lambda.body);
   668             }
   669         }
   670     }
   671 }

mercurial