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