Infer.java revision 4038:95c92c634f60
1/* 2 * Copyright (c) 1999, 2016, 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.tools.javac.code.Type.UndetVar.UndetVarListener; 29import com.sun.tools.javac.code.Types.TypeMapping; 30import com.sun.tools.javac.tree.JCTree; 31import com.sun.tools.javac.tree.JCTree.JCTypeCast; 32import com.sun.tools.javac.tree.TreeInfo; 33import com.sun.tools.javac.util.*; 34import com.sun.tools.javac.util.GraphUtils.DottableNode; 35import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 36import com.sun.tools.javac.util.List; 37import com.sun.tools.javac.code.*; 38import com.sun.tools.javac.code.Type.*; 39import com.sun.tools.javac.code.Type.UndetVar.InferenceBound; 40import com.sun.tools.javac.code.Symbol.*; 41import com.sun.tools.javac.comp.DeferredAttr.AttrMode; 42import com.sun.tools.javac.comp.DeferredAttr.DeferredAttrContext; 43import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph; 44import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node; 45import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; 46import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; 47 48import java.io.IOException; 49import java.io.Writer; 50import java.nio.file.Files; 51import java.nio.file.Path; 52import java.nio.file.Paths; 53import java.util.ArrayList; 54import java.util.Collection; 55import java.util.Collections; 56import java.util.EnumSet; 57import java.util.HashMap; 58import java.util.HashSet; 59import java.util.Map; 60import java.util.Optional; 61import java.util.Properties; 62import java.util.Set; 63import java.util.function.BiFunction; 64import java.util.function.BiPredicate; 65 66import static com.sun.tools.javac.code.TypeTag.*; 67 68/** Helper class for type parameter inference, used by the attribution phase. 69 * 70 * <p><b>This is NOT part of any supported API. 71 * If you write code that depends on this, you do so at your own risk. 72 * This code and its internal interfaces are subject to change or 73 * deletion without notice.</b> 74 */ 75public class Infer { 76 protected static final Context.Key<Infer> inferKey = new Context.Key<>(); 77 78 Resolve rs; 79 Check chk; 80 Symtab syms; 81 Types types; 82 JCDiagnostic.Factory diags; 83 Log log; 84 85 /** should the graph solver be used? */ 86 boolean allowGraphInference; 87 88 /** 89 * folder in which the inference dependency graphs should be written. 90 */ 91 private final String dependenciesFolder; 92 93 /** 94 * List of graphs awaiting to be dumped to a file. 95 */ 96 private List<String> pendingGraphs; 97 98 public static Infer instance(Context context) { 99 Infer instance = context.get(inferKey); 100 if (instance == null) 101 instance = new Infer(context); 102 return instance; 103 } 104 105 protected Infer(Context context) { 106 context.put(inferKey, this); 107 108 rs = Resolve.instance(context); 109 chk = Check.instance(context); 110 syms = Symtab.instance(context); 111 types = Types.instance(context); 112 diags = JCDiagnostic.Factory.instance(context); 113 log = Log.instance(context); 114 inferenceException = new InferenceException(diags); 115 Options options = Options.instance(context); 116 allowGraphInference = Source.instance(context).allowGraphInference() 117 && options.isUnset("useLegacyInference"); 118 dependenciesFolder = options.get("debug.dumpInferenceGraphsTo"); 119 pendingGraphs = List.nil(); 120 121 emptyContext = new InferenceContext(this, List.nil()); 122 } 123 124 /** A value for prototypes that admit any type, including polymorphic ones. */ 125 public static final Type anyPoly = new JCNoType(); 126 127 /** 128 * This exception class is design to store a list of diagnostics corresponding 129 * to inference errors that can arise during a method applicability check. 130 */ 131 public static class InferenceException extends InapplicableMethodException { 132 private static final long serialVersionUID = 0; 133 134 List<JCDiagnostic> messages = List.nil(); 135 136 InferenceException(JCDiagnostic.Factory diags) { 137 super(diags); 138 } 139 140 @Override 141 InapplicableMethodException setMessage() { 142 //no message to set 143 return this; 144 } 145 146 @Override 147 InapplicableMethodException setMessage(JCDiagnostic diag) { 148 messages = messages.append(diag); 149 return this; 150 } 151 152 @Override 153 public JCDiagnostic getDiagnostic() { 154 return messages.head; 155 } 156 157 void clear() { 158 messages = List.nil(); 159 } 160 } 161 162 protected final InferenceException inferenceException; 163 164 // <editor-fold defaultstate="collapsed" desc="Inference routines"> 165 /** 166 * Main inference entry point - instantiate a generic method type 167 * using given argument types and (possibly) an expected target-type. 168 */ 169 Type instantiateMethod( Env<AttrContext> env, 170 List<Type> tvars, 171 MethodType mt, 172 Attr.ResultInfo resultInfo, 173 MethodSymbol msym, 174 List<Type> argtypes, 175 boolean allowBoxing, 176 boolean useVarargs, 177 Resolve.MethodResolutionContext resolveContext, 178 Warner warn) throws InferenceException { 179 //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG 180 final InferenceContext inferenceContext = new InferenceContext(this, tvars); //B0 181 inferenceException.clear(); 182 try { 183 DeferredAttr.DeferredAttrContext deferredAttrContext = 184 resolveContext.deferredAttrContext(msym, inferenceContext, resultInfo, warn); 185 186 resolveContext.methodCheck.argumentsAcceptable(env, deferredAttrContext, //B2 187 argtypes, mt.getParameterTypes(), warn); 188 189 if (allowGraphInference && resultInfo != null && resultInfo.pt == anyPoly) { 190 doIncorporation(inferenceContext, warn); 191 //we are inside method attribution - just return a partially inferred type 192 return new PartiallyInferredMethodType(mt, inferenceContext, env, warn); 193 } else if (allowGraphInference && 194 resultInfo != null && 195 !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { 196 //inject return constraints earlier 197 doIncorporation(inferenceContext, warn); //propagation 198 199 boolean shouldPropagate = shouldPropagate(mt.getReturnType(), resultInfo, inferenceContext); 200 201 InferenceContext minContext = shouldPropagate ? 202 inferenceContext.min(roots(mt, deferredAttrContext), true, warn) : 203 inferenceContext; 204 205 Type newRestype = generateReturnConstraints(env.tree, resultInfo, //B3 206 mt, minContext); 207 mt = (MethodType)types.createMethodTypeWithReturn(mt, newRestype); 208 209 //propagate outwards if needed 210 if (shouldPropagate) { 211 //propagate inference context outwards and exit 212 minContext.dupTo(resultInfo.checkContext.inferenceContext()); 213 deferredAttrContext.complete(); 214 return mt; 215 } 216 } 217 218 deferredAttrContext.complete(); 219 220 // minimize as yet undetermined type variables 221 if (allowGraphInference) { 222 inferenceContext.solve(warn); 223 } else { 224 inferenceContext.solveLegacy(true, warn, LegacyInferenceSteps.EQ_LOWER.steps); //minimizeInst 225 } 226 227 mt = (MethodType)inferenceContext.asInstType(mt); 228 229 if (!allowGraphInference && 230 inferenceContext.restvars().nonEmpty() && 231 resultInfo != null && 232 !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { 233 generateReturnConstraints(env.tree, resultInfo, mt, inferenceContext); 234 inferenceContext.solveLegacy(false, warn, LegacyInferenceSteps.EQ_UPPER.steps); //maximizeInst 235 mt = (MethodType)inferenceContext.asInstType(mt); 236 } 237 238 if (resultInfo != null && rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) { 239 log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt); 240 } 241 242 // return instantiated version of method type 243 return mt; 244 } finally { 245 if (resultInfo != null || !allowGraphInference) { 246 inferenceContext.notifyChange(); 247 } else { 248 inferenceContext.notifyChange(inferenceContext.boundedVars()); 249 } 250 if (resultInfo == null) { 251 /* if the is no result info then we can clear the capture types 252 * cache without affecting any result info check 253 */ 254 inferenceContext.captureTypeCache.clear(); 255 } 256 dumpGraphsIfNeeded(env.tree, msym, resolveContext); 257 } 258 } 259 //where 260 private boolean shouldPropagate(Type restype, Attr.ResultInfo target, InferenceContext inferenceContext) { 261 return target.checkContext.inferenceContext() != emptyContext && //enclosing context is a generic method 262 inferenceContext.free(restype) && //return type contains inference vars 263 (!inferenceContext.inferencevars.contains(restype) || //no eager instantiation is required (as per 18.5.2) 264 !needsEagerInstantiation((UndetVar)inferenceContext.asUndetVar(restype), target.pt, inferenceContext)); 265 } 266 267 private List<Type> roots(MethodType mt, DeferredAttrContext deferredAttrContext) { 268 ListBuffer<Type> roots = new ListBuffer<>(); 269 roots.add(mt.getReturnType()); 270 if (deferredAttrContext != null && deferredAttrContext.mode == AttrMode.CHECK) { 271 roots.addAll(mt.getThrownTypes()); 272 for (DeferredAttr.DeferredAttrNode n : deferredAttrContext.deferredAttrNodes) { 273 roots.addAll(n.deferredStuckPolicy.stuckVars()); 274 roots.addAll(n.deferredStuckPolicy.depVars()); 275 } 276 } 277 return roots.toList(); 278 } 279 280 /** 281 * A partially infered method/constructor type; such a type can be checked multiple times 282 * against different targets. 283 */ 284 public class PartiallyInferredMethodType extends MethodType { 285 public PartiallyInferredMethodType(MethodType mtype, InferenceContext inferenceContext, Env<AttrContext> env, Warner warn) { 286 super(mtype.getParameterTypes(), mtype.getReturnType(), mtype.getThrownTypes(), mtype.tsym); 287 this.inferenceContext = inferenceContext; 288 this.env = env; 289 this.warn = warn; 290 } 291 292 /** The inference context. */ 293 final InferenceContext inferenceContext; 294 295 /** The attribution environment. */ 296 Env<AttrContext> env; 297 298 /** The warner. */ 299 final Warner warn; 300 301 @Override 302 public boolean isPartial() { 303 return true; 304 } 305 306 /** 307 * Checks this type against a target; this means generating return type constraints, solve 308 * and then roll back the results (to avoid poolluting the context). 309 */ 310 Type check(Attr.ResultInfo resultInfo) { 311 Warner noWarnings = new Warner(null); 312 inferenceException.clear(); 313 List<Type> saved_undet = null; 314 try { 315 /** we need to save the inference context before generating target type constraints. 316 * This constraints may pollute the inference context and make it useless in case we 317 * need to use it several times: with several targets. 318 */ 319 saved_undet = inferenceContext.save(); 320 boolean unchecked = warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED); 321 if (allowGraphInference && !unchecked) { 322 boolean shouldPropagate = shouldPropagate(getReturnType(), resultInfo, inferenceContext); 323 324 InferenceContext minContext = shouldPropagate ? 325 inferenceContext.min(roots(asMethodType(), null), false, warn) : 326 inferenceContext; 327 328 MethodType other = (MethodType)minContext.update(asMethodType()); 329 Type newRestype = generateReturnConstraints(env.tree, resultInfo, //B3 330 other, minContext); 331 332 if (shouldPropagate) { 333 //propagate inference context outwards and exit 334 minContext.dupTo(resultInfo.checkContext.inferenceContext(), 335 resultInfo.checkContext.deferredAttrContext().insideOverloadPhase()); 336 return newRestype; 337 } 338 } 339 inferenceContext.solve(noWarnings); 340 Type ret = inferenceContext.asInstType(this).getReturnType(); 341 //inline logic from Attr.checkMethod - if unchecked conversion was required, erase 342 //return type _after_ resolution 343 return unchecked ? types.erasure(ret) : ret; 344 } catch (InferenceException ex) { 345 resultInfo.checkContext.report(null, ex.getDiagnostic()); 346 Assert.error(); //cannot get here (the above should throw) 347 return null; 348 } finally { 349 if (saved_undet != null) { 350 inferenceContext.rollback(saved_undet); 351 } 352 } 353 } 354 } 355 356 private void dumpGraphsIfNeeded(DiagnosticPosition pos, Symbol msym, Resolve.MethodResolutionContext rsContext) { 357 int round = 0; 358 try { 359 for (String graph : pendingGraphs.reverse()) { 360 Assert.checkNonNull(dependenciesFolder); 361 Name name = msym.name == msym.name.table.names.init ? 362 msym.owner.name : msym.name; 363 String filename = String.format("%s@%s[mode=%s,step=%s]_%d.dot", 364 name, 365 pos.getStartPosition(), 366 rsContext.attrMode(), 367 rsContext.step, 368 round); 369 Path dotFile = Paths.get(dependenciesFolder, filename); 370 try (Writer w = Files.newBufferedWriter(dotFile)) { 371 w.append(graph); 372 } 373 round++; 374 } 375 } catch (IOException ex) { 376 Assert.error("Error occurred when dumping inference graph: " + ex.getMessage()); 377 } finally { 378 pendingGraphs = List.nil(); 379 } 380 } 381 382 /** 383 * Generate constraints from the generic method's return type. If the method 384 * call occurs in a context where a type T is expected, use the expected 385 * type to derive more constraints on the generic method inference variables. 386 */ 387 Type generateReturnConstraints(JCTree tree, Attr.ResultInfo resultInfo, 388 MethodType mt, InferenceContext inferenceContext) { 389 InferenceContext rsInfoInfContext = resultInfo.checkContext.inferenceContext(); 390 Type from = mt.getReturnType(); 391 if (mt.getReturnType().containsAny(inferenceContext.inferencevars) && 392 rsInfoInfContext != emptyContext) { 393 from = types.capture(from); 394 //add synthetic captured ivars 395 for (Type t : from.getTypeArguments()) { 396 if (t.hasTag(TYPEVAR) && ((TypeVar)t).isCaptured()) { 397 inferenceContext.addVar((TypeVar)t); 398 } 399 } 400 } 401 Type qtype = inferenceContext.asUndetVar(from); 402 Type to = resultInfo.pt; 403 404 if (qtype.hasTag(VOID)) { 405 to = syms.voidType; 406 } else if (to.hasTag(NONE)) { 407 to = from.isPrimitive() ? from : syms.objectType; 408 } else if (qtype.hasTag(UNDETVAR)) { 409 if (needsEagerInstantiation((UndetVar)qtype, to, inferenceContext) && 410 (allowGraphInference || !to.isPrimitive())) { 411 to = generateReferenceToTargetConstraint(tree, (UndetVar)qtype, to, resultInfo, inferenceContext); 412 } else if (to.isPrimitive()) { 413 to = types.boxedClass(to).type; 414 } 415 } else if (rsInfoInfContext.free(resultInfo.pt)) { 416 //propagation - cache captured vars 417 qtype = inferenceContext.asUndetVar(rsInfoInfContext.cachedCapture(tree, from, false)); 418 } 419 Assert.check(allowGraphInference || !rsInfoInfContext.free(to), 420 "legacy inference engine cannot handle constraints on both sides of a subtyping assertion"); 421 //we need to skip capture? 422 Warner retWarn = new Warner(); 423 if (!resultInfo.checkContext.compatible(qtype, rsInfoInfContext.asUndetVar(to), retWarn) || 424 //unchecked conversion is not allowed in source 7 mode 425 (!allowGraphInference && retWarn.hasLint(Lint.LintCategory.UNCHECKED))) { 426 throw inferenceException 427 .setMessage("infer.no.conforming.instance.exists", 428 inferenceContext.restvars(), mt.getReturnType(), to); 429 } 430 return from; 431 } 432 433 private boolean needsEagerInstantiation(UndetVar from, Type to, InferenceContext inferenceContext) { 434 if (to.isPrimitive()) { 435 /* T is a primitive type, and one of the primitive wrapper classes is an instantiation, 436 * upper bound, or lower bound for alpha in B2. 437 */ 438 for (Type t : from.getBounds(InferenceBound.values())) { 439 Type boundAsPrimitive = types.unboxedType(t); 440 if (boundAsPrimitive == null || boundAsPrimitive.hasTag(NONE)) { 441 continue; 442 } 443 return true; 444 } 445 return false; 446 } 447 448 Type captureOfTo = types.capture(to); 449 /* T is a reference type, but is not a wildcard-parameterized type, and either 450 */ 451 if (captureOfTo == to) { //not a wildcard parameterized type 452 /* i) B2 contains a bound of one of the forms alpha = S or S <: alpha, 453 * where S is a wildcard-parameterized type, or 454 */ 455 for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) { 456 Type captureOfBound = types.capture(t); 457 if (captureOfBound != t) { 458 return true; 459 } 460 } 461 462 /* ii) B2 contains two bounds of the forms S1 <: alpha and S2 <: alpha, 463 * where S1 and S2 have supertypes that are two different 464 * parameterizations of the same generic class or interface. 465 */ 466 for (Type aLowerBound : from.getBounds(InferenceBound.LOWER)) { 467 for (Type anotherLowerBound : from.getBounds(InferenceBound.LOWER)) { 468 if (aLowerBound != anotherLowerBound && 469 !inferenceContext.free(aLowerBound) && 470 !inferenceContext.free(anotherLowerBound) && 471 commonSuperWithDiffParameterization(aLowerBound, anotherLowerBound)) { 472 return true; 473 } 474 } 475 } 476 } 477 478 /* T is a parameterization of a generic class or interface, G, 479 * and B2 contains a bound of one of the forms alpha = S or S <: alpha, 480 * where there exists no type of the form G<...> that is a 481 * supertype of S, but the raw type G is a supertype of S 482 */ 483 if (to.isParameterized()) { 484 for (Type t : from.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) { 485 Type sup = types.asSuper(t, to.tsym); 486 if (sup != null && sup.isRaw()) { 487 return true; 488 } 489 } 490 } 491 return false; 492 } 493 494 private boolean commonSuperWithDiffParameterization(Type t, Type s) { 495 for (Pair<Type, Type> supers : getParameterizedSupers(t, s)) { 496 if (!types.isSameType(supers.fst, supers.snd)) return true; 497 } 498 return false; 499 } 500 501 private Type generateReferenceToTargetConstraint(JCTree tree, UndetVar from, 502 Type to, Attr.ResultInfo resultInfo, 503 InferenceContext inferenceContext) { 504 inferenceContext.solve(List.of(from.qtype), new Warner()); 505 inferenceContext.notifyChange(); 506 Type capturedType = resultInfo.checkContext.inferenceContext() 507 .cachedCapture(tree, from.getInst(), false); 508 if (types.isConvertible(capturedType, 509 resultInfo.checkContext.inferenceContext().asUndetVar(to))) { 510 //effectively skip additional return-type constraint generation (compatibility) 511 return syms.objectType; 512 } 513 return to; 514 } 515 516 /** 517 * Infer cyclic inference variables as described in 15.12.2.8. 518 */ 519 void instantiateAsUninferredVars(List<Type> vars, InferenceContext inferenceContext) { 520 ListBuffer<Type> todo = new ListBuffer<>(); 521 //step 1 - create fresh tvars 522 for (Type t : vars) { 523 UndetVar uv = (UndetVar)inferenceContext.asUndetVar(t); 524 List<Type> upperBounds = uv.getBounds(InferenceBound.UPPER); 525 if (Type.containsAny(upperBounds, vars)) { 526 TypeSymbol fresh_tvar = new TypeVariableSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner); 527 fresh_tvar.type = new TypeVar(fresh_tvar, types.makeIntersectionType(uv.getBounds(InferenceBound.UPPER)), null); 528 todo.append(uv); 529 uv.setInst(fresh_tvar.type); 530 } else if (upperBounds.nonEmpty()) { 531 uv.setInst(types.glb(upperBounds)); 532 } else { 533 uv.setInst(syms.objectType); 534 } 535 } 536 //step 2 - replace fresh tvars in their bounds 537 List<Type> formals = vars; 538 for (Type t : todo) { 539 UndetVar uv = (UndetVar)t; 540 TypeVar ct = (TypeVar)uv.getInst(); 541 ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct))); 542 if (ct.bound.isErroneous()) { 543 //report inference error if glb fails 544 reportBoundError(uv, InferenceBound.UPPER); 545 } 546 formals = formals.tail; 547 } 548 } 549 550 /** 551 * Compute a synthetic method type corresponding to the requested polymorphic 552 * method signature. The target return type is computed from the immediately 553 * enclosing scope surrounding the polymorphic-signature call. 554 */ 555 Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env, 556 MethodSymbol spMethod, // sig. poly. method or null if none 557 Resolve.MethodResolutionContext resolveContext, 558 List<Type> argtypes) { 559 final Type restype; 560 561 if (spMethod == null || types.isSameType(spMethod.getReturnType(), syms.objectType, true)) { 562 // The return type of the polymorphic signature is polymorphic, 563 // and is computed from the enclosing tree E, as follows: 564 // if E is a cast, then use the target type of the cast expression 565 // as a return type; if E is an expression statement, the return 566 // type is 'void'; otherwise 567 // the return type is simply 'Object'. A correctness check ensures 568 // that env.next refers to the lexically enclosing environment in 569 // which the polymorphic signature call environment is nested. 570 571 switch (env.next.tree.getTag()) { 572 case TYPECAST: 573 JCTypeCast castTree = (JCTypeCast)env.next.tree; 574 restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ? 575 castTree.clazz.type : 576 syms.objectType; 577 break; 578 case EXEC: 579 JCTree.JCExpressionStatement execTree = 580 (JCTree.JCExpressionStatement)env.next.tree; 581 restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ? 582 syms.voidType : 583 syms.objectType; 584 break; 585 default: 586 restype = syms.objectType; 587 } 588 } else { 589 // The return type of the polymorphic signature is fixed 590 // (not polymorphic) 591 restype = spMethod.getReturnType(); 592 } 593 594 List<Type> paramtypes = argtypes.map(new ImplicitArgType(spMethod, resolveContext.step)); 595 List<Type> exType = spMethod != null ? 596 spMethod.getThrownTypes() : 597 List.of(syms.throwableType); // make it throw all exceptions 598 599 MethodType mtype = new MethodType(paramtypes, 600 restype, 601 exType, 602 syms.methodClass); 603 return mtype; 604 } 605 //where 606 class ImplicitArgType extends DeferredAttr.DeferredTypeMap { 607 608 public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) { 609 (rs.deferredAttr).super(AttrMode.SPECULATIVE, msym, phase); 610 } 611 612 @Override 613 public Type visitClassType(ClassType t, Void aVoid) { 614 return types.erasure(t); 615 } 616 617 @Override 618 public Type visitType(Type t, Void _unused) { 619 if (t.hasTag(DEFERRED)) { 620 return visit(super.visitType(t, null)); 621 } else if (t.hasTag(BOT)) 622 // nulls type as the marker type Null (which has no instances) 623 // infer as java.lang.Void for now 624 t = types.boxedClass(syms.voidType).type; 625 return t; 626 } 627 } 628 629 TypeMapping<Void> fromTypeVarFun = new StructuralTypeMapping<Void>() { 630 @Override 631 public Type visitTypeVar(TypeVar tv, Void aVoid) { 632 UndetVar uv = new UndetVar(tv, incorporationEngine(), types); 633 if ((tv.tsym.flags() & Flags.THROWS) != 0) { 634 uv.setThrow(); 635 } 636 return uv; 637 } 638 }; 639 640 /** 641 * This method is used to infer a suitable target SAM in case the original 642 * SAM type contains one or more wildcards. An inference process is applied 643 * so that wildcard bounds, as well as explicit lambda/method ref parameters 644 * (where applicable) are used to constraint the solution. 645 */ 646 public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface, 647 List<Type> paramTypes, Check.CheckContext checkContext) { 648 if (types.capture(funcInterface) == funcInterface) { 649 //if capture doesn't change the type then return the target unchanged 650 //(this means the target contains no wildcards!) 651 return funcInterface; 652 } else { 653 Type formalInterface = funcInterface.tsym.type; 654 InferenceContext funcInterfaceContext = 655 new InferenceContext(this, funcInterface.tsym.type.getTypeArguments()); 656 657 Assert.check(paramTypes != null); 658 //get constraints from explicit params (this is done by 659 //checking that explicit param types are equal to the ones 660 //in the functional interface descriptors) 661 List<Type> descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes(); 662 if (descParameterTypes.size() != paramTypes.size()) { 663 checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda")); 664 return types.createErrorType(funcInterface); 665 } 666 for (Type p : descParameterTypes) { 667 if (!types.isSameType(funcInterfaceContext.asUndetVar(p), paramTypes.head)) { 668 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface)); 669 return types.createErrorType(funcInterface); 670 } 671 paramTypes = paramTypes.tail; 672 } 673 674 List<Type> actualTypeargs = funcInterface.getTypeArguments(); 675 for (Type t : funcInterfaceContext.undetvars) { 676 UndetVar uv = (UndetVar)t; 677 Optional<Type> inst = uv.getBounds(InferenceBound.EQ).stream() 678 .filter(b -> !b.containsAny(formalInterface.getTypeArguments())).findFirst(); 679 uv.setInst(inst.orElse(actualTypeargs.head)); 680 actualTypeargs = actualTypeargs.tail; 681 } 682 683 Type owntype = funcInterfaceContext.asInstType(formalInterface); 684 if (!chk.checkValidGenericType(owntype)) { 685 //if the inferred functional interface type is not well-formed, 686 //or if it's not a subtype of the original target, issue an error 687 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface)); 688 } 689 //propagate constraints as per JLS 18.2.1 690 checkContext.compatible(owntype, funcInterface, types.noWarnings); 691 return owntype; 692 } 693 } 694 // </editor-fold> 695 696 // <editor-fold defaultstate="collapsed" desc="Incorporation"> 697 698 /** 699 * This class is the root of all incorporation actions. 700 */ 701 public abstract class IncorporationAction { 702 UndetVar uv; 703 Type t; 704 705 IncorporationAction(UndetVar uv, Type t) { 706 this.uv = uv; 707 this.t = t; 708 } 709 710 public abstract IncorporationAction dup(UndetVar that); 711 712 /** 713 * Incorporation action entry-point. Subclasses should define the logic associated with 714 * this incorporation action. 715 */ 716 abstract void apply(InferenceContext ic, Warner warn); 717 718 /** 719 * Helper function: perform subtyping through incorporation cache. 720 */ 721 boolean isSubtype(Type s, Type t, Warner warn) { 722 return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn); 723 } 724 725 /** 726 * Helper function: perform type-equivalence through incorporation cache. 727 */ 728 boolean isSameType(Type s, Type t) { 729 return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null); 730 } 731 732 @Override 733 public String toString() { 734 return String.format("%s[undet=%s,t=%s]", getClass().getSimpleName(), uv.qtype, t); 735 } 736 } 737 738 /** 739 * Bound-check incorporation action. A newly added bound is checked against existing bounds, 740 * to verify its compatibility; each bound is checked using either subtyping or type equivalence. 741 */ 742 class CheckBounds extends IncorporationAction { 743 744 InferenceBound from; 745 BiFunction<InferenceContext, Type, Type> typeFunc; 746 BiPredicate<InferenceContext, Type> optFilter; 747 748 CheckBounds(UndetVar uv, Type t, InferenceBound from) { 749 this(uv, t, InferenceContext::asUndetVar, null, from); 750 } 751 752 CheckBounds(UndetVar uv, Type t, BiFunction<InferenceContext, Type, Type> typeFunc, 753 BiPredicate<InferenceContext, Type> typeFilter, InferenceBound from) { 754 super(uv, t); 755 this.from = from; 756 this.typeFunc = typeFunc; 757 this.optFilter = typeFilter; 758 } 759 760 @Override 761 public IncorporationAction dup(UndetVar that) { 762 return new CheckBounds(that, t, typeFunc, optFilter, from); 763 } 764 765 @Override 766 void apply(InferenceContext inferenceContext, Warner warn) { 767 t = typeFunc.apply(inferenceContext, t); 768 if (optFilter != null && optFilter.test(inferenceContext, t)) return; 769 for (InferenceBound to : boundsToCheck()) { 770 for (Type b : uv.getBounds(to)) { 771 b = typeFunc.apply(inferenceContext, b); 772 if (optFilter != null && optFilter.test(inferenceContext, b)) continue; 773 boolean success = checkBound(t, b, from, to, warn); 774 if (!success) { 775 report(from, to); 776 } 777 } 778 } 779 } 780 781 /** 782 * The list of bound kinds to be checked. 783 */ 784 EnumSet<InferenceBound> boundsToCheck() { 785 return (from == InferenceBound.EQ) ? 786 EnumSet.allOf(InferenceBound.class) : 787 EnumSet.complementOf(EnumSet.of(from)); 788 } 789 790 /** 791 * Is source type 's' compatible with target type 't' given source and target bound kinds? 792 */ 793 boolean checkBound(Type s, Type t, InferenceBound ib_s, InferenceBound ib_t, Warner warn) { 794 if (ib_s.lessThan(ib_t)) { 795 return isSubtype(s, t, warn); 796 } else if (ib_t.lessThan(ib_s)) { 797 return isSubtype(t, s, warn); 798 } else { 799 return isSameType(s, t); 800 } 801 } 802 803 /** 804 * Report a bound check error. 805 */ 806 void report(InferenceBound from, InferenceBound to) { 807 //this is a workaround to preserve compatibility with existing messages 808 if (from == to) { 809 reportBoundError(uv, from); 810 } else if (from == InferenceBound.LOWER || to == InferenceBound.EQ) { 811 reportBoundError(uv, to, from); 812 } else { 813 reportBoundError(uv, from, to); 814 } 815 } 816 817 @Override 818 public String toString() { 819 return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, from); 820 } 821 } 822 823 /** 824 * Custom check executed by the legacy incorporation engine. Newly added bounds are checked 825 * against existing eq bounds. 826 */ 827 class EqCheckLegacy extends CheckBounds { 828 EqCheckLegacy(UndetVar uv, Type t, InferenceBound from) { 829 super(uv, t, InferenceContext::asInstType, InferenceContext::free, from); 830 } 831 832 @Override 833 public IncorporationAction dup(UndetVar that) { 834 return new EqCheckLegacy(that, t, from); 835 } 836 837 @Override 838 EnumSet<InferenceBound> boundsToCheck() { 839 return (from == InferenceBound.EQ) ? 840 EnumSet.allOf(InferenceBound.class) : 841 EnumSet.of(InferenceBound.EQ); 842 } 843 } 844 845 /** 846 * Check that the inferred type conforms to all bounds. 847 */ 848 class CheckInst extends CheckBounds { 849 850 EnumSet<InferenceBound> to; 851 852 CheckInst(UndetVar uv, InferenceBound ib, InferenceBound... rest) { 853 this(uv, EnumSet.of(ib, rest)); 854 } 855 856 CheckInst(UndetVar uv, EnumSet<InferenceBound> to) { 857 super(uv, uv.getInst(), InferenceBound.EQ); 858 this.to = to; 859 } 860 861 @Override 862 public IncorporationAction dup(UndetVar that) { 863 return new CheckInst(that, to); 864 } 865 866 @Override 867 EnumSet<InferenceBound> boundsToCheck() { 868 return to; 869 } 870 871 @Override 872 void report(InferenceBound from, InferenceBound to) { 873 reportInstError(uv, to); 874 } 875 } 876 877 /** 878 * Replace undetvars in bounds and check that the inferred type conforms to all bounds. 879 */ 880 class SubstBounds extends CheckInst { 881 SubstBounds(UndetVar uv) { 882 super(uv, InferenceBound.LOWER, InferenceBound.EQ, InferenceBound.UPPER); 883 } 884 885 @Override 886 public IncorporationAction dup(UndetVar that) { 887 return new SubstBounds(that); 888 } 889 890 @Override 891 void apply(InferenceContext inferenceContext, Warner warn) { 892 for (Type undet : inferenceContext.undetvars) { 893 //we could filter out variables not mentioning uv2... 894 UndetVar uv2 = (UndetVar)undet; 895 uv2.substBounds(List.of(uv.qtype), List.of(uv.getInst()), types); 896 checkCompatibleUpperBounds(uv2, inferenceContext); 897 } 898 super.apply(inferenceContext, warn); 899 } 900 901 /** 902 * Make sure that the upper bounds we got so far lead to a solvable inference 903 * variable by making sure that a glb exists. 904 */ 905 void checkCompatibleUpperBounds(UndetVar uv, InferenceContext inferenceContext) { 906 List<Type> hibounds = 907 Type.filter(uv.getBounds(InferenceBound.UPPER), new BoundFilter(inferenceContext)); 908 final Type hb; 909 if (hibounds.isEmpty()) 910 hb = syms.objectType; 911 else if (hibounds.tail.isEmpty()) 912 hb = hibounds.head; 913 else 914 hb = types.glb(hibounds); 915 if (hb == null || hb.isErroneous()) 916 reportBoundError(uv, InferenceBound.UPPER); 917 } 918 } 919 920 /** 921 * Perform pairwise comparison between common generic supertypes of two upper bounds. 922 */ 923 class CheckUpperBounds extends IncorporationAction { 924 925 public CheckUpperBounds(UndetVar uv, Type t) { 926 super(uv, t); 927 } 928 929 @Override 930 public IncorporationAction dup(UndetVar that) { 931 return new CheckUpperBounds(that, t); 932 } 933 934 @Override 935 void apply(InferenceContext inferenceContext, Warner warn) { 936 List<Type> boundList = uv.getBounds(InferenceBound.UPPER).stream() 937 .collect(types.closureCollector(true, types::isSameType)); 938 for (Type b2 : boundList) { 939 if (t == b2) continue; 940 /* This wildcard check is temporary workaround. This code may need to be 941 * revisited once spec bug JDK-7034922 is fixed. 942 */ 943 if (t != b2 && !t.hasTag(WILDCARD) && !b2.hasTag(WILDCARD)) { 944 for (Pair<Type, Type> commonSupers : getParameterizedSupers(t, b2)) { 945 List<Type> allParamsSuperBound1 = commonSupers.fst.allparams(); 946 List<Type> allParamsSuperBound2 = commonSupers.snd.allparams(); 947 while (allParamsSuperBound1.nonEmpty() && allParamsSuperBound2.nonEmpty()) { 948 //traverse the list of all params comparing them 949 if (!allParamsSuperBound1.head.hasTag(WILDCARD) && 950 !allParamsSuperBound2.head.hasTag(WILDCARD)) { 951 if (!isSameType(inferenceContext.asUndetVar(allParamsSuperBound1.head), 952 inferenceContext.asUndetVar(allParamsSuperBound2.head))) { 953 reportBoundError(uv, InferenceBound.UPPER); 954 } 955 } 956 allParamsSuperBound1 = allParamsSuperBound1.tail; 957 allParamsSuperBound2 = allParamsSuperBound2.tail; 958 } 959 Assert.check(allParamsSuperBound1.isEmpty() && allParamsSuperBound2.isEmpty()); 960 } 961 } 962 } 963 } 964 } 965 966 /** 967 * Perform propagation of bounds. Given a constraint of the kind {@code alpha <: T}, three 968 * kind of propagation occur: 969 * 970 * <li>T is copied into all matching bounds (i.e. lower/eq bounds) B of alpha such that B=beta (forward propagation)</li> 971 * <li>if T=beta, matching bounds (i.e. upper bounds) of beta are copied into alpha (backwards propagation)</li> 972 * <li>if T=beta, sets a symmetric bound on beta (i.e. beta :> alpha) (symmetric propagation) </li> 973 */ 974 class PropagateBounds extends IncorporationAction { 975 976 InferenceBound ib; 977 978 public PropagateBounds(UndetVar uv, Type t, InferenceBound ib) { 979 super(uv, t); 980 this.ib = ib; 981 } 982 983 @Override 984 public IncorporationAction dup(UndetVar that) { 985 return new PropagateBounds(that, t, ib); 986 } 987 988 void apply(InferenceContext inferenceContext, Warner warner) { 989 Type undetT = inferenceContext.asUndetVar(t); 990 if (undetT.hasTag(UNDETVAR) && !((UndetVar)undetT).isCaptured()) { 991 UndetVar uv2 = (UndetVar)undetT; 992 //symmetric propagation 993 uv2.addBound(ib.complement(), uv, types); 994 //backwards propagation 995 for (InferenceBound ib2 : backwards()) { 996 for (Type b : uv2.getBounds(ib2)) { 997 uv.addBound(ib2, b, types); 998 } 999 } 1000 } 1001 //forward propagation 1002 for (InferenceBound ib2 : forward()) { 1003 for (Type l : uv.getBounds(ib2)) { 1004 Type undet = inferenceContext.asUndetVar(l); 1005 if (undet.hasTag(TypeTag.UNDETVAR) && !((UndetVar)undet).isCaptured()) { 1006 UndetVar uv2 = (UndetVar)undet; 1007 uv2.addBound(ib, inferenceContext.asInstType(t), types); 1008 } 1009 } 1010 } 1011 } 1012 1013 EnumSet<InferenceBound> forward() { 1014 return (ib == InferenceBound.EQ) ? 1015 EnumSet.of(InferenceBound.EQ) : EnumSet.complementOf(EnumSet.of(ib)); 1016 } 1017 1018 EnumSet<InferenceBound> backwards() { 1019 return (ib == InferenceBound.EQ) ? 1020 EnumSet.allOf(InferenceBound.class) : EnumSet.of(ib); 1021 } 1022 1023 @Override 1024 public String toString() { 1025 return String.format("%s[undet=%s,t=%s,bound=%s]", getClass().getSimpleName(), uv.qtype, t, ib); 1026 } 1027 } 1028 1029 /** 1030 * This class models an incorporation engine. The engine is responsible for listening to 1031 * changes in inference variables and register incorporation actions accordingly. 1032 */ 1033 abstract class AbstractIncorporationEngine implements UndetVarListener { 1034 1035 @Override 1036 public void varInstantiated(UndetVar uv) { 1037 uv.incorporationActions.addFirst(new SubstBounds(uv)); 1038 } 1039 1040 @Override 1041 public void varBoundChanged(UndetVar uv, InferenceBound ib, Type bound, boolean update) { 1042 if (uv.isCaptured()) return; 1043 uv.incorporationActions.addAll(getIncorporationActions(uv, ib, bound, update)); 1044 } 1045 1046 abstract List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update); 1047 } 1048 1049 /** 1050 * A legacy incorporation engine. Used for source <= 7. 1051 */ 1052 AbstractIncorporationEngine legacyEngine = new AbstractIncorporationEngine() { 1053 1054 List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) { 1055 ListBuffer<IncorporationAction> actions = new ListBuffer<>(); 1056 Type inst = uv.getInst(); 1057 if (inst != null) { 1058 actions.add(new CheckInst(uv, ib)); 1059 } 1060 actions.add(new EqCheckLegacy(uv, t, ib)); 1061 return actions.toList(); 1062 } 1063 }; 1064 1065 /** 1066 * The standard incorporation engine. Used for source >= 8. 1067 */ 1068 AbstractIncorporationEngine graphEngine = new AbstractIncorporationEngine() { 1069 1070 @Override 1071 List<IncorporationAction> getIncorporationActions(UndetVar uv, InferenceBound ib, Type t, boolean update) { 1072 ListBuffer<IncorporationAction> actions = new ListBuffer<>(); 1073 Type inst = uv.getInst(); 1074 if (inst != null) { 1075 actions.add(new CheckInst(uv, ib)); 1076 } 1077 actions.add(new CheckBounds(uv, t, ib)); 1078 1079 if (update) { 1080 return actions.toList(); 1081 } 1082 1083 if (ib == InferenceBound.UPPER) { 1084 actions.add(new CheckUpperBounds(uv, t)); 1085 } 1086 1087 actions.add(new PropagateBounds(uv, t, ib)); 1088 1089 return actions.toList(); 1090 } 1091 }; 1092 1093 /** 1094 * Get the incorporation engine to be used in this compilation. 1095 */ 1096 AbstractIncorporationEngine incorporationEngine() { 1097 return allowGraphInference ? graphEngine : legacyEngine; 1098 } 1099 1100 /** max number of incorporation rounds. */ 1101 static final int MAX_INCORPORATION_STEPS = 10000; 1102 1103 /** 1104 * Check bounds and perform incorporation. 1105 */ 1106 void doIncorporation(InferenceContext inferenceContext, Warner warn) throws InferenceException { 1107 try { 1108 boolean progress = true; 1109 int round = 0; 1110 while (progress && round < MAX_INCORPORATION_STEPS) { 1111 progress = false; 1112 for (Type t : inferenceContext.undetvars) { 1113 UndetVar uv = (UndetVar)t; 1114 if (!uv.incorporationActions.isEmpty()) { 1115 progress = true; 1116 uv.incorporationActions.removeFirst().apply(inferenceContext, warn); 1117 } 1118 } 1119 round++; 1120 } 1121 } finally { 1122 incorporationCache.clear(); 1123 } 1124 } 1125 1126 /* If for two types t and s there is a least upper bound that contains 1127 * parameterized types G1, G2 ... Gn, then there exists supertypes of 't' of the form 1128 * G1<T1, ..., Tn>, G2<T1, ..., Tn>, ... Gn<T1, ..., Tn> and supertypes of 's' of the form 1129 * G1<S1, ..., Sn>, G2<S1, ..., Sn>, ... Gn<S1, ..., Sn> which will be returned by this method. 1130 * If no such common supertypes exists then an empty list is returned. 1131 * 1132 * As an example for the following input: 1133 * 1134 * t = java.util.ArrayList<java.lang.String> 1135 * s = java.util.List<T> 1136 * 1137 * we get this ouput (singleton list): 1138 * 1139 * [Pair[java.util.List<java.lang.String>,java.util.List<T>]] 1140 */ 1141 private List<Pair<Type, Type>> getParameterizedSupers(Type t, Type s) { 1142 Type lubResult = types.lub(t, s); 1143 if (lubResult == syms.errType || lubResult == syms.botType) { 1144 return List.nil(); 1145 } 1146 List<Type> supertypesToCheck = lubResult.isIntersection() ? 1147 ((IntersectionClassType)lubResult).getComponents() : 1148 List.of(lubResult); 1149 ListBuffer<Pair<Type, Type>> commonSupertypes = new ListBuffer<>(); 1150 for (Type sup : supertypesToCheck) { 1151 if (sup.isParameterized()) { 1152 Type asSuperOfT = asSuper(t, sup); 1153 Type asSuperOfS = asSuper(s, sup); 1154 commonSupertypes.add(new Pair<>(asSuperOfT, asSuperOfS)); 1155 } 1156 } 1157 return commonSupertypes.toList(); 1158 } 1159 //where 1160 private Type asSuper(Type t, Type sup) { 1161 return (sup.hasTag(ARRAY)) ? 1162 new ArrayType(asSuper(types.elemtype(t), types.elemtype(sup)), syms.arrayClass) : 1163 types.asSuper(t, sup.tsym); 1164 } 1165 1166 boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn) { 1167 IncorporationBinaryOp newOp = new IncorporationBinaryOp(opKind, op1, op2); 1168 Boolean res = incorporationCache.get(newOp); 1169 if (res == null) { 1170 incorporationCache.put(newOp, res = newOp.apply(warn)); 1171 } 1172 return res; 1173 } 1174 1175 /** 1176 * Three kinds of basic operation are supported as part of an incorporation step: 1177 * (i) subtype check, (ii) same type check and (iii) bound addition (either 1178 * upper/lower/eq bound). 1179 */ 1180 enum IncorporationBinaryOpKind { 1181 IS_SUBTYPE() { 1182 @Override 1183 boolean apply(Type op1, Type op2, Warner warn, Types types) { 1184 return types.isSubtypeUnchecked(op1, op2, warn); 1185 } 1186 }, 1187 IS_SAME_TYPE() { 1188 @Override 1189 boolean apply(Type op1, Type op2, Warner warn, Types types) { 1190 return types.isSameType(op1, op2); 1191 } 1192 }; 1193 1194 abstract boolean apply(Type op1, Type op2, Warner warn, Types types); 1195 } 1196 1197 /** 1198 * This class encapsulates a basic incorporation operation; incorporation 1199 * operations takes two type operands and a kind. Each operation performed 1200 * during an incorporation round is stored in a cache, so that operations 1201 * are not executed unnecessarily (which would potentially lead to adding 1202 * same bounds over and over). 1203 */ 1204 class IncorporationBinaryOp { 1205 1206 IncorporationBinaryOpKind opKind; 1207 Type op1; 1208 Type op2; 1209 1210 IncorporationBinaryOp(IncorporationBinaryOpKind opKind, Type op1, Type op2) { 1211 this.opKind = opKind; 1212 this.op1 = op1; 1213 this.op2 = op2; 1214 } 1215 1216 @Override 1217 public boolean equals(Object o) { 1218 if (!(o instanceof IncorporationBinaryOp)) { 1219 return false; 1220 } else { 1221 IncorporationBinaryOp that = (IncorporationBinaryOp)o; 1222 return opKind == that.opKind && 1223 types.isSameType(op1, that.op1, true) && 1224 types.isSameType(op2, that.op2, true); 1225 } 1226 } 1227 1228 @Override 1229 public int hashCode() { 1230 int result = opKind.hashCode(); 1231 result *= 127; 1232 result += types.hashCode(op1); 1233 result *= 127; 1234 result += types.hashCode(op2); 1235 return result; 1236 } 1237 1238 boolean apply(Warner warn) { 1239 return opKind.apply(op1, op2, warn, types); 1240 } 1241 } 1242 1243 /** an incorporation cache keeps track of all executed incorporation-related operations */ 1244 Map<IncorporationBinaryOp, Boolean> incorporationCache = new HashMap<>(); 1245 1246 protected static class BoundFilter implements Filter<Type> { 1247 1248 InferenceContext inferenceContext; 1249 1250 public BoundFilter(InferenceContext inferenceContext) { 1251 this.inferenceContext = inferenceContext; 1252 } 1253 1254 @Override 1255 public boolean accepts(Type t) { 1256 return !t.isErroneous() && !inferenceContext.free(t) && 1257 !t.hasTag(BOT); 1258 } 1259 } 1260 1261 /** 1262 * Incorporation error: mismatch between inferred type and given bound. 1263 */ 1264 void reportInstError(UndetVar uv, InferenceBound ib) { 1265 reportInferenceError( 1266 String.format("inferred.do.not.conform.to.%s.bounds", StringUtils.toLowerCase(ib.name())), 1267 uv.getInst(), 1268 uv.getBounds(ib)); 1269 } 1270 1271 /** 1272 * Incorporation error: mismatch between two (or more) bounds of same kind. 1273 */ 1274 void reportBoundError(UndetVar uv, InferenceBound ib) { 1275 reportInferenceError( 1276 String.format("incompatible.%s.bounds", StringUtils.toLowerCase(ib.name())), 1277 uv.qtype, 1278 uv.getBounds(ib)); 1279 } 1280 1281 /** 1282 * Incorporation error: mismatch between two (or more) bounds of different kinds. 1283 */ 1284 void reportBoundError(UndetVar uv, InferenceBound ib1, InferenceBound ib2) { 1285 reportInferenceError( 1286 String.format("incompatible.%s.%s.bounds", 1287 StringUtils.toLowerCase(ib1.name()), 1288 StringUtils.toLowerCase(ib2.name())), 1289 uv.qtype, 1290 uv.getBounds(ib1), 1291 uv.getBounds(ib2)); 1292 } 1293 1294 /** 1295 * Helper method: reports an inference error. 1296 */ 1297 void reportInferenceError(String key, Object... args) { 1298 throw inferenceException.setMessage(key, args); 1299 } 1300 // </editor-fold> 1301 1302 // <editor-fold defaultstate="collapsed" desc="Inference engine"> 1303 /** 1304 * Graph inference strategy - act as an input to the inference solver; a strategy is 1305 * composed of two ingredients: (i) find a node to solve in the inference graph, 1306 * and (ii) tell th engine when we are done fixing inference variables 1307 */ 1308 interface GraphStrategy { 1309 1310 /** 1311 * A NodeNotFoundException is thrown whenever an inference strategy fails 1312 * to pick the next node to solve in the inference graph. 1313 */ 1314 public static class NodeNotFoundException extends RuntimeException { 1315 private static final long serialVersionUID = 0; 1316 1317 InferenceGraph graph; 1318 1319 public NodeNotFoundException(InferenceGraph graph) { 1320 this.graph = graph; 1321 } 1322 } 1323 /** 1324 * Pick the next node (leaf) to solve in the graph 1325 */ 1326 Node pickNode(InferenceGraph g) throws NodeNotFoundException; 1327 /** 1328 * Is this the last step? 1329 */ 1330 boolean done(); 1331 } 1332 1333 /** 1334 * Simple solver strategy class that locates all leaves inside a graph 1335 * and picks the first leaf as the next node to solve 1336 */ 1337 abstract class LeafSolver implements GraphStrategy { 1338 public Node pickNode(InferenceGraph g) { 1339 if (g.nodes.isEmpty()) { 1340 //should not happen 1341 throw new NodeNotFoundException(g); 1342 } 1343 return g.nodes.get(0); 1344 } 1345 } 1346 1347 /** 1348 * This solver uses an heuristic to pick the best leaf - the heuristic 1349 * tries to select the node that has maximal probability to contain one 1350 * or more inference variables in a given list 1351 */ 1352 abstract class BestLeafSolver extends LeafSolver { 1353 1354 /** list of ivars of which at least one must be solved */ 1355 List<Type> varsToSolve; 1356 1357 BestLeafSolver(List<Type> varsToSolve) { 1358 this.varsToSolve = varsToSolve; 1359 } 1360 1361 /** 1362 * Computes a path that goes from a given node to the leafs in the graph. 1363 * Typically this will start from a node containing a variable in 1364 * {@code varsToSolve}. For any given path, the cost is computed as the total 1365 * number of type-variables that should be eagerly instantiated across that path. 1366 */ 1367 Pair<List<Node>, Integer> computeTreeToLeafs(Node n) { 1368 Pair<List<Node>, Integer> cachedPath = treeCache.get(n); 1369 if (cachedPath == null) { 1370 //cache miss 1371 if (n.isLeaf()) { 1372 //if leaf, stop 1373 cachedPath = new Pair<>(List.of(n), n.data.length()); 1374 } else { 1375 //if non-leaf, proceed recursively 1376 Pair<List<Node>, Integer> path = new Pair<>(List.of(n), n.data.length()); 1377 for (Node n2 : n.getAllDependencies()) { 1378 if (n2 == n) continue; 1379 Pair<List<Node>, Integer> subpath = computeTreeToLeafs(n2); 1380 path = new Pair<>(path.fst.prependList(subpath.fst), 1381 path.snd + subpath.snd); 1382 } 1383 cachedPath = path; 1384 } 1385 //save results in cache 1386 treeCache.put(n, cachedPath); 1387 } 1388 return cachedPath; 1389 } 1390 1391 /** cache used to avoid redundant computation of tree costs */ 1392 final Map<Node, Pair<List<Node>, Integer>> treeCache = new HashMap<>(); 1393 1394 /** constant value used to mark non-existent paths */ 1395 final Pair<List<Node>, Integer> noPath = new Pair<>(null, Integer.MAX_VALUE); 1396 1397 /** 1398 * Pick the leaf that minimize cost 1399 */ 1400 @Override 1401 public Node pickNode(final InferenceGraph g) { 1402 treeCache.clear(); //graph changes at every step - cache must be cleared 1403 Pair<List<Node>, Integer> bestPath = noPath; 1404 for (Node n : g.nodes) { 1405 if (!Collections.disjoint(n.data, varsToSolve)) { 1406 Pair<List<Node>, Integer> path = computeTreeToLeafs(n); 1407 //discard all paths containing at least a node in the 1408 //closure computed above 1409 if (path.snd < bestPath.snd) { 1410 bestPath = path; 1411 } 1412 } 1413 } 1414 if (bestPath == noPath) { 1415 //no path leads there 1416 throw new NodeNotFoundException(g); 1417 } 1418 return bestPath.fst.head; 1419 } 1420 } 1421 1422 /** 1423 * The inference process can be thought of as a sequence of steps. Each step 1424 * instantiates an inference variable using a subset of the inference variable 1425 * bounds, if certain condition are met. Decisions such as the sequence in which 1426 * steps are applied, or which steps are to be applied are left to the inference engine. 1427 */ 1428 enum InferenceStep { 1429 1430 /** 1431 * Instantiate an inference variables using one of its (ground) equality 1432 * constraints 1433 */ 1434 EQ(InferenceBound.EQ) { 1435 @Override 1436 Type solve(UndetVar uv, InferenceContext inferenceContext) { 1437 return filterBounds(uv, inferenceContext).head; 1438 } 1439 }, 1440 /** 1441 * Instantiate an inference variables using its (ground) lower bounds. Such 1442 * bounds are merged together using lub(). 1443 */ 1444 LOWER(InferenceBound.LOWER) { 1445 @Override 1446 Type solve(UndetVar uv, InferenceContext inferenceContext) { 1447 Infer infer = inferenceContext.infer; 1448 List<Type> lobounds = filterBounds(uv, inferenceContext); 1449 //note: lobounds should have at least one element 1450 Type owntype = lobounds.tail.tail == null ? lobounds.head : infer.types.lub(lobounds); 1451 if (owntype.isPrimitive() || owntype.hasTag(ERROR)) { 1452 throw infer.inferenceException 1453 .setMessage("no.unique.minimal.instance.exists", 1454 uv.qtype, lobounds); 1455 } else { 1456 return owntype; 1457 } 1458 } 1459 }, 1460 /** 1461 * Infer uninstantiated/unbound inference variables occurring in 'throws' 1462 * clause as RuntimeException 1463 */ 1464 THROWS(InferenceBound.UPPER) { 1465 @Override 1466 public boolean accepts(UndetVar t, InferenceContext inferenceContext) { 1467 if (!t.isThrows()) { 1468 //not a throws undet var 1469 return false; 1470 } 1471 Types types = inferenceContext.types; 1472 Symtab syms = inferenceContext.infer.syms; 1473 return t.getBounds(InferenceBound.UPPER).stream() 1474 .filter(b -> !inferenceContext.free(b)) 1475 .allMatch(u -> types.isSubtype(syms.runtimeExceptionType, u)); 1476 } 1477 1478 @Override 1479 Type solve(UndetVar uv, InferenceContext inferenceContext) { 1480 return inferenceContext.infer.syms.runtimeExceptionType; 1481 } 1482 }, 1483 /** 1484 * Instantiate an inference variables using its (ground) upper bounds. Such 1485 * bounds are merged together using glb(). 1486 */ 1487 UPPER(InferenceBound.UPPER) { 1488 @Override 1489 Type solve(UndetVar uv, InferenceContext inferenceContext) { 1490 Infer infer = inferenceContext.infer; 1491 List<Type> hibounds = filterBounds(uv, inferenceContext); 1492 //note: hibounds should have at least one element 1493 Type owntype = hibounds.tail.tail == null ? hibounds.head : infer.types.glb(hibounds); 1494 if (owntype.isPrimitive() || owntype.hasTag(ERROR)) { 1495 throw infer.inferenceException 1496 .setMessage("no.unique.maximal.instance.exists", 1497 uv.qtype, hibounds); 1498 } else { 1499 return owntype; 1500 } 1501 } 1502 }, 1503 /** 1504 * Like the former; the only difference is that this step can only be applied 1505 * if all upper bounds are ground. 1506 */ 1507 UPPER_LEGACY(InferenceBound.UPPER) { 1508 @Override 1509 public boolean accepts(UndetVar t, InferenceContext inferenceContext) { 1510 return !inferenceContext.free(t.getBounds(ib)) && !t.isCaptured(); 1511 } 1512 1513 @Override 1514 Type solve(UndetVar uv, InferenceContext inferenceContext) { 1515 return UPPER.solve(uv, inferenceContext); 1516 } 1517 }, 1518 /** 1519 * Like the former; the only difference is that this step can only be applied 1520 * if all upper/lower bounds are ground. 1521 */ 1522 CAPTURED(InferenceBound.UPPER) { 1523 @Override 1524 public boolean accepts(UndetVar t, InferenceContext inferenceContext) { 1525 return t.isCaptured() && 1526 !inferenceContext.free(t.getBounds(InferenceBound.UPPER, InferenceBound.LOWER)); 1527 } 1528 1529 @Override 1530 Type solve(UndetVar uv, InferenceContext inferenceContext) { 1531 Infer infer = inferenceContext.infer; 1532 Type upper = UPPER.filterBounds(uv, inferenceContext).nonEmpty() ? 1533 UPPER.solve(uv, inferenceContext) : 1534 infer.syms.objectType; 1535 Type lower = LOWER.filterBounds(uv, inferenceContext).nonEmpty() ? 1536 LOWER.solve(uv, inferenceContext) : 1537 infer.syms.botType; 1538 CapturedType prevCaptured = (CapturedType)uv.qtype; 1539 return new CapturedType(prevCaptured.tsym.name, prevCaptured.tsym.owner, 1540 upper, lower, prevCaptured.wildcard); 1541 } 1542 }; 1543 1544 final InferenceBound ib; 1545 1546 InferenceStep(InferenceBound ib) { 1547 this.ib = ib; 1548 } 1549 1550 /** 1551 * Find an instantiated type for a given inference variable within 1552 * a given inference context 1553 */ 1554 abstract Type solve(UndetVar uv, InferenceContext inferenceContext); 1555 1556 /** 1557 * Can the inference variable be instantiated using this step? 1558 */ 1559 public boolean accepts(UndetVar t, InferenceContext inferenceContext) { 1560 return filterBounds(t, inferenceContext).nonEmpty() && !t.isCaptured(); 1561 } 1562 1563 /** 1564 * Return the subset of ground bounds in a given bound set (i.e. eq/lower/upper) 1565 */ 1566 List<Type> filterBounds(UndetVar uv, InferenceContext inferenceContext) { 1567 return Type.filter(uv.getBounds(ib), new BoundFilter(inferenceContext)); 1568 } 1569 } 1570 1571 /** 1572 * This enumeration defines the sequence of steps to be applied when the 1573 * solver works in legacy mode. The steps in this enumeration reflect 1574 * the behavior of old inference routine (see JLS SE 7 15.12.2.7/15.12.2.8). 1575 */ 1576 enum LegacyInferenceSteps { 1577 1578 EQ_LOWER(EnumSet.of(InferenceStep.EQ, InferenceStep.LOWER)), 1579 EQ_UPPER(EnumSet.of(InferenceStep.EQ, InferenceStep.UPPER_LEGACY)); 1580 1581 final EnumSet<InferenceStep> steps; 1582 1583 LegacyInferenceSteps(EnumSet<InferenceStep> steps) { 1584 this.steps = steps; 1585 } 1586 } 1587 1588 /** 1589 * This enumeration defines the sequence of steps to be applied when the 1590 * graph solver is used. This order is defined so as to maximize compatibility 1591 * w.r.t. old inference routine (see JLS SE 7 15.12.2.7/15.12.2.8). 1592 */ 1593 enum GraphInferenceSteps { 1594 1595 EQ(EnumSet.of(InferenceStep.EQ)), 1596 EQ_LOWER(EnumSet.of(InferenceStep.EQ, InferenceStep.LOWER)), 1597 EQ_LOWER_THROWS_UPPER_CAPTURED(EnumSet.of(InferenceStep.EQ, InferenceStep.LOWER, InferenceStep.UPPER, InferenceStep.THROWS, InferenceStep.CAPTURED)); 1598 1599 final EnumSet<InferenceStep> steps; 1600 1601 GraphInferenceSteps(EnumSet<InferenceStep> steps) { 1602 this.steps = steps; 1603 } 1604 } 1605 1606 /** 1607 * There are two kinds of dependencies between inference variables. The basic 1608 * kind of dependency (or bound dependency) arises when a variable mention 1609 * another variable in one of its bounds. There's also a more subtle kind 1610 * of dependency that arises when a variable 'might' lead to better constraints 1611 * on another variable (this is typically the case with variables holding up 1612 * stuck expressions). 1613 */ 1614 enum DependencyKind implements GraphUtils.DependencyKind { 1615 1616 /** bound dependency */ 1617 BOUND("dotted"), 1618 /** stuck dependency */ 1619 STUCK("dashed"); 1620 1621 final String dotSyle; 1622 1623 private DependencyKind(String dotSyle) { 1624 this.dotSyle = dotSyle; 1625 } 1626 } 1627 1628 /** 1629 * This is the graph inference solver - the solver organizes all inference variables in 1630 * a given inference context by bound dependencies - in the general case, such dependencies 1631 * would lead to a cyclic directed graph (hence the name); the dependency info is used to build 1632 * an acyclic graph, where all cyclic variables are bundled together. An inference 1633 * step corresponds to solving a node in the acyclic graph - this is done by 1634 * relying on a given strategy (see GraphStrategy). 1635 */ 1636 class GraphSolver { 1637 1638 InferenceContext inferenceContext; 1639 Warner warn; 1640 1641 GraphSolver(InferenceContext inferenceContext, Warner warn) { 1642 this.inferenceContext = inferenceContext; 1643 this.warn = warn; 1644 } 1645 1646 /** 1647 * Solve variables in a given inference context. The amount of variables 1648 * to be solved, and the way in which the underlying acyclic graph is explored 1649 * depends on the selected solver strategy. 1650 */ 1651 void solve(GraphStrategy sstrategy) { 1652 doIncorporation(inferenceContext, warn); //initial propagation of bounds 1653 InferenceGraph inferenceGraph = new InferenceGraph(); 1654 while (!sstrategy.done()) { 1655 if (dependenciesFolder != null) { 1656 //add this graph to the pending queue 1657 pendingGraphs = pendingGraphs.prepend(inferenceGraph.toDot()); 1658 } 1659 InferenceGraph.Node nodeToSolve = sstrategy.pickNode(inferenceGraph); 1660 List<Type> varsToSolve = List.from(nodeToSolve.data); 1661 List<Type> saved_undet = inferenceContext.save(); 1662 try { 1663 //repeat until all variables are solved 1664 outer: while (Type.containsAny(inferenceContext.restvars(), varsToSolve)) { 1665 //for each inference phase 1666 for (GraphInferenceSteps step : GraphInferenceSteps.values()) { 1667 if (inferenceContext.solveBasic(varsToSolve, step.steps).nonEmpty()) { 1668 doIncorporation(inferenceContext, warn); 1669 continue outer; 1670 } 1671 } 1672 //no progress 1673 throw inferenceException.setMessage(); 1674 } 1675 } 1676 catch (InferenceException ex) { 1677 //did we fail because of interdependent ivars? 1678 inferenceContext.rollback(saved_undet); 1679 instantiateAsUninferredVars(varsToSolve, inferenceContext); 1680 doIncorporation(inferenceContext, warn); 1681 } 1682 inferenceGraph.deleteNode(nodeToSolve); 1683 } 1684 } 1685 1686 /** 1687 * The dependencies between the inference variables that need to be solved 1688 * form a (possibly cyclic) graph. This class reduces the original dependency graph 1689 * to an acyclic version, where cyclic nodes are folded into a single 'super node'. 1690 */ 1691 class InferenceGraph { 1692 1693 /** 1694 * This class represents a node in the graph. Each node corresponds 1695 * to an inference variable and has edges (dependencies) on other 1696 * nodes. The node defines an entry point that can be used to receive 1697 * updates on the structure of the graph this node belongs to (used to 1698 * keep dependencies in sync). 1699 */ 1700 class Node extends GraphUtils.TarjanNode<ListBuffer<Type>, Node> implements DottableNode<ListBuffer<Type>, Node> { 1701 1702 /** node dependencies */ 1703 Set<Node> deps; 1704 1705 Node(Type ivar) { 1706 super(ListBuffer.of(ivar)); 1707 this.deps = new HashSet<>(); 1708 } 1709 1710 @Override 1711 public GraphUtils.DependencyKind[] getSupportedDependencyKinds() { 1712 return new GraphUtils.DependencyKind[] { DependencyKind.BOUND }; 1713 } 1714 1715 public Iterable<? extends Node> getAllDependencies() { 1716 return deps; 1717 } 1718 1719 @Override 1720 public Collection<? extends Node> getDependenciesByKind(GraphUtils.DependencyKind dk) { 1721 if (dk == DependencyKind.BOUND) { 1722 return deps; 1723 } else { 1724 throw new IllegalStateException(); 1725 } 1726 } 1727 1728 /** 1729 * Adds dependency with given kind. 1730 */ 1731 protected void addDependency(Node depToAdd) { 1732 deps.add(depToAdd); 1733 } 1734 1735 /** 1736 * Add multiple dependencies of same given kind. 1737 */ 1738 protected void addDependencies(Set<Node> depsToAdd) { 1739 for (Node n : depsToAdd) { 1740 addDependency(n); 1741 } 1742 } 1743 1744 /** 1745 * Remove a dependency, regardless of its kind. 1746 */ 1747 protected boolean removeDependency(Node n) { 1748 return deps.remove(n); 1749 } 1750 1751 /** 1752 * Compute closure of a give node, by recursively walking 1753 * through all its dependencies (of given kinds) 1754 */ 1755 protected Set<Node> closure() { 1756 boolean progress = true; 1757 Set<Node> closure = new HashSet<>(); 1758 closure.add(this); 1759 while (progress) { 1760 progress = false; 1761 for (Node n1 : new HashSet<>(closure)) { 1762 progress = closure.addAll(n1.deps); 1763 } 1764 } 1765 return closure; 1766 } 1767 1768 /** 1769 * Is this node a leaf? This means either the node has no dependencies, 1770 * or it just has self-dependencies. 1771 */ 1772 protected boolean isLeaf() { 1773 //no deps, or only one self dep 1774 if (deps.isEmpty()) return true; 1775 for (Node n : deps) { 1776 if (n != this) { 1777 return false; 1778 } 1779 } 1780 return true; 1781 } 1782 1783 /** 1784 * Merge this node with another node, acquiring its dependencies. 1785 * This routine is used to merge all cyclic node together and 1786 * form an acyclic graph. 1787 */ 1788 protected void mergeWith(List<? extends Node> nodes) { 1789 for (Node n : nodes) { 1790 Assert.check(n.data.length() == 1, "Attempt to merge a compound node!"); 1791 data.appendList(n.data); 1792 addDependencies(n.deps); 1793 } 1794 //update deps 1795 Set<Node> deps2 = new HashSet<>(); 1796 for (Node d : deps) { 1797 if (data.contains(d.data.first())) { 1798 deps2.add(this); 1799 } else { 1800 deps2.add(d); 1801 } 1802 } 1803 deps = deps2; 1804 } 1805 1806 /** 1807 * Notify all nodes that something has changed in the graph 1808 * topology. 1809 */ 1810 private void graphChanged(Node from, Node to) { 1811 if (removeDependency(from)) { 1812 if (to != null) { 1813 addDependency(to); 1814 } 1815 } 1816 } 1817 1818 @Override 1819 public Properties nodeAttributes() { 1820 Properties p = new Properties(); 1821 p.put("label", "\"" + toString() + "\""); 1822 return p; 1823 } 1824 1825 @Override 1826 public Properties dependencyAttributes(Node sink, GraphUtils.DependencyKind dk) { 1827 Properties p = new Properties(); 1828 p.put("style", ((DependencyKind)dk).dotSyle); 1829 StringBuilder buf = new StringBuilder(); 1830 String sep = ""; 1831 for (Type from : data) { 1832 UndetVar uv = (UndetVar)inferenceContext.asUndetVar(from); 1833 for (Type bound : uv.getBounds(InferenceBound.values())) { 1834 if (bound.containsAny(List.from(sink.data))) { 1835 buf.append(sep); 1836 buf.append(bound); 1837 sep = ","; 1838 } 1839 } 1840 } 1841 p.put("label", "\"" + buf.toString() + "\""); 1842 return p; 1843 } 1844 } 1845 1846 /** the nodes in the inference graph */ 1847 ArrayList<Node> nodes; 1848 1849 InferenceGraph() { 1850 initNodes(); 1851 } 1852 1853 /** 1854 * Basic lookup helper for retrieving a graph node given an inference 1855 * variable type. 1856 */ 1857 public Node findNode(Type t) { 1858 for (Node n : nodes) { 1859 if (n.data.contains(t)) { 1860 return n; 1861 } 1862 } 1863 return null; 1864 } 1865 1866 /** 1867 * Delete a node from the graph. This update the underlying structure 1868 * of the graph (including dependencies) via listeners updates. 1869 */ 1870 public void deleteNode(Node n) { 1871 Assert.check(nodes.contains(n)); 1872 nodes.remove(n); 1873 notifyUpdate(n, null); 1874 } 1875 1876 /** 1877 * Notify all nodes of a change in the graph. If the target node is 1878 * {@code null} the source node is assumed to be removed. 1879 */ 1880 void notifyUpdate(Node from, Node to) { 1881 for (Node n : nodes) { 1882 n.graphChanged(from, to); 1883 } 1884 } 1885 1886 /** 1887 * Create the graph nodes. First a simple node is created for every inference 1888 * variables to be solved. Then Tarjan is used to found all connected components 1889 * in the graph. For each component containing more than one node, a super node is 1890 * created, effectively replacing the original cyclic nodes. 1891 */ 1892 void initNodes() { 1893 //add nodes 1894 nodes = new ArrayList<>(); 1895 for (Type t : inferenceContext.restvars()) { 1896 nodes.add(new Node(t)); 1897 } 1898 //add dependencies 1899 for (Node n_i : nodes) { 1900 Type i = n_i.data.first(); 1901 for (Node n_j : nodes) { 1902 Type j = n_j.data.first(); 1903 UndetVar uv_i = (UndetVar)inferenceContext.asUndetVar(i); 1904 if (Type.containsAny(uv_i.getBounds(InferenceBound.values()), List.of(j))) { 1905 //update i's bound dependencies 1906 n_i.addDependency(n_j); 1907 } 1908 } 1909 } 1910 //merge cyclic nodes 1911 ArrayList<Node> acyclicNodes = new ArrayList<>(); 1912 for (List<? extends Node> conSubGraph : GraphUtils.tarjan(nodes)) { 1913 if (conSubGraph.length() > 1) { 1914 Node root = conSubGraph.head; 1915 root.mergeWith(conSubGraph.tail); 1916 for (Node n : conSubGraph) { 1917 notifyUpdate(n, root); 1918 } 1919 } 1920 acyclicNodes.add(conSubGraph.head); 1921 } 1922 nodes = acyclicNodes; 1923 } 1924 1925 /** 1926 * Debugging: dot representation of this graph 1927 */ 1928 String toDot() { 1929 StringBuilder buf = new StringBuilder(); 1930 for (Type t : inferenceContext.undetvars) { 1931 UndetVar uv = (UndetVar)t; 1932 buf.append(String.format("var %s - upper bounds = %s, lower bounds = %s, eq bounds = %s\\n", 1933 uv.qtype, uv.getBounds(InferenceBound.UPPER), uv.getBounds(InferenceBound.LOWER), 1934 uv.getBounds(InferenceBound.EQ))); 1935 } 1936 return GraphUtils.toDot(nodes, "inferenceGraph" + hashCode(), buf.toString()); 1937 } 1938 } 1939 } 1940 // </editor-fold> 1941 1942 // <editor-fold defaultstate="collapsed" desc="Inference context"> 1943 /** 1944 * Functional interface for defining inference callbacks. Certain actions 1945 * (i.e. subtyping checks) might need to be redone after all inference variables 1946 * have been fixed. 1947 */ 1948 interface FreeTypeListener { 1949 void typesInferred(InferenceContext inferenceContext); 1950 } 1951 1952 final InferenceContext emptyContext; 1953 // </editor-fold> 1954} 1955