DeferredAttr.java revision 3405:d671601a2f2e
1/* 2 * Copyright (c) 2012, 2015, 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 */ 25 26package com.sun.tools.javac.comp; 27 28import com.sun.source.tree.LambdaExpressionTree.BodyKind; 29import com.sun.source.tree.NewClassTree; 30import com.sun.tools.javac.code.*; 31import com.sun.tools.javac.code.Type.TypeMapping; 32import com.sun.tools.javac.comp.ArgumentAttr.LocalCacheContext; 33import com.sun.tools.javac.comp.Resolve.ResolveError; 34import com.sun.tools.javac.resources.CompilerProperties.Fragments; 35import com.sun.tools.javac.tree.*; 36import com.sun.tools.javac.util.*; 37import com.sun.tools.javac.util.DefinedBy.Api; 38import com.sun.tools.javac.util.GraphUtils.DependencyKind; 39import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 40import com.sun.tools.javac.code.Symbol.*; 41import com.sun.tools.javac.comp.Attr.ResultInfo; 42import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; 43import com.sun.tools.javac.tree.JCTree.*; 44import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType; 45import com.sun.tools.javac.util.Log.DeferredDiagnosticHandler; 46 47import java.util.ArrayList; 48import java.util.Collection; 49import java.util.Collections; 50import java.util.EnumSet; 51import java.util.HashSet; 52import java.util.LinkedHashSet; 53import java.util.Map; 54import java.util.Set; 55import java.util.WeakHashMap; 56import java.util.function.Function; 57 58import static com.sun.tools.javac.code.TypeTag.*; 59import static com.sun.tools.javac.tree.JCTree.Tag.*; 60 61/** 62 * This is an helper class that is used to perform deferred type-analysis. 63 * Each time a poly expression occurs in argument position, javac attributes it 64 * with a temporary 'deferred type' that is checked (possibly multiple times) 65 * against an expected formal type. 66 * 67 * <p><b>This is NOT part of any supported API. 68 * If you write code that depends on this, you do so at your own risk. 69 * This code and its internal interfaces are subject to change or 70 * deletion without notice.</b> 71 */ 72public class DeferredAttr extends JCTree.Visitor { 73 protected static final Context.Key<DeferredAttr> deferredAttrKey = new Context.Key<>(); 74 75 final Attr attr; 76 final ArgumentAttr argumentAttr; 77 final Check chk; 78 final JCDiagnostic.Factory diags; 79 final Enter enter; 80 final Infer infer; 81 final Resolve rs; 82 final Log log; 83 final Symtab syms; 84 final TreeMaker make; 85 final TreeCopier<Void> treeCopier; 86 final TypeMapping<Void> deferredCopier; 87 final Types types; 88 final Flow flow; 89 final Names names; 90 final TypeEnvs typeEnvs; 91 92 public static DeferredAttr instance(Context context) { 93 DeferredAttr instance = context.get(deferredAttrKey); 94 if (instance == null) 95 instance = new DeferredAttr(context); 96 return instance; 97 } 98 99 protected DeferredAttr(Context context) { 100 context.put(deferredAttrKey, this); 101 attr = Attr.instance(context); 102 argumentAttr = ArgumentAttr.instance(context); 103 chk = Check.instance(context); 104 diags = JCDiagnostic.Factory.instance(context); 105 enter = Enter.instance(context); 106 infer = Infer.instance(context); 107 rs = Resolve.instance(context); 108 log = Log.instance(context); 109 syms = Symtab.instance(context); 110 make = TreeMaker.instance(context); 111 types = Types.instance(context); 112 flow = Flow.instance(context); 113 names = Names.instance(context); 114 stuckTree = make.Ident(names.empty).setType(Type.stuckType); 115 typeEnvs = TypeEnvs.instance(context); 116 emptyDeferredAttrContext = 117 new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) { 118 @Override 119 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, DeferredStuckPolicy deferredStuckPolicy) { 120 Assert.error("Empty deferred context!"); 121 } 122 @Override 123 void complete() { 124 Assert.error("Empty deferred context!"); 125 } 126 127 @Override 128 public String toString() { 129 return "Empty deferred context!"; 130 } 131 }; 132 133 // For speculative attribution, skip the class definition in <>. 134 treeCopier = 135 new TreeCopier<Void>(make) { 136 @Override @DefinedBy(Api.COMPILER_TREE) 137 public JCTree visitNewClass(NewClassTree node, Void p) { 138 JCNewClass t = (JCNewClass) node; 139 if (TreeInfo.isDiamond(t)) { 140 JCExpression encl = copy(t.encl, p); 141 List<JCExpression> typeargs = copy(t.typeargs, p); 142 JCExpression clazz = copy(t.clazz, p); 143 List<JCExpression> args = copy(t.args, p); 144 JCClassDecl def = null; 145 return make.at(t.pos).NewClass(encl, typeargs, clazz, args, def); 146 } else { 147 return super.visitNewClass(node, p); 148 } 149 } 150 }; 151 deferredCopier = new TypeMapping<Void> () { 152 @Override 153 public Type visitType(Type t, Void v) { 154 if (t.hasTag(DEFERRED)) { 155 DeferredType dt = (DeferredType) t; 156 return new DeferredType(treeCopier.copy(dt.tree), dt.env); 157 } 158 return t; 159 } 160 }; 161 } 162 163 /** shared tree for stuck expressions */ 164 final JCTree stuckTree; 165 166 /** 167 * This type represents a deferred type. A deferred type starts off with 168 * no information on the underlying expression type. Such info needs to be 169 * discovered through type-checking the deferred type against a target-type. 170 * Every deferred type keeps a pointer to the AST node from which it originated. 171 */ 172 public class DeferredType extends Type { 173 174 public JCExpression tree; 175 Env<AttrContext> env; 176 AttrMode mode; 177 boolean pertinentToApplicability = true; 178 SpeculativeCache speculativeCache; 179 180 DeferredType(JCExpression tree, Env<AttrContext> env) { 181 super(null, TypeMetadata.EMPTY); 182 this.tree = tree; 183 this.env = attr.copyEnv(env); 184 this.speculativeCache = new SpeculativeCache(); 185 } 186 187 @Override 188 public DeferredType cloneWithMetadata(TypeMetadata md) { 189 throw new AssertionError("Cannot add metadata to a deferred type"); 190 } 191 192 @Override 193 public TypeTag getTag() { 194 return DEFERRED; 195 } 196 197 @Override @DefinedBy(Api.LANGUAGE_MODEL) 198 public String toString() { 199 return "DeferredType"; 200 } 201 202 /** 203 * A speculative cache is used to keep track of all overload resolution rounds 204 * that triggered speculative attribution on a given deferred type. Each entry 205 * stores a pointer to the speculative tree and the resolution phase in which the entry 206 * has been added. 207 */ 208 class SpeculativeCache { 209 210 private Map<Symbol, List<Entry>> cache = new WeakHashMap<>(); 211 212 class Entry { 213 JCTree speculativeTree; 214 ResultInfo resultInfo; 215 216 public Entry(JCTree speculativeTree, ResultInfo resultInfo) { 217 this.speculativeTree = speculativeTree; 218 this.resultInfo = resultInfo; 219 } 220 221 boolean matches(MethodResolutionPhase phase) { 222 return resultInfo.checkContext.deferredAttrContext().phase == phase; 223 } 224 } 225 226 /** 227 * Retrieve a speculative cache entry corresponding to given symbol 228 * and resolution phase 229 */ 230 Entry get(Symbol msym, MethodResolutionPhase phase) { 231 List<Entry> entries = cache.get(msym); 232 if (entries == null) return null; 233 for (Entry e : entries) { 234 if (e.matches(phase)) return e; 235 } 236 return null; 237 } 238 239 /** 240 * Stores a speculative cache entry corresponding to given symbol 241 * and resolution phase 242 */ 243 void put(JCTree speculativeTree, ResultInfo resultInfo) { 244 Symbol msym = resultInfo.checkContext.deferredAttrContext().msym; 245 List<Entry> entries = cache.get(msym); 246 if (entries == null) { 247 entries = List.nil(); 248 } 249 cache.put(msym, entries.prepend(new Entry(speculativeTree, resultInfo))); 250 } 251 } 252 253 /** 254 * Get the type that has been computed during a speculative attribution round 255 */ 256 Type speculativeType(Symbol msym, MethodResolutionPhase phase) { 257 SpeculativeCache.Entry e = speculativeCache.get(msym, phase); 258 return e != null ? e.speculativeTree.type : Type.noType; 259 } 260 261 JCTree speculativeTree(DeferredAttrContext deferredAttrContext) { 262 DeferredType.SpeculativeCache.Entry e = speculativeCache.get(deferredAttrContext.msym, deferredAttrContext.phase); 263 return e != null ? e.speculativeTree : stuckTree; 264 } 265 266 DeferredTypeCompleter completer() { 267 return basicCompleter; 268 } 269 270 /** 271 * Check a deferred type against a potential target-type. Depending on 272 * the current attribution mode, a normal vs. speculative attribution 273 * round is performed on the underlying AST node. There can be only one 274 * speculative round for a given target method symbol; moreover, a normal 275 * attribution round must follow one or more speculative rounds. 276 */ 277 Type check(ResultInfo resultInfo) { 278 DeferredStuckPolicy deferredStuckPolicy; 279 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) { 280 deferredStuckPolicy = dummyStuckPolicy; 281 } else if (resultInfo.checkContext.deferredAttrContext().mode == AttrMode.SPECULATIVE || 282 resultInfo.checkContext.deferredAttrContext().insideOverloadPhase()) { 283 deferredStuckPolicy = new OverloadStuckPolicy(resultInfo, this); 284 } else { 285 deferredStuckPolicy = new CheckStuckPolicy(resultInfo, this); 286 } 287 return check(resultInfo, deferredStuckPolicy, completer()); 288 } 289 290 private Type check(ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy, 291 DeferredTypeCompleter deferredTypeCompleter) { 292 DeferredAttrContext deferredAttrContext = 293 resultInfo.checkContext.deferredAttrContext(); 294 Assert.check(deferredAttrContext != emptyDeferredAttrContext); 295 if (deferredStuckPolicy.isStuck()) { 296 pertinentToApplicability = false; 297 deferredAttrContext.addDeferredAttrNode(this, resultInfo, deferredStuckPolicy); 298 return Type.noType; 299 } else { 300 try { 301 return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext); 302 } finally { 303 mode = deferredAttrContext.mode; 304 } 305 } 306 } 307 } 308 309 /** 310 * A completer for deferred types. Defines an entry point for type-checking 311 * a deferred type. 312 */ 313 interface DeferredTypeCompleter { 314 /** 315 * Entry point for type-checking a deferred type. Depending on the 316 * circumstances, type-checking could amount to full attribution 317 * or partial structural check (aka potential applicability). 318 */ 319 Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext); 320 } 321 322 323 /** 324 * A basic completer for deferred types. This completer type-checks a deferred type 325 * using attribution; depending on the attribution mode, this could be either standard 326 * or speculative attribution. 327 */ 328 DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() { 329 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { 330 switch (deferredAttrContext.mode) { 331 case SPECULATIVE: 332 //Note: if a symbol is imported twice we might do two identical 333 //speculative rounds... 334 Assert.check(dt.mode == null || dt.mode == AttrMode.SPECULATIVE); 335 JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo); 336 dt.speculativeCache.put(speculativeTree, resultInfo); 337 return speculativeTree.type; 338 case CHECK: 339 Assert.check(dt.mode != null); 340 return attr.attribTree(dt.tree, dt.env, resultInfo); 341 } 342 Assert.error(); 343 return null; 344 } 345 }; 346 347 /** 348 * Policy for detecting stuck expressions. Different criteria might cause 349 * an expression to be judged as stuck, depending on whether the check 350 * is performed during overload resolution or after most specific. 351 */ 352 interface DeferredStuckPolicy { 353 /** 354 * Has the policy detected that a given expression should be considered stuck? 355 */ 356 boolean isStuck(); 357 /** 358 * Get the set of inference variables a given expression depends upon. 359 */ 360 Set<Type> stuckVars(); 361 /** 362 * Get the set of inference variables which might get new constraints 363 * if a given expression is being type-checked. 364 */ 365 Set<Type> depVars(); 366 } 367 368 /** 369 * Basic stuck policy; an expression is never considered to be stuck. 370 */ 371 DeferredStuckPolicy dummyStuckPolicy = new DeferredStuckPolicy() { 372 @Override 373 public boolean isStuck() { 374 return false; 375 } 376 @Override 377 public Set<Type> stuckVars() { 378 return Collections.emptySet(); 379 } 380 @Override 381 public Set<Type> depVars() { 382 return Collections.emptySet(); 383 } 384 }; 385 386 /** 387 * The 'mode' in which the deferred type is to be type-checked 388 */ 389 public enum AttrMode { 390 /** 391 * A speculative type-checking round is used during overload resolution 392 * mainly to generate constraints on inference variables. Side-effects 393 * arising from type-checking the expression associated with the deferred 394 * type are reversed after the speculative round finishes. This means the 395 * expression tree will be left in a blank state. 396 */ 397 SPECULATIVE, 398 /** 399 * This is the plain type-checking mode. Produces side-effects on the underlying AST node 400 */ 401 CHECK 402 } 403 404 /** 405 * Performs speculative attribution of a lambda body and returns the speculative lambda tree, 406 * in the absence of a target-type. Since {@link Attr#visitLambda(JCLambda)} cannot type-check 407 * lambda bodies w/o a suitable target-type, this routine 'unrolls' the lambda by turning it 408 * into a regular block, speculatively type-checks the block and then puts back the pieces. 409 */ 410 JCLambda attribSpeculativeLambda(JCLambda that, Env<AttrContext> env, ResultInfo resultInfo) { 411 ListBuffer<JCStatement> stats = new ListBuffer<>(); 412 stats.addAll(that.params); 413 if (that.getBodyKind() == JCLambda.BodyKind.EXPRESSION) { 414 stats.add(make.Return((JCExpression)that.body)); 415 } else { 416 stats.add((JCBlock)that.body); 417 } 418 JCBlock lambdaBlock = make.Block(0, stats.toList()); 419 Env<AttrContext> localEnv = attr.lambdaEnv(that, env); 420 try { 421 localEnv.info.returnResult = resultInfo; 422 JCBlock speculativeTree = (JCBlock)attribSpeculative(lambdaBlock, localEnv, resultInfo); 423 List<JCVariableDecl> args = speculativeTree.getStatements().stream() 424 .filter(s -> s.hasTag(Tag.VARDEF)) 425 .map(t -> (JCVariableDecl)t) 426 .collect(List.collector()); 427 JCTree lambdaBody = speculativeTree.getStatements().last(); 428 if (lambdaBody.hasTag(Tag.RETURN)) { 429 lambdaBody = ((JCReturn)lambdaBody).expr; 430 } 431 JCLambda speculativeLambda = make.Lambda(args, lambdaBody); 432 attr.preFlow(speculativeLambda); 433 flow.analyzeLambda(env, speculativeLambda, make, false); 434 return speculativeLambda; 435 } finally { 436 localEnv.info.scope.leave(); 437 } 438 } 439 440 /** 441 * Routine that performs speculative type-checking; the input AST node is 442 * cloned (to avoid side-effects cause by Attr) and compiler state is 443 * restored after type-checking. All diagnostics (but critical ones) are 444 * disabled during speculative type-checking. 445 */ 446 JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) { 447 return attribSpeculative(tree, env, resultInfo, treeCopier, 448 (newTree)->new DeferredAttrDiagHandler(log, newTree)); 449 } 450 451 <Z> JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo, TreeCopier<Z> deferredCopier, 452 Function<JCTree, DeferredDiagnosticHandler> diagHandlerCreator) { 453 final JCTree newTree = deferredCopier.copy(tree); 454 Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner))); 455 speculativeEnv.info.isSpeculative = true; 456 Log.DeferredDiagnosticHandler deferredDiagnosticHandler = diagHandlerCreator.apply(newTree); 457 try { 458 attr.attribTree(newTree, speculativeEnv, resultInfo); 459 return newTree; 460 } finally { 461 new UnenterScanner(env.toplevel.modle).scan(newTree); 462 log.popDiagnosticHandler(deferredDiagnosticHandler); 463 } 464 } 465 //where 466 467 class UnenterScanner extends TreeScanner { 468 private final ModuleSymbol msym; 469 470 public UnenterScanner(ModuleSymbol msym) { 471 this.msym = msym; 472 } 473 474 @Override 475 public void visitClassDef(JCClassDecl tree) { 476 ClassSymbol csym = tree.sym; 477 //if something went wrong during method applicability check 478 //it is possible that nested expressions inside argument expression 479 //are left unchecked - in such cases there's nothing to clean up. 480 if (csym == null) return; 481 typeEnvs.remove(csym); 482 chk.removeCompiled(csym); 483 chk.clearLocalClassNameIndexes(csym); 484 syms.removeClass(msym, csym.flatname); 485 super.visitClassDef(tree); 486 } 487 } 488 489 static class DeferredAttrDiagHandler extends Log.DeferredDiagnosticHandler { 490 491 static class PosScanner extends TreeScanner { 492 DiagnosticPosition pos; 493 boolean found = false; 494 495 PosScanner(DiagnosticPosition pos) { 496 this.pos = pos; 497 } 498 499 @Override 500 public void scan(JCTree tree) { 501 if (tree != null && 502 tree.pos() == pos) { 503 found = true; 504 } 505 super.scan(tree); 506 } 507 } 508 509 DeferredAttrDiagHandler(Log log, JCTree newTree) { 510 super(log, new Filter<JCDiagnostic>() { 511 public boolean accepts(JCDiagnostic d) { 512 PosScanner posScanner = new PosScanner(d.getDiagnosticPosition()); 513 posScanner.scan(newTree); 514 return posScanner.found; 515 } 516 }); 517 } 518 } 519 520 /** 521 * A deferred context is created on each method check. A deferred context is 522 * used to keep track of information associated with the method check, such as 523 * the symbol of the method being checked, the overload resolution phase, 524 * the kind of attribution mode to be applied to deferred types and so forth. 525 * As deferred types are processed (by the method check routine) stuck AST nodes 526 * are added (as new deferred attribution nodes) to this context. The complete() 527 * routine makes sure that all pending nodes are properly processed, by 528 * progressively instantiating all inference variables on which one or more 529 * deferred attribution node is stuck. 530 */ 531 class DeferredAttrContext { 532 533 /** attribution mode */ 534 final AttrMode mode; 535 536 /** symbol of the method being checked */ 537 final Symbol msym; 538 539 /** method resolution step */ 540 final Resolve.MethodResolutionPhase phase; 541 542 /** inference context */ 543 final InferenceContext inferenceContext; 544 545 /** parent deferred context */ 546 final DeferredAttrContext parent; 547 548 /** Warner object to report warnings */ 549 final Warner warn; 550 551 /** list of deferred attribution nodes to be processed */ 552 ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<>(); 553 554 DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, 555 InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) { 556 this.mode = mode; 557 this.msym = msym; 558 this.phase = phase; 559 this.parent = parent; 560 this.warn = warn; 561 this.inferenceContext = inferenceContext; 562 } 563 564 /** 565 * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable 566 * Nodes added this way act as 'roots' for the out-of-order method checking process. 567 */ 568 void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, 569 DeferredStuckPolicy deferredStuckPolicy) { 570 deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, deferredStuckPolicy)); 571 } 572 573 /** 574 * Incrementally process all nodes, by skipping 'stuck' nodes and attributing 575 * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes) 576 * some inference variable might get eagerly instantiated so that all nodes 577 * can be type-checked. 578 */ 579 void complete() { 580 while (!deferredAttrNodes.isEmpty()) { 581 boolean progress = false; 582 //scan a defensive copy of the node list - this is because a deferred 583 //attribution round can add new nodes to the list 584 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) { 585 if (deferredAttrNode.process(this)) { 586 deferredAttrNodes.remove(deferredAttrNode); 587 progress = true; 588 } 589 } 590 if (!progress) { 591 if (insideOverloadPhase()) { 592 for (DeferredAttrNode deferredNode: deferredAttrNodes) { 593 deferredNode.dt.tree.type = Type.noType; 594 } 595 return; 596 } 597 //remove all variables that have already been instantiated 598 //from the list of stuck variables 599 try { 600 //find stuck expression to unstuck 601 DeferredAttrNode toUnstuck = pickDeferredNode(); 602 inferenceContext.solveAny(List.from(toUnstuck.deferredStuckPolicy.stuckVars()), warn); 603 inferenceContext.notifyChange(); 604 } catch (Infer.GraphStrategy.NodeNotFoundException ex) { 605 //this means that we are in speculative mode and the 606 //set of contraints are too tight for progess to be made. 607 //Just leave the remaining expressions as stuck. 608 break; 609 } 610 } 611 } 612 } 613 614 public boolean insideOverloadPhase() { 615 DeferredAttrContext dac = this; 616 if (dac == emptyDeferredAttrContext) { 617 return false; 618 } 619 if (dac.mode == AttrMode.SPECULATIVE) { 620 return true; 621 } 622 return dac.parent.insideOverloadPhase(); 623 } 624 625 /** 626 * Pick the deferred node to be unstuck. The chosen node is the first strongly connected 627 * component containing exactly one node found in the dependency graph induced by deferred nodes. 628 * If no such component is found, the first deferred node is returned. 629 */ 630 DeferredAttrNode pickDeferredNode() { 631 List<StuckNode> nodes = deferredAttrNodes.stream() 632 .map(StuckNode::new) 633 .collect(List.collector()); 634 //init stuck expression graph; a deferred node A depends on a deferred node B iff 635 //the intersection between A's input variable and B's output variable is non-empty. 636 for (StuckNode sn1 : nodes) { 637 for (Type t : sn1.data.deferredStuckPolicy.stuckVars()) { 638 for (StuckNode sn2 : nodes) { 639 if (sn1 != sn2 && sn2.data.deferredStuckPolicy.depVars().contains(t)) { 640 sn1.deps.add(sn2); 641 } 642 } 643 } 644 } 645 //compute tarjan on the stuck graph 646 List<? extends StuckNode> csn = GraphUtils.tarjan(nodes).get(0); 647 return csn.length() == 1 ? csn.get(0).data : deferredAttrNodes.get(0); 648 } 649 650 class StuckNode extends GraphUtils.TarjanNode<DeferredAttrNode, StuckNode> { 651 652 Set<StuckNode> deps = new HashSet<>(); 653 654 StuckNode(DeferredAttrNode data) { 655 super(data); 656 } 657 658 @Override 659 public DependencyKind[] getSupportedDependencyKinds() { 660 return new DependencyKind[] { Infer.DependencyKind.STUCK }; 661 } 662 663 @Override 664 public Collection<? extends StuckNode> getDependenciesByKind(DependencyKind dk) { 665 if (dk == Infer.DependencyKind.STUCK) { 666 return deps; 667 } else { 668 throw new IllegalStateException(); 669 } 670 } 671 672 @Override 673 public Iterable<? extends StuckNode> getAllDependencies() { 674 return deps; 675 } 676 } 677 } 678 679 /** 680 * Class representing a deferred attribution node. It keeps track of 681 * a deferred type, along with the expected target type information. 682 */ 683 class DeferredAttrNode { 684 685 /** underlying deferred type */ 686 DeferredType dt; 687 688 /** underlying target type information */ 689 ResultInfo resultInfo; 690 691 /** stuck policy associated with this node */ 692 DeferredStuckPolicy deferredStuckPolicy; 693 694 DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy) { 695 this.dt = dt; 696 this.resultInfo = resultInfo; 697 this.deferredStuckPolicy = deferredStuckPolicy; 698 } 699 700 /** 701 * Process a deferred attribution node. 702 * Invariant: a stuck node cannot be processed. 703 */ 704 @SuppressWarnings("fallthrough") 705 boolean process(final DeferredAttrContext deferredAttrContext) { 706 switch (deferredAttrContext.mode) { 707 case SPECULATIVE: 708 if (deferredStuckPolicy.isStuck()) { 709 dt.check(resultInfo, dummyStuckPolicy, new StructuralStuckChecker()); 710 return true; 711 } else { 712 Assert.error("Cannot get here"); 713 } 714 case CHECK: 715 if (deferredStuckPolicy.isStuck()) { 716 //stuck expression - see if we can propagate 717 if (deferredAttrContext.parent != emptyDeferredAttrContext && 718 Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, 719 List.from(deferredStuckPolicy.stuckVars()))) { 720 deferredAttrContext.parent.addDeferredAttrNode(dt, 721 resultInfo.dup(new Check.NestedCheckContext(resultInfo.checkContext) { 722 @Override 723 public InferenceContext inferenceContext() { 724 return deferredAttrContext.parent.inferenceContext; 725 } 726 @Override 727 public DeferredAttrContext deferredAttrContext() { 728 return deferredAttrContext.parent; 729 } 730 }), deferredStuckPolicy); 731 dt.tree.type = Type.stuckType; 732 return true; 733 } else { 734 return false; 735 } 736 } else { 737 Assert.check(!deferredAttrContext.insideOverloadPhase(), 738 "attribution shouldn't be happening here"); 739 ResultInfo instResultInfo = 740 resultInfo.dup(deferredAttrContext.inferenceContext.asInstType(resultInfo.pt)); 741 dt.check(instResultInfo, dummyStuckPolicy, basicCompleter); 742 return true; 743 } 744 default: 745 throw new AssertionError("Bad mode"); 746 } 747 } 748 749 /** 750 * Structural checker for stuck expressions 751 */ 752 class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter { 753 754 ResultInfo resultInfo; 755 InferenceContext inferenceContext; 756 Env<AttrContext> env; 757 758 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { 759 this.resultInfo = resultInfo; 760 this.inferenceContext = deferredAttrContext.inferenceContext; 761 this.env = dt.env; 762 dt.tree.accept(this); 763 dt.speculativeCache.put(stuckTree, resultInfo); 764 return Type.noType; 765 } 766 767 @Override 768 public void visitLambda(JCLambda tree) { 769 Check.CheckContext checkContext = resultInfo.checkContext; 770 Type pt = resultInfo.pt; 771 if (!inferenceContext.inferencevars.contains(pt)) { 772 //must be a functional descriptor 773 Type descriptorType = null; 774 try { 775 descriptorType = types.findDescriptorType(pt); 776 } catch (Types.FunctionDescriptorLookupError ex) { 777 checkContext.report(null, ex.getDiagnostic()); 778 } 779 780 if (descriptorType.getParameterTypes().length() != tree.params.length()) { 781 checkContext.report(tree, 782 diags.fragment("incompatible.arg.types.in.lambda")); 783 } 784 785 Type currentReturnType = descriptorType.getReturnType(); 786 boolean returnTypeIsVoid = currentReturnType.hasTag(VOID); 787 if (tree.getBodyKind() == BodyKind.EXPRESSION) { 788 boolean isExpressionCompatible = !returnTypeIsVoid || 789 TreeInfo.isExpressionStatement((JCExpression)tree.getBody()); 790 if (!isExpressionCompatible) { 791 resultInfo.checkContext.report(tree.pos(), 792 diags.fragment("incompatible.ret.type.in.lambda", 793 diags.fragment("missing.ret.val", currentReturnType))); 794 } 795 } else { 796 LambdaBodyStructChecker lambdaBodyChecker = 797 new LambdaBodyStructChecker(); 798 799 tree.body.accept(lambdaBodyChecker); 800 boolean isVoidCompatible = lambdaBodyChecker.isVoidCompatible; 801 802 if (returnTypeIsVoid) { 803 if (!isVoidCompatible) { 804 resultInfo.checkContext.report(tree.pos(), 805 diags.fragment("unexpected.ret.val")); 806 } 807 } else { 808 boolean isValueCompatible = lambdaBodyChecker.isPotentiallyValueCompatible 809 && !canLambdaBodyCompleteNormally(tree); 810 if (!isValueCompatible && !isVoidCompatible) { 811 log.error(tree.body.pos(), 812 "lambda.body.neither.value.nor.void.compatible"); 813 } 814 815 if (!isValueCompatible) { 816 resultInfo.checkContext.report(tree.pos(), 817 diags.fragment("incompatible.ret.type.in.lambda", 818 diags.fragment("missing.ret.val", currentReturnType))); 819 } 820 } 821 } 822 } 823 } 824 825 boolean canLambdaBodyCompleteNormally(JCLambda tree) { 826 List<JCVariableDecl> oldParams = tree.params; 827 LocalCacheContext localCacheContext = argumentAttr.withLocalCacheContext(); 828 try { 829 tree.params = tree.params.stream() 830 .map(vd -> make.VarDef(vd.mods, vd.name, make.Erroneous(), null)) 831 .collect(List.collector()); 832 return attribSpeculativeLambda(tree, env, attr.unknownExprInfo).canCompleteNormally; 833 } finally { 834 localCacheContext.leave(); 835 tree.params = oldParams; 836 } 837 } 838 839 @Override 840 public void visitNewClass(JCNewClass tree) { 841 //do nothing 842 } 843 844 @Override 845 public void visitApply(JCMethodInvocation tree) { 846 //do nothing 847 } 848 849 @Override 850 public void visitReference(JCMemberReference tree) { 851 Check.CheckContext checkContext = resultInfo.checkContext; 852 Type pt = resultInfo.pt; 853 if (!inferenceContext.inferencevars.contains(pt)) { 854 try { 855 types.findDescriptorType(pt); 856 } catch (Types.FunctionDescriptorLookupError ex) { 857 checkContext.report(null, ex.getDiagnostic()); 858 } 859 Env<AttrContext> localEnv = env.dup(tree); 860 JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv, 861 attr.memberReferenceQualifierResult(tree)); 862 ListBuffer<Type> argtypes = new ListBuffer<>(); 863 for (Type t : types.findDescriptorType(pt).getParameterTypes()) { 864 argtypes.append(Type.noType); 865 } 866 JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree); 867 mref2.expr = exprTree; 868 Symbol lookupSym = 869 rs.resolveMemberReference(localEnv, mref2, exprTree.type, 870 tree.name, argtypes.toList(), List.nil(), rs.arityMethodCheck, 871 inferenceContext, rs.structuralReferenceChooser).fst; 872 switch (lookupSym.kind) { 873 case WRONG_MTH: 874 case WRONG_MTHS: 875 //note: as argtypes are erroneous types, type-errors must 876 //have been caused by arity mismatch 877 checkContext.report(tree, diags.fragment(Fragments.IncompatibleArgTypesInMref)); 878 break; 879 case ABSENT_MTH: 880 case STATICERR: 881 //if no method found, or method found with wrong staticness, report better message 882 checkContext.report(tree, ((ResolveError)lookupSym).getDiagnostic(DiagnosticType.FRAGMENT, 883 tree, exprTree.type.tsym, exprTree.type, tree.name, argtypes.toList(), List.nil())); 884 break; 885 } 886 } 887 } 888 } 889 890 /* This visitor looks for return statements, its analysis will determine if 891 * a lambda body is void or value compatible. We must analyze return 892 * statements contained in the lambda body only, thus any return statement 893 * contained in an inner class or inner lambda body, should be ignored. 894 */ 895 class LambdaBodyStructChecker extends TreeScanner { 896 boolean isVoidCompatible = true; 897 boolean isPotentiallyValueCompatible = true; 898 899 @Override 900 public void visitClassDef(JCClassDecl tree) { 901 // do nothing 902 } 903 904 @Override 905 public void visitLambda(JCLambda tree) { 906 // do nothing 907 } 908 909 @Override 910 public void visitNewClass(JCNewClass tree) { 911 // do nothing 912 } 913 914 @Override 915 public void visitReturn(JCReturn tree) { 916 if (tree.expr != null) { 917 isVoidCompatible = false; 918 } else { 919 isPotentiallyValueCompatible = false; 920 } 921 } 922 } 923 } 924 925 /** an empty deferred attribution context - all methods throw exceptions */ 926 final DeferredAttrContext emptyDeferredAttrContext; 927 928 /** 929 * Map a list of types possibly containing one or more deferred types 930 * into a list of ordinary types. Each deferred type D is mapped into a type T, 931 * where T is computed by retrieving the type that has already been 932 * computed for D during a previous deferred attribution round of the given kind. 933 */ 934 class DeferredTypeMap extends TypeMapping<Void> { 935 DeferredAttrContext deferredAttrContext; 936 937 protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) { 938 this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, 939 infer.emptyContext, emptyDeferredAttrContext, types.noWarnings); 940 } 941 942 @Override 943 public Type visitType(Type t, Void _unused) { 944 if (!t.hasTag(DEFERRED)) { 945 return super.visitType(t, null); 946 } else { 947 DeferredType dt = (DeferredType)t; 948 return typeOf(dt); 949 } 950 } 951 952 protected Type typeOf(DeferredType dt) { 953 switch (deferredAttrContext.mode) { 954 case CHECK: 955 return dt.tree.type == null ? Type.noType : dt.tree.type; 956 case SPECULATIVE: 957 return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase); 958 } 959 Assert.error(); 960 return null; 961 } 962 } 963 964 /** 965 * Specialized recovery deferred mapping. 966 * Each deferred type D is mapped into a type T, where T is computed either by 967 * (i) retrieving the type that has already been computed for D during a previous 968 * attribution round (as before), or (ii) by synthesizing a new type R for D 969 * (the latter step is useful in a recovery scenario). 970 */ 971 public class RecoveryDeferredTypeMap extends DeferredTypeMap { 972 973 public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) { 974 super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX); 975 } 976 977 @Override 978 protected Type typeOf(DeferredType dt) { 979 Type owntype = super.typeOf(dt); 980 return owntype == Type.noType ? 981 recover(dt) : owntype; 982 } 983 984 /** 985 * Synthesize a type for a deferred type that hasn't been previously 986 * reduced to an ordinary type. Functional deferred types and conditionals 987 * are mapped to themselves, in order to have a richer diagnostic 988 * representation. Remaining deferred types are attributed using 989 * a default expected type (j.l.Object). 990 */ 991 private Type recover(DeferredType dt) { 992 dt.check(attr.new RecoveryInfo(deferredAttrContext) { 993 @Override 994 protected Type check(DiagnosticPosition pos, Type found) { 995 return chk.checkNonVoid(pos, super.check(pos, found)); 996 } 997 }); 998 return super.visit(dt); 999 } 1000 } 1001 1002 /** 1003 * A special tree scanner that would only visit portions of a given tree. 1004 * The set of nodes visited by the scanner can be customized at construction-time. 1005 */ 1006 abstract static class FilterScanner extends com.sun.tools.javac.tree.TreeScanner { 1007 1008 final Filter<JCTree> treeFilter; 1009 1010 FilterScanner(final Set<JCTree.Tag> validTags) { 1011 this.treeFilter = new Filter<JCTree>() { 1012 public boolean accepts(JCTree t) { 1013 return validTags.contains(t.getTag()); 1014 } 1015 }; 1016 } 1017 1018 @Override 1019 public void scan(JCTree tree) { 1020 if (tree != null) { 1021 if (treeFilter.accepts(tree)) { 1022 super.scan(tree); 1023 } else { 1024 skip(tree); 1025 } 1026 } 1027 } 1028 1029 /** 1030 * handler that is executed when a node has been discarded 1031 */ 1032 void skip(JCTree tree) {} 1033 } 1034 1035 /** 1036 * A tree scanner suitable for visiting the target-type dependent nodes of 1037 * a given argument expression. 1038 */ 1039 static class PolyScanner extends FilterScanner { 1040 1041 PolyScanner() { 1042 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE)); 1043 } 1044 } 1045 1046 /** 1047 * A tree scanner suitable for visiting the target-type dependent nodes nested 1048 * within a lambda expression body. 1049 */ 1050 static class LambdaReturnScanner extends FilterScanner { 1051 1052 LambdaReturnScanner() { 1053 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP, 1054 FORLOOP, IF, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP)); 1055 } 1056 } 1057 1058 /** 1059 * This visitor is used to check that structural expressions conform 1060 * to their target - this step is required as inference could end up 1061 * inferring types that make some of the nested expressions incompatible 1062 * with their corresponding instantiated target 1063 */ 1064 class CheckStuckPolicy extends PolyScanner implements DeferredStuckPolicy, Infer.FreeTypeListener { 1065 1066 Type pt; 1067 InferenceContext inferenceContext; 1068 Set<Type> stuckVars = new LinkedHashSet<>(); 1069 Set<Type> depVars = new LinkedHashSet<>(); 1070 1071 @Override 1072 public boolean isStuck() { 1073 return !stuckVars.isEmpty(); 1074 } 1075 1076 @Override 1077 public Set<Type> stuckVars() { 1078 return stuckVars; 1079 } 1080 1081 @Override 1082 public Set<Type> depVars() { 1083 return depVars; 1084 } 1085 1086 public CheckStuckPolicy(ResultInfo resultInfo, DeferredType dt) { 1087 this.pt = resultInfo.pt; 1088 this.inferenceContext = resultInfo.checkContext.inferenceContext(); 1089 scan(dt.tree); 1090 if (!stuckVars.isEmpty()) { 1091 resultInfo.checkContext.inferenceContext() 1092 .addFreeTypeListener(List.from(stuckVars), this); 1093 } 1094 } 1095 1096 @Override 1097 public void typesInferred(InferenceContext inferenceContext) { 1098 stuckVars.clear(); 1099 } 1100 1101 @Override 1102 public void visitLambda(JCLambda tree) { 1103 if (inferenceContext.inferenceVars().contains(pt)) { 1104 stuckVars.add(pt); 1105 } 1106 if (!types.isFunctionalInterface(pt)) { 1107 return; 1108 } 1109 Type descType = types.findDescriptorType(pt); 1110 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); 1111 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT && 1112 freeArgVars.nonEmpty()) { 1113 stuckVars.addAll(freeArgVars); 1114 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); 1115 } 1116 scanLambdaBody(tree, descType.getReturnType()); 1117 } 1118 1119 @Override 1120 public void visitReference(JCMemberReference tree) { 1121 scan(tree.expr); 1122 if (inferenceContext.inferenceVars().contains(pt)) { 1123 stuckVars.add(pt); 1124 return; 1125 } 1126 if (!types.isFunctionalInterface(pt)) { 1127 return; 1128 } 1129 1130 Type descType = types.findDescriptorType(pt); 1131 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); 1132 if (freeArgVars.nonEmpty() && 1133 tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) { 1134 stuckVars.addAll(freeArgVars); 1135 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); 1136 } 1137 } 1138 1139 void scanLambdaBody(JCLambda lambda, final Type pt) { 1140 if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { 1141 Type prevPt = this.pt; 1142 try { 1143 this.pt = pt; 1144 scan(lambda.body); 1145 } finally { 1146 this.pt = prevPt; 1147 } 1148 } else { 1149 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() { 1150 @Override 1151 public void visitReturn(JCReturn tree) { 1152 if (tree.expr != null) { 1153 Type prevPt = CheckStuckPolicy.this.pt; 1154 try { 1155 CheckStuckPolicy.this.pt = pt; 1156 CheckStuckPolicy.this.scan(tree.expr); 1157 } finally { 1158 CheckStuckPolicy.this.pt = prevPt; 1159 } 1160 } 1161 } 1162 }; 1163 lambdaScanner.scan(lambda.body); 1164 } 1165 } 1166 } 1167 1168 /** 1169 * This visitor is used to check that structural expressions conform 1170 * to their target - this step is required as inference could end up 1171 * inferring types that make some of the nested expressions incompatible 1172 * with their corresponding instantiated target 1173 */ 1174 class OverloadStuckPolicy extends CheckStuckPolicy implements DeferredStuckPolicy { 1175 1176 boolean stuck; 1177 1178 @Override 1179 public boolean isStuck() { 1180 return super.isStuck() || stuck; 1181 } 1182 1183 public OverloadStuckPolicy(ResultInfo resultInfo, DeferredType dt) { 1184 super(resultInfo, dt); 1185 } 1186 1187 @Override 1188 public void visitLambda(JCLambda tree) { 1189 super.visitLambda(tree); 1190 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT) { 1191 stuck = true; 1192 } 1193 } 1194 1195 @Override 1196 public void visitReference(JCMemberReference tree) { 1197 super.visitReference(tree); 1198 if (tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) { 1199 stuck = true; 1200 } 1201 } 1202 } 1203} 1204