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