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

Mon, 15 Apr 2013 14:18:30 +0100

author
mcimadamore
date
Mon, 15 Apr 2013 14:18:30 +0100
changeset 1697
950e8ac120f0
parent 1674
b71a61d39cf7
child 1850
6debfa63a4a1
permissions
-rw-r--r--

8010923: Avoid redundant speculative attribution
Summary: Add optimization to avoid speculative attribution for certain argument expressions
Reviewed-by: jjg

     1 /*
     2  * Copyright (c) 2012, 2013, 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.util.JCDiagnostic.DiagnosticPosition;
    32 import com.sun.tools.javac.code.Symbol.*;
    33 import com.sun.tools.javac.code.Type.*;
    34 import com.sun.tools.javac.comp.Attr.ResultInfo;
    35 import com.sun.tools.javac.comp.Infer.InferenceContext;
    36 import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
    37 import com.sun.tools.javac.tree.JCTree.*;
    39 import javax.tools.JavaFileObject;
    41 import java.util.ArrayList;
    42 import java.util.EnumSet;
    43 import java.util.LinkedHashSet;
    44 import java.util.Map;
    45 import java.util.Queue;
    46 import java.util.Set;
    47 import java.util.WeakHashMap;
    49 import static com.sun.tools.javac.code.TypeTag.*;
    50 import static com.sun.tools.javac.tree.JCTree.Tag.*;
    52 /**
    53  * This is an helper class that is used to perform deferred type-analysis.
    54  * Each time a poly expression occurs in argument position, javac attributes it
    55  * with a temporary 'deferred type' that is checked (possibly multiple times)
    56  * against an expected formal type.
    57  *
    58  *  <p><b>This is NOT part of any supported API.
    59  *  If you write code that depends on this, you do so at your own risk.
    60  *  This code and its internal interfaces are subject to change or
    61  *  deletion without notice.</b>
    62  */
    63 public class DeferredAttr extends JCTree.Visitor {
    64     protected static final Context.Key<DeferredAttr> deferredAttrKey =
    65         new Context.Key<DeferredAttr>();
    67     final Attr attr;
    68     final Check chk;
    69     final JCDiagnostic.Factory diags;
    70     final Enter enter;
    71     final Infer infer;
    72     final Resolve rs;
    73     final Log log;
    74     final Symtab syms;
    75     final TreeMaker make;
    76     final Types types;
    78     public static DeferredAttr instance(Context context) {
    79         DeferredAttr instance = context.get(deferredAttrKey);
    80         if (instance == null)
    81             instance = new DeferredAttr(context);
    82         return instance;
    83     }
    85     protected DeferredAttr(Context context) {
    86         context.put(deferredAttrKey, this);
    87         attr = Attr.instance(context);
    88         chk = Check.instance(context);
    89         diags = JCDiagnostic.Factory.instance(context);
    90         enter = Enter.instance(context);
    91         infer = Infer.instance(context);
    92         rs = Resolve.instance(context);
    93         log = Log.instance(context);
    94         syms = Symtab.instance(context);
    95         make = TreeMaker.instance(context);
    96         types = Types.instance(context);
    97         Names names = Names.instance(context);
    98         stuckTree = make.Ident(names.empty).setType(Type.noType);
    99     }
   101     /** shared tree for stuck expressions */
   102     final JCTree stuckTree;
   104     /**
   105      * This type represents a deferred type. A deferred type starts off with
   106      * no information on the underlying expression type. Such info needs to be
   107      * discovered through type-checking the deferred type against a target-type.
   108      * Every deferred type keeps a pointer to the AST node from which it originated.
   109      */
   110     public class DeferredType extends Type {
   112         public JCExpression tree;
   113         Env<AttrContext> env;
   114         AttrMode mode;
   115         SpeculativeCache speculativeCache;
   117         DeferredType(JCExpression tree, Env<AttrContext> env) {
   118             super(DEFERRED, null);
   119             this.tree = tree;
   120             this.env = env.dup(tree, env.info.dup());
   121             this.speculativeCache = new SpeculativeCache();
   122         }
   124         /**
   125          * A speculative cache is used to keep track of all overload resolution rounds
   126          * that triggered speculative attribution on a given deferred type. Each entry
   127          * stores a pointer to the speculative tree and the resolution phase in which the entry
   128          * has been added.
   129          */
   130         class SpeculativeCache {
   132             private Map<Symbol, List<Entry>> cache =
   133                     new WeakHashMap<Symbol, List<Entry>>();
   135             class Entry {
   136                 JCTree speculativeTree;
   137                 Resolve.MethodResolutionPhase phase;
   139                 public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
   140                     this.speculativeTree = speculativeTree;
   141                     this.phase = phase;
   142                 }
   144                 boolean matches(Resolve.MethodResolutionPhase phase) {
   145                     return this.phase == phase;
   146                 }
   147             }
   149             /**
   150              * Retrieve a speculative cache entry corresponding to given symbol
   151              * and resolution phase
   152              */
   153             Entry get(Symbol msym, MethodResolutionPhase phase) {
   154                 List<Entry> entries = cache.get(msym);
   155                 if (entries == null) return null;
   156                 for (Entry e : entries) {
   157                     if (e.matches(phase)) return e;
   158                 }
   159                 return null;
   160             }
   162             /**
   163              * Stores a speculative cache entry corresponding to given symbol
   164              * and resolution phase
   165              */
   166             void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
   167                 List<Entry> entries = cache.get(msym);
   168                 if (entries == null) {
   169                     entries = List.nil();
   170                 }
   171                 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
   172             }
   173         }
   175         /**
   176          * Get the type that has been computed during a speculative attribution round
   177          */
   178         Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
   179             SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
   180             return e != null ? e.speculativeTree.type : Type.noType;
   181         }
   183         /**
   184          * Check a deferred type against a potential target-type. Depending on
   185          * the current attribution mode, a normal vs. speculative attribution
   186          * round is performed on the underlying AST node. There can be only one
   187          * speculative round for a given target method symbol; moreover, a normal
   188          * attribution round must follow one or more speculative rounds.
   189          */
   190         Type check(ResultInfo resultInfo) {
   191             return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter);
   192         }
   194         Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) {
   195             DeferredAttrContext deferredAttrContext =
   196                     resultInfo.checkContext.deferredAttrContext();
   197             Assert.check(deferredAttrContext != emptyDeferredAttrContext);
   198             if (stuckVars.nonEmpty()) {
   199                 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
   200                 return Type.noType;
   201             } else {
   202                 try {
   203                     return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
   204                 } finally {
   205                     mode = deferredAttrContext.mode;
   206                 }
   207             }
   208         }
   209     }
   211     /**
   212      * A completer for deferred types. Defines an entry point for type-checking
   213      * a deferred type.
   214      */
   215     interface DeferredTypeCompleter {
   216         /**
   217          * Entry point for type-checking a deferred type. Depending on the
   218          * circumstances, type-checking could amount to full attribution
   219          * or partial structural check (aka potential applicability).
   220          */
   221         Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
   222     }
   224     /**
   225      * A basic completer for deferred types. This completer type-checks a deferred type
   226      * using attribution; depending on the attribution mode, this could be either standard
   227      * or speculative attribution.
   228      */
   229     DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
   230         public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
   231             switch (deferredAttrContext.mode) {
   232                 case SPECULATIVE:
   233                     //Note: if a symbol is imported twice we might do two identical
   234                     //speculative rounds...
   235                     Assert.check(dt.mode == null || dt.mode == AttrMode.SPECULATIVE);
   236                     JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
   237                     dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
   238                     return speculativeTree.type;
   239                 case CHECK:
   240                     Assert.check(dt.mode != null);
   241                     return attr.attribTree(dt.tree, dt.env, resultInfo);
   242             }
   243             Assert.error();
   244             return null;
   245         }
   246     };
   248     DeferredTypeCompleter dummyCompleter = new DeferredTypeCompleter() {
   249         public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
   250             Assert.check(deferredAttrContext.mode == AttrMode.CHECK);
   251             return dt.tree.type = Type.noType;
   252         }
   253     };
   255     /**
   256      * The 'mode' in which the deferred type is to be type-checked
   257      */
   258     public enum AttrMode {
   259         /**
   260          * A speculative type-checking round is used during overload resolution
   261          * mainly to generate constraints on inference variables. Side-effects
   262          * arising from type-checking the expression associated with the deferred
   263          * type are reversed after the speculative round finishes. This means the
   264          * expression tree will be left in a blank state.
   265          */
   266         SPECULATIVE,
   267         /**
   268          * This is the plain type-checking mode. Produces side-effects on the underlying AST node
   269          */
   270         CHECK;
   271     }
   273     /**
   274      * Routine that performs speculative type-checking; the input AST node is
   275      * cloned (to avoid side-effects cause by Attr) and compiler state is
   276      * restored after type-checking. All diagnostics (but critical ones) are
   277      * disabled during speculative type-checking.
   278      */
   279     JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   280         final JCTree newTree = new TreeCopier<Object>(make).copy(tree);
   281         Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
   282         speculativeEnv.info.scope.owner = env.info.scope.owner;
   283         Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
   284                 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
   285             public boolean accepts(final JCDiagnostic d) {
   286                 class PosScanner extends TreeScanner {
   287                     boolean found = false;
   289                     @Override
   290                     public void scan(JCTree tree) {
   291                         if (tree != null &&
   292                                 tree.pos() == d.getDiagnosticPosition()) {
   293                             found = true;
   294                         }
   295                         super.scan(tree);
   296                     }
   297                 };
   298                 PosScanner posScanner = new PosScanner();
   299                 posScanner.scan(newTree);
   300                 return posScanner.found;
   301             }
   302         });
   303         try {
   304             attr.attribTree(newTree, speculativeEnv, resultInfo);
   305             unenterScanner.scan(newTree);
   306             return newTree;
   307         } finally {
   308             unenterScanner.scan(newTree);
   309             log.popDiagnosticHandler(deferredDiagnosticHandler);
   310         }
   311     }
   312     //where
   313         protected TreeScanner unenterScanner = new TreeScanner() {
   314             @Override
   315             public void visitClassDef(JCClassDecl tree) {
   316                 ClassSymbol csym = tree.sym;
   317                 //if something went wrong during method applicability check
   318                 //it is possible that nested expressions inside argument expression
   319                 //are left unchecked - in such cases there's nothing to clean up.
   320                 if (csym == null) return;
   321                 enter.typeEnvs.remove(csym);
   322                 chk.compiled.remove(csym.flatname);
   323                 syms.classes.remove(csym.flatname);
   324                 super.visitClassDef(tree);
   325             }
   326         };
   328     /**
   329      * A deferred context is created on each method check. A deferred context is
   330      * used to keep track of information associated with the method check, such as
   331      * the symbol of the method being checked, the overload resolution phase,
   332      * the kind of attribution mode to be applied to deferred types and so forth.
   333      * As deferred types are processed (by the method check routine) stuck AST nodes
   334      * are added (as new deferred attribution nodes) to this context. The complete()
   335      * routine makes sure that all pending nodes are properly processed, by
   336      * progressively instantiating all inference variables on which one or more
   337      * deferred attribution node is stuck.
   338      */
   339     class DeferredAttrContext {
   341         /** attribution mode */
   342         final AttrMode mode;
   344         /** symbol of the method being checked */
   345         final Symbol msym;
   347         /** method resolution step */
   348         final Resolve.MethodResolutionPhase phase;
   350         /** inference context */
   351         final InferenceContext inferenceContext;
   353         /** parent deferred context */
   354         final DeferredAttrContext parent;
   356         /** Warner object to report warnings */
   357         final Warner warn;
   359         /** list of deferred attribution nodes to be processed */
   360         ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
   362         DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase,
   363                 InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) {
   364             this.mode = mode;
   365             this.msym = msym;
   366             this.phase = phase;
   367             this.parent = parent;
   368             this.warn = warn;
   369             this.inferenceContext = inferenceContext;
   370         }
   372         /**
   373          * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
   374          * Nodes added this way act as 'roots' for the out-of-order method checking process.
   375          */
   376         void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   377             deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
   378         }
   380         /**
   381          * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
   382          * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
   383          * some inference variable might get eagerly instantiated so that all nodes
   384          * can be type-checked.
   385          */
   386         void complete() {
   387             while (!deferredAttrNodes.isEmpty()) {
   388                 Set<Type> stuckVars = new LinkedHashSet<Type>();
   389                 boolean progress = false;
   390                 //scan a defensive copy of the node list - this is because a deferred
   391                 //attribution round can add new nodes to the list
   392                 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
   393                     if (!deferredAttrNode.process(this)) {
   394                         stuckVars.addAll(deferredAttrNode.stuckVars);
   395                     } else {
   396                         deferredAttrNodes.remove(deferredAttrNode);
   397                         progress = true;
   398                     }
   399                 }
   400                 if (!progress) {
   401                     //remove all variables that have already been instantiated
   402                     //from the list of stuck variables
   403                     inferenceContext.solveAny(List.from(stuckVars), warn);
   404                     inferenceContext.notifyChange();
   405                 }
   406             }
   407         }
   408     }
   410     /**
   411      * Class representing a deferred attribution node. It keeps track of
   412      * a deferred type, along with the expected target type information.
   413      */
   414     class DeferredAttrNode implements Infer.FreeTypeListener {
   416         /** underlying deferred type */
   417         DeferredType dt;
   419         /** underlying target type information */
   420         ResultInfo resultInfo;
   422         /** list of uninferred inference variables causing this node to be stuck */
   423         List<Type> stuckVars;
   425         DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
   426             this.dt = dt;
   427             this.resultInfo = resultInfo;
   428             this.stuckVars = stuckVars;
   429             if (!stuckVars.isEmpty()) {
   430                 resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
   431             }
   432         }
   434         @Override
   435         public void typesInferred(InferenceContext inferenceContext) {
   436             stuckVars = List.nil();
   437             resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt));
   438         }
   440         /**
   441          * Process a deferred attribution node.
   442          * Invariant: a stuck node cannot be processed.
   443          */
   444         @SuppressWarnings("fallthrough")
   445         boolean process(DeferredAttrContext deferredAttrContext) {
   446             switch (deferredAttrContext.mode) {
   447                 case SPECULATIVE:
   448                     dt.check(resultInfo, List.<Type>nil(), new StructuralStuckChecker());
   449                     return true;
   450                 case CHECK:
   451                     if (stuckVars.nonEmpty()) {
   452                         //stuck expression - see if we can propagate
   453                         if (deferredAttrContext.parent != emptyDeferredAttrContext &&
   454                                 Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, List.from(stuckVars))) {
   455                             deferredAttrContext.parent.deferredAttrNodes.add(this);
   456                             dt.check(resultInfo, List.<Type>nil(), dummyCompleter);
   457                             return true;
   458                         } else {
   459                             return false;
   460                         }
   461                     } else {
   462                         dt.check(resultInfo, stuckVars, basicCompleter);
   463                         return true;
   464                     }
   465                 default:
   466                     throw new AssertionError("Bad mode");
   467             }
   468         }
   470         /**
   471          * Structural checker for stuck expressions
   472          */
   473         class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter {
   475             ResultInfo resultInfo;
   476             InferenceContext inferenceContext;
   477             Env<AttrContext> env;
   479             public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
   480                 this.resultInfo = resultInfo;
   481                 this.inferenceContext = deferredAttrContext.inferenceContext;
   482                 this.env = dt.env.dup(dt.tree, dt.env.info.dup());
   483                 dt.tree.accept(this);
   484                 dt.speculativeCache.put(deferredAttrContext.msym, stuckTree, deferredAttrContext.phase);
   485                 return Type.noType;
   486             }
   488             @Override
   489             public void visitLambda(JCLambda tree) {
   490                 Check.CheckContext checkContext = resultInfo.checkContext;
   491                 Type pt = resultInfo.pt;
   492                 if (inferenceContext.inferencevars.contains(pt)) {
   493                     //ok
   494                     return;
   495                 } else {
   496                     //must be a functional descriptor
   497                     try {
   498                         Type desc = types.findDescriptorType(pt);
   499                         if (desc.getParameterTypes().length() != tree.params.length()) {
   500                             checkContext.report(tree, diags.fragment("incompatible.arg.types.in.lambda"));
   501                         }
   502                     } catch (Types.FunctionDescriptorLookupError ex) {
   503                         checkContext.report(null, ex.getDiagnostic());
   504                     }
   505                 }
   506             }
   508             @Override
   509             public void visitNewClass(JCNewClass tree) {
   510                 //do nothing
   511             }
   513             @Override
   514             public void visitApply(JCMethodInvocation tree) {
   515                 //do nothing
   516             }
   518             @Override
   519             public void visitReference(JCMemberReference tree) {
   520                 Check.CheckContext checkContext = resultInfo.checkContext;
   521                 Type pt = resultInfo.pt;
   522                 if (inferenceContext.inferencevars.contains(pt)) {
   523                     //ok
   524                     return;
   525                 } else {
   526                     try {
   527                         types.findDescriptorType(pt);
   528                     } catch (Types.FunctionDescriptorLookupError ex) {
   529                         checkContext.report(null, ex.getDiagnostic());
   530                     }
   531                     JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), env,
   532                             attr.memberReferenceQualifierResult(tree));
   533                     ListBuffer<Type> argtypes = ListBuffer.lb();
   534                     for (Type t : types.findDescriptorType(pt).getParameterTypes()) {
   535                         argtypes.append(Type.noType);
   536                     }
   537                     JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree);
   538                     mref2.expr = exprTree;
   539                     Pair<Symbol, ?> lookupRes =
   540                             rs.resolveMemberReference(tree, env, mref2, exprTree.type,
   541                                 tree.name, argtypes.toList(), null, true, rs.arityMethodCheck);
   542                     switch (lookupRes.fst.kind) {
   543                         //note: as argtypes are erroneous types, type-errors must
   544                         //have been caused by arity mismatch
   545                         case Kinds.ABSENT_MTH:
   546                         case Kinds.WRONG_MTH:
   547                         case Kinds.WRONG_MTHS:
   548                         case Kinds.STATICERR:
   549                         case Kinds.MISSING_ENCL:
   550                            checkContext.report(null, diags.fragment("incompatible.arg.types.in.mref"));
   551                     }
   552                 }
   553             }
   554         }
   555     }
   557     /** an empty deferred attribution context - all methods throw exceptions */
   558     final DeferredAttrContext emptyDeferredAttrContext =
   559             new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, null, null, null) {
   560                 @Override
   561                 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
   562                     Assert.error("Empty deferred context!");
   563                 }
   564                 @Override
   565                 void complete() {
   566                     Assert.error("Empty deferred context!");
   567                 }
   568             };
   570     /**
   571      * Map a list of types possibly containing one or more deferred types
   572      * into a list of ordinary types. Each deferred type D is mapped into a type T,
   573      * where T is computed by retrieving the type that has already been
   574      * computed for D during a previous deferred attribution round of the given kind.
   575      */
   576     class DeferredTypeMap extends Type.Mapping {
   578         DeferredAttrContext deferredAttrContext;
   580         protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   581             super(String.format("deferredTypeMap[%s]", mode));
   582             this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase,
   583                     infer.emptyContext, emptyDeferredAttrContext, types.noWarnings);
   584         }
   586         protected boolean validState(DeferredType dt) {
   587             return dt.mode != null &&
   588                     deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
   589         }
   591         @Override
   592         public Type apply(Type t) {
   593             if (!t.hasTag(DEFERRED)) {
   594                 return t.map(this);
   595             } else {
   596                 DeferredType dt = (DeferredType)t;
   597                 Assert.check(validState(dt));
   598                 return typeOf(dt);
   599             }
   600         }
   602         protected Type typeOf(DeferredType dt) {
   603             switch (deferredAttrContext.mode) {
   604                 case CHECK:
   605                     return dt.tree.type == null ? Type.noType : dt.tree.type;
   606                 case SPECULATIVE:
   607                     return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
   608             }
   609             Assert.error();
   610             return null;
   611         }
   612     }
   614     /**
   615      * Specialized recovery deferred mapping.
   616      * Each deferred type D is mapped into a type T, where T is computed either by
   617      * (i) retrieving the type that has already been computed for D during a previous
   618      * attribution round (as before), or (ii) by synthesizing a new type R for D
   619      * (the latter step is useful in a recovery scenario).
   620      */
   621     public class RecoveryDeferredTypeMap extends DeferredTypeMap {
   623         public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
   624             super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
   625         }
   627         @Override
   628         protected Type typeOf(DeferredType dt) {
   629             Type owntype = super.typeOf(dt);
   630             return owntype == Type.noType ?
   631                         recover(dt) : owntype;
   632         }
   634         @Override
   635         protected boolean validState(DeferredType dt) {
   636             return true;
   637         }
   639         /**
   640          * Synthesize a type for a deferred type that hasn't been previously
   641          * reduced to an ordinary type. Functional deferred types and conditionals
   642          * are mapped to themselves, in order to have a richer diagnostic
   643          * representation. Remaining deferred types are attributed using
   644          * a default expected type (j.l.Object).
   645          */
   646         private Type recover(DeferredType dt) {
   647             dt.check(attr.new RecoveryInfo(deferredAttrContext));
   648             return super.apply(dt);
   649         }
   650     }
   652     /**
   653      * Retrieves the list of inference variables that need to be inferred before
   654      * an AST node can be type-checked
   655      */
   656     @SuppressWarnings("fallthrough")
   657     List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
   658                 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
   659             return List.nil();
   660         } else {
   661             return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext());
   662         }
   663     }
   664     //where
   665         private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) {
   666             StuckChecker sc = new StuckChecker(pt, inferenceContext);
   667             sc.scan(tree);
   668             return List.from(sc.stuckVars);
   669         }
   671     /**
   672      * A special tree scanner that would only visit portions of a given tree.
   673      * The set of nodes visited by the scanner can be customized at construction-time.
   674      */
   675     abstract static class FilterScanner extends TreeScanner {
   677         final Filter<JCTree> treeFilter;
   679         FilterScanner(final Set<JCTree.Tag> validTags) {
   680             this.treeFilter = new Filter<JCTree>() {
   681                 public boolean accepts(JCTree t) {
   682                     return validTags.contains(t.getTag());
   683                 }
   684             };
   685         }
   687         @Override
   688         public void scan(JCTree tree) {
   689             if (tree != null) {
   690                 if (treeFilter.accepts(tree)) {
   691                     super.scan(tree);
   692                 } else {
   693                     skip(tree);
   694                 }
   695             }
   696         }
   698         /**
   699          * handler that is executed when a node has been discarded
   700          */
   701         abstract void skip(JCTree tree);
   702     }
   704     /**
   705      * A tree scanner suitable for visiting the target-type dependent nodes of
   706      * a given argument expression.
   707      */
   708     static class PolyScanner extends FilterScanner {
   710         PolyScanner() {
   711             super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
   712         }
   714         @Override
   715         void skip(JCTree tree) {
   716             //do nothing
   717         }
   718     }
   720     /**
   721      * A tree scanner suitable for visiting the target-type dependent nodes nested
   722      * within a lambda expression body.
   723      */
   724     static class LambdaReturnScanner extends FilterScanner {
   726         LambdaReturnScanner() {
   727             super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
   728                     FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
   729         }
   731         @Override
   732         void skip(JCTree tree) {
   733             //do nothing
   734         }
   735     }
   737     /**
   738      * This visitor is used to check that structural expressions conform
   739      * to their target - this step is required as inference could end up
   740      * inferring types that make some of the nested expressions incompatible
   741      * with their corresponding instantiated target
   742      */
   743     class StuckChecker extends PolyScanner {
   745         Type pt;
   746         Infer.InferenceContext inferenceContext;
   747         Set<Type> stuckVars = new LinkedHashSet<Type>();
   749         StuckChecker(Type pt, Infer.InferenceContext inferenceContext) {
   750             this.pt = pt;
   751             this.inferenceContext = inferenceContext;
   752         }
   754         @Override
   755         public void visitLambda(JCLambda tree) {
   756             if (inferenceContext.inferenceVars().contains(pt)) {
   757                 stuckVars.add(pt);
   758             }
   759             if (!types.isFunctionalInterface(pt)) {
   760                 return;
   761             }
   762             Type descType = types.findDescriptorType(pt);
   763             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   764             if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT &&
   765                     freeArgVars.nonEmpty()) {
   766                 stuckVars.addAll(freeArgVars);
   767             }
   768             scanLambdaBody(tree, descType.getReturnType());
   769         }
   771         @Override
   772         public void visitReference(JCMemberReference tree) {
   773             scan(tree.expr);
   774             if (inferenceContext.inferenceVars().contains(pt)) {
   775                 stuckVars.add(pt);
   776                 return;
   777             }
   778             if (!types.isFunctionalInterface(pt)) {
   779                 return;
   780             }
   782             Type descType = types.findDescriptorType(pt);
   783             List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
   784             stuckVars.addAll(freeArgVars);
   785         }
   787         void scanLambdaBody(JCLambda lambda, final Type pt) {
   788             if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
   789                 stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext));
   790             } else {
   791                 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
   792                     @Override
   793                     public void visitReturn(JCReturn tree) {
   794                         if (tree.expr != null) {
   795                             stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext));
   796                         }
   797                     }
   798                 };
   799                 lambdaScanner.scan(lambda.body);
   800             }
   801         }
   802     }
   804     /**
   805      * Does the argument expression {@code expr} need speculative type-checking?
   806      */
   807     boolean isDeferred(Env<AttrContext> env, JCExpression expr) {
   808         DeferredChecker dc = new DeferredChecker(env);
   809         dc.scan(expr);
   810         return dc.result.isPoly();
   811     }
   813     /**
   814      * The kind of an argument expression. This is used by the analysis that
   815      * determines as to whether speculative attribution is necessary.
   816      */
   817     enum ArgumentExpressionKind {
   819         /** kind that denotes poly argument expression */
   820         POLY,
   821         /** kind that denotes a standalone expression */
   822         NO_POLY,
   823         /** kind that denotes a primitive/boxed standalone expression */
   824         PRIMITIVE;
   826         /**
   827          * Does this kind denote a poly argument expression
   828          */
   829         public final boolean isPoly() {
   830             return this == POLY;
   831         }
   833         /**
   834          * Does this kind denote a primitive standalone expression
   835          */
   836         public final boolean isPrimitive() {
   837             return this == PRIMITIVE;
   838         }
   840         /**
   841          * Compute the kind of a standalone expression of a given type
   842          */
   843         static ArgumentExpressionKind standaloneKind(Type type, Types types) {
   844             return types.unboxedTypeOrType(type).isPrimitive() ?
   845                     ArgumentExpressionKind.PRIMITIVE :
   846                     ArgumentExpressionKind.NO_POLY;
   847         }
   849         /**
   850          * Compute the kind of a method argument expression given its symbol
   851          */
   852         static ArgumentExpressionKind methodKind(Symbol sym, Types types) {
   853             Type restype = sym.type.getReturnType();
   854             if (sym.type.hasTag(FORALL) &&
   855                     restype.containsAny(((ForAll)sym.type).tvars)) {
   856                 return ArgumentExpressionKind.POLY;
   857             } else {
   858                 return ArgumentExpressionKind.standaloneKind(restype, types);
   859             }
   860         }
   861     }
   863     /**
   864      * Tree scanner used for checking as to whether an argument expression
   865      * requires speculative attribution
   866      */
   867     final class DeferredChecker extends FilterScanner {
   869         Env<AttrContext> env;
   870         ArgumentExpressionKind result;
   872         public DeferredChecker(Env<AttrContext> env) {
   873             super(deferredCheckerTags);
   874             this.env = env;
   875         }
   877         @Override
   878         public void visitLambda(JCLambda tree) {
   879             //a lambda is always a poly expression
   880             result = ArgumentExpressionKind.POLY;
   881         }
   883         @Override
   884         public void visitReference(JCMemberReference tree) {
   885             //a method reference is always a poly expression
   886             result = ArgumentExpressionKind.POLY;
   887         }
   889         @Override
   890         public void visitTypeCast(JCTypeCast tree) {
   891             //a cast is always a standalone expression
   892             result = ArgumentExpressionKind.NO_POLY;
   893         }
   895         @Override
   896         public void visitConditional(JCConditional tree) {
   897             scan(tree.truepart);
   898             if (!result.isPrimitive()) {
   899                 result = ArgumentExpressionKind.POLY;
   900                 return;
   901             }
   902             scan(tree.falsepart);
   903             result = reduce(ArgumentExpressionKind.PRIMITIVE);
   904         }
   906         @Override
   907         public void visitNewClass(JCNewClass tree) {
   908             result = (TreeInfo.isDiamond(tree) || attr.findDiamonds) ?
   909                     ArgumentExpressionKind.POLY : ArgumentExpressionKind.NO_POLY;
   910         }
   912         @Override
   913         public void visitApply(JCMethodInvocation tree) {
   914             Name name = TreeInfo.name(tree.meth);
   916             //fast path
   917             if (tree.typeargs.nonEmpty() ||
   918                     name == name.table.names._this ||
   919                     name == name.table.names._super) {
   920                 result = ArgumentExpressionKind.NO_POLY;
   921                 return;
   922             }
   924             //slow path
   925             final JCExpression rec = tree.meth.hasTag(SELECT) ?
   926                     ((JCFieldAccess)tree.meth).selected :
   927                     null;
   929             if (rec != null && !isSimpleReceiver(rec)) {
   930                 //give up if receiver is too complex (to cut down analysis time)
   931                 result = ArgumentExpressionKind.POLY;
   932                 return;
   933             }
   935             Type site = rec != null ?
   936                     attribSpeculative(rec, env, attr.unknownTypeExprInfo).type :
   937                     env.enclClass.sym.type;
   939             ListBuffer<Type> args = ListBuffer.lb();
   940             for (int i = 0; i < tree.args.length(); i ++) {
   941                 args.append(Type.noType);
   942             }
   944             Resolve.LookupHelper lh = rs.new LookupHelper(name, site, args.toList(), List.<Type>nil(), MethodResolutionPhase.VARARITY) {
   945                 @Override
   946                 Symbol lookup(Env<AttrContext> env, MethodResolutionPhase phase) {
   947                     return rec == null ?
   948                         rs.findFun(env, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()) :
   949                         rs.findMethod(env, site, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired(), false);
   950                 }
   951                 @Override
   952                 Symbol access(Env<AttrContext> env, DiagnosticPosition pos, Symbol location, Symbol sym) {
   953                     return sym;
   954                 }
   955             };
   957             Symbol sym = rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh);
   959             if (sym.kind == Kinds.AMBIGUOUS) {
   960                 Resolve.AmbiguityError err = (Resolve.AmbiguityError)sym.baseSymbol();
   961                 result = ArgumentExpressionKind.PRIMITIVE;
   962                 for (List<Symbol> ambigousSyms = err.ambiguousSyms ;
   963                         ambigousSyms.nonEmpty() && !result.isPoly() ;
   964                         ambigousSyms = ambigousSyms.tail) {
   965                     Symbol s = ambigousSyms.head;
   966                     if (s.kind == Kinds.MTH) {
   967                         result = reduce(ArgumentExpressionKind.methodKind(s, types));
   968                     }
   969                 }
   970             } else {
   971                 result = (sym.kind == Kinds.MTH) ?
   972                     ArgumentExpressionKind.methodKind(sym, types) :
   973                     ArgumentExpressionKind.NO_POLY;
   974             }
   975         }
   976         //where
   977             private boolean isSimpleReceiver(JCTree rec) {
   978                 switch (rec.getTag()) {
   979                     case IDENT:
   980                         return true;
   981                     case SELECT:
   982                         return isSimpleReceiver(((JCFieldAccess)rec).selected);
   983                     case TYPEAPPLY:
   984                     case TYPEARRAY:
   985                         return true;
   986                     case ANNOTATED_TYPE:
   987                         return isSimpleReceiver(((JCAnnotatedType)rec).underlyingType);
   988                     default:
   989                         return false;
   990                 }
   991             }
   992             private ArgumentExpressionKind reduce(ArgumentExpressionKind kind) {
   993                 switch (result) {
   994                     case PRIMITIVE: return kind;
   995                     case NO_POLY: return kind.isPoly() ? kind : result;
   996                     case POLY: return result;
   997                     default:
   998                         Assert.error();
   999                         return null;
  1003         @Override
  1004         public void visitLiteral(JCLiteral tree) {
  1005             Type litType = attr.litType(tree.typetag);
  1006             result = ArgumentExpressionKind.standaloneKind(litType, types);
  1009         @Override
  1010         void skip(JCTree tree) {
  1011             result = ArgumentExpressionKind.NO_POLY;
  1014     //where
  1015     private EnumSet<JCTree.Tag> deferredCheckerTags =
  1016             EnumSet.of(LAMBDA, REFERENCE, PARENS, TYPECAST,
  1017                     CONDEXPR, NEWCLASS, APPLY, LITERAL);

mercurial