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