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

Tue, 08 Jan 2013 10:17:29 +0100

author
mcimadamore
date
Tue, 08 Jan 2013 10:17:29 +0100
changeset 1481
d07340b61e6a
parent 1415
01c9d4161882
child 1510
7873d37f5b37
permissions
-rw-r--r--

8005184: Restructure DeferredAttr to allow pluggable deferred type completers
Summary: Add hooks to generalize deferred type completion via custom helper objects
Reviewed-by: jjg

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@1481 41 import java.util.EnumSet;
mcimadamore@1415 42 import java.util.LinkedHashSet;
mcimadamore@1347 43 import java.util.Map;
mcimadamore@1347 44 import java.util.Queue;
mcimadamore@1347 45 import java.util.Set;
mcimadamore@1347 46 import java.util.WeakHashMap;
mcimadamore@1347 47
mcimadamore@1415 48 import static com.sun.tools.javac.code.TypeTag.*;
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 * Retrieve a speculative cache entry corresponding to given symbol
mcimadamore@1347 141 * and resolution phase
mcimadamore@1347 142 */
mcimadamore@1347 143 Entry get(Symbol msym, MethodResolutionPhase phase) {
mcimadamore@1347 144 List<Entry> entries = cache.get(msym);
mcimadamore@1347 145 if (entries == null) return null;
mcimadamore@1347 146 for (Entry e : entries) {
mcimadamore@1347 147 if (e.matches(phase)) return e;
mcimadamore@1347 148 }
mcimadamore@1347 149 return null;
mcimadamore@1347 150 }
mcimadamore@1347 151
mcimadamore@1347 152 /**
mcimadamore@1347 153 * Stores a speculative cache entry corresponding to given symbol
mcimadamore@1347 154 * and resolution phase
mcimadamore@1347 155 */
mcimadamore@1347 156 void put(Symbol msym, JCTree speculativeTree, MethodResolutionPhase phase) {
mcimadamore@1347 157 List<Entry> entries = cache.get(msym);
mcimadamore@1347 158 if (entries == null) {
mcimadamore@1347 159 entries = List.nil();
mcimadamore@1347 160 }
mcimadamore@1347 161 cache.put(msym, entries.prepend(new Entry(speculativeTree, phase)));
mcimadamore@1347 162 }
mcimadamore@1347 163 }
mcimadamore@1347 164
mcimadamore@1347 165 /**
mcimadamore@1347 166 * Get the type that has been computed during a speculative attribution round
mcimadamore@1347 167 */
mcimadamore@1347 168 Type speculativeType(Symbol msym, MethodResolutionPhase phase) {
mcimadamore@1347 169 SpeculativeCache.Entry e = speculativeCache.get(msym, phase);
mcimadamore@1347 170 return e != null ? e.speculativeTree.type : Type.noType;
mcimadamore@1347 171 }
mcimadamore@1347 172
mcimadamore@1347 173 /**
mcimadamore@1347 174 * Check a deferred type against a potential target-type. Depending on
mcimadamore@1347 175 * the current attribution mode, a normal vs. speculative attribution
mcimadamore@1347 176 * round is performed on the underlying AST node. There can be only one
mcimadamore@1347 177 * speculative round for a given target method symbol; moreover, a normal
mcimadamore@1347 178 * attribution round must follow one or more speculative rounds.
mcimadamore@1347 179 */
mcimadamore@1347 180 Type check(ResultInfo resultInfo) {
mcimadamore@1481 181 return check(resultInfo, stuckVars(tree, env, resultInfo), basicCompleter);
mcimadamore@1481 182 }
mcimadamore@1481 183
mcimadamore@1481 184 Type check(ResultInfo resultInfo, List<Type> stuckVars, DeferredTypeCompleter deferredTypeCompleter) {
mcimadamore@1347 185 DeferredAttrContext deferredAttrContext =
mcimadamore@1347 186 resultInfo.checkContext.deferredAttrContext();
mcimadamore@1347 187 Assert.check(deferredAttrContext != emptyDeferredAttrContext);
mcimadamore@1347 188 if (stuckVars.nonEmpty()) {
mcimadamore@1347 189 deferredAttrContext.addDeferredAttrNode(this, resultInfo, stuckVars);
mcimadamore@1347 190 return Type.noType;
mcimadamore@1347 191 } else {
mcimadamore@1347 192 try {
mcimadamore@1481 193 return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext);
mcimadamore@1347 194 } finally {
mcimadamore@1347 195 mode = deferredAttrContext.mode;
mcimadamore@1347 196 }
mcimadamore@1347 197 }
mcimadamore@1347 198 }
mcimadamore@1347 199 }
mcimadamore@1347 200
mcimadamore@1347 201 /**
mcimadamore@1481 202 * A completer for deferred types. Defines an entry point for type-checking
mcimadamore@1481 203 * a deferred type.
mcimadamore@1481 204 */
mcimadamore@1481 205 interface DeferredTypeCompleter {
mcimadamore@1481 206 /**
mcimadamore@1481 207 * Entry point for type-checking a deferred type. Depending on the
mcimadamore@1481 208 * circumstances, type-checking could amount to full attribution
mcimadamore@1481 209 * or partial structural check (aka potential applicability).
mcimadamore@1481 210 */
mcimadamore@1481 211 Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext);
mcimadamore@1481 212 }
mcimadamore@1481 213
mcimadamore@1481 214 /**
mcimadamore@1481 215 * A basic completer for deferred types. This completer type-checks a deferred type
mcimadamore@1481 216 * using attribution; depending on the attribution mode, this could be either standard
mcimadamore@1481 217 * or speculative attribution.
mcimadamore@1481 218 */
mcimadamore@1481 219 DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() {
mcimadamore@1481 220 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) {
mcimadamore@1481 221 switch (deferredAttrContext.mode) {
mcimadamore@1481 222 case SPECULATIVE:
mcimadamore@1481 223 Assert.check(dt.mode == null ||
mcimadamore@1481 224 (dt.mode == AttrMode.SPECULATIVE &&
mcimadamore@1481 225 dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase).hasTag(NONE)));
mcimadamore@1481 226 JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo);
mcimadamore@1481 227 dt.speculativeCache.put(deferredAttrContext.msym, speculativeTree, deferredAttrContext.phase);
mcimadamore@1481 228 return speculativeTree.type;
mcimadamore@1481 229 case CHECK:
mcimadamore@1481 230 Assert.check(dt.mode == AttrMode.SPECULATIVE);
mcimadamore@1481 231 return attr.attribTree(dt.tree, dt.env, resultInfo);
mcimadamore@1481 232 }
mcimadamore@1481 233 Assert.error();
mcimadamore@1481 234 return null;
mcimadamore@1481 235 }
mcimadamore@1481 236 };
mcimadamore@1481 237
mcimadamore@1481 238 /**
mcimadamore@1347 239 * The 'mode' in which the deferred type is to be type-checked
mcimadamore@1347 240 */
mcimadamore@1347 241 public enum AttrMode {
mcimadamore@1347 242 /**
mcimadamore@1347 243 * A speculative type-checking round is used during overload resolution
mcimadamore@1347 244 * mainly to generate constraints on inference variables. Side-effects
mcimadamore@1347 245 * arising from type-checking the expression associated with the deferred
mcimadamore@1347 246 * type are reversed after the speculative round finishes. This means the
mcimadamore@1347 247 * expression tree will be left in a blank state.
mcimadamore@1347 248 */
mcimadamore@1347 249 SPECULATIVE,
mcimadamore@1347 250 /**
mcimadamore@1347 251 * This is the plain type-checking mode. Produces side-effects on the underlying AST node
mcimadamore@1347 252 */
mcimadamore@1347 253 CHECK;
mcimadamore@1347 254 }
mcimadamore@1347 255
mcimadamore@1347 256 /**
mcimadamore@1347 257 * Routine that performs speculative type-checking; the input AST node is
mcimadamore@1347 258 * cloned (to avoid side-effects cause by Attr) and compiler state is
mcimadamore@1347 259 * restored after type-checking. All diagnostics (but critical ones) are
mcimadamore@1347 260 * disabled during speculative type-checking.
mcimadamore@1347 261 */
mcimadamore@1347 262 JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
mcimadamore@1347 263 JCTree newTree = new TreeCopier<Object>(make).copy(tree);
mcimadamore@1347 264 Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared()));
mcimadamore@1347 265 speculativeEnv.info.scope.owner = env.info.scope.owner;
mcimadamore@1347 266 final JavaFileObject currentSource = log.currentSourceFile();
jjg@1406 267 Log.DeferredDiagnosticHandler deferredDiagnosticHandler =
jjg@1406 268 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() {
jjg@1406 269 public boolean accepts(JCDiagnostic t) {
jjg@1406 270 return t.getDiagnosticSource().getFile().equals(currentSource);
jjg@1406 271 }
jjg@1406 272 });
mcimadamore@1347 273 try {
mcimadamore@1347 274 attr.attribTree(newTree, speculativeEnv, resultInfo);
mcimadamore@1347 275 unenterScanner.scan(newTree);
mcimadamore@1347 276 return newTree;
mcimadamore@1347 277 } catch (Abort ex) {
mcimadamore@1347 278 //if some very bad condition occurred during deferred attribution
mcimadamore@1347 279 //we should dump all errors before killing javac
jjg@1406 280 deferredDiagnosticHandler.reportDeferredDiagnostics();
mcimadamore@1347 281 throw ex;
mcimadamore@1347 282 } finally {
mcimadamore@1347 283 unenterScanner.scan(newTree);
jjg@1406 284 log.popDiagnosticHandler(deferredDiagnosticHandler);
mcimadamore@1347 285 }
mcimadamore@1347 286 }
mcimadamore@1347 287 //where
mcimadamore@1347 288 protected TreeScanner unenterScanner = new TreeScanner() {
mcimadamore@1347 289 @Override
mcimadamore@1347 290 public void visitClassDef(JCClassDecl tree) {
mcimadamore@1347 291 ClassSymbol csym = tree.sym;
mcimadamore@1415 292 //if something went wrong during method applicability check
mcimadamore@1415 293 //it is possible that nested expressions inside argument expression
mcimadamore@1415 294 //are left unchecked - in such cases there's nothing to clean up.
mcimadamore@1415 295 if (csym == null) return;
mcimadamore@1347 296 enter.typeEnvs.remove(csym);
mcimadamore@1347 297 chk.compiled.remove(csym.flatname);
mcimadamore@1347 298 syms.classes.remove(csym.flatname);
mcimadamore@1347 299 super.visitClassDef(tree);
mcimadamore@1347 300 }
mcimadamore@1347 301 };
mcimadamore@1347 302
mcimadamore@1347 303 /**
mcimadamore@1347 304 * A deferred context is created on each method check. A deferred context is
mcimadamore@1347 305 * used to keep track of information associated with the method check, such as
mcimadamore@1347 306 * the symbol of the method being checked, the overload resolution phase,
mcimadamore@1347 307 * the kind of attribution mode to be applied to deferred types and so forth.
mcimadamore@1347 308 * As deferred types are processed (by the method check routine) stuck AST nodes
mcimadamore@1347 309 * are added (as new deferred attribution nodes) to this context. The complete()
mcimadamore@1347 310 * routine makes sure that all pending nodes are properly processed, by
mcimadamore@1347 311 * progressively instantiating all inference variables on which one or more
mcimadamore@1347 312 * deferred attribution node is stuck.
mcimadamore@1347 313 */
mcimadamore@1347 314 class DeferredAttrContext {
mcimadamore@1347 315
mcimadamore@1347 316 /** attribution mode */
mcimadamore@1347 317 final AttrMode mode;
mcimadamore@1347 318
mcimadamore@1347 319 /** symbol of the method being checked */
mcimadamore@1347 320 final Symbol msym;
mcimadamore@1347 321
mcimadamore@1347 322 /** method resolution step */
mcimadamore@1347 323 final Resolve.MethodResolutionPhase phase;
mcimadamore@1347 324
mcimadamore@1347 325 /** inference context */
mcimadamore@1347 326 final InferenceContext inferenceContext;
mcimadamore@1347 327
mcimadamore@1347 328 /** list of deferred attribution nodes to be processed */
mcimadamore@1347 329 ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<DeferredAttrNode>();
mcimadamore@1347 330
mcimadamore@1347 331 DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, InferenceContext inferenceContext) {
mcimadamore@1347 332 this.mode = mode;
mcimadamore@1347 333 this.msym = msym;
mcimadamore@1347 334 this.phase = phase;
mcimadamore@1347 335 this.inferenceContext = inferenceContext;
mcimadamore@1347 336 }
mcimadamore@1347 337
mcimadamore@1347 338 /**
mcimadamore@1347 339 * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable
mcimadamore@1347 340 * Nodes added this way act as 'roots' for the out-of-order method checking process.
mcimadamore@1347 341 */
mcimadamore@1347 342 void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
mcimadamore@1347 343 deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, stuckVars));
mcimadamore@1347 344 }
mcimadamore@1347 345
mcimadamore@1347 346 /**
mcimadamore@1347 347 * Incrementally process all nodes, by skipping 'stuck' nodes and attributing
mcimadamore@1347 348 * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes)
mcimadamore@1347 349 * some inference variable might get eagerly instantiated so that all nodes
mcimadamore@1347 350 * can be type-checked.
mcimadamore@1347 351 */
mcimadamore@1347 352 void complete() {
mcimadamore@1347 353 while (!deferredAttrNodes.isEmpty()) {
mcimadamore@1415 354 Set<Type> stuckVars = new LinkedHashSet<Type>();
mcimadamore@1347 355 boolean progress = false;
mcimadamore@1347 356 //scan a defensive copy of the node list - this is because a deferred
mcimadamore@1347 357 //attribution round can add new nodes to the list
mcimadamore@1347 358 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) {
mcimadamore@1347 359 if (!deferredAttrNode.isStuck()) {
mcimadamore@1347 360 deferredAttrNode.process();
mcimadamore@1347 361 deferredAttrNodes.remove(deferredAttrNode);
mcimadamore@1347 362 progress = true;
mcimadamore@1347 363 } else {
mcimadamore@1347 364 stuckVars.addAll(deferredAttrNode.stuckVars);
mcimadamore@1347 365 }
mcimadamore@1347 366 }
mcimadamore@1347 367 if (!progress) {
mcimadamore@1347 368 //remove all variables that have already been instantiated
mcimadamore@1347 369 //from the list of stuck variables
mcimadamore@1347 370 inferenceContext.solveAny(inferenceContext.freeVarsIn(List.from(stuckVars)), types, infer);
mcimadamore@1347 371 inferenceContext.notifyChange(types);
mcimadamore@1347 372 }
mcimadamore@1347 373 }
mcimadamore@1347 374 }
mcimadamore@1347 375
mcimadamore@1347 376 /**
mcimadamore@1347 377 * Class representing a deferred attribution node. It keeps track of
mcimadamore@1347 378 * a deferred type, along with the expected target type information.
mcimadamore@1347 379 */
mcimadamore@1347 380 class DeferredAttrNode implements Infer.InferenceContext.FreeTypeListener {
mcimadamore@1347 381
mcimadamore@1347 382 /** underlying deferred type */
mcimadamore@1347 383 DeferredType dt;
mcimadamore@1347 384
mcimadamore@1347 385 /** underlying target type information */
mcimadamore@1347 386 ResultInfo resultInfo;
mcimadamore@1347 387
mcimadamore@1347 388 /** list of uninferred inference variables causing this node to be stuck */
mcimadamore@1347 389 List<Type> stuckVars;
mcimadamore@1347 390
mcimadamore@1347 391 DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, List<Type> stuckVars) {
mcimadamore@1347 392 this.dt = dt;
mcimadamore@1347 393 this.resultInfo = resultInfo;
mcimadamore@1347 394 this.stuckVars = stuckVars;
mcimadamore@1347 395 if (!stuckVars.isEmpty()) {
mcimadamore@1347 396 resultInfo.checkContext.inferenceContext().addFreeTypeListener(stuckVars, this);
mcimadamore@1347 397 }
mcimadamore@1347 398 }
mcimadamore@1347 399
mcimadamore@1347 400 @Override
mcimadamore@1347 401 public void typesInferred(InferenceContext inferenceContext) {
mcimadamore@1347 402 stuckVars = List.nil();
mcimadamore@1347 403 resultInfo = resultInfo.dup(inferenceContext.asInstType(resultInfo.pt, types));
mcimadamore@1347 404 }
mcimadamore@1347 405
mcimadamore@1347 406 /**
mcimadamore@1347 407 * is this node stuck?
mcimadamore@1347 408 */
mcimadamore@1347 409 boolean isStuck() {
mcimadamore@1347 410 return stuckVars.nonEmpty();
mcimadamore@1347 411 }
mcimadamore@1347 412
mcimadamore@1347 413 /**
mcimadamore@1347 414 * Process a deferred attribution node.
mcimadamore@1347 415 * Invariant: a stuck node cannot be processed.
mcimadamore@1347 416 */
mcimadamore@1347 417 void process() {
mcimadamore@1347 418 if (isStuck()) {
mcimadamore@1347 419 throw new IllegalStateException("Cannot process a stuck deferred node");
mcimadamore@1347 420 }
mcimadamore@1347 421 dt.check(resultInfo);
mcimadamore@1347 422 }
mcimadamore@1347 423 }
mcimadamore@1347 424 }
mcimadamore@1347 425
mcimadamore@1347 426 /** an empty deferred attribution context - all methods throw exceptions */
mcimadamore@1347 427 final DeferredAttrContext emptyDeferredAttrContext =
mcimadamore@1415 428 new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, null) {
mcimadamore@1347 429 @Override
mcimadamore@1347 430 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, List<Type> stuckVars) {
mcimadamore@1347 431 Assert.error("Empty deferred context!");
mcimadamore@1347 432 }
mcimadamore@1347 433 @Override
mcimadamore@1347 434 void complete() {
mcimadamore@1347 435 Assert.error("Empty deferred context!");
mcimadamore@1347 436 }
mcimadamore@1347 437 };
mcimadamore@1347 438
mcimadamore@1347 439 /**
mcimadamore@1347 440 * Map a list of types possibly containing one or more deferred types
mcimadamore@1347 441 * into a list of ordinary types. Each deferred type D is mapped into a type T,
mcimadamore@1347 442 * where T is computed by retrieving the type that has already been
mcimadamore@1347 443 * computed for D during a previous deferred attribution round of the given kind.
mcimadamore@1347 444 */
mcimadamore@1347 445 class DeferredTypeMap extends Type.Mapping {
mcimadamore@1347 446
mcimadamore@1347 447 DeferredAttrContext deferredAttrContext;
mcimadamore@1347 448
mcimadamore@1347 449 protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
mcimadamore@1347 450 super(String.format("deferredTypeMap[%s]", mode));
mcimadamore@1347 451 this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, infer.emptyContext);
mcimadamore@1347 452 }
mcimadamore@1347 453
mcimadamore@1347 454 protected boolean validState(DeferredType dt) {
mcimadamore@1347 455 return dt.mode != null &&
mcimadamore@1347 456 deferredAttrContext.mode.ordinal() <= dt.mode.ordinal();
mcimadamore@1347 457 }
mcimadamore@1347 458
mcimadamore@1347 459 @Override
mcimadamore@1347 460 public Type apply(Type t) {
jjg@1374 461 if (!t.hasTag(DEFERRED)) {
mcimadamore@1347 462 return t.map(this);
mcimadamore@1347 463 } else {
mcimadamore@1347 464 DeferredType dt = (DeferredType)t;
mcimadamore@1347 465 Assert.check(validState(dt));
mcimadamore@1347 466 return typeOf(dt);
mcimadamore@1347 467 }
mcimadamore@1347 468 }
mcimadamore@1347 469
mcimadamore@1347 470 protected Type typeOf(DeferredType dt) {
mcimadamore@1347 471 switch (deferredAttrContext.mode) {
mcimadamore@1347 472 case CHECK:
mcimadamore@1347 473 return dt.tree.type == null ? Type.noType : dt.tree.type;
mcimadamore@1347 474 case SPECULATIVE:
mcimadamore@1347 475 return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase);
mcimadamore@1347 476 }
mcimadamore@1347 477 Assert.error();
mcimadamore@1347 478 return null;
mcimadamore@1347 479 }
mcimadamore@1347 480 }
mcimadamore@1347 481
mcimadamore@1347 482 /**
mcimadamore@1347 483 * Specialized recovery deferred mapping.
mcimadamore@1347 484 * Each deferred type D is mapped into a type T, where T is computed either by
mcimadamore@1347 485 * (i) retrieving the type that has already been computed for D during a previous
mcimadamore@1347 486 * attribution round (as before), or (ii) by synthesizing a new type R for D
mcimadamore@1347 487 * (the latter step is useful in a recovery scenario).
mcimadamore@1347 488 */
mcimadamore@1347 489 public class RecoveryDeferredTypeMap extends DeferredTypeMap {
mcimadamore@1347 490
mcimadamore@1347 491 public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) {
mcimadamore@1415 492 super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX);
mcimadamore@1347 493 }
mcimadamore@1347 494
mcimadamore@1347 495 @Override
mcimadamore@1347 496 protected Type typeOf(DeferredType dt) {
mcimadamore@1347 497 Type owntype = super.typeOf(dt);
mcimadamore@1415 498 return owntype == Type.noType ?
mcimadamore@1347 499 recover(dt) : owntype;
mcimadamore@1347 500 }
mcimadamore@1347 501
mcimadamore@1347 502 @Override
mcimadamore@1347 503 protected boolean validState(DeferredType dt) {
mcimadamore@1347 504 return true;
mcimadamore@1347 505 }
mcimadamore@1347 506
mcimadamore@1347 507 /**
mcimadamore@1347 508 * Synthesize a type for a deferred type that hasn't been previously
mcimadamore@1347 509 * reduced to an ordinary type. Functional deferred types and conditionals
mcimadamore@1347 510 * are mapped to themselves, in order to have a richer diagnostic
mcimadamore@1347 511 * representation. Remaining deferred types are attributed using
mcimadamore@1347 512 * a default expected type (j.l.Object).
mcimadamore@1347 513 */
mcimadamore@1347 514 private Type recover(DeferredType dt) {
mcimadamore@1348 515 dt.check(attr.new RecoveryInfo(deferredAttrContext));
mcimadamore@1415 516 return super.apply(dt);
mcimadamore@1347 517 }
mcimadamore@1347 518 }
mcimadamore@1347 519
mcimadamore@1347 520 /**
mcimadamore@1347 521 * Retrieves the list of inference variables that need to be inferred before
mcimadamore@1347 522 * an AST node can be type-checked
mcimadamore@1347 523 */
mcimadamore@1347 524 @SuppressWarnings("fallthrough")
mcimadamore@1415 525 List<Type> stuckVars(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) {
mcimadamore@1415 526 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) {
mcimadamore@1348 527 return List.nil();
mcimadamore@1348 528 } else {
mcimadamore@1481 529 return stuckVarsInternal(tree, resultInfo.pt, resultInfo.checkContext.inferenceContext());
mcimadamore@1481 530 }
mcimadamore@1481 531 }
mcimadamore@1481 532 //where
mcimadamore@1481 533 private List<Type> stuckVarsInternal(JCTree tree, Type pt, Infer.InferenceContext inferenceContext) {
mcimadamore@1481 534 StuckChecker sc = new StuckChecker(pt, inferenceContext);
mcimadamore@1348 535 sc.scan(tree);
mcimadamore@1348 536 return List.from(sc.stuckVars);
mcimadamore@1348 537 }
mcimadamore@1481 538
mcimadamore@1481 539 /**
mcimadamore@1481 540 * A special tree scanner that would only visit portions of a given tree.
mcimadamore@1481 541 * The set of nodes visited by the scanner can be customized at construction-time.
mcimadamore@1481 542 */
mcimadamore@1481 543 abstract static class FilterScanner extends TreeScanner {
mcimadamore@1481 544
mcimadamore@1481 545 final Filter<JCTree> treeFilter;
mcimadamore@1481 546
mcimadamore@1481 547 FilterScanner(final Set<JCTree.Tag> validTags) {
mcimadamore@1481 548 this.treeFilter = new Filter<JCTree>() {
mcimadamore@1481 549 public boolean accepts(JCTree t) {
mcimadamore@1481 550 return validTags.contains(t.getTag());
mcimadamore@1481 551 }
mcimadamore@1481 552 };
mcimadamore@1481 553 }
mcimadamore@1481 554
mcimadamore@1481 555 @Override
mcimadamore@1481 556 public void scan(JCTree tree) {
mcimadamore@1481 557 if (tree != null) {
mcimadamore@1481 558 if (treeFilter.accepts(tree)) {
mcimadamore@1481 559 super.scan(tree);
mcimadamore@1481 560 } else {
mcimadamore@1481 561 skip(tree);
mcimadamore@1481 562 }
mcimadamore@1481 563 }
mcimadamore@1481 564 }
mcimadamore@1481 565
mcimadamore@1481 566 /**
mcimadamore@1481 567 * handler that is executed when a node has been discarded
mcimadamore@1481 568 */
mcimadamore@1481 569 abstract void skip(JCTree tree);
mcimadamore@1481 570 }
mcimadamore@1481 571
mcimadamore@1481 572 /**
mcimadamore@1481 573 * A tree scanner suitable for visiting the target-type dependent nodes of
mcimadamore@1481 574 * a given argument expression.
mcimadamore@1481 575 */
mcimadamore@1481 576 static class PolyScanner extends FilterScanner {
mcimadamore@1481 577
mcimadamore@1481 578 PolyScanner() {
mcimadamore@1481 579 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE));
mcimadamore@1481 580 }
mcimadamore@1481 581
mcimadamore@1481 582 @Override
mcimadamore@1481 583 void skip(JCTree tree) {
mcimadamore@1481 584 //do nothing
mcimadamore@1481 585 }
mcimadamore@1481 586 }
mcimadamore@1481 587
mcimadamore@1481 588 /**
mcimadamore@1481 589 * A tree scanner suitable for visiting the target-type dependent nodes nested
mcimadamore@1481 590 * within a lambda expression body.
mcimadamore@1481 591 */
mcimadamore@1481 592 static class LambdaReturnScanner extends FilterScanner {
mcimadamore@1481 593
mcimadamore@1481 594 LambdaReturnScanner() {
mcimadamore@1481 595 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP,
mcimadamore@1481 596 FORLOOP, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP));
mcimadamore@1481 597 }
mcimadamore@1481 598
mcimadamore@1481 599 @Override
mcimadamore@1481 600 void skip(JCTree tree) {
mcimadamore@1481 601 //do nothing
mcimadamore@1481 602 }
mcimadamore@1348 603 }
mcimadamore@1348 604
mcimadamore@1348 605 /**
mcimadamore@1348 606 * This visitor is used to check that structural expressions conform
mcimadamore@1348 607 * to their target - this step is required as inference could end up
mcimadamore@1348 608 * inferring types that make some of the nested expressions incompatible
mcimadamore@1348 609 * with their corresponding instantiated target
mcimadamore@1348 610 */
mcimadamore@1481 611 class StuckChecker extends PolyScanner {
mcimadamore@1348 612
mcimadamore@1348 613 Type pt;
mcimadamore@1348 614 Infer.InferenceContext inferenceContext;
mcimadamore@1415 615 Set<Type> stuckVars = new LinkedHashSet<Type>();
mcimadamore@1348 616
mcimadamore@1481 617 StuckChecker(Type pt, Infer.InferenceContext inferenceContext) {
mcimadamore@1481 618 this.pt = pt;
mcimadamore@1481 619 this.inferenceContext = inferenceContext;
mcimadamore@1348 620 }
mcimadamore@1348 621
mcimadamore@1348 622 @Override
mcimadamore@1348 623 public void visitLambda(JCLambda tree) {
mcimadamore@1481 624 if (inferenceContext.inferenceVars().contains(pt)) {
mcimadamore@1481 625 stuckVars.add(pt);
mcimadamore@1348 626 }
mcimadamore@1481 627 if (!types.isFunctionalInterface(pt.tsym)) {
mcimadamore@1481 628 return;
mcimadamore@1481 629 }
mcimadamore@1481 630 Type descType = types.findDescriptorType(pt);
mcimadamore@1481 631 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
mcimadamore@1481 632 if (!TreeInfo.isExplicitLambda(tree) &&
mcimadamore@1481 633 freeArgVars.nonEmpty()) {
mcimadamore@1481 634 stuckVars.addAll(freeArgVars);
mcimadamore@1481 635 }
mcimadamore@1481 636 scanLambdaBody(tree, descType.getReturnType());
mcimadamore@1348 637 }
mcimadamore@1348 638
mcimadamore@1348 639 @Override
mcimadamore@1348 640 public void visitReference(JCMemberReference tree) {
mcimadamore@1348 641 scan(tree.expr);
mcimadamore@1348 642 if (inferenceContext.inferenceVars().contains(pt)) {
mcimadamore@1348 643 stuckVars.add(pt);
mcimadamore@1348 644 return;
mcimadamore@1348 645 }
mcimadamore@1348 646 if (!types.isFunctionalInterface(pt.tsym)) {
mcimadamore@1348 647 return;
mcimadamore@1348 648 }
mcimadamore@1415 649
mcimadamore@1348 650 Type descType = types.findDescriptorType(pt);
mcimadamore@1348 651 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes());
mcimadamore@1348 652 stuckVars.addAll(freeArgVars);
mcimadamore@1348 653 }
mcimadamore@1348 654
mcimadamore@1481 655 void scanLambdaBody(JCLambda lambda, final Type pt) {
mcimadamore@1481 656 if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) {
mcimadamore@1481 657 stuckVars.addAll(stuckVarsInternal(lambda.body, pt, inferenceContext));
mcimadamore@1481 658 } else {
mcimadamore@1481 659 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() {
mcimadamore@1481 660 @Override
mcimadamore@1481 661 public void visitReturn(JCReturn tree) {
mcimadamore@1481 662 if (tree.expr != null) {
mcimadamore@1481 663 stuckVars.addAll(stuckVarsInternal(tree.expr, pt, inferenceContext));
mcimadamore@1481 664 }
mcimadamore@1481 665 }
mcimadamore@1481 666 };
mcimadamore@1481 667 lambdaScanner.scan(lambda.body);
mcimadamore@1348 668 }
mcimadamore@1347 669 }
mcimadamore@1347 670 }
mcimadamore@1347 671 }

mercurial