DeferredAttr.java revision 2646:ff1998c1ecab
1/* 2 * Copyright (c) 2012, 2014, 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.tools.javac.code.*; 30import com.sun.tools.javac.tree.*; 31import com.sun.tools.javac.util.*; 32import com.sun.tools.javac.util.DefinedBy.Api; 33import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 34import com.sun.tools.javac.code.Symbol.*; 35import com.sun.tools.javac.code.Type.*; 36import com.sun.tools.javac.comp.Attr.ResultInfo; 37import com.sun.tools.javac.comp.Infer.InferenceContext; 38import com.sun.tools.javac.comp.Resolve.MethodResolutionPhase; 39import com.sun.tools.javac.tree.JCTree.*; 40 41import java.util.ArrayList; 42import java.util.Collections; 43import java.util.EnumSet; 44import java.util.LinkedHashMap; 45import java.util.LinkedHashSet; 46import java.util.Map; 47import java.util.Set; 48import java.util.WeakHashMap; 49 50import static com.sun.tools.javac.code.Kinds.VAL; 51import static com.sun.tools.javac.code.TypeTag.*; 52import static com.sun.tools.javac.tree.JCTree.Tag.*; 53 54/** 55 * This is an helper class that is used to perform deferred type-analysis. 56 * Each time a poly expression occurs in argument position, javac attributes it 57 * with a temporary 'deferred type' that is checked (possibly multiple times) 58 * against an expected formal type. 59 * 60 * <p><b>This is NOT part of any supported API. 61 * If you write code that depends on this, you do so at your own risk. 62 * This code and its internal interfaces are subject to change or 63 * deletion without notice.</b> 64 */ 65public class DeferredAttr extends JCTree.Visitor { 66 protected static final Context.Key<DeferredAttr> deferredAttrKey = new Context.Key<>(); 67 68 final Attr attr; 69 final Check chk; 70 final JCDiagnostic.Factory diags; 71 final Enter enter; 72 final Infer infer; 73 final Resolve rs; 74 final Log log; 75 final Symtab syms; 76 final TreeMaker make; 77 final Types types; 78 final Flow flow; 79 final Names names; 80 final TypeEnvs typeEnvs; 81 82 public static DeferredAttr instance(Context context) { 83 DeferredAttr instance = context.get(deferredAttrKey); 84 if (instance == null) 85 instance = new DeferredAttr(context); 86 return instance; 87 } 88 89 protected DeferredAttr(Context context) { 90 context.put(deferredAttrKey, this); 91 attr = Attr.instance(context); 92 chk = Check.instance(context); 93 diags = JCDiagnostic.Factory.instance(context); 94 enter = Enter.instance(context); 95 infer = Infer.instance(context); 96 rs = Resolve.instance(context); 97 log = Log.instance(context); 98 syms = Symtab.instance(context); 99 make = TreeMaker.instance(context); 100 types = Types.instance(context); 101 flow = Flow.instance(context); 102 names = Names.instance(context); 103 stuckTree = make.Ident(names.empty).setType(Type.stuckType); 104 typeEnvs = TypeEnvs.instance(context); 105 emptyDeferredAttrContext = 106 new DeferredAttrContext(AttrMode.CHECK, null, MethodResolutionPhase.BOX, infer.emptyContext, null, null) { 107 @Override 108 void addDeferredAttrNode(DeferredType dt, ResultInfo ri, DeferredStuckPolicy deferredStuckPolicy) { 109 Assert.error("Empty deferred context!"); 110 } 111 @Override 112 void complete() { 113 Assert.error("Empty deferred context!"); 114 } 115 116 @Override 117 public String toString() { 118 return "Empty deferred context!"; 119 } 120 }; 121 } 122 123 /** shared tree for stuck expressions */ 124 final JCTree stuckTree; 125 126 /** 127 * This type represents a deferred type. A deferred type starts off with 128 * no information on the underlying expression type. Such info needs to be 129 * discovered through type-checking the deferred type against a target-type. 130 * Every deferred type keeps a pointer to the AST node from which it originated. 131 */ 132 public class DeferredType extends Type { 133 134 public JCExpression tree; 135 Env<AttrContext> env; 136 AttrMode mode; 137 SpeculativeCache speculativeCache; 138 139 DeferredType(JCExpression tree, Env<AttrContext> env) { 140 super(null, TypeMetadata.empty); 141 this.tree = tree; 142 this.env = attr.copyEnv(env); 143 this.speculativeCache = new SpeculativeCache(); 144 } 145 146 @Override 147 public DeferredType clone(TypeMetadata md) { 148 throw new AssertionError("Cannot add metadata to a deferred type"); 149 } 150 151 @Override 152 public TypeTag getTag() { 153 return DEFERRED; 154 } 155 156 @Override @DefinedBy(Api.LANGUAGE_MODEL) 157 public String toString() { 158 return "DeferredType"; 159 } 160 161 /** 162 * A speculative cache is used to keep track of all overload resolution rounds 163 * that triggered speculative attribution on a given deferred type. Each entry 164 * stores a pointer to the speculative tree and the resolution phase in which the entry 165 * has been added. 166 */ 167 class SpeculativeCache { 168 169 private Map<Symbol, List<Entry>> cache = new WeakHashMap<>(); 170 171 class Entry { 172 JCTree speculativeTree; 173 ResultInfo resultInfo; 174 175 public Entry(JCTree speculativeTree, ResultInfo resultInfo) { 176 this.speculativeTree = speculativeTree; 177 this.resultInfo = resultInfo; 178 } 179 180 boolean matches(MethodResolutionPhase phase) { 181 return resultInfo.checkContext.deferredAttrContext().phase == phase; 182 } 183 } 184 185 /** 186 * Retrieve a speculative cache entry corresponding to given symbol 187 * and resolution phase 188 */ 189 Entry get(Symbol msym, MethodResolutionPhase phase) { 190 List<Entry> entries = cache.get(msym); 191 if (entries == null) return null; 192 for (Entry e : entries) { 193 if (e.matches(phase)) return e; 194 } 195 return null; 196 } 197 198 /** 199 * Stores a speculative cache entry corresponding to given symbol 200 * and resolution phase 201 */ 202 void put(JCTree speculativeTree, ResultInfo resultInfo) { 203 Symbol msym = resultInfo.checkContext.deferredAttrContext().msym; 204 List<Entry> entries = cache.get(msym); 205 if (entries == null) { 206 entries = List.nil(); 207 } 208 cache.put(msym, entries.prepend(new Entry(speculativeTree, resultInfo))); 209 } 210 } 211 212 /** 213 * Get the type that has been computed during a speculative attribution round 214 */ 215 Type speculativeType(Symbol msym, MethodResolutionPhase phase) { 216 SpeculativeCache.Entry e = speculativeCache.get(msym, phase); 217 return e != null ? e.speculativeTree.type : Type.noType; 218 } 219 220 /** 221 * Check a deferred type against a potential target-type. Depending on 222 * the current attribution mode, a normal vs. speculative attribution 223 * round is performed on the underlying AST node. There can be only one 224 * speculative round for a given target method symbol; moreover, a normal 225 * attribution round must follow one or more speculative rounds. 226 */ 227 Type check(ResultInfo resultInfo) { 228 DeferredStuckPolicy deferredStuckPolicy; 229 if (resultInfo.pt.hasTag(NONE) || resultInfo.pt.isErroneous()) { 230 deferredStuckPolicy = dummyStuckPolicy; 231 } else if (resultInfo.checkContext.deferredAttrContext().mode == AttrMode.SPECULATIVE) { 232 deferredStuckPolicy = new OverloadStuckPolicy(resultInfo, this); 233 } else { 234 deferredStuckPolicy = new CheckStuckPolicy(resultInfo, this); 235 } 236 return check(resultInfo, deferredStuckPolicy, basicCompleter); 237 } 238 239 private Type check(ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy, 240 DeferredTypeCompleter deferredTypeCompleter) { 241 DeferredAttrContext deferredAttrContext = 242 resultInfo.checkContext.deferredAttrContext(); 243 Assert.check(deferredAttrContext != emptyDeferredAttrContext); 244 if (deferredStuckPolicy.isStuck()) { 245 deferredAttrContext.addDeferredAttrNode(this, resultInfo, deferredStuckPolicy); 246 return Type.noType; 247 } else { 248 try { 249 return deferredTypeCompleter.complete(this, resultInfo, deferredAttrContext); 250 } finally { 251 mode = deferredAttrContext.mode; 252 } 253 } 254 } 255 } 256 257 /** 258 * A completer for deferred types. Defines an entry point for type-checking 259 * a deferred type. 260 */ 261 interface DeferredTypeCompleter { 262 /** 263 * Entry point for type-checking a deferred type. Depending on the 264 * circumstances, type-checking could amount to full attribution 265 * or partial structural check (aka potential applicability). 266 */ 267 Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext); 268 } 269 270 271 /** 272 * A basic completer for deferred types. This completer type-checks a deferred type 273 * using attribution; depending on the attribution mode, this could be either standard 274 * or speculative attribution. 275 */ 276 DeferredTypeCompleter basicCompleter = new DeferredTypeCompleter() { 277 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { 278 switch (deferredAttrContext.mode) { 279 case SPECULATIVE: 280 //Note: if a symbol is imported twice we might do two identical 281 //speculative rounds... 282 Assert.check(dt.mode == null || dt.mode == AttrMode.SPECULATIVE); 283 JCTree speculativeTree = attribSpeculative(dt.tree, dt.env, resultInfo); 284 dt.speculativeCache.put(speculativeTree, resultInfo); 285 return speculativeTree.type; 286 case CHECK: 287 Assert.check(dt.mode != null); 288 return attr.attribTree(dt.tree, dt.env, resultInfo); 289 } 290 Assert.error(); 291 return null; 292 } 293 }; 294 295 DeferredTypeCompleter dummyCompleter = new DeferredTypeCompleter() { 296 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { 297 Assert.check(deferredAttrContext.mode == AttrMode.CHECK); 298 return dt.tree.type = Type.stuckType; 299 } 300 }; 301 302 /** 303 * Policy for detecting stuck expressions. Different criteria might cause 304 * an expression to be judged as stuck, depending on whether the check 305 * is performed during overload resolution or after most specific. 306 */ 307 interface DeferredStuckPolicy { 308 /** 309 * Has the policy detected that a given expression should be considered stuck? 310 */ 311 boolean isStuck(); 312 /** 313 * Get the set of inference variables a given expression depends upon. 314 */ 315 Set<Type> stuckVars(); 316 /** 317 * Get the set of inference variables which might get new constraints 318 * if a given expression is being type-checked. 319 */ 320 Set<Type> depVars(); 321 } 322 323 /** 324 * Basic stuck policy; an expression is never considered to be stuck. 325 */ 326 DeferredStuckPolicy dummyStuckPolicy = new DeferredStuckPolicy() { 327 @Override 328 public boolean isStuck() { 329 return false; 330 } 331 @Override 332 public Set<Type> stuckVars() { 333 return Collections.emptySet(); 334 } 335 @Override 336 public Set<Type> depVars() { 337 return Collections.emptySet(); 338 } 339 }; 340 341 /** 342 * The 'mode' in which the deferred type is to be type-checked 343 */ 344 public enum AttrMode { 345 /** 346 * A speculative type-checking round is used during overload resolution 347 * mainly to generate constraints on inference variables. Side-effects 348 * arising from type-checking the expression associated with the deferred 349 * type are reversed after the speculative round finishes. This means the 350 * expression tree will be left in a blank state. 351 */ 352 SPECULATIVE, 353 /** 354 * This is the plain type-checking mode. Produces side-effects on the underlying AST node 355 */ 356 CHECK 357 } 358 359 /** 360 * Routine that performs speculative type-checking; the input AST node is 361 * cloned (to avoid side-effects cause by Attr) and compiler state is 362 * restored after type-checking. All diagnostics (but critical ones) are 363 * disabled during speculative type-checking. 364 */ 365 JCTree attribSpeculative(JCTree tree, Env<AttrContext> env, ResultInfo resultInfo) { 366 final JCTree newTree = new TreeCopier<>(make).copy(tree); 367 Env<AttrContext> speculativeEnv = env.dup(newTree, env.info.dup(env.info.scope.dupUnshared(env.info.scope.owner))); 368 Log.DeferredDiagnosticHandler deferredDiagnosticHandler = 369 new Log.DeferredDiagnosticHandler(log, new Filter<JCDiagnostic>() { 370 public boolean accepts(final JCDiagnostic d) { 371 class PosScanner extends TreeScanner { 372 boolean found = false; 373 374 @Override 375 public void scan(JCTree tree) { 376 if (tree != null && 377 tree.pos() == d.getDiagnosticPosition()) { 378 found = true; 379 } 380 super.scan(tree); 381 } 382 } 383 PosScanner posScanner = new PosScanner(); 384 posScanner.scan(newTree); 385 return posScanner.found; 386 } 387 }); 388 try { 389 attr.attribTree(newTree, speculativeEnv, resultInfo); 390 unenterScanner.scan(newTree); 391 return newTree; 392 } finally { 393 unenterScanner.scan(newTree); 394 log.popDiagnosticHandler(deferredDiagnosticHandler); 395 } 396 } 397 //where 398 protected UnenterScanner unenterScanner = new UnenterScanner(); 399 400 class UnenterScanner extends TreeScanner { 401 @Override 402 public void visitClassDef(JCClassDecl tree) { 403 ClassSymbol csym = tree.sym; 404 //if something went wrong during method applicability check 405 //it is possible that nested expressions inside argument expression 406 //are left unchecked - in such cases there's nothing to clean up. 407 if (csym == null) return; 408 typeEnvs.remove(csym); 409 chk.compiled.remove(csym.flatname); 410 syms.classes.remove(csym.flatname); 411 super.visitClassDef(tree); 412 } 413 } 414 415 /** 416 * A deferred context is created on each method check. A deferred context is 417 * used to keep track of information associated with the method check, such as 418 * the symbol of the method being checked, the overload resolution phase, 419 * the kind of attribution mode to be applied to deferred types and so forth. 420 * As deferred types are processed (by the method check routine) stuck AST nodes 421 * are added (as new deferred attribution nodes) to this context. The complete() 422 * routine makes sure that all pending nodes are properly processed, by 423 * progressively instantiating all inference variables on which one or more 424 * deferred attribution node is stuck. 425 */ 426 class DeferredAttrContext { 427 428 /** attribution mode */ 429 final AttrMode mode; 430 431 /** symbol of the method being checked */ 432 final Symbol msym; 433 434 /** method resolution step */ 435 final Resolve.MethodResolutionPhase phase; 436 437 /** inference context */ 438 final InferenceContext inferenceContext; 439 440 /** parent deferred context */ 441 final DeferredAttrContext parent; 442 443 /** Warner object to report warnings */ 444 final Warner warn; 445 446 /** list of deferred attribution nodes to be processed */ 447 ArrayList<DeferredAttrNode> deferredAttrNodes = new ArrayList<>(); 448 449 DeferredAttrContext(AttrMode mode, Symbol msym, MethodResolutionPhase phase, 450 InferenceContext inferenceContext, DeferredAttrContext parent, Warner warn) { 451 this.mode = mode; 452 this.msym = msym; 453 this.phase = phase; 454 this.parent = parent; 455 this.warn = warn; 456 this.inferenceContext = inferenceContext; 457 } 458 459 /** 460 * Adds a node to the list of deferred attribution nodes - used by Resolve.rawCheckArgumentsApplicable 461 * Nodes added this way act as 'roots' for the out-of-order method checking process. 462 */ 463 void addDeferredAttrNode(final DeferredType dt, ResultInfo resultInfo, 464 DeferredStuckPolicy deferredStuckPolicy) { 465 deferredAttrNodes.add(new DeferredAttrNode(dt, resultInfo, deferredStuckPolicy)); 466 } 467 468 /** 469 * Incrementally process all nodes, by skipping 'stuck' nodes and attributing 470 * 'unstuck' ones. If at any point no progress can be made (no 'unstuck' nodes) 471 * some inference variable might get eagerly instantiated so that all nodes 472 * can be type-checked. 473 */ 474 void complete() { 475 while (!deferredAttrNodes.isEmpty()) { 476 Map<Type, Set<Type>> depVarsMap = new LinkedHashMap<>(); 477 List<Type> stuckVars = List.nil(); 478 boolean progress = false; 479 //scan a defensive copy of the node list - this is because a deferred 480 //attribution round can add new nodes to the list 481 for (DeferredAttrNode deferredAttrNode : List.from(deferredAttrNodes)) { 482 if (!deferredAttrNode.process(this)) { 483 List<Type> restStuckVars = 484 List.from(deferredAttrNode.deferredStuckPolicy.stuckVars()) 485 .intersect(inferenceContext.restvars()); 486 stuckVars = stuckVars.prependList(restStuckVars); 487 //update dependency map 488 for (Type t : List.from(deferredAttrNode.deferredStuckPolicy.depVars()) 489 .intersect(inferenceContext.restvars())) { 490 Set<Type> prevDeps = depVarsMap.get(t); 491 if (prevDeps == null) { 492 prevDeps = new LinkedHashSet<>(); 493 depVarsMap.put(t, prevDeps); 494 } 495 prevDeps.addAll(restStuckVars); 496 } 497 } else { 498 deferredAttrNodes.remove(deferredAttrNode); 499 progress = true; 500 } 501 } 502 if (!progress) { 503 if (insideOverloadPhase()) { 504 for (DeferredAttrNode deferredNode: deferredAttrNodes) { 505 deferredNode.dt.tree.type = Type.noType; 506 } 507 return; 508 } 509 //remove all variables that have already been instantiated 510 //from the list of stuck variables 511 try { 512 inferenceContext.solveAny(stuckVars, depVarsMap, warn); 513 inferenceContext.notifyChange(); 514 } catch (Infer.GraphStrategy.NodeNotFoundException ex) { 515 //this means that we are in speculative mode and the 516 //set of contraints are too tight for progess to be made. 517 //Just leave the remaining expressions as stuck. 518 break; 519 } 520 } 521 } 522 } 523 524 private boolean insideOverloadPhase() { 525 DeferredAttrContext dac = this; 526 if (dac == emptyDeferredAttrContext) { 527 return false; 528 } 529 if (dac.mode == AttrMode.SPECULATIVE) { 530 return true; 531 } 532 return dac.parent.insideOverloadPhase(); 533 } 534 } 535 536 /** 537 * Class representing a deferred attribution node. It keeps track of 538 * a deferred type, along with the expected target type information. 539 */ 540 class DeferredAttrNode { 541 542 /** underlying deferred type */ 543 DeferredType dt; 544 545 /** underlying target type information */ 546 ResultInfo resultInfo; 547 548 /** stuck policy associated with this node */ 549 DeferredStuckPolicy deferredStuckPolicy; 550 551 DeferredAttrNode(DeferredType dt, ResultInfo resultInfo, DeferredStuckPolicy deferredStuckPolicy) { 552 this.dt = dt; 553 this.resultInfo = resultInfo; 554 this.deferredStuckPolicy = deferredStuckPolicy; 555 } 556 557 /** 558 * Process a deferred attribution node. 559 * Invariant: a stuck node cannot be processed. 560 */ 561 @SuppressWarnings("fallthrough") 562 boolean process(final DeferredAttrContext deferredAttrContext) { 563 switch (deferredAttrContext.mode) { 564 case SPECULATIVE: 565 if (deferredStuckPolicy.isStuck()) { 566 dt.check(resultInfo, dummyStuckPolicy, new StructuralStuckChecker()); 567 return true; 568 } else { 569 Assert.error("Cannot get here"); 570 } 571 case CHECK: 572 if (deferredStuckPolicy.isStuck()) { 573 //stuck expression - see if we can propagate 574 if (deferredAttrContext.parent != emptyDeferredAttrContext && 575 Type.containsAny(deferredAttrContext.parent.inferenceContext.inferencevars, 576 List.from(deferredStuckPolicy.stuckVars()))) { 577 deferredAttrContext.parent.addDeferredAttrNode(dt, 578 resultInfo.dup(new Check.NestedCheckContext(resultInfo.checkContext) { 579 @Override 580 public InferenceContext inferenceContext() { 581 return deferredAttrContext.parent.inferenceContext; 582 } 583 @Override 584 public DeferredAttrContext deferredAttrContext() { 585 return deferredAttrContext.parent; 586 } 587 }), deferredStuckPolicy); 588 dt.tree.type = Type.stuckType; 589 return true; 590 } else { 591 return false; 592 } 593 } else { 594 Assert.check(!deferredAttrContext.insideOverloadPhase(), 595 "attribution shouldn't be happening here"); 596 ResultInfo instResultInfo = 597 resultInfo.dup(deferredAttrContext.inferenceContext.asInstType(resultInfo.pt)); 598 dt.check(instResultInfo, dummyStuckPolicy, basicCompleter); 599 return true; 600 } 601 default: 602 throw new AssertionError("Bad mode"); 603 } 604 } 605 606 /** 607 * Structural checker for stuck expressions 608 */ 609 class StructuralStuckChecker extends TreeScanner implements DeferredTypeCompleter { 610 611 ResultInfo resultInfo; 612 InferenceContext inferenceContext; 613 Env<AttrContext> env; 614 615 public Type complete(DeferredType dt, ResultInfo resultInfo, DeferredAttrContext deferredAttrContext) { 616 this.resultInfo = resultInfo; 617 this.inferenceContext = deferredAttrContext.inferenceContext; 618 this.env = dt.env; 619 dt.tree.accept(this); 620 dt.speculativeCache.put(stuckTree, resultInfo); 621 return Type.noType; 622 } 623 624 @Override 625 public void visitLambda(JCLambda tree) { 626 Check.CheckContext checkContext = resultInfo.checkContext; 627 Type pt = resultInfo.pt; 628 if (!inferenceContext.inferencevars.contains(pt)) { 629 //must be a functional descriptor 630 Type descriptorType = null; 631 try { 632 descriptorType = types.findDescriptorType(pt); 633 } catch (Types.FunctionDescriptorLookupError ex) { 634 checkContext.report(null, ex.getDiagnostic()); 635 } 636 637 if (descriptorType.getParameterTypes().length() != tree.params.length()) { 638 checkContext.report(tree, 639 diags.fragment("incompatible.arg.types.in.lambda")); 640 } 641 642 Type currentReturnType = descriptorType.getReturnType(); 643 boolean returnTypeIsVoid = currentReturnType.hasTag(VOID); 644 if (tree.getBodyKind() == BodyKind.EXPRESSION) { 645 boolean isExpressionCompatible = !returnTypeIsVoid || 646 TreeInfo.isExpressionStatement((JCExpression)tree.getBody()); 647 if (!isExpressionCompatible) { 648 resultInfo.checkContext.report(tree.pos(), 649 diags.fragment("incompatible.ret.type.in.lambda", 650 diags.fragment("missing.ret.val", currentReturnType))); 651 } 652 } else { 653 LambdaBodyStructChecker lambdaBodyChecker = 654 new LambdaBodyStructChecker(); 655 656 tree.body.accept(lambdaBodyChecker); 657 boolean isVoidCompatible = lambdaBodyChecker.isVoidCompatible; 658 659 if (returnTypeIsVoid) { 660 if (!isVoidCompatible) { 661 resultInfo.checkContext.report(tree.pos(), 662 diags.fragment("unexpected.ret.val")); 663 } 664 } else { 665 boolean isValueCompatible = lambdaBodyChecker.isPotentiallyValueCompatible 666 && !canLambdaBodyCompleteNormally(tree); 667 if (!isValueCompatible && !isVoidCompatible) { 668 log.error(tree.body.pos(), 669 "lambda.body.neither.value.nor.void.compatible"); 670 } 671 672 if (!isValueCompatible) { 673 resultInfo.checkContext.report(tree.pos(), 674 diags.fragment("incompatible.ret.type.in.lambda", 675 diags.fragment("missing.ret.val", currentReturnType))); 676 } 677 } 678 } 679 } 680 } 681 682 boolean canLambdaBodyCompleteNormally(JCLambda tree) { 683 JCLambda newTree = new TreeCopier<>(make).copy(tree); 684 /* attr.lambdaEnv will create a meaningful env for the 685 * lambda expression. This is specially useful when the 686 * lambda is used as the init of a field. But we need to 687 * remove any added symbol. 688 */ 689 Env<AttrContext> localEnv = attr.lambdaEnv(newTree, env); 690 try { 691 List<JCVariableDecl> tmpParams = newTree.params; 692 while (tmpParams.nonEmpty()) { 693 tmpParams.head.vartype = make.at(tmpParams.head).Type(syms.errType); 694 tmpParams = tmpParams.tail; 695 } 696 697 attr.attribStats(newTree.params, localEnv); 698 699 /* set pt to Type.noType to avoid generating any bound 700 * which may happen if lambda's return type is an 701 * inference variable 702 */ 703 Attr.ResultInfo bodyResultInfo = attr.new ResultInfo(VAL, Type.noType); 704 localEnv.info.returnResult = bodyResultInfo; 705 706 // discard any log output 707 Log.DiagnosticHandler diagHandler = new Log.DiscardDiagnosticHandler(log); 708 try { 709 JCBlock body = (JCBlock)newTree.body; 710 /* we need to attribute the lambda body before 711 * doing the aliveness analysis. This is because 712 * constant folding occurs during attribution 713 * and the reachability of some statements depends 714 * on constant values, for example: 715 * 716 * while (true) {...} 717 */ 718 attr.attribStats(body.stats, localEnv); 719 720 attr.preFlow(newTree); 721 /* make an aliveness / reachability analysis of the lambda 722 * to determine if it can complete normally 723 */ 724 flow.analyzeLambda(localEnv, newTree, make, true); 725 } finally { 726 log.popDiagnosticHandler(diagHandler); 727 } 728 return newTree.canCompleteNormally; 729 } finally { 730 JCBlock body = (JCBlock)newTree.body; 731 unenterScanner.scan(body.stats); 732 localEnv.info.scope.leave(); 733 } 734 } 735 736 @Override 737 public void visitNewClass(JCNewClass tree) { 738 //do nothing 739 } 740 741 @Override 742 public void visitApply(JCMethodInvocation tree) { 743 //do nothing 744 } 745 746 @Override 747 public void visitReference(JCMemberReference tree) { 748 Check.CheckContext checkContext = resultInfo.checkContext; 749 Type pt = resultInfo.pt; 750 if (!inferenceContext.inferencevars.contains(pt)) { 751 try { 752 types.findDescriptorType(pt); 753 } catch (Types.FunctionDescriptorLookupError ex) { 754 checkContext.report(null, ex.getDiagnostic()); 755 } 756 Env<AttrContext> localEnv = env.dup(tree); 757 JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv, 758 attr.memberReferenceQualifierResult(tree)); 759 ListBuffer<Type> argtypes = new ListBuffer<>(); 760 for (Type t : types.findDescriptorType(pt).getParameterTypes()) { 761 argtypes.append(Type.noType); 762 } 763 JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree); 764 mref2.expr = exprTree; 765 Symbol lookupSym = 766 rs.resolveMemberReferenceByArity(localEnv, mref2, exprTree.type, 767 tree.name, argtypes.toList(), inferenceContext); 768 switch (lookupSym.kind) { 769 //note: as argtypes are erroneous types, type-errors must 770 //have been caused by arity mismatch 771 case Kinds.ABSENT_MTH: 772 case Kinds.WRONG_MTH: 773 case Kinds.WRONG_MTHS: 774 case Kinds.WRONG_STATICNESS: 775 checkContext.report(tree, diags.fragment("incompatible.arg.types.in.mref")); 776 } 777 } 778 } 779 } 780 781 /* This visitor looks for return statements, its analysis will determine if 782 * a lambda body is void or value compatible. We must analyze return 783 * statements contained in the lambda body only, thus any return statement 784 * contained in an inner class or inner lambda body, should be ignored. 785 */ 786 class LambdaBodyStructChecker extends TreeScanner { 787 boolean isVoidCompatible = true; 788 boolean isPotentiallyValueCompatible = true; 789 790 @Override 791 public void visitClassDef(JCClassDecl tree) { 792 // do nothing 793 } 794 795 @Override 796 public void visitLambda(JCLambda tree) { 797 // do nothing 798 } 799 800 @Override 801 public void visitNewClass(JCNewClass tree) { 802 // do nothing 803 } 804 805 @Override 806 public void visitReturn(JCReturn tree) { 807 if (tree.expr != null) { 808 isVoidCompatible = false; 809 } else { 810 isPotentiallyValueCompatible = false; 811 } 812 } 813 } 814 } 815 816 /** an empty deferred attribution context - all methods throw exceptions */ 817 final DeferredAttrContext emptyDeferredAttrContext; 818 819 /** 820 * Map a list of types possibly containing one or more deferred types 821 * into a list of ordinary types. Each deferred type D is mapped into a type T, 822 * where T is computed by retrieving the type that has already been 823 * computed for D during a previous deferred attribution round of the given kind. 824 */ 825 class DeferredTypeMap extends Type.Mapping { 826 827 DeferredAttrContext deferredAttrContext; 828 829 protected DeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) { 830 super(String.format("deferredTypeMap[%s]", mode)); 831 this.deferredAttrContext = new DeferredAttrContext(mode, msym, phase, 832 infer.emptyContext, emptyDeferredAttrContext, types.noWarnings); 833 } 834 835 @Override 836 public Type apply(Type t) { 837 if (!t.hasTag(DEFERRED)) { 838 return t.map(this); 839 } else { 840 DeferredType dt = (DeferredType)t; 841 return typeOf(dt); 842 } 843 } 844 845 protected Type typeOf(DeferredType dt) { 846 switch (deferredAttrContext.mode) { 847 case CHECK: 848 return dt.tree.type == null ? Type.noType : dt.tree.type; 849 case SPECULATIVE: 850 return dt.speculativeType(deferredAttrContext.msym, deferredAttrContext.phase); 851 } 852 Assert.error(); 853 return null; 854 } 855 } 856 857 /** 858 * Specialized recovery deferred mapping. 859 * Each deferred type D is mapped into a type T, where T is computed either by 860 * (i) retrieving the type that has already been computed for D during a previous 861 * attribution round (as before), or (ii) by synthesizing a new type R for D 862 * (the latter step is useful in a recovery scenario). 863 */ 864 public class RecoveryDeferredTypeMap extends DeferredTypeMap { 865 866 public RecoveryDeferredTypeMap(AttrMode mode, Symbol msym, MethodResolutionPhase phase) { 867 super(mode, msym, phase != null ? phase : MethodResolutionPhase.BOX); 868 } 869 870 @Override 871 protected Type typeOf(DeferredType dt) { 872 Type owntype = super.typeOf(dt); 873 return owntype == Type.noType ? 874 recover(dt) : owntype; 875 } 876 877 /** 878 * Synthesize a type for a deferred type that hasn't been previously 879 * reduced to an ordinary type. Functional deferred types and conditionals 880 * are mapped to themselves, in order to have a richer diagnostic 881 * representation. Remaining deferred types are attributed using 882 * a default expected type (j.l.Object). 883 */ 884 private Type recover(DeferredType dt) { 885 dt.check(attr.new RecoveryInfo(deferredAttrContext) { 886 @Override 887 protected Type check(DiagnosticPosition pos, Type found) { 888 return chk.checkNonVoid(pos, super.check(pos, found)); 889 } 890 }); 891 return super.apply(dt); 892 } 893 } 894 895 /** 896 * A special tree scanner that would only visit portions of a given tree. 897 * The set of nodes visited by the scanner can be customized at construction-time. 898 */ 899 abstract static class FilterScanner extends TreeScanner { 900 901 final Filter<JCTree> treeFilter; 902 903 FilterScanner(final Set<JCTree.Tag> validTags) { 904 this.treeFilter = new Filter<JCTree>() { 905 public boolean accepts(JCTree t) { 906 return validTags.contains(t.getTag()); 907 } 908 }; 909 } 910 911 @Override 912 public void scan(JCTree tree) { 913 if (tree != null) { 914 if (treeFilter.accepts(tree)) { 915 super.scan(tree); 916 } else { 917 skip(tree); 918 } 919 } 920 } 921 922 /** 923 * handler that is executed when a node has been discarded 924 */ 925 void skip(JCTree tree) {} 926 } 927 928 /** 929 * A tree scanner suitable for visiting the target-type dependent nodes of 930 * a given argument expression. 931 */ 932 static class PolyScanner extends FilterScanner { 933 934 PolyScanner() { 935 super(EnumSet.of(CONDEXPR, PARENS, LAMBDA, REFERENCE)); 936 } 937 } 938 939 /** 940 * A tree scanner suitable for visiting the target-type dependent nodes nested 941 * within a lambda expression body. 942 */ 943 static class LambdaReturnScanner extends FilterScanner { 944 945 LambdaReturnScanner() { 946 super(EnumSet.of(BLOCK, CASE, CATCH, DOLOOP, FOREACHLOOP, 947 FORLOOP, IF, RETURN, SYNCHRONIZED, SWITCH, TRY, WHILELOOP)); 948 } 949 } 950 951 /** 952 * This visitor is used to check that structural expressions conform 953 * to their target - this step is required as inference could end up 954 * inferring types that make some of the nested expressions incompatible 955 * with their corresponding instantiated target 956 */ 957 class CheckStuckPolicy extends PolyScanner implements DeferredStuckPolicy, Infer.FreeTypeListener { 958 959 Type pt; 960 Infer.InferenceContext inferenceContext; 961 Set<Type> stuckVars = new LinkedHashSet<>(); 962 Set<Type> depVars = new LinkedHashSet<>(); 963 964 @Override 965 public boolean isStuck() { 966 return !stuckVars.isEmpty(); 967 } 968 969 @Override 970 public Set<Type> stuckVars() { 971 return stuckVars; 972 } 973 974 @Override 975 public Set<Type> depVars() { 976 return depVars; 977 } 978 979 public CheckStuckPolicy(ResultInfo resultInfo, DeferredType dt) { 980 this.pt = resultInfo.pt; 981 this.inferenceContext = resultInfo.checkContext.inferenceContext(); 982 scan(dt.tree); 983 if (!stuckVars.isEmpty()) { 984 resultInfo.checkContext.inferenceContext() 985 .addFreeTypeListener(List.from(stuckVars), this); 986 } 987 } 988 989 @Override 990 public void typesInferred(InferenceContext inferenceContext) { 991 stuckVars.clear(); 992 } 993 994 @Override 995 public void visitLambda(JCLambda tree) { 996 if (inferenceContext.inferenceVars().contains(pt)) { 997 stuckVars.add(pt); 998 } 999 if (!types.isFunctionalInterface(pt)) { 1000 return; 1001 } 1002 Type descType = types.findDescriptorType(pt); 1003 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); 1004 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT && 1005 freeArgVars.nonEmpty()) { 1006 stuckVars.addAll(freeArgVars); 1007 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); 1008 } 1009 scanLambdaBody(tree, descType.getReturnType()); 1010 } 1011 1012 @Override 1013 public void visitReference(JCMemberReference tree) { 1014 scan(tree.expr); 1015 if (inferenceContext.inferenceVars().contains(pt)) { 1016 stuckVars.add(pt); 1017 return; 1018 } 1019 if (!types.isFunctionalInterface(pt)) { 1020 return; 1021 } 1022 1023 Type descType = types.findDescriptorType(pt); 1024 List<Type> freeArgVars = inferenceContext.freeVarsIn(descType.getParameterTypes()); 1025 if (freeArgVars.nonEmpty() && 1026 tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) { 1027 stuckVars.addAll(freeArgVars); 1028 depVars.addAll(inferenceContext.freeVarsIn(descType.getReturnType())); 1029 } 1030 } 1031 1032 void scanLambdaBody(JCLambda lambda, final Type pt) { 1033 if (lambda.getBodyKind() == JCTree.JCLambda.BodyKind.EXPRESSION) { 1034 Type prevPt = this.pt; 1035 try { 1036 this.pt = pt; 1037 scan(lambda.body); 1038 } finally { 1039 this.pt = prevPt; 1040 } 1041 } else { 1042 LambdaReturnScanner lambdaScanner = new LambdaReturnScanner() { 1043 @Override 1044 public void visitReturn(JCReturn tree) { 1045 if (tree.expr != null) { 1046 Type prevPt = CheckStuckPolicy.this.pt; 1047 try { 1048 CheckStuckPolicy.this.pt = pt; 1049 CheckStuckPolicy.this.scan(tree.expr); 1050 } finally { 1051 CheckStuckPolicy.this.pt = prevPt; 1052 } 1053 } 1054 } 1055 }; 1056 lambdaScanner.scan(lambda.body); 1057 } 1058 } 1059 } 1060 1061 /** 1062 * This visitor is used to check that structural expressions conform 1063 * to their target - this step is required as inference could end up 1064 * inferring types that make some of the nested expressions incompatible 1065 * with their corresponding instantiated target 1066 */ 1067 class OverloadStuckPolicy extends CheckStuckPolicy implements DeferredStuckPolicy { 1068 1069 boolean stuck; 1070 1071 @Override 1072 public boolean isStuck() { 1073 return super.isStuck() || stuck; 1074 } 1075 1076 public OverloadStuckPolicy(ResultInfo resultInfo, DeferredType dt) { 1077 super(resultInfo, dt); 1078 } 1079 1080 @Override 1081 public void visitLambda(JCLambda tree) { 1082 super.visitLambda(tree); 1083 if (tree.paramKind == JCLambda.ParameterKind.IMPLICIT) { 1084 stuck = true; 1085 } 1086 } 1087 1088 @Override 1089 public void visitReference(JCMemberReference tree) { 1090 super.visitReference(tree); 1091 if (tree.overloadKind == JCMemberReference.OverloadKind.OVERLOADED) { 1092 stuck = true; 1093 } 1094 } 1095 } 1096 1097 /** 1098 * Does the argument expression {@code expr} need speculative type-checking? 1099 */ 1100 boolean isDeferred(Env<AttrContext> env, JCExpression expr) { 1101 DeferredChecker dc = new DeferredChecker(env); 1102 dc.scan(expr); 1103 return dc.result.isPoly(); 1104 } 1105 1106 /** 1107 * The kind of an argument expression. This is used by the analysis that 1108 * determines as to whether speculative attribution is necessary. 1109 */ 1110 enum ArgumentExpressionKind { 1111 1112 /** kind that denotes poly argument expression */ 1113 POLY, 1114 /** kind that denotes a standalone expression */ 1115 NO_POLY, 1116 /** kind that denotes a primitive/boxed standalone expression */ 1117 PRIMITIVE; 1118 1119 /** 1120 * Does this kind denote a poly argument expression 1121 */ 1122 public final boolean isPoly() { 1123 return this == POLY; 1124 } 1125 1126 /** 1127 * Does this kind denote a primitive standalone expression 1128 */ 1129 public final boolean isPrimitive() { 1130 return this == PRIMITIVE; 1131 } 1132 1133 /** 1134 * Compute the kind of a standalone expression of a given type 1135 */ 1136 static ArgumentExpressionKind standaloneKind(Type type, Types types) { 1137 return types.unboxedTypeOrType(type).isPrimitive() ? 1138 ArgumentExpressionKind.PRIMITIVE : 1139 ArgumentExpressionKind.NO_POLY; 1140 } 1141 1142 /** 1143 * Compute the kind of a method argument expression given its symbol 1144 */ 1145 static ArgumentExpressionKind methodKind(Symbol sym, Types types) { 1146 Type restype = sym.type.getReturnType(); 1147 if (sym.type.hasTag(FORALL) && 1148 restype.containsAny(((ForAll)sym.type).tvars)) { 1149 return ArgumentExpressionKind.POLY; 1150 } else { 1151 return ArgumentExpressionKind.standaloneKind(restype, types); 1152 } 1153 } 1154 } 1155 1156 /** 1157 * Tree scanner used for checking as to whether an argument expression 1158 * requires speculative attribution 1159 */ 1160 final class DeferredChecker extends FilterScanner { 1161 1162 Env<AttrContext> env; 1163 ArgumentExpressionKind result; 1164 1165 public DeferredChecker(Env<AttrContext> env) { 1166 super(deferredCheckerTags); 1167 this.env = env; 1168 } 1169 1170 @Override 1171 public void visitLambda(JCLambda tree) { 1172 //a lambda is always a poly expression 1173 result = ArgumentExpressionKind.POLY; 1174 } 1175 1176 @Override 1177 public void visitReference(JCMemberReference tree) { 1178 //perform arity-based check 1179 Env<AttrContext> localEnv = env.dup(tree); 1180 JCExpression exprTree = (JCExpression)attribSpeculative(tree.getQualifierExpression(), localEnv, 1181 attr.memberReferenceQualifierResult(tree)); 1182 JCMemberReference mref2 = new TreeCopier<Void>(make).copy(tree); 1183 mref2.expr = exprTree; 1184 Symbol res = 1185 rs.getMemberReference(tree, localEnv, mref2, 1186 exprTree.type, tree.name); 1187 tree.sym = res; 1188 if (res.kind >= Kinds.ERRONEOUS || 1189 res.type.hasTag(FORALL) || 1190 (res.flags() & Flags.VARARGS) != 0 || 1191 (TreeInfo.isStaticSelector(exprTree, tree.name.table.names) && 1192 exprTree.type.isRaw())) { 1193 tree.overloadKind = JCMemberReference.OverloadKind.OVERLOADED; 1194 } else { 1195 tree.overloadKind = JCMemberReference.OverloadKind.UNOVERLOADED; 1196 } 1197 //a method reference is always a poly expression 1198 result = ArgumentExpressionKind.POLY; 1199 } 1200 1201 @Override 1202 public void visitTypeCast(JCTypeCast tree) { 1203 //a cast is always a standalone expression 1204 result = ArgumentExpressionKind.NO_POLY; 1205 } 1206 1207 @Override 1208 public void visitConditional(JCConditional tree) { 1209 scan(tree.truepart); 1210 if (!result.isPrimitive()) { 1211 result = ArgumentExpressionKind.POLY; 1212 return; 1213 } 1214 scan(tree.falsepart); 1215 result = reduce(ArgumentExpressionKind.PRIMITIVE); 1216 } 1217 1218 @Override 1219 public void visitNewClass(JCNewClass tree) { 1220 result = (TreeInfo.isDiamond(tree) || attr.findDiamonds) ? 1221 ArgumentExpressionKind.POLY : ArgumentExpressionKind.NO_POLY; 1222 } 1223 1224 @Override 1225 public void visitApply(JCMethodInvocation tree) { 1226 Name name = TreeInfo.name(tree.meth); 1227 1228 //fast path 1229 if (tree.typeargs.nonEmpty() || 1230 name == name.table.names._this || 1231 name == name.table.names._super) { 1232 result = ArgumentExpressionKind.NO_POLY; 1233 return; 1234 } 1235 1236 //slow path 1237 Symbol sym = quicklyResolveMethod(env, tree); 1238 1239 if (sym == null) { 1240 result = ArgumentExpressionKind.POLY; 1241 return; 1242 } 1243 1244 result = analyzeCandidateMethods(sym, ArgumentExpressionKind.PRIMITIVE, 1245 argumentKindAnalyzer); 1246 } 1247 //where 1248 private boolean isSimpleReceiver(JCTree rec) { 1249 switch (rec.getTag()) { 1250 case IDENT: 1251 return true; 1252 case SELECT: 1253 return isSimpleReceiver(((JCFieldAccess)rec).selected); 1254 case TYPEAPPLY: 1255 case TYPEARRAY: 1256 return true; 1257 case ANNOTATED_TYPE: 1258 return isSimpleReceiver(((JCAnnotatedType)rec).underlyingType); 1259 case APPLY: 1260 return true; 1261 default: 1262 return false; 1263 } 1264 } 1265 private ArgumentExpressionKind reduce(ArgumentExpressionKind kind) { 1266 return argumentKindAnalyzer.reduce(result, kind); 1267 } 1268 MethodAnalyzer<ArgumentExpressionKind> argumentKindAnalyzer = 1269 new MethodAnalyzer<ArgumentExpressionKind>() { 1270 @Override 1271 public ArgumentExpressionKind process(MethodSymbol ms) { 1272 return ArgumentExpressionKind.methodKind(ms, types); 1273 } 1274 @Override 1275 public ArgumentExpressionKind reduce(ArgumentExpressionKind kind1, 1276 ArgumentExpressionKind kind2) { 1277 switch (kind1) { 1278 case PRIMITIVE: return kind2; 1279 case NO_POLY: return kind2.isPoly() ? kind2 : kind1; 1280 case POLY: return kind1; 1281 default: 1282 Assert.error(); 1283 return null; 1284 } 1285 } 1286 @Override 1287 public boolean shouldStop(ArgumentExpressionKind result) { 1288 return result.isPoly(); 1289 } 1290 }; 1291 1292 @Override 1293 public void visitLiteral(JCLiteral tree) { 1294 Type litType = attr.litType(tree.typetag); 1295 result = ArgumentExpressionKind.standaloneKind(litType, types); 1296 } 1297 1298 @Override 1299 void skip(JCTree tree) { 1300 result = ArgumentExpressionKind.NO_POLY; 1301 } 1302 1303 private Symbol quicklyResolveMethod(Env<AttrContext> env, final JCMethodInvocation tree) { 1304 final JCExpression rec = tree.meth.hasTag(SELECT) ? 1305 ((JCFieldAccess)tree.meth).selected : 1306 null; 1307 1308 if (rec != null && !isSimpleReceiver(rec)) { 1309 return null; 1310 } 1311 1312 Type site; 1313 1314 if (rec != null) { 1315 if (rec.hasTag(APPLY)) { 1316 Symbol recSym = quicklyResolveMethod(env, (JCMethodInvocation) rec); 1317 if (recSym == null) 1318 return null; 1319 Symbol resolvedReturnType = 1320 analyzeCandidateMethods(recSym, syms.errSymbol, returnSymbolAnalyzer); 1321 if (resolvedReturnType == null) 1322 return null; 1323 site = resolvedReturnType.type; 1324 } else { 1325 site = attribSpeculative(rec, env, attr.unknownTypeExprInfo).type; 1326 } 1327 } else { 1328 site = env.enclClass.sym.type; 1329 } 1330 1331 while (site.hasTag(TYPEVAR)) { 1332 site = site.getUpperBound(); 1333 } 1334 1335 site = types.capture(site); 1336 1337 List<Type> args = rs.dummyArgs(tree.args.length()); 1338 Name name = TreeInfo.name(tree.meth); 1339 1340 Resolve.LookupHelper lh = rs.new LookupHelper(name, site, args, List.<Type>nil(), MethodResolutionPhase.VARARITY) { 1341 @Override 1342 Symbol lookup(Env<AttrContext> env, MethodResolutionPhase phase) { 1343 return rec == null ? 1344 rs.findFun(env, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired()) : 1345 rs.findMethod(env, site, name, argtypes, typeargtypes, phase.isBoxingRequired(), phase.isVarargsRequired(), false); 1346 } 1347 @Override 1348 Symbol access(Env<AttrContext> env, DiagnosticPosition pos, Symbol location, Symbol sym) { 1349 return sym; 1350 } 1351 }; 1352 1353 return rs.lookupMethod(env, tree, site.tsym, rs.arityMethodCheck, lh); 1354 } 1355 //where: 1356 MethodAnalyzer<Symbol> returnSymbolAnalyzer = new MethodAnalyzer<Symbol>() { 1357 @Override 1358 public Symbol process(MethodSymbol ms) { 1359 ArgumentExpressionKind kind = ArgumentExpressionKind.methodKind(ms, types); 1360 if (kind == ArgumentExpressionKind.POLY || ms.getReturnType().hasTag(TYPEVAR)) 1361 return null; 1362 return ms.getReturnType().tsym; 1363 } 1364 @Override 1365 public Symbol reduce(Symbol s1, Symbol s2) { 1366 return s1 == syms.errSymbol ? s2 : s1 == s2 ? s1 : null; 1367 } 1368 @Override 1369 public boolean shouldStop(Symbol result) { 1370 return result == null; 1371 } 1372 }; 1373 1374 /** 1375 * Process the result of Resolve.lookupMethod. If sym is a method symbol, the result of 1376 * MethodAnalyzer.process is returned. If sym is an ambiguous symbol, all the candidate 1377 * methods are inspected one by one, using MethodAnalyzer.process. The outcomes are 1378 * reduced using MethodAnalyzer.reduce (using defaultValue as the first value over which 1379 * the reduction runs). MethodAnalyzer.shouldStop can be used to stop the inspection early. 1380 */ 1381 <E> E analyzeCandidateMethods(Symbol sym, E defaultValue, MethodAnalyzer<E> analyzer) { 1382 switch (sym.kind) { 1383 case Kinds.MTH: 1384 return analyzer.process((MethodSymbol) sym); 1385 case Kinds.AMBIGUOUS: 1386 Resolve.AmbiguityError err = (Resolve.AmbiguityError)sym.baseSymbol(); 1387 E res = defaultValue; 1388 for (Symbol s : err.ambiguousSyms) { 1389 if (s.kind == Kinds.MTH) { 1390 res = analyzer.reduce(res, analyzer.process((MethodSymbol) s)); 1391 if (analyzer.shouldStop(res)) 1392 return res; 1393 } 1394 } 1395 return res; 1396 default: 1397 return defaultValue; 1398 } 1399 } 1400 } 1401 1402 /** Analyzer for methods - used by analyzeCandidateMethods. */ 1403 interface MethodAnalyzer<E> { 1404 E process(MethodSymbol ms); 1405 E reduce(E e1, E e2); 1406 boolean shouldStop(E result); 1407 } 1408 1409 //where 1410 private EnumSet<JCTree.Tag> deferredCheckerTags = 1411 EnumSet.of(LAMBDA, REFERENCE, PARENS, TYPECAST, 1412 CONDEXPR, NEWCLASS, APPLY, LITERAL); 1413} 1414