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

Tue, 13 Nov 2012 15:09:15 -0800

author
jjg
date
Tue, 13 Nov 2012 15:09:15 -0800
changeset 1406
2901c7b5339e
parent 1374
c002fdee76fd
child 1415
01c9d4161882
permissions
-rw-r--r--

8003299: Cleanup javac Log support for deferred diagnostics
Reviewed-by: mcimadamore, jfranck

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

mercurial