InferenceContext.java revision 3031:286fc9270404
172878Skris/* 272878Skris * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3100772Sjhb * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4100772Sjhb * 5100772Sjhb * This code is free software; you can redistribute it and/or modify it 672878Skris * under the terms of the GNU General Public License version 2 only, as 7101232Sru * published by the Free Software Foundation. Oracle designates this 8136606Sobrien * particular file as subject to the "Classpath" exception as provided 9100773Sjhb * by Oracle in the LICENSE file that accompanied this code. 10113374Sobrien * 11100773Sjhb * This code is distributed in the hope that it will be useful, but WITHOUT 12100772Sjhb * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13103560Sjhb * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14126657Sbde * version 2 for more details (a copy is included in the LICENSE file that 15115175Speter * accompanied this code). 16100773Sjhb * 17103560Sjhb * You should have received a copy of the GNU General Public License version 18100773Sjhb * 2 along with this work; if not, write to the Free Software Foundation, 19129217Scognet * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20129217Scognet * 21100773Sjhb * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22100772Sjhb * or visit www.oracle.com if you need additional information or have any 2372878Skris * questions. 2472878Skris */ 2572878Skris 2672878Skrispackage com.sun.tools.javac.comp; 27100773Sjhb 28136607Sobrienimport java.util.EnumSet; 29136607Sobrienimport java.util.HashMap; 30136607Sobrienimport java.util.Map; 31136606Sobrienimport java.util.Set; 32136606Sobrien 33136606Sobrienimport com.sun.tools.javac.code.Symtab; 34136606Sobrienimport com.sun.tools.javac.code.Type; 35136606Sobrienimport com.sun.tools.javac.code.Type.CapturedType; 36136606Sobrienimport com.sun.tools.javac.code.Type.CapturedUndetVar; 37136606Sobrienimport com.sun.tools.javac.code.Type.TypeMapping; 38136606Sobrienimport com.sun.tools.javac.code.Type.TypeVar; 39136606Sobrienimport com.sun.tools.javac.code.Type.UndetVar; 40136606Sobrienimport com.sun.tools.javac.code.Type.UndetVar.InferenceBound; 41136606Sobrienimport com.sun.tools.javac.code.Types; 42136606Sobrienimport com.sun.tools.javac.comp.Infer.BestLeafSolver; 43136606Sobrienimport com.sun.tools.javac.comp.Infer.FreeTypeListener; 44136606Sobrienimport com.sun.tools.javac.comp.Infer.GraphSolver; 45136606Sobrienimport com.sun.tools.javac.comp.Infer.GraphStrategy; 46136606Sobrienimport com.sun.tools.javac.comp.Infer.InferenceException; 47136606Sobrienimport com.sun.tools.javac.comp.Infer.InferenceStep; 48136607Sobrienimport com.sun.tools.javac.comp.Infer.LeafSolver; 49136607Sobrienimport com.sun.tools.javac.tree.JCTree; 50133525Sobrienimport com.sun.tools.javac.tree.TreeMaker; 51103045Smuximport com.sun.tools.javac.util.Assert; 52103045Smuximport com.sun.tools.javac.util.Context; 53100773Sjhbimport com.sun.tools.javac.util.Filter; 54136607Sobrienimport com.sun.tools.javac.util.JCDiagnostic; 55136607Sobrienimport com.sun.tools.javac.util.JCDiagnostic.Factory; 56136607Sobrienimport com.sun.tools.javac.util.List; 57136607Sobrienimport com.sun.tools.javac.util.ListBuffer; 5872878Skrisimport com.sun.tools.javac.util.Log; 5972878Skrisimport com.sun.tools.javac.util.Warner; 60136606Sobrien 6196421Sobrien/** 6272878Skris * An inference context keeps track of the set of variables that are free 6396421Sobrien * in the current context. It provides utility methods for opening/closing 64160536Simp * types to their corresponding free/closed forms. It also provide hooks for 65127258Smarcel * attaching deferred post-inference action (see PendingCheck). Finally, 66127258Smarcel * it can be used as an entry point for performing upper/lower bound inference 6796421Sobrien * (see InferenceKind). 68127258Smarcel * 6972878Skris * <p><b>This is NOT part of any supported API. 7072878Skris * If you write code that depends on this, you do so at your own risk. 71127888Sdfr * This code and its internal interfaces are subject to change or 72133000Sobrien * deletion without notice.</b> 73136606Sobrien */ 74136606Sobrienclass InferenceContext { 75136606Sobrien 76136606Sobrien /** list of inference vars as undet vars */ 77136606Sobrien List<Type> undetvars; 78136606Sobrien 79127888Sdfr /** list of inference vars in this context */ 80127888Sdfr List<Type> inferencevars; 81126938Strhodes 82126890Strhodes Map<FreeTypeListener, List<Type>> freeTypeListeners = new HashMap<>(); 83126890Strhodes 84126890Strhodes List<FreeTypeListener> freetypeListeners = List.nil(); 85112768Sobrien 86126890Strhodes Types types; 8772878Skris Infer infer; 88126890Strhodes 89136606Sobrien public InferenceContext(Infer infer, List<Type> inferencevars) { 90126890Strhodes this.inferencevars = inferencevars; 91136606Sobrien 92136606Sobrien this.infer = infer; 93126890Strhodes this.types = infer.types; 94136606Sobrien 95126890Strhodes fromTypeVarFun = new TypeMapping<Void>() { 96136606Sobrien @Override 97126890Strhodes public Type visitTypeVar(TypeVar tv, Void aVoid) { 98136606Sobrien return new UndetVar(tv, types); 99126890Strhodes } 100136606Sobrien 101126890Strhodes @Override 102136606Sobrien public Type visitCapturedType(CapturedType t, Void aVoid) { 10372878Skris return new CapturedUndetVar(t, types); 104136606Sobrien } 105136607Sobrien }; 106136607Sobrien this.undetvars = inferencevars.map(fromTypeVarFun); 107135678Scognet } 108136606Sobrien 109135678Scognet TypeMapping<Void> fromTypeVarFun; 110135678Scognet 111146589Scognet /** 112136606Sobrien * add a new inference var to this inference context 113136606Sobrien */ 114135678Scognet void addVar(TypeVar t) { 11572878Skris this.undetvars = this.undetvars.prepend(fromTypeVarFun.apply(t)); 11672878Skris this.inferencevars = this.inferencevars.prepend(t); 11772878Skris } 11872878Skris 11972878Skris /** 12072878Skris * returns the list of free variables (as type-variables) in this 121126657Sbde * inference context 122136607Sobrien */ 123136607Sobrien List<Type> inferenceVars() { 124136607Sobrien return inferencevars; 125125254Sbde } 126136606Sobrien 127126657Sbde /** 128112768Sobrien * returns the list of uninstantiated variables (as type-variables) in this 129126657Sbde * inference context 130103045Smux */ 131103562Sjhb List<Type> restvars() { 13274069Ssobomax return filterVars(new Filter<UndetVar>() { 133103562Sjhb public boolean accepts(UndetVar uv) { 13472878Skris return uv.inst == null; 135160497Sdes } 136160497Sdes }); 137160497Sdes } 138160497Sdes 139136607Sobrien /** 140103045Smux * returns the list of instantiated variables (as type-variables) in this 141136606Sobrien * inference context 14273145Skris */ 143136606Sobrien List<Type> instvars() { 14474553Skris return filterVars(new Filter<UndetVar>() { 145136606Sobrien public boolean accepts(UndetVar uv) { 14672878Skris return uv.inst != null; 147136606Sobrien } 14873145Skris }); 149136606Sobrien } 15072878Skris 151103562Sjhb /** 15272878Skris * Get list of bounded inference variables (where bound is other than 153103562Sjhb * declared bounds). 15472878Skris */ 155103562Sjhb final List<Type> boundedVars() { 156103562Sjhb return filterVars(new Filter<UndetVar>() { 157153169Sru public boolean accepts(UndetVar uv) { 158153169Sru return uv.getBounds(InferenceBound.UPPER) 159153169Sru .diff(uv.getDeclaredBounds()) 16072878Skris .appendList(uv.getBounds(InferenceBound.EQ, InferenceBound.LOWER)).nonEmpty(); 161103562Sjhb } 16272878Skris }); 163103562Sjhb } 16472878Skris 165103562Sjhb /* Returns the corresponding inference variables. 16672878Skris */ 167103562Sjhb private List<Type> filterVars(Filter<UndetVar> fu) { 16872878Skris ListBuffer<Type> res = new ListBuffer<>(); 169103562Sjhb for (Type t : undetvars) { 17072878Skris UndetVar uv = (UndetVar)t; 171103562Sjhb if (fu.accepts(uv)) { 172115175Speter res.append(uv.qtype); 173136607Sobrien } 174138685Sobrien } 175136607Sobrien return res.toList(); 176138685Sobrien } 177136607Sobrien 178138685Sobrien /** 179103562Sjhb * is this type free? 180103562Sjhb */ 18172878Skris final boolean free(Type t) { 182103562Sjhb return t.containsAny(inferencevars); 18372878Skris } 18472878Skris 185103561Sjhb final boolean free(List<Type> ts) { 186112769Sobrien for (Type t : ts) { 187112769Sobrien if (free(t)) return true; 188112769Sobrien } 189112769Sobrien return false; 190160534Scognet } 191160534Scognet 192160535Scognet /** 193160534Scognet * Returns a list of free variables in a given type 194160534Scognet */ 195124347Sru final List<Type> freeVarsIn(Type t) { 196103561Sjhb ListBuffer<Type> buf = new ListBuffer<>(); 197103561Sjhb for (Type iv : inferenceVars()) { 198126890Strhodes if (t.contains(iv)) { 199126890Strhodes buf.add(iv); 200126890Strhodes } 201103561Sjhb } 202126890Strhodes return buf.toList(); 203103561Sjhb } 204 205 final List<Type> freeVarsIn(List<Type> ts) { 206 ListBuffer<Type> buf = new ListBuffer<>(); 207 for (Type t : ts) { 208 buf.appendList(freeVarsIn(t)); 209 } 210 ListBuffer<Type> buf2 = new ListBuffer<>(); 211 for (Type t : buf) { 212 if (!buf2.contains(t)) { 213 buf2.add(t); 214 } 215 } 216 return buf2.toList(); 217 } 218 219 /** 220 * Replace all free variables in a given type with corresponding 221 * undet vars (used ahead of subtyping/compatibility checks to allow propagation 222 * of inference constraints). 223 */ 224 final Type asUndetVar(Type t) { 225 return types.subst(t, inferencevars, undetvars); 226 } 227 228 final List<Type> asUndetVars(List<Type> ts) { 229 ListBuffer<Type> buf = new ListBuffer<>(); 230 for (Type t : ts) { 231 buf.append(asUndetVar(t)); 232 } 233 return buf.toList(); 234 } 235 236 List<Type> instTypes() { 237 ListBuffer<Type> buf = new ListBuffer<>(); 238 for (Type t : undetvars) { 239 UndetVar uv = (UndetVar)t; 240 buf.append(uv.inst != null ? uv.inst : uv.qtype); 241 } 242 return buf.toList(); 243 } 244 245 /** 246 * Replace all free variables in a given type with corresponding 247 * instantiated types - if one or more free variable has not been 248 * fully instantiated, it will still be available in the resulting type. 249 */ 250 Type asInstType(Type t) { 251 return types.subst(t, inferencevars, instTypes()); 252 } 253 254 List<Type> asInstTypes(List<Type> ts) { 255 ListBuffer<Type> buf = new ListBuffer<>(); 256 for (Type t : ts) { 257 buf.append(asInstType(t)); 258 } 259 return buf.toList(); 260 } 261 262 /** 263 * Add custom hook for performing post-inference action 264 */ 265 void addFreeTypeListener(List<Type> types, FreeTypeListener ftl) { 266 freeTypeListeners.put(ftl, freeVarsIn(types)); 267 } 268 269 /** 270 * Mark the inference context as complete and trigger evaluation 271 * of all deferred checks. 272 */ 273 void notifyChange() { 274 notifyChange(inferencevars.diff(restvars())); 275 } 276 277 void notifyChange(List<Type> inferredVars) { 278 InferenceException thrownEx = null; 279 for (Map.Entry<FreeTypeListener, List<Type>> entry : 280 new HashMap<>(freeTypeListeners).entrySet()) { 281 if (!Type.containsAny(entry.getValue(), inferencevars.diff(inferredVars))) { 282 try { 283 entry.getKey().typesInferred(this); 284 freeTypeListeners.remove(entry.getKey()); 285 } catch (InferenceException ex) { 286 if (thrownEx == null) { 287 thrownEx = ex; 288 } 289 } 290 } 291 } 292 //inference exception multiplexing - present any inference exception 293 //thrown when processing listeners as a single one 294 if (thrownEx != null) { 295 throw thrownEx; 296 } 297 } 298 299 /** 300 * Save the state of this inference context 301 */ 302 List<Type> save() { 303 ListBuffer<Type> buf = new ListBuffer<>(); 304 for (Type t : undetvars) { 305 UndetVar uv = (UndetVar)t; 306 UndetVar uv2 = new UndetVar((TypeVar)uv.qtype, types); 307 for (InferenceBound ib : InferenceBound.values()) { 308 for (Type b : uv.getBounds(ib)) { 309 uv2.addBound(ib, b, types); 310 } 311 } 312 uv2.inst = uv.inst; 313 buf.add(uv2); 314 } 315 return buf.toList(); 316 } 317 318 /** Restore the state of this inference context to the previous known checkpoint. 319 * Consider that the number of saved undetermined variables can be different to the current 320 * amount. This is because new captured variables could have been added. 321 */ 322 void rollback(List<Type> saved_undet) { 323 Assert.check(saved_undet != null); 324 //restore bounds (note: we need to preserve the old instances) 325 ListBuffer<Type> newUndetVars = new ListBuffer<>(); 326 ListBuffer<Type> newInferenceVars = new ListBuffer<>(); 327 while (saved_undet.nonEmpty() && undetvars.nonEmpty()) { 328 UndetVar uv = (UndetVar)undetvars.head; 329 UndetVar uv_saved = (UndetVar)saved_undet.head; 330 if (uv.qtype == uv_saved.qtype) { 331 for (InferenceBound ib : InferenceBound.values()) { 332 uv.setBounds(ib, uv_saved.getBounds(ib)); 333 } 334 uv.inst = uv_saved.inst; 335 undetvars = undetvars.tail; 336 saved_undet = saved_undet.tail; 337 newUndetVars.add(uv); 338 newInferenceVars.add(uv.qtype); 339 } else { 340 undetvars = undetvars.tail; 341 } 342 } 343 undetvars = newUndetVars.toList(); 344 inferencevars = newInferenceVars.toList(); 345 } 346 347 /** 348 * Copy variable in this inference context to the given context 349 */ 350 void dupTo(final InferenceContext that) { 351 dupTo(that, false); 352 } 353 354 void dupTo(final InferenceContext that, boolean clone) { 355 that.inferencevars = that.inferencevars.appendList(inferencevars.diff(that.inferencevars)); 356 List<Type> undetsToPropagate = clone ? save() : undetvars; 357 that.undetvars = that.undetvars.appendList(undetsToPropagate.diff(that.undetvars)); //propagate cloned undet!! 358 //set up listeners to notify original inference contexts as 359 //propagated vars are inferred in new context 360 for (Type t : inferencevars) { 361 that.freeTypeListeners.put(new FreeTypeListener() { 362 public void typesInferred(InferenceContext inferenceContext) { 363 InferenceContext.this.notifyChange(); 364 } 365 }, List.of(t)); 366 } 367 } 368 369 private void solve(GraphStrategy ss, Warner warn) { 370 solve(ss, new HashMap<Type, Set<Type>>(), warn); 371 } 372 373 /** 374 * Solve with given graph strategy. 375 */ 376 private void solve(GraphStrategy ss, Map<Type, Set<Type>> stuckDeps, Warner warn) { 377 GraphSolver s = infer.new GraphSolver(this, stuckDeps, warn); 378 s.solve(ss); 379 } 380 381 /** 382 * Solve all variables in this context. 383 */ 384 public void solve(Warner warn) { 385 solve(infer.new LeafSolver() { 386 public boolean done() { 387 return restvars().isEmpty(); 388 } 389 }, warn); 390 } 391 392 /** 393 * Solve all variables in the given list. 394 */ 395 public void solve(final List<Type> vars, Warner warn) { 396 solve(infer.new BestLeafSolver(vars) { 397 public boolean done() { 398 return !free(asInstTypes(vars)); 399 } 400 }, warn); 401 } 402 403 /** 404 * Solve at least one variable in given list. 405 */ 406 public void solveAny(List<Type> varsToSolve, Map<Type, Set<Type>> optDeps, Warner warn) { 407 solve(infer.new BestLeafSolver(varsToSolve.intersect(restvars())) { 408 public boolean done() { 409 return instvars().intersect(varsToSolve).nonEmpty(); 410 } 411 }, optDeps, warn); 412 } 413 414 /** 415 * Apply a set of inference steps 416 */ 417 private boolean solveBasic(EnumSet<InferenceStep> steps) { 418 return solveBasic(inferencevars, steps); 419 } 420 421 boolean solveBasic(List<Type> varsToSolve, EnumSet<InferenceStep> steps) { 422 boolean changed = false; 423 for (Type t : varsToSolve.intersect(restvars())) { 424 UndetVar uv = (UndetVar)asUndetVar(t); 425 for (InferenceStep step : steps) { 426 if (step.accepts(uv, this)) { 427 uv.inst = step.solve(uv, this); 428 changed = true; 429 break; 430 } 431 } 432 } 433 return changed; 434 } 435 436 /** 437 * Instantiate inference variables in legacy mode (JLS 15.12.2.7, 15.12.2.8). 438 * During overload resolution, instantiation is done by doing a partial 439 * inference process using eq/lower bound instantiation. During check, 440 * we also instantiate any remaining vars by repeatedly using eq/upper 441 * instantiation, until all variables are solved. 442 */ 443 public void solveLegacy(boolean partial, Warner warn, EnumSet<InferenceStep> steps) { 444 while (true) { 445 boolean stuck = !solveBasic(steps); 446 if (restvars().isEmpty() || partial) { 447 //all variables have been instantiated - exit 448 break; 449 } else if (stuck) { 450 //some variables could not be instantiated because of cycles in 451 //upper bounds - provide a (possibly recursive) default instantiation 452 infer.instantiateAsUninferredVars(restvars(), this); 453 break; 454 } else { 455 //some variables have been instantiated - replace newly instantiated 456 //variables in remaining upper bounds and continue 457 for (Type t : undetvars) { 458 UndetVar uv = (UndetVar)t; 459 uv.substBounds(inferenceVars(), instTypes(), types); 460 } 461 } 462 } 463 infer.checkWithinBounds(this, warn); 464 } 465 466 @Override 467 public String toString() { 468 return "Inference vars: " + inferencevars + '\n' + 469 "Undet vars: " + undetvars; 470 } 471 472 /* Method Types.capture() generates a new type every time it's applied 473 * to a wildcard parameterized type. This is intended functionality but 474 * there are some cases when what you need is not to generate a new 475 * captured type but to check that a previously generated captured type 476 * is correct. There are cases when caching a captured type for later 477 * reuse is sound. In general two captures from the same AST are equal. 478 * This is why the tree is used as the key of the map below. This map 479 * stores a Type per AST. 480 */ 481 Map<JCTree, Type> captureTypeCache = new HashMap<>(); 482 483 Type cachedCapture(JCTree tree, Type t, boolean readOnly) { 484 Type captured = captureTypeCache.get(tree); 485 if (captured != null) { 486 return captured; 487 } 488 489 Type result = types.capture(t); 490 if (result != t && !readOnly) { // then t is a wildcard parameterized type 491 captureTypeCache.put(tree, result); 492 } 493 return result; 494 } 495} 496