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