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