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

Tue, 26 Feb 2013 09:04:19 +0000

author
vromero
date
Tue, 26 Feb 2013 09:04:19 +0000
changeset 1607
bd49e0304281
parent 1596
3a39d123d33a
child 1613
d2a98dde7ecc
permissions
-rw-r--r--

8008436: javac should not issue a warning for overriding equals without hasCode if hashCode has been overriden by a superclass
Reviewed-by: jjg, mcimadamore

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

mercurial