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