DeferredAttr.java revision 3827:44bdefe64114
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, d -> { 511 PosScanner posScanner = new PosScanner(d.getDiagnosticPosition()); 512 posScanner.scan(newTree); 513 return posScanner.found; 514 }); 515 } 516 } 517 518 /** 519 * A deferred context is created on each method check. A deferred context is 520 * used to keep track of information associated with the method check, such as 521 * the symbol of the method being checked, the overload resolution phase, 522 * the kind of attribution mode to be applied to deferred types and so forth. 523 * As deferred types are processed (by the method check routine) stuck AST nodes 524 * are added (as new deferred attribution nodes) to this context. The complete() 525 * routine makes sure that all pending nodes are properly processed, by 526 * progressively instantiating all inference variables on which one or more 527 * deferred attribution node is stuck. 528 */ 529 class DeferredAttrContext { 530 531 /** attribution mode */ 532 final AttrMode mode; 533 534 /** symbol of the method being checked */ 535 final Symbol msym; 536 537 /** method resolution step */ 538 final Resolve.MethodResolutionPhase phase; 539 540 /** inference context */ 541 final InferenceContext inferenceContext; 542 543 /** parent deferred context */ 544 final DeferredAttrContext parent; 545 546 /** Warner object to report warnings */ 547 final Warner warn; 548 549 /** list of deferred attribution nodes to be processed */ 550 ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<>(); 551 552 DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, 553 InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) { 554 this.mode = mode; 555 this.msym = msym; 556 this.phase = phase; 557 this.parent = parent; 558 this.warn = warn; 559 this.inferenceContext = inferenceContext; 560 } 561 562 /** 563 * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable 564 * Nodes added this way act as 'roots' for the out-of-order method checking process. 565 */ 566 void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, 567 DeferredStuckPolicy deferredStuckPolicy) { 568 deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, deferredStuckPolicy)); 569 } 570 571 /** 572 * Incrementally process all nodes, by skipping 'stuck' nodes and attributing 573 * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes) 574 * some inference variable might get eagerly instantiated so that all nodes 575 * can be type-checked. 576 */ 577 void complete() { 578 while (!deferredAttrNodes.isEmpty()) { 579 boolean progress = false; 580 //scan a defensive copy of the node list - this is because a deferred 581 //attribution round can add new nodes to the list 582 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) { 583 if (deferredAttrNode.process(this)) { 584 deferredAttrNodes.remove(deferredAttrNode); 585 progress = true; 586 } 587 } 588 if (!progress) { 589 if (insideOverloadPhase()) { 590 for (DeferredAttrNode deferredNode: deferredAttrNodes) { 591 deferredNode.dt.tree.type = Type.noType; 592 } 593 return; 594 } 595 //remove all variables that have already been instantiated 596 //from the list of stuck variables 597 try { 598 //find stuck expression to unstuck 599 DeferredAttrNode toUnstuck = pickDeferredNode(); 600 inferenceContext.solveAny(List.from(toUnstuck.deferredStuckPolicy.stuckVars()), warn); 601 inferenceContext.notifyChange(); 602 } catch (Infer.GraphStrategy.NodeNotFoundException ex) { 603 //this means that we are in speculative mode and the 604 //set of contraints are too tight for progess to be made. 605 //Just leave the remaining expressions as stuck. 606 break; 607 } 608 } 609 } 610 } 611 612 public boolean insideOverloadPhase() { 613 DeferredAttrContext dac = this; 614 if (dac == emptyDeferredAttrContext) { 615 return false; 616 } 617 if (dac.mode == AttrMode.SPECULATIVE) { 618 return true; 619 } 620 return dac.parent.insideOverloadPhase(); 621 } 622 623 /** 624 * Pick the deferred node to be unstuck. The chosen node is the first strongly connected 625 * component containing exactly one node found in the dependency graph induced by deferred nodes. 626 * If no such component is found, the first deferred node is returned. 627 */ 628 DeferredAttrNode pickDeferredNode() { 629 List<StuckNode> nodes = deferredAttrNodes.stream() 630 .map(StuckNode::new) 631 .collect(List.collector()); 632 //init stuck expression graph; a deferred node A depends on a deferred node B iff 633 //the intersection between A's input variable and B's output variable is non-empty. 634 for (StuckNode sn1 : nodes) { 635 for (Type t : sn1.data.deferredStuckPolicy.stuckVars()) { 636 for (StuckNode sn2 : nodes) { 637 if (sn1 != sn2 && sn2.data.deferredStuckPolicy.depVars().contains(t)) { 638 sn1.deps.add(sn2); 639 } 640 } 641 } 642 } 643 //compute tarjan on the stuck graph 644 List<? extends StuckNode> csn = GraphUtils.tarjan(nodes).get(0); 645 return csn.length() == 1 ? csn.get(0).data : deferredAttrNodes.get(0); 646 } 647 648 class StuckNode extends GraphUtils.TarjanNode<DeferredAttrNode, StuckNode> { 649 650 Set<StuckNode> deps = new HashSet<>(); 651 652 StuckNode(DeferredAttrNode data) { 653 super(data); 654 } 655 656 @Override 657 public DependencyKind[] getSupportedDependencyKinds() { 658 return new DependencyKind[] { Infer.DependencyKind.STUCK }; 659 } 660 661 @Override 662 public Collection<? extends StuckNode> getDependenciesByKind(DependencyKind dk) { 663 if (dk == Infer.DependencyKind.STUCK) { 664 return deps; 665 } else { 666 throw new IllegalStateException(); 667 } 668 } 669 670 @Override 671 public Iterable<? extends StuckNode> getAllDependencies() { 672 return deps; 673 } 674 } 675 } 676 677 /** 678 * Class representing a deferred attribution node. It keeps track of 679 * a deferred type, along with the expected target type information. 680 */ 681 class DeferredAttrNode { 682 683 /** underlying deferred type */ 684 DeferredType dt; 685 686 /** underlying target type information */ 687 ResultInfo resultInfo; 688 689 /** stuck policy associated with this node */ 690 DeferredStuckPolicy deferredStuckPolicy; 691 692 DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy) { 693 this.dt = dt; 694 this.resultInfo = resultInfo; 695 this.deferredStuckPolicy = deferredStuckPolicy; 696 } 697 698 /** 699 * Process a deferred attribution node. 700 * Invariant: a stuck node cannot be processed. 701 */ 702 @SuppressWarnings("fallthrough") 703 boolean process(final DeferredAttrContext deferredAttrContext) { 704 switch (deferredAttrContext.mode) { 705 case SPECULATIVE: 706 if (deferredStuckPolicy.isStuck()) { 707 dt.check(resultInfo, dummyStuckPolicy, new StructuralStuckChecker()); 708 return true; 709 } else { 710 Assert.error("Cannot get here"); 711 } 712 case CHECK: 713 if (deferredStuckPolicy.isStuck()) { 714 //stuck expression - see if we can propagate 715 if (deferredAttrContext.parent != emptyDeferredAttrContext && 716 Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, 717 List.from(deferredStuckPolicy.stuckVars()))) { 718 deferredAttrContext.parent.addDeferredAttrNode(dt, 719 resultInfo.dup(new Check.NestedCheckContext(resultInfo.checkContext) { 720 @Override 721 public InferenceContext inferenceContext() { 722 return deferredAttrContext.parent.inferenceContext; 723 } 724 @Override 725 public DeferredAttrContext deferredAttrContext() { 726 return deferredAttrContext.parent; 727 } 728 }), deferredStuckPolicy); 729 dt.tree.type = Type.stuckType; 730 return true; 731 } else { 732 return false; 733 } 734 } else { 735 Assert.check(!deferredAttrContext.insideOverloadPhase(), 736 "attribution shouldn't be happening here"); 737 ResultInfo instResultInfo = 738 resultInfo.dup(deferredAttrContext.inferenceContext.asInstType(resultInfo.pt)); 739 dt.check(instResultInfo, dummyStuckPolicy, basicCompleter); 740 return true; 741 } 742 default: 743 throw new AssertionError("Bad mode"); 744 } 745 } 746 747 /** 748 * Structural checker for stuck expressions 749 */ 750 class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter { 751 752 ResultInfo resultInfo; 753 InferenceContext inferenceContext; 754 Env<AttrContext> env; 755 756 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { 757 this.resultInfo = resultInfo; 758 this.inferenceContext = deferredAttrContext.inferenceContext; 759 this.env = dt.env; 760 dt.tree.accept(this); 761 dt.speculativeCache.put(stuckTree, resultInfo); 762 return Type.noType; 763 } 764 765 @Override 766 public void visitLambda(JCLambda tree) { 767 Check.CheckContext checkContext = resultInfo.checkContext; 768 Type pt = resultInfo.pt; 769 if (!inferenceContext.inferencevars.contains(pt)) { 770 //must be a functional descriptor 771 Type descriptorType = null; 772 try { 773 descriptorType = types.findDescriptorType(pt); 774 } catch (Types.FunctionDescriptorLookupError ex) { 775 checkContext.report(null, ex.getDiagnostic()); 776 } 777 778 if (descriptorType.getParameterTypes().length() != tree.params.length()) { 779 checkContext.report(tree, 780 diags.fragment("incompatible.arg.types.in.lambda")); 781 } 782 783 Type currentReturnType = descriptorType.getReturnType(); 784 boolean returnTypeIsVoid = currentReturnType.hasTag(VOID); 785 if (tree.getBodyKind() == BodyKind.EXPRESSION) { 786 boolean isExpressionCompatible = !returnTypeIsVoid || 787 TreeInfo.isExpressionStatement((JCExpression)tree.getBody()); 788 if (!isExpressionCompatible) { 789 resultInfo.checkContext.report(tree.pos(), 790 diags.fragment("incompatible.ret.type.in.lambda", 791 diags.fragment("missing.ret.val", currentReturnType))); 792 } 793 } else { 794 LambdaBodyStructChecker lambdaBodyChecker = 795 new LambdaBodyStructChecker(); 796 797 tree.body.accept(lambdaBodyChecker); 798 boolean isVoidCompatible = lambdaBodyChecker.isVoidCompatible; 799 800 if (returnTypeIsVoid) { 801 if (!isVoidCompatible) { 802 resultInfo.checkContext.report(tree.pos(), 803 diags.fragment("unexpected.ret.val")); 804 } 805 } else { 806 boolean isValueCompatible = lambdaBodyChecker.isPotentiallyValueCompatible 807 && !canLambdaBodyCompleteNormally(tree); 808 if (!isValueCompatible && !isVoidCompatible) { 809 log.error(tree.body.pos(), 810 "lambda.body.neither.value.nor.void.compatible"); 811 } 812 813 if (!isValueCompatible) { 814 resultInfo.checkContext.report(tree.pos(), 815 diags.fragment("incompatible.ret.type.in.lambda", 816 diags.fragment("missing.ret.val", currentReturnType))); 817 } 818 } 819 } 820 } 821 } 822 823 boolean canLambdaBodyCompleteNormally(JCLambda tree) { 824 List<JCVariableDecl> oldParams = tree.params; 825 LocalCacheContext localCacheContext = argumentAttr.withLocalCacheContext(); 826 try { 827 tree.params = tree.params.stream() 828 .map(vd -> make.VarDef(vd.mods, vd.name, make.Erroneous(), null)) 829 .collect(List.collector()); 830 return attribSpeculativeLambda(tree, env, attr.unknownExprInfo).canCompleteNormally; 831 } finally { 832 localCacheContext.leave(); 833 tree.params = oldParams; 834 } 835 } 836 837 @Override 838 public void visitNewClass(JCNewClass tree) { 839 //do nothing 840 } 841 842 @Override 843 public void visitApply(JCMethodInvocation tree) { 844 //do nothing 845 } 846 847 @Override 848 public void visitReference(JCMemberReference tree) { 849 Check.CheckContext checkContext = resultInfo.checkContext; 850 Type pt = resultInfo.pt; 851 if (!inferenceContext.inferencevars.contains(pt)) { 852 try { 853 types.findDescriptorType(pt); 854 } catch (Types.FunctionDescriptorLookupError ex) { 855 checkContext.report(null, ex.getDiagnostic()); 856 } 857 Env<AttrContext> localEnv = env.dup(tree); 858 JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv, 859 attr.memberReferenceQualifierResult(tree)); 860 ListBuffer<Type> argtypes = new ListBuffer<>(); 861 for (Type t : types.findDescriptorType(pt).getParameterTypes()) { 862 argtypes.append(Type.noType); 863 } 864 JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree); 865 mref2.expr = exprTree; 866 Symbol lookupSym = 867 rs.resolveMemberReference(localEnv, mref2, exprTree.type, 868 tree.name, argtypes.toList(), List.nil(), rs.arityMethodCheck, 869 inferenceContext, rs.structuralReferenceChooser).fst; 870 switch (lookupSym.kind) { 871 case WRONG_MTH: 872 case WRONG_MTHS: 873 //note: as argtypes are erroneous types, type-errors must 874 //have been caused by arity mismatch 875 checkContext.report(tree, diags.fragment(Fragments.IncompatibleArgTypesInMref)); 876 break; 877 case ABSENT_MTH: 878 case STATICERR: 879 //if no method found, or method found with wrong staticness, report better message 880 checkContext.report(tree, ((ResolveError)lookupSym).getDiagnostic(DiagnosticType.FRAGMENT, 881 tree, exprTree.type.tsym, exprTree.type, tree.name, argtypes.toList(), List.nil())); 882 break; 883 } 884 } 885 } 886 } 887 888 /* This visitor looks for return statements, its analysis will determine if 889 * a lambda body is void or value compatible. We must analyze return 890 * statements contained in the lambda body only, thus any return statement 891 * contained in an inner class or inner lambda body, should be ignored. 892 */ 893 class LambdaBodyStructChecker extends TreeScanner { 894 boolean isVoidCompatible = true; 895 boolean isPotentiallyValueCompatible = true; 896 897 @Override 898 public void visitClassDef(JCClassDecl tree) { 899 // do nothing 900 } 901 902 @Override 903 public void visitLambda(JCLambda tree) { 904 // do nothing 905 } 906 907 @Override 908 public void visitNewClass(JCNewClass tree) { 909 // do nothing 910 } 911 912 @Override 913 public void visitReturn(JCReturn tree) { 914 if (tree.expr != null) { 915 isVoidCompatible = false; 916 } else { 917 isPotentiallyValueCompatible = false; 918 } 919 } 920 } 921 } 922 923 /** an empty deferred attribution context - all methods throw exceptions */ 924 final DeferredAttrContext emptyDeferredAttrContext; 925 926 /** 927 * Map a list of types possibly containing one or more deferred types 928 * into a list of ordinary types. Each deferred type D is mapped into a type T, 929 * where T is computed by retrieving the type that has already been 930 * computed for D during a previous deferred attribution round of the given kind. 931 */ 932 class DeferredTypeMap extends TypeMapping<Void> { 933 DeferredAttrContext deferredAttrContext; 934 935 protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) { 936 this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, 937 infer.emptyContext, emptyDeferredAttrContext, types.noWarnings); 938 } 939 940 @Override 941 public Type visitType(Type t, Void _unused) { 942 if (!t.hasTag(DEFERRED)) { 943 return super.visitType(t, null); 944 } else { 945 DeferredType dt = (DeferredType)t; 946 return typeOf(dt); 947 } 948 } 949 950 protected Type typeOf(DeferredType dt) { 951 switch (deferredAttrContext.mode) { 952 case CHECK: 953 return dt.tree.type == null ? Type.noType : dt.tree.type; 954 case SPECULATIVE: 955 return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase); 956 } 957 Assert.error(); 958 return null; 959 } 960 } 961 962 /** 963 * Specialized recovery deferred mapping. 964 * Each deferred type D is mapped into a type T, where T is computed either by 965 * (i) retrieving the type that has already been computed for D during a previous 966 * attribution round (as before), or (ii) by synthesizing a new type R for D 967 * (the latter step is useful in a recovery scenario). 968 */ 969 public class RecoveryDeferredTypeMap extends DeferredTypeMap { 970 971 public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) { 972 super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX); 973 } 974 975 @Override 976 protected Type typeOf(DeferredType dt) { 977 Type owntype = super.typeOf(dt); 978 return owntype == Type.noType ? 979 recover(dt) : owntype; 980 } 981 982 /** 983 * Synthesize a type for a deferred type that hasn't been previously 984 * reduced to an ordinary type. Functional deferred types and conditionals 985 * are mapped to themselves, in order to have a richer diagnostic 986 * representation. Remaining deferred types are attributed using 987 * a default expected type (j.l.Object). 988 */ 989 private Type recover(DeferredType dt) { 990 dt.check(attr.new RecoveryInfo(deferredAttrContext) { 991 @Override 992 protected Type check(DiagnosticPosition pos, Type found) { 993 return chk.checkNonVoid(pos, super.check(pos, found)); 994 } 995 }); 996 return super.visit(dt); 997 } 998 } 999 1000 /** 1001 * A special tree scanner that would only visit portions of a given tree. 1002 * The set of nodes visited by the scanner can be customized at construction-time. 1003 */ 1004 abstract static class FilterScanner extends com.sun.tools.javac.tree.TreeScanner { 1005 1006 final Filter<JCTree> treeFilter; 1007 1008 FilterScanner(final Set<JCTree.Tag> validTags) { 1009 this.treeFilter = t -> validTags.contains(t.getTag()); 1010 } 1011 1012 @Override 1013 public void scan(JCTree tree) { 1014 if (tree != null) { 1015 if (treeFilter.accepts(tree)) { 1016 super.scan(tree); 1017 } else { 1018 skip(tree); 1019 } 1020 } 1021 } 1022 1023 /** 1024 * handler that is executed when a node has been discarded 1025 */ 1026 void skip(JCTree tree) {} 1027 } 1028 1029 /** 1030 * A tree scanner suitable for visiting the target-type dependent nodes of 1031 * a given argument expression. 1032 */ 1033 static class PolyScanner extends FilterScanner { 1034 1035 PolyScanner() { 1036 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE)); 1037 } 1038 } 1039 1040 /** 1041 * A tree scanner suitable for visiting the target-type dependent nodes nested 1042 * within a lambda expression body. 1043 */ 1044 static class LambdaReturnScanner extends FilterScanner { 1045 1046 LambdaReturnScanner() { 1047 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP, 1048 FORLOOP, IF, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP)); 1049 } 1050 } 1051 1052 /** 1053 * This visitor is used to check that structural expressions conform 1054 * to their target - this step is required as inference could end up 1055 * inferring types that make some of the nested expressions incompatible 1056 * with their corresponding instantiated target 1057 */ 1058 class CheckStuckPolicy extends PolyScanner implements DeferredStuckPolicy, Infer.FreeTypeListener { 1059 1060 Type pt; 1061 InferenceContext inferenceContext; 1062 Set<Type> stuckVars = new LinkedHashSet<>(); 1063 Set<Type> depVars = new LinkedHashSet<>(); 1064 1065 @Override 1066 public boolean isStuck() { 1067 return !stuckVars.isEmpty(); 1068 } 1069 1070 @Override 1071 public Set<Type> stuckVars() { 1072 return stuckVars; 1073 } 1074 1075 @Override 1076 public Set<Type> depVars() { 1077 return depVars; 1078 } 1079 1080 public CheckStuckPolicy(ResultInfo resultInfo, DeferredType dt) { 1081 this.pt = resultInfo.pt; 1082 this.inferenceContext = resultInfo.checkContext.inferenceContext(); 1083 scan(dt.tree); 1084 if (!stuckVars.isEmpty()) { 1085 resultInfo.checkContext.inferenceContext() 1086 .addFreeTypeListener(List.from(stuckVars), this); 1087 } 1088 } 1089 1090 @Override 1091 public void typesInferred(InferenceContext inferenceContext) { 1092 stuckVars.clear(); 1093 } 1094 1095 @Override 1096 public void visitLambda(JCLambda tree) { 1097 if (inferenceContext.inferenceVars().contains(pt)) { 1098 stuckVars.add(pt); 1099 } 1100 if (!types.isFunctionalInterface(pt)) { 1101 return; 1102 } 1103 Type descType = types.findDescriptorType(pt); 1104 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); 1105 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT && 1106 freeArgVars.nonEmpty()) { 1107 stuckVars.addAll(freeArgVars); 1108 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); 1109 } 1110 scanLambdaBody(tree, descType.getReturnType()); 1111 } 1112 1113 @Override 1114 public void visitReference(JCMemberReference tree) { 1115 scan(tree.expr); 1116 if (inferenceContext.inferenceVars().contains(pt)) { 1117 stuckVars.add(pt); 1118 return; 1119 } 1120 if (!types.isFunctionalInterface(pt)) { 1121 return; 1122 } 1123 1124 Type descType = types.findDescriptorType(pt); 1125 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); 1126 if (freeArgVars.nonEmpty() && 1127 tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) { 1128 stuckVars.addAll(freeArgVars); 1129 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); 1130 } 1131 } 1132 1133 void scanLambdaBody(JCLambda lambda, final Type pt) { 1134 if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { 1135 Type prevPt = this.pt; 1136 try { 1137 this.pt = pt; 1138 scan(lambda.body); 1139 } finally { 1140 this.pt = prevPt; 1141 } 1142 } else { 1143 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() { 1144 @Override 1145 public void visitReturn(JCReturn tree) { 1146 if (tree.expr != null) { 1147 Type prevPt = CheckStuckPolicy.this.pt; 1148 try { 1149 CheckStuckPolicy.this.pt = pt; 1150 CheckStuckPolicy.this.scan(tree.expr); 1151 } finally { 1152 CheckStuckPolicy.this.pt = prevPt; 1153 } 1154 } 1155 } 1156 }; 1157 lambdaScanner.scan(lambda.body); 1158 } 1159 } 1160 } 1161 1162 /** 1163 * This visitor is used to check that structural expressions conform 1164 * to their target - this step is required as inference could end up 1165 * inferring types that make some of the nested expressions incompatible 1166 * with their corresponding instantiated target 1167 */ 1168 class OverloadStuckPolicy extends CheckStuckPolicy implements DeferredStuckPolicy { 1169 1170 boolean stuck; 1171 1172 @Override 1173 public boolean isStuck() { 1174 return super.isStuck() || stuck; 1175 } 1176 1177 public OverloadStuckPolicy(ResultInfo resultInfo, DeferredType dt) { 1178 super(resultInfo, dt); 1179 } 1180 1181 @Override 1182 public void visitLambda(JCLambda tree) { 1183 super.visitLambda(tree); 1184 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT) { 1185 stuck = true; 1186 } 1187 } 1188 1189 @Override 1190 public void visitReference(JCMemberReference tree) { 1191 super.visitReference(tree); 1192 if (tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) { 1193 stuck = true; 1194 } 1195 } 1196 } 1197} 1198