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

Tue, 09 Oct 2012 19:10:00 -0700

author
jjg
date
Tue, 09 Oct 2012 19:10:00 -0700
changeset 1357
c75be5bc5283
parent 1348
573ceb23beeb
child 1374
c002fdee76fd
permissions
-rw-r--r--

8000663: clean up langtools imports
Reviewed-by: darcy

mcimadamore@1347 1 /*
mcimadamore@1347 2 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
mcimadamore@1347 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
mcimadamore@1347 4 *
mcimadamore@1347 5 * This code is free software; you can redistribute it and/or modify it
mcimadamore@1347 6 * under the terms of the GNU General Public License version 2 only, as
mcimadamore@1347 7 * published by the Free Software Foundation. Oracle designates this
mcimadamore@1347 8 * particular file as subject to the "Classpath" exception as provided
mcimadamore@1347 9 * by Oracle in the LICENSE file that accompanied this code.
mcimadamore@1347 10 *
mcimadamore@1347 11 * This code is distributed in the hope that it will be useful, but WITHOUT
mcimadamore@1347 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
mcimadamore@1347 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
mcimadamore@1347 14 * version 2 for more details (a copy is included in the LICENSE file that
mcimadamore@1347 15 * accompanied this code).
mcimadamore@1347 16 *
mcimadamore@1347 17 * You should have received a copy of the GNU General Public License version
mcimadamore@1347 18 * 2 along with this work; if not, write to the Free Software Foundation,
mcimadamore@1347 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
mcimadamore@1347 20 *
mcimadamore@1347 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
mcimadamore@1347 22 * or visit www.oracle.com if you need additional information or have any
mcimadamore@1347 23 * questions.
mcimadamore@1347 24 */
mcimadamore@1347 25
mcimadamore@1347 26 package com.sun.tools.javac.comp;
mcimadamore@1347 27
mcimadamore@1347 28 import com.sun.tools.javac.code.*;
mcimadamore@1347 29 import com.sun.tools.javac.tree.*;
mcimadamore@1347 30 import com.sun.tools.javac.util.*;
mcimadamore@1347 31 import com.sun.tools.javac.code.Symbol.*;
mcimadamore@1347 32 import com.sun.tools.javac.code.Type.*;
mcimadamore@1347 33 import com.sun.tools.javac.comp.Attr.ResultInfo;
mcimadamore@1347 34 import com.sun.tools.javac.comp.Infer.InferenceContext;
mcimadamore@1347 35 import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase;
mcimadamore@1347 36 import com.sun.tools.javac.tree.JCTree.*;
mcimadamore@1347 37
mcimadamore@1347 38 import javax.tools.JavaFileObject;
mcimadamore@1347 39
mcimadamore@1347 40 import java.util.ArrayList;
mcimadamore@1347 41 import java.util.HashSet;
mcimadamore@1347 42 import java.util.Map;
mcimadamore@1347 43 import java.util.Queue;
mcimadamore@1347 44 import java.util.Set;
mcimadamore@1347 45 import java.util.WeakHashMap;
mcimadamore@1347 46
mcimadamore@1347 47 import static com.sun.tools.javac.code.TypeTags.*;
mcimadamore@1347 48 import static com.sun.tools.javac.tree.JCTree.Tag.*;
mcimadamore@1347 49
mcimadamore@1347 50 /**
mcimadamore@1347 51 * This is an helper class that is used to perform deferred type-analysis.
mcimadamore@1347 52 * Each time a poly expression occurs in argument position, javac attributes it
mcimadamore@1347 53 * with a temporary 'deferred type' that is checked (possibly multiple times)
mcimadamore@1347 54 * against an expected formal type.
mcimadamore@1347 55 *
mcimadamore@1347 56 * <p><b>This is NOT part of any supported API.
mcimadamore@1347 57 * If you write code that depends on this, you do so at your own risk.
mcimadamore@1347 58 * This code and its internal interfaces are subject to change or
mcimadamore@1347 59 * deletion without notice.</b>
mcimadamore@1347 60 */
mcimadamore@1347 61 public class DeferredAttr extends JCTree.Visitor {
mcimadamore@1347 62 protected static final Context.Key<DeferredAttr> deferredAttrKey =
mcimadamore@1347 63 new Context.Key<DeferredAttr>();
mcimadamore@1347 64
mcimadamore@1347 65 final Attr attr;
mcimadamore@1347 66 final Check chk;
mcimadamore@1347 67 final Enter enter;
mcimadamore@1347 68 final Infer infer;
mcimadamore@1347 69 final Log log;
mcimadamore@1347 70 final Symtab syms;
mcimadamore@1347 71 final TreeMaker make;
mcimadamore@1347 72 final Types types;
mcimadamore@1347 73
mcimadamore@1347 74 public static DeferredAttr instance(Context context) {
mcimadamore@1347 75 DeferredAttr instance = context.get(deferredAttrKey);
mcimadamore@1347 76 if (instance == null)
mcimadamore@1347 77 instance = new DeferredAttr(context);
mcimadamore@1347 78 return instance;
mcimadamore@1347 79 }
mcimadamore@1347 80
mcimadamore@1347 81 protected DeferredAttr(Context context) {
mcimadamore@1347 82 context.put(deferredAttrKey, this);
mcimadamore@1347 83 attr = Attr.instance(context);
mcimadamore@1347 84 chk = Check.instance(context);
mcimadamore@1347 85 enter = Enter.instance(context);
mcimadamore@1347 86 infer = Infer.instance(context);
mcimadamore@1347 87 log = Log.instance(context);
mcimadamore@1347 88 syms = Symtab.instance(context);
mcimadamore@1347 89 make = TreeMaker.instance(context);
mcimadamore@1347 90 types = Types.instance(context);
mcimadamore@1347 91 }
mcimadamore@1347 92
mcimadamore@1347 93 /**
mcimadamore@1347 94 * This type represents a deferred type. A deferred type starts off with
mcimadamore@1347 95 * no information on the underlying expression type. Such info needs to be
mcimadamore@1347 96 * discovered through type-checking the deferred type against a target-type.
mcimadamore@1347 97 * Every deferred type keeps a pointer to the AST node from which it originated.
mcimadamore@1347 98 */
mcimadamore@1347 99 public class DeferredType extends Type {
mcimadamore@1347 100
mcimadamore@1347 101 public JCExpression tree;
mcimadamore@1347 102 Env<AttrContext> env;
mcimadamore@1347 103 AttrMode mode;
mcimadamore@1347 104 SpeculativeCache speculativeCache;
mcimadamore@1347 105
mcimadamore@1347 106 DeferredType(JCExpression tree, Env<AttrContext> env) {
mcimadamore@1347 107 super(DEFERRED, null);
mcimadamore@1347 108 this.tree = tree;
mcimadamore@1347 109 this.env = env.dup(tree, env.info.dup());
mcimadamore@1347 110 this.speculativeCache = new SpeculativeCache();
mcimadamore@1347 111 }
mcimadamore@1347 112
mcimadamore@1347 113 /**
mcimadamore@1347 114 * A speculative cache is used to keep track of all overload resolution rounds
mcimadamore@1347 115 * that triggered speculative attribution on a given deferred type. Each entry
mcimadamore@1347 116 * stores a pointer to the speculative tree and the resolution phase in which the entry
mcimadamore@1347 117 * has been added.
mcimadamore@1347 118 */
mcimadamore@1347 119 class SpeculativeCache {
mcimadamore@1347 120
mcimadamore@1347 121 private Map<Symbol, List<Entry>> cache =
mcimadamore@1347 122 new WeakHashMap<Symbol, List<Entry>>();
mcimadamore@1347 123
mcimadamore@1347 124 class Entry {
mcimadamore@1347 125 JCTree speculativeTree;
mcimadamore@1347 126 Resolve.MethodResolutionPhase phase;
mcimadamore@1347 127
mcimadamore@1347 128 public Entry(JCTree speculativeTree, MethodResolutionPhase phase) {
mcimadamore@1347 129 this.speculativeTree = speculativeTree;
mcimadamore@1347 130 this.phase = phase;
mcimadamore@1347 131 }
mcimadamore@1347 132
mcimadamore@1347 133 boolean matches(Resolve.MethodResolutionPhase phase) {
mcimadamore@1347 134 return this.phase == phase;
mcimadamore@1347 135 }
mcimadamore@1347 136 }
mcimadamore@1347 137
mcimadamore@1347 138 /**
mcimadamore@1347 139 * Clone a speculative cache entry as a fresh entry associated
mcimadamore@1347 140 * with a new method (this maybe required to fixup speculative cache
mcimadamore@1347 141 * misses after Resolve.access())
mcimadamore@1347 142 */
mcimadamore@1347 143 void dupAllTo(Symbol from, Symbol to) {
mcimadamore@1347 144 Assert.check(cache.get(to) == null);
mcimadamore@1347 145 List<Entry> entries = cache.get(from);
mcimadamore@1347 146 if (entries != null) {
mcimadamore@1347 147 cache.put(to, entries);
mcimadamore@1347 148 }
mcimadamore@1347 149 }
mcimadamore@1347 150
mcimadamore@1347 151 /**
mcimadamore@1347 152 * Retrieve a speculative cache entry corresponding to given symbol
mcimadamore@1347 153 * and resolution phase
mcimadamore@1347 154 */
mcimadamore@1347 155 Entry get(Symbol msym, MethodResolutionPhase phase) {
mcimadamore@1347 156 List<Entry> entries = cache.get(msym);
mcimadamore@1347 157 if (entries == null) return null;
mcimadamore@1347 158 for (Entry e : entries) {
mcimadamore@1347 159 if (e.matches(phase)) return e;
mcimadamore@1347 160 }
mcimadamore@1347 161 return null;
mcimadamore@1347 162 }
mcimadamore@1347 163
mcimadamore@1347 164 /**
mcimadamore@1347 165 * Stores a speculative cache entry corresponding to given symbol
mcimadamore@1347 166 * and resolution phase
mcimadamore@1347 167 */
mcimadamore@1347 168 void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
mcimadamore@1347 169 List<Entry> entries = cache.get(msym);
mcimadamore@1347 170 if (entries == null) {
mcimadamore@1347 171 entries = List.nil();
mcimadamore@1347 172 }
mcimadamore@1347 173 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
mcimadamore@1347 174 }
mcimadamore@1347 175 }
mcimadamore@1347 176
mcimadamore@1347 177 /**
mcimadamore@1347 178 * Get the type that has been computed during a speculative attribution round
mcimadamore@1347 179 */
mcimadamore@1347 180 Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
mcimadamore@1347 181 SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
mcimadamore@1347 182 return e != null ? e.speculativeTree.type : Type.noType;
mcimadamore@1347 183 }
mcimadamore@1347 184
mcimadamore@1347 185 /**
mcimadamore@1347 186 * Check a deferred type against a potential target-type. Depending on
mcimadamore@1347 187 * the current attribution mode, a normal vs. speculative attribution
mcimadamore@1347 188 * round is performed on the underlying AST node. There can be only one
mcimadamore@1347 189 * speculative round for a given target method symbol; moreover, a normal
mcimadamore@1347 190 * attribution round must follow one or more speculative rounds.
mcimadamore@1347 191 */
mcimadamore@1347 192 Type check(ResultInfo resultInfo) {
mcimadamore@1347 193 DeferredAttrContext deferredAttrContext =
mcimadamore@1347 194 resultInfo.checkContext.deferredAttrContext();
mcimadamore@1347 195 Assert.check(deferredAttrContext != emptyDeferredAttrContext);
mcimadamore@1347 196 List<Type> stuckVars = stuckVars(tree, resultInfo);
mcimadamore@1347 197 if (stuckVars.nonEmpty()) {
mcimadamore@1347 198 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
mcimadamore@1347 199 return Type.noType;
mcimadamore@1347 200 } else {
mcimadamore@1347 201 try {
mcimadamore@1347 202 switch (deferredAttrContext.mode) {
mcimadamore@1347 203 case SPECULATIVE:
mcimadamore@1347 204 Assert.check(mode == null ||
mcimadamore@1347 205 (mode == AttrMode.SPECULATIVE &&
mcimadamore@1347 206 speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).tag == NONE));
mcimadamore@1347 207 JCTree speculativeTree = attribSpeculative(tree, env, resultInfo);
mcimadamore@1347 208 speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
mcimadamore@1347 209 return speculativeTree.type;
mcimadamore@1347 210 case CHECK:
mcimadamore@1347 211 Assert.check(mode == AttrMode.SPECULATIVE);
mcimadamore@1347 212 return attr.attribTree(tree, env, resultInfo);
mcimadamore@1347 213 }
mcimadamore@1347 214 Assert.error();
mcimadamore@1347 215 return null;
mcimadamore@1347 216 } finally {
mcimadamore@1347 217 mode = deferredAttrContext.mode;
mcimadamore@1347 218 }
mcimadamore@1347 219 }
mcimadamore@1347 220 }
mcimadamore@1347 221 }
mcimadamore@1347 222
mcimadamore@1347 223 /**
mcimadamore@1347 224 * The 'mode' in which the deferred type is to be type-checked
mcimadamore@1347 225 */
mcimadamore@1347 226 public enum AttrMode {
mcimadamore@1347 227 /**
mcimadamore@1347 228 * A speculative type-checking round is used during overload resolution
mcimadamore@1347 229 * mainly to generate constraints on inference variables. Side-effects
mcimadamore@1347 230 * arising from type-checking the expression associated with the deferred
mcimadamore@1347 231 * type are reversed after the speculative round finishes. This means the
mcimadamore@1347 232 * expression tree will be left in a blank state.
mcimadamore@1347 233 */
mcimadamore@1347 234 SPECULATIVE,
mcimadamore@1347 235 /**
mcimadamore@1347 236 * This is the plain type-checking mode. Produces side-effects on the underlying AST node
mcimadamore@1347 237 */
mcimadamore@1347 238 CHECK;
mcimadamore@1347 239 }
mcimadamore@1347 240
mcimadamore@1347 241 /**
mcimadamore@1347 242 * Routine that performs speculative type-checking; the input AST node is
mcimadamore@1347 243 * cloned (to avoid side-effects cause by Attr) and compiler state is
mcimadamore@1347 244 * restored after type-checking. All diagnostics (but critical ones) are
mcimadamore@1347 245 * disabled during speculative type-checking.
mcimadamore@1347 246 */
mcimadamore@1347 247 JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
mcimadamore@1347 248 JCTree newTree = new TreeCopier<Object>(make).copy(tree);
mcimadamore@1347 249 Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
mcimadamore@1347 250 speculativeEnv.info.scope.owner = env.info.scope.owner;
mcimadamore@1347 251 Filter<JCDiagnostic> prevDeferDiagsFilter = log.deferredDiagFilter;
mcimadamore@1347 252 Queue<JCDiagnostic> prevDeferredDiags = log.deferredDiagnostics;
mcimadamore@1347 253 final JavaFileObject currentSource = log.currentSourceFile();
mcimadamore@1347 254 try {
mcimadamore@1347 255 log.deferredDiagnostics = new ListBuffer<JCDiagnostic>();
mcimadamore@1347 256 log.deferredDiagFilter = new Filter<JCDiagnostic>() {
mcimadamore@1347 257 public boolean accepts(JCDiagnostic t) {
mcimadamore@1347 258 return t.getDiagnosticSource().getFile().equals(currentSource);
mcimadamore@1347 259 }
mcimadamore@1347 260 };
mcimadamore@1347 261 attr.attribTree(newTree, speculativeEnv, resultInfo);
mcimadamore@1347 262 unenterScanner.scan(newTree);
mcimadamore@1347 263 return newTree;
mcimadamore@1347 264 } catch (Abort ex) {
mcimadamore@1347 265 //if some very bad condition occurred during deferred attribution
mcimadamore@1347 266 //we should dump all errors before killing javac
mcimadamore@1347 267 log.reportDeferredDiagnostics();
mcimadamore@1347 268 throw ex;
mcimadamore@1347 269 } finally {
mcimadamore@1347 270 unenterScanner.scan(newTree);
mcimadamore@1347 271 log.deferredDiagFilter = prevDeferDiagsFilter;
mcimadamore@1347 272 log.deferredDiagnostics = prevDeferredDiags;
mcimadamore@1347 273 }
mcimadamore@1347 274 }
mcimadamore@1347 275 //where
mcimadamore@1347 276 protected TreeScanner unenterScanner = new TreeScanner() {
mcimadamore@1347 277 @Override
mcimadamore@1347 278 public void visitClassDef(JCClassDecl tree) {
mcimadamore@1347 279 ClassSymbol csym = tree.sym;
mcimadamore@1347 280 enter.typeEnvs.remove(csym);
mcimadamore@1347 281 chk.compiled.remove(csym.flatname);
mcimadamore@1347 282 syms.classes.remove(csym.flatname);
mcimadamore@1347 283 super.visitClassDef(tree);
mcimadamore@1347 284 }
mcimadamore@1347 285 };
mcimadamore@1347 286
mcimadamore@1347 287 /**
mcimadamore@1347 288 * A deferred context is created on each method check. A deferred context is
mcimadamore@1347 289 * used to keep track of information associated with the method check, such as
mcimadamore@1347 290 * the symbol of the method being checked, the overload resolution phase,
mcimadamore@1347 291 * the kind of attribution mode to be applied to deferred types and so forth.
mcimadamore@1347 292 * As deferred types are processed (by the method check routine) stuck AST nodes
mcimadamore@1347 293 * are added (as new deferred attribution nodes) to this context. The complete()
mcimadamore@1347 294 * routine makes sure that all pending nodes are properly processed, by
mcimadamore@1347 295 * progressively instantiating all inference variables on which one or more
mcimadamore@1347 296 * deferred attribution node is stuck.
mcimadamore@1347 297 */
mcimadamore@1347 298 class DeferredAttrContext {
mcimadamore@1347 299
mcimadamore@1347 300 /** attribution mode */
mcimadamore@1347 301 final AttrMode mode;
mcimadamore@1347 302
mcimadamore@1347 303 /** symbol of the method being checked */
mcimadamore@1347 304 final Symbol msym;
mcimadamore@1347 305
mcimadamore@1347 306 /** method resolution step */
mcimadamore@1347 307 final Resolve.MethodResolutionPhase phase;
mcimadamore@1347 308
mcimadamore@1347 309 /** inference context */
mcimadamore@1347 310 final InferenceContext inferenceContext;
mcimadamore@1347 311
mcimadamore@1347 312 /** list of deferred attribution nodes to be processed */
mcimadamore@1347 313 ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
mcimadamore@1347 314
mcimadamore@1347 315 DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, InferenceContext inferenceContext) {
mcimadamore@1347 316 this.mode = mode;
mcimadamore@1347 317 this.msym = msym;
mcimadamore@1347 318 this.phase = phase;
mcimadamore@1347 319 this.inferenceContext = inferenceContext;
mcimadamore@1347 320 }
mcimadamore@1347 321
mcimadamore@1347 322 /**
mcimadamore@1347 323 * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
mcimadamore@1347 324 * Nodes added this way act as 'roots' for the out-of-order method checking process.
mcimadamore@1347 325 */
mcimadamore@1347 326 void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
mcimadamore@1347 327 deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
mcimadamore@1347 328 }
mcimadamore@1347 329
mcimadamore@1347 330 /**
mcimadamore@1347 331 * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
mcimadamore@1347 332 * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
mcimadamore@1347 333 * some inference variable might get eagerly instantiated so that all nodes
mcimadamore@1347 334 * can be type-checked.
mcimadamore@1347 335 */
mcimadamore@1347 336 void complete() {
mcimadamore@1347 337 while (!deferredAttrNodes.isEmpty()) {
mcimadamore@1347 338 Set<Type> stuckVars = new HashSet<Type>();
mcimadamore@1347 339 boolean progress = false;
mcimadamore@1347 340 //scan a defensive copy of the node list - this is because a deferred
mcimadamore@1347 341 //attribution round can add new nodes to the list
mcimadamore@1347 342 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
mcimadamore@1347 343 if (!deferredAttrNode.isStuck()) {
mcimadamore@1347 344 deferredAttrNode.process();
mcimadamore@1347 345 deferredAttrNodes.remove(deferredAttrNode);
mcimadamore@1347 346 progress = true;
mcimadamore@1347 347 } else {
mcimadamore@1347 348 stuckVars.addAll(deferredAttrNode.stuckVars);
mcimadamore@1347 349 }
mcimadamore@1347 350 }
mcimadamore@1347 351 if (!progress) {
mcimadamore@1347 352 //remove all variables that have already been instantiated
mcimadamore@1347 353 //from the list of stuck variables
mcimadamore@1347 354 inferenceContext.solveAny(inferenceContext.freeVarsIn(List.from(stuckVars)), types, infer);
mcimadamore@1347 355 inferenceContext.notifyChange(types);
mcimadamore@1347 356 }
mcimadamore@1347 357 }
mcimadamore@1347 358 }
mcimadamore@1347 359
mcimadamore@1347 360 /**
mcimadamore@1347 361 * Class representing a deferred attribution node. It keeps track of
mcimadamore@1347 362 * a deferred type, along with the expected target type information.
mcimadamore@1347 363 */
mcimadamore@1347 364 class DeferredAttrNode implements Infer.InferenceContext.FreeTypeListener {
mcimadamore@1347 365
mcimadamore@1347 366 /** underlying deferred type */
mcimadamore@1347 367 DeferredType dt;
mcimadamore@1347 368
mcimadamore@1347 369 /** underlying target type information */
mcimadamore@1347 370 ResultInfo resultInfo;
mcimadamore@1347 371
mcimadamore@1347 372 /** list of uninferred inference variables causing this node to be stuck */
mcimadamore@1347 373 List<Type> stuckVars;
mcimadamore@1347 374
mcimadamore@1347 375 DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
mcimadamore@1347 376 this.dt = dt;
mcimadamore@1347 377 this.resultInfo = resultInfo;
mcimadamore@1347 378 this.stuckVars = stuckVars;
mcimadamore@1347 379 if (!stuckVars.isEmpty()) {
mcimadamore@1347 380 resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
mcimadamore@1347 381 }
mcimadamore@1347 382 }
mcimadamore@1347 383
mcimadamore@1347 384 @Override
mcimadamore@1347 385 public void typesInferred(InferenceContext inferenceContext) {
mcimadamore@1347 386 stuckVars = List.nil();
mcimadamore@1347 387 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types));
mcimadamore@1347 388 }
mcimadamore@1347 389
mcimadamore@1347 390 /**
mcimadamore@1347 391 * is this node stuck?
mcimadamore@1347 392 */
mcimadamore@1347 393 boolean isStuck() {
mcimadamore@1347 394 return stuckVars.nonEmpty();
mcimadamore@1347 395 }
mcimadamore@1347 396
mcimadamore@1347 397 /**
mcimadamore@1347 398 * Process a deferred attribution node.
mcimadamore@1347 399 * Invariant: a stuck node cannot be processed.
mcimadamore@1347 400 */
mcimadamore@1347 401 void process() {
mcimadamore@1347 402 if (isStuck()) {
mcimadamore@1347 403 throw new IllegalStateException("Cannot process a stuck deferred node");
mcimadamore@1347 404 }
mcimadamore@1347 405 dt.check(resultInfo);
mcimadamore@1347 406 }
mcimadamore@1347 407 }
mcimadamore@1347 408 }
mcimadamore@1347 409
mcimadamore@1347 410 /** an empty deferred attribution context - all methods throw exceptions */
mcimadamore@1347 411 final DeferredAttrContext emptyDeferredAttrContext =
mcimadamore@1347 412 new DeferredAttrContext(null, null, null, null) {
mcimadamore@1347 413 @Override
mcimadamore@1347 414 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
mcimadamore@1347 415 Assert.error("Empty deferred context!");
mcimadamore@1347 416 }
mcimadamore@1347 417 @Override
mcimadamore@1347 418 void complete() {
mcimadamore@1347 419 Assert.error("Empty deferred context!");
mcimadamore@1347 420 }
mcimadamore@1347 421 };
mcimadamore@1347 422
mcimadamore@1347 423 /**
mcimadamore@1347 424 * Map a list of types possibly containing one or more deferred types
mcimadamore@1347 425 * into a list of ordinary types. Each deferred type D is mapped into a type T,
mcimadamore@1347 426 * where T is computed by retrieving the type that has already been
mcimadamore@1347 427 * computed for D during a previous deferred attribution round of the given kind.
mcimadamore@1347 428 */
mcimadamore@1347 429 class DeferredTypeMap extends Type.Mapping {
mcimadamore@1347 430
mcimadamore@1347 431 DeferredAttrContext deferredAttrContext;
mcimadamore@1347 432
mcimadamore@1347 433 protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
mcimadamore@1347 434 super(String.format("deferredTypeMap[%s]", mode));
mcimadamore@1347 435 this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, infer.emptyContext);
mcimadamore@1347 436 }
mcimadamore@1347 437
mcimadamore@1347 438 protected boolean validState(DeferredType dt) {
mcimadamore@1347 439 return dt.mode != null &&
mcimadamore@1347 440 deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
mcimadamore@1347 441 }
mcimadamore@1347 442
mcimadamore@1347 443 @Override
mcimadamore@1347 444 public Type apply(Type t) {
mcimadamore@1347 445 if (t.tag != DEFERRED) {
mcimadamore@1347 446 return t.map(this);
mcimadamore@1347 447 } else {
mcimadamore@1347 448 DeferredType dt = (DeferredType)t;
mcimadamore@1347 449 Assert.check(validState(dt));
mcimadamore@1347 450 return typeOf(dt);
mcimadamore@1347 451 }
mcimadamore@1347 452 }
mcimadamore@1347 453
mcimadamore@1347 454 protected Type typeOf(DeferredType dt) {
mcimadamore@1347 455 switch (deferredAttrContext.mode) {
mcimadamore@1347 456 case CHECK:
mcimadamore@1347 457 return dt.tree.type == null ? Type.noType : dt.tree.type;
mcimadamore@1347 458 case SPECULATIVE:
mcimadamore@1347 459 return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
mcimadamore@1347 460 }
mcimadamore@1347 461 Assert.error();
mcimadamore@1347 462 return null;
mcimadamore@1347 463 }
mcimadamore@1347 464 }
mcimadamore@1347 465
mcimadamore@1347 466 /**
mcimadamore@1347 467 * Specialized recovery deferred mapping.
mcimadamore@1347 468 * Each deferred type D is mapped into a type T, where T is computed either by
mcimadamore@1347 469 * (i) retrieving the type that has already been computed for D during a previous
mcimadamore@1347 470 * attribution round (as before), or (ii) by synthesizing a new type R for D
mcimadamore@1347 471 * (the latter step is useful in a recovery scenario).
mcimadamore@1347 472 */
mcimadamore@1347 473 public class RecoveryDeferredTypeMap extends DeferredTypeMap {
mcimadamore@1347 474
mcimadamore@1347 475 public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
mcimadamore@1347 476 super(mode, msym, phase);
mcimadamore@1347 477 }
mcimadamore@1347 478
mcimadamore@1347 479 @Override
mcimadamore@1347 480 protected Type typeOf(DeferredType dt) {
mcimadamore@1347 481 Type owntype = super.typeOf(dt);
mcimadamore@1347 482 return owntype.tag == NONE ?
mcimadamore@1347 483 recover(dt) : owntype;
mcimadamore@1347 484 }
mcimadamore@1347 485
mcimadamore@1347 486 @Override
mcimadamore@1347 487 protected boolean validState(DeferredType dt) {
mcimadamore@1347 488 return true;
mcimadamore@1347 489 }
mcimadamore@1347 490
mcimadamore@1347 491 /**
mcimadamore@1347 492 * Synthesize a type for a deferred type that hasn't been previously
mcimadamore@1347 493 * reduced to an ordinary type. Functional deferred types and conditionals
mcimadamore@1347 494 * are mapped to themselves, in order to have a richer diagnostic
mcimadamore@1347 495 * representation. Remaining deferred types are attributed using
mcimadamore@1347 496 * a default expected type (j.l.Object).
mcimadamore@1347 497 */
mcimadamore@1347 498 private Type recover(DeferredType dt) {
mcimadamore@1348 499 dt.check(attr.new RecoveryInfo(deferredAttrContext));
mcimadamore@1347 500 switch (TreeInfo.skipParens(dt.tree).getTag()) {
mcimadamore@1347 501 case LAMBDA:
mcimadamore@1347 502 case REFERENCE:
mcimadamore@1347 503 case CONDEXPR:
mcimadamore@1347 504 //propagate those deferred types to the
mcimadamore@1347 505 //diagnostic formatter
mcimadamore@1347 506 return dt;
mcimadamore@1347 507 default:
mcimadamore@1347 508 return super.apply(dt);
mcimadamore@1347 509 }
mcimadamore@1347 510 }
mcimadamore@1347 511 }
mcimadamore@1347 512
mcimadamore@1347 513 /**
mcimadamore@1347 514 * Retrieves the list of inference variables that need to be inferred before
mcimadamore@1347 515 * an AST node can be type-checked
mcimadamore@1347 516 */
mcimadamore@1347 517 @SuppressWarnings("fallthrough")
mcimadamore@1348 518 List<Type> stuckVars(JCTree tree, ResultInfo resultInfo) {
mcimadamore@1348 519 if (resultInfo.pt.tag == NONE || resultInfo.pt.isErroneous()) {
mcimadamore@1348 520 return List.nil();
mcimadamore@1348 521 } else {
mcimadamore@1348 522 StuckChecker sc = new StuckChecker(resultInfo);
mcimadamore@1348 523 sc.scan(tree);
mcimadamore@1348 524 return List.from(sc.stuckVars);
mcimadamore@1348 525 }
mcimadamore@1348 526 }
mcimadamore@1348 527
mcimadamore@1348 528 /**
mcimadamore@1348 529 * This visitor is used to check that structural expressions conform
mcimadamore@1348 530 * to their target - this step is required as inference could end up
mcimadamore@1348 531 * inferring types that make some of the nested expressions incompatible
mcimadamore@1348 532 * with their corresponding instantiated target
mcimadamore@1348 533 */
mcimadamore@1348 534 class StuckChecker extends TreeScanner {
mcimadamore@1348 535
mcimadamore@1348 536 Type pt;
mcimadamore@1348 537 Filter<JCTree> treeFilter;
mcimadamore@1348 538 Infer.InferenceContext inferenceContext;
mcimadamore@1348 539 Set<Type> stuckVars = new HashSet<Type>();
mcimadamore@1348 540
mcimadamore@1348 541 final Filter<JCTree> argsFilter = new Filter<JCTree>() {
mcimadamore@1348 542 public boolean accepts(JCTree t) {
mcimadamore@1348 543 switch (t.getTag()) {
mcimadamore@1348 544 case CONDEXPR:
mcimadamore@1348 545 case LAMBDA:
mcimadamore@1348 546 case PARENS:
mcimadamore@1348 547 case REFERENCE:
mcimadamore@1348 548 return true;
mcimadamore@1348 549 default:
mcimadamore@1348 550 return false;
mcimadamore@1348 551 }
mcimadamore@1348 552 }
mcimadamore@1348 553 };
mcimadamore@1348 554
mcimadamore@1348 555 final Filter<JCTree> lambdaBodyFilter = new Filter<JCTree>() {
mcimadamore@1348 556 public boolean accepts(JCTree t) {
mcimadamore@1348 557 switch (t.getTag()) {
mcimadamore@1348 558 case BLOCK: case CASE: case CATCH: case DOLOOP:
mcimadamore@1348 559 case FOREACHLOOP: case FORLOOP: case RETURN:
mcimadamore@1348 560 case SYNCHRONIZED: case SWITCH: case TRY: case WHILELOOP:
mcimadamore@1348 561 return true;
mcimadamore@1348 562 default:
mcimadamore@1348 563 return false;
mcimadamore@1348 564 }
mcimadamore@1348 565 }
mcimadamore@1348 566 };
mcimadamore@1348 567
mcimadamore@1348 568 StuckChecker(ResultInfo resultInfo) {
mcimadamore@1348 569 this.pt = resultInfo.pt;
mcimadamore@1348 570 this.inferenceContext = resultInfo.checkContext.inferenceContext();
mcimadamore@1348 571 this.treeFilter = argsFilter;
mcimadamore@1348 572 }
mcimadamore@1348 573
mcimadamore@1348 574 @Override
mcimadamore@1348 575 public void scan(JCTree tree) {
mcimadamore@1348 576 if (tree != null && treeFilter.accepts(tree)) {
mcimadamore@1348 577 super.scan(tree);
mcimadamore@1348 578 }
mcimadamore@1348 579 }
mcimadamore@1348 580
mcimadamore@1348 581 @Override
mcimadamore@1348 582 public void visitLambda(JCLambda tree) {
mcimadamore@1348 583 Type prevPt = pt;
mcimadamore@1348 584 Filter<JCTree> prevFilter = treeFilter;
mcimadamore@1348 585 try {
mcimadamore@1348 586 if (inferenceContext.inferenceVars().contains(pt)) {
mcimadamore@1348 587 stuckVars.add(pt);
mcimadamore@1348 588 }
mcimadamore@1348 589 if (!types.isFunctionalInterface(pt.tsym)) {
mcimadamore@1348 590 return;
mcimadamore@1348 591 }
mcimadamore@1348 592 Type descType = types.findDescriptorType(pt);
mcimadamore@1348 593 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
mcimadamore@1348 594 if (!TreeInfo.isExplicitLambda(tree) &&
mcimadamore@1348 595 freeArgVars.nonEmpty()) {
mcimadamore@1348 596 stuckVars.addAll(freeArgVars);
mcimadamore@1348 597 }
mcimadamore@1348 598 pt = descType.getReturnType();
mcimadamore@1348 599 if (tree.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
mcimadamore@1348 600 scan(tree.getBody());
mcimadamore@1348 601 } else {
mcimadamore@1348 602 treeFilter = lambdaBodyFilter;
mcimadamore@1348 603 super.visitLambda(tree);
mcimadamore@1348 604 }
mcimadamore@1348 605 } finally {
mcimadamore@1348 606 pt = prevPt;
mcimadamore@1348 607 treeFilter = prevFilter;
mcimadamore@1348 608 }
mcimadamore@1348 609 }
mcimadamore@1348 610
mcimadamore@1348 611 @Override
mcimadamore@1348 612 public void visitReference(JCMemberReference tree) {
mcimadamore@1348 613 scan(tree.expr);
mcimadamore@1348 614 if (inferenceContext.inferenceVars().contains(pt)) {
mcimadamore@1348 615 stuckVars.add(pt);
mcimadamore@1348 616 return;
mcimadamore@1348 617 }
mcimadamore@1348 618 if (!types.isFunctionalInterface(pt.tsym)) {
mcimadamore@1348 619 return;
mcimadamore@1348 620 }
mcimadamore@1348 621 Type descType = types.findDescriptorType(pt);
mcimadamore@1348 622 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
mcimadamore@1348 623 stuckVars.addAll(freeArgVars);
mcimadamore@1348 624 }
mcimadamore@1348 625
mcimadamore@1348 626 @Override
mcimadamore@1348 627 public void visitReturn(JCReturn tree) {
mcimadamore@1348 628 Filter<JCTree> prevFilter = treeFilter;
mcimadamore@1348 629 try {
mcimadamore@1348 630 treeFilter = argsFilter;
mcimadamore@1348 631 if (tree.expr != null) {
mcimadamore@1348 632 scan(tree.expr);
mcimadamore@1348 633 }
mcimadamore@1348 634 } finally {
mcimadamore@1348 635 treeFilter = prevFilter;
mcimadamore@1348 636 }
mcimadamore@1347 637 }
mcimadamore@1347 638 }
mcimadamore@1347 639 }

mercurial