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

Fri, 05 Jul 2013 11:02:17 +0100

author
mcimadamore
date
Fri, 05 Jul 2013 11:02:17 +0100
changeset 1888
70b37cdb19d5
parent 1853
831467c4c6a7
child 1889
b0386f0dc28e
permissions
-rw-r--r--

8019480: Javac crashes when method is called on a type-variable receiver from lambda expression
Summary: Logic for shortcircuiting speculative attribution doesn't handle type-variable receivers
Reviewed-by: jjg, vromero

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

mercurial