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