Tue, 25 Sep 2012 11:56:46 +0100
7177306: Regression: unchecked method call does not erase return type
Summary: Spurious extra call to Attr.checkMethod when method call is unchecked
Reviewed-by: jjg, dlsmith
1 /*
2 * Copyright (c) 1999, 2012, 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 */
26 package com.sun.tools.javac.comp;
28 import com.sun.tools.javac.code.*;
29 import com.sun.tools.javac.code.Symbol.*;
30 import com.sun.tools.javac.code.Type.*;
31 import com.sun.tools.javac.code.Type.UndetVar.InferenceBound;
32 import com.sun.tools.javac.comp.Resolve.InapplicableMethodException;
33 import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode;
34 import com.sun.tools.javac.tree.JCTree;
35 import com.sun.tools.javac.tree.JCTree.JCTypeCast;
36 import com.sun.tools.javac.tree.TreeInfo;
37 import com.sun.tools.javac.util.*;
38 import com.sun.tools.javac.util.List;
39 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
41 import java.util.HashMap;
42 import java.util.Map;
43 import java.util.Set;
45 import static com.sun.tools.javac.code.TypeTags.*;
47 /** Helper class for type parameter inference, used by the attribution phase.
48 *
49 * <p><b>This is NOT part of any supported API.
50 * If you write code that depends on this, you do so at your own risk.
51 * This code and its internal interfaces are subject to change or
52 * deletion without notice.</b>
53 */
54 public class Infer {
55 protected static final Context.Key<Infer> inferKey =
56 new Context.Key<Infer>();
58 /** A value for prototypes that admit any type, including polymorphic ones. */
59 public static final Type anyPoly = new Type(NONE, null);
61 Symtab syms;
62 Types types;
63 Check chk;
64 Resolve rs;
65 Log log;
66 JCDiagnostic.Factory diags;
68 public static Infer instance(Context context) {
69 Infer instance = context.get(inferKey);
70 if (instance == null)
71 instance = new Infer(context);
72 return instance;
73 }
75 protected Infer(Context context) {
76 context.put(inferKey, this);
77 syms = Symtab.instance(context);
78 types = Types.instance(context);
79 rs = Resolve.instance(context);
80 log = Log.instance(context);
81 chk = Check.instance(context);
82 diags = JCDiagnostic.Factory.instance(context);
83 inferenceException = new InferenceException(diags);
84 }
86 /**
87 * This exception class is design to store a list of diagnostics corresponding
88 * to inference errors that can arise during a method applicability check.
89 */
90 public static class InferenceException extends InapplicableMethodException {
91 private static final long serialVersionUID = 0;
93 List<JCDiagnostic> messages = List.nil();
95 InferenceException(JCDiagnostic.Factory diags) {
96 super(diags);
97 }
99 @Override
100 InapplicableMethodException setMessage(JCDiagnostic diag) {
101 messages = messages.append(diag);
102 return this;
103 }
105 @Override
106 public JCDiagnostic getDiagnostic() {
107 return messages.head;
108 }
110 void clear() {
111 messages = List.nil();
112 }
113 }
115 private final InferenceException inferenceException;
117 /***************************************************************************
118 * Mini/Maximization of UndetVars
119 ***************************************************************************/
121 /** Instantiate undetermined type variable to its minimal upper bound.
122 * Throw a NoInstanceException if this not possible.
123 */
124 void maximizeInst(UndetVar that, Warner warn) throws InferenceException {
125 List<Type> hibounds = Type.filter(that.getBounds(InferenceBound.UPPER), boundFilter);
126 if (that.getBounds(InferenceBound.EQ).isEmpty()) {
127 if (hibounds.isEmpty())
128 that.inst = syms.objectType;
129 else if (hibounds.tail.isEmpty())
130 that.inst = hibounds.head;
131 else
132 that.inst = types.glb(hibounds);
133 } else {
134 that.inst = that.getBounds(InferenceBound.EQ).head;
135 }
136 if (that.inst == null ||
137 that.inst.isErroneous())
138 throw inferenceException
139 .setMessage("no.unique.maximal.instance.exists",
140 that.qtype, hibounds);
141 }
143 private Filter<Type> boundFilter = new Filter<Type>() {
144 @Override
145 public boolean accepts(Type t) {
146 return !t.isErroneous() && t.tag != BOT;
147 }
148 };
150 /** Instantiate undetermined type variable to the lub of all its lower bounds.
151 * Throw a NoInstanceException if this not possible.
152 */
153 void minimizeInst(UndetVar that, Warner warn) throws InferenceException {
154 List<Type> lobounds = Type.filter(that.getBounds(InferenceBound.LOWER), boundFilter);
155 if (that.getBounds(InferenceBound.EQ).isEmpty()) {
156 if (lobounds.isEmpty()) {
157 //do nothing - the inference variable is under-constrained
158 return;
159 } else if (lobounds.tail.isEmpty())
160 that.inst = lobounds.head.isPrimitive() ? syms.errType : lobounds.head;
161 else {
162 that.inst = types.lub(lobounds);
163 }
164 if (that.inst == null || that.inst.tag == ERROR)
165 throw inferenceException
166 .setMessage("no.unique.minimal.instance.exists",
167 that.qtype, lobounds);
168 } else {
169 that.inst = that.getBounds(InferenceBound.EQ).head;
170 }
171 }
173 /***************************************************************************
174 * Exported Methods
175 ***************************************************************************/
177 /**
178 * Instantiate uninferred inference variables (JLS 15.12.2.8). First
179 * if the method return type is non-void, we derive constraints from the
180 * expected type - then we use declared bound well-formedness to derive additional
181 * constraints. If no instantiation exists, or if several incomparable
182 * best instantiations exist throw a NoInstanceException.
183 */
184 public void instantiateUninferred(DiagnosticPosition pos,
185 InferenceContext inferenceContext,
186 MethodType mtype,
187 Attr.ResultInfo resultInfo,
188 Warner warn) throws InferenceException {
189 Type to = resultInfo.pt;
190 if (to.tag == NONE) {
191 to = mtype.getReturnType().tag <= VOID ?
192 mtype.getReturnType() : syms.objectType;
193 }
194 Type qtype1 = inferenceContext.asFree(mtype.getReturnType(), types);
195 if (!types.isSubtype(qtype1,
196 qtype1.tag == UNDETVAR ? types.boxedTypeOrType(to) : to)) {
197 throw inferenceException
198 .setMessage("infer.no.conforming.instance.exists",
199 inferenceContext.restvars(), mtype.getReturnType(), to);
200 }
202 while (true) {
203 boolean stuck = true;
204 for (Type t : inferenceContext.undetvars) {
205 UndetVar uv = (UndetVar)t;
206 if (uv.inst == null && (uv.getBounds(InferenceBound.EQ).nonEmpty() ||
207 !inferenceContext.free(uv.getBounds(InferenceBound.UPPER)))) {
208 maximizeInst((UndetVar)t, warn);
209 stuck = false;
210 }
211 }
212 if (inferenceContext.restvars().isEmpty()) {
213 //all variables have been instantiated - exit
214 break;
215 } else if (stuck) {
216 //some variables could not be instantiated because of cycles in
217 //upper bounds - provide a (possibly recursive) default instantiation
218 instantiateAsUninferredVars(inferenceContext);
219 break;
220 } else {
221 //some variables have been instantiated - replace newly instantiated
222 //variables in remaining upper bounds and continue
223 for (Type t : inferenceContext.undetvars) {
224 UndetVar uv = (UndetVar)t;
225 uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
226 }
227 }
228 }
229 }
231 /**
232 * Infer cyclic inference variables as described in 15.12.2.8.
233 */
234 private void instantiateAsUninferredVars(InferenceContext inferenceContext) {
235 ListBuffer<Type> todo = ListBuffer.lb();
236 //step 1 - create fresh tvars
237 for (Type t : inferenceContext.undetvars) {
238 UndetVar uv = (UndetVar)t;
239 if (uv.inst == null) {
240 TypeSymbol fresh_tvar = new TypeSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner);
241 fresh_tvar.type = new TypeVar(fresh_tvar, types.makeCompoundType(uv.getBounds(InferenceBound.UPPER)), null);
242 todo.append(uv);
243 uv.inst = fresh_tvar.type;
244 }
245 }
246 //step 2 - replace fresh tvars in their bounds
247 List<Type> formals = inferenceContext.inferenceVars();
248 for (Type t : todo) {
249 UndetVar uv = (UndetVar)t;
250 TypeVar ct = (TypeVar)uv.inst;
251 ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct), types));
252 if (ct.bound.isErroneous()) {
253 //report inference error if glb fails
254 reportBoundError(uv, BoundErrorKind.BAD_UPPER);
255 }
256 formals = formals.tail;
257 }
258 }
260 /** Instantiate a generic method type by finding instantiations for all its
261 * inference variables so that it can be applied to a given argument type list.
262 */
263 public Type instantiateMethod(Env<AttrContext> env,
264 List<Type> tvars,
265 MethodType mt,
266 Attr.ResultInfo resultInfo,
267 Symbol msym,
268 List<Type> argtypes,
269 boolean allowBoxing,
270 boolean useVarargs,
271 Warner warn) throws InferenceException {
272 //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
273 final InferenceContext inferenceContext = new InferenceContext(tvars, this);
274 inferenceException.clear();
276 try {
277 rs.checkRawArgumentsAcceptable(env, inferenceContext, argtypes, mt.getParameterTypes(),
278 allowBoxing, useVarargs, warn, new InferenceCheckHandler(inferenceContext));
280 // minimize as yet undetermined type variables
281 for (Type t : inferenceContext.undetvars) {
282 minimizeInst((UndetVar)t, warn);
283 }
285 checkWithinBounds(inferenceContext, warn);
287 mt = (MethodType)inferenceContext.asInstType(mt, types);
289 List<Type> restvars = inferenceContext.restvars();
291 if (!restvars.isEmpty()) {
292 if (resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
293 instantiateUninferred(env.tree.pos(), inferenceContext, mt, resultInfo, warn);
294 checkWithinBounds(inferenceContext, warn);
295 mt = (MethodType)inferenceContext.asInstType(mt, types);
296 if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
297 log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
298 }
299 }
300 }
302 // return instantiated version of method type
303 return mt;
304 } finally {
305 inferenceContext.notifyChange(types);
306 }
307 }
308 //where
310 /** inference check handler **/
311 class InferenceCheckHandler implements Resolve.MethodCheckHandler {
313 InferenceContext inferenceContext;
315 public InferenceCheckHandler(InferenceContext inferenceContext) {
316 this.inferenceContext = inferenceContext;
317 }
319 public InapplicableMethodException arityMismatch() {
320 return inferenceException.setMessage("infer.arg.length.mismatch", inferenceContext.inferenceVars());
321 }
322 public InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details) {
323 String key = varargs ?
324 "infer.varargs.argument.mismatch" :
325 "infer.no.conforming.assignment.exists";
326 return inferenceException.setMessage(key,
327 inferenceContext.inferenceVars(), details);
328 }
329 public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) {
330 return inferenceException.setMessage("inaccessible.varargs.type",
331 expected, Kinds.kindName(location), location);
332 }
333 }
335 /** check that type parameters are within their bounds.
336 */
337 void checkWithinBounds(InferenceContext inferenceContext,
338 Warner warn) throws InferenceException {
339 //step 1 - check compatibility of instantiated type w.r.t. initial bounds
340 for (Type t : inferenceContext.undetvars) {
341 UndetVar uv = (UndetVar)t;
342 uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
343 checkCompatibleUpperBounds(uv, inferenceContext.inferenceVars());
344 if (!inferenceContext.restvars().contains(uv.qtype)) {
345 Type inst = inferenceContext.asInstType(t, types);
346 for (Type u : uv.getBounds(InferenceBound.UPPER)) {
347 if (!types.isSubtypeUnchecked(inst, inferenceContext.asFree(u, types), warn)) {
348 reportBoundError(uv, BoundErrorKind.UPPER);
349 }
350 }
351 for (Type l : uv.getBounds(InferenceBound.LOWER)) {
352 Assert.check(!inferenceContext.free(l));
353 if (!types.isSubtypeUnchecked(l, inst, warn)) {
354 reportBoundError(uv, BoundErrorKind.LOWER);
355 }
356 }
357 for (Type e : uv.getBounds(InferenceBound.EQ)) {
358 Assert.check(!inferenceContext.free(e));
359 if (!types.isSameType(inst, e)) {
360 reportBoundError(uv, BoundErrorKind.EQ);
361 }
362 }
363 }
364 }
366 //step 2 - check that eq bounds are consistent w.r.t. eq/lower bounds
367 for (Type t : inferenceContext.undetvars) {
368 UndetVar uv = (UndetVar)t;
369 //check eq bounds consistency
370 Type eq = null;
371 for (Type e : uv.getBounds(InferenceBound.EQ)) {
372 Assert.check(!inferenceContext.free(e));
373 if (eq != null && !types.isSameType(e, eq)) {
374 reportBoundError(uv, BoundErrorKind.EQ);
375 }
376 eq = e;
377 for (Type l : uv.getBounds(InferenceBound.LOWER)) {
378 Assert.check(!inferenceContext.free(l));
379 if (!types.isSubtypeUnchecked(l, e, warn)) {
380 reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER);
381 }
382 }
383 for (Type u : uv.getBounds(InferenceBound.UPPER)) {
384 if (inferenceContext.free(u)) continue;
385 if (!types.isSubtypeUnchecked(e, u, warn)) {
386 reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER);
387 }
388 }
389 }
390 }
391 }
393 void checkCompatibleUpperBounds(UndetVar uv, List<Type> tvars) {
394 // VGJ: sort of inlined maximizeInst() below. Adding
395 // bounds can cause lobounds that are above hibounds.
396 ListBuffer<Type> hiboundsNoVars = ListBuffer.lb();
397 for (Type t : Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter)) {
398 if (!t.containsAny(tvars)) {
399 hiboundsNoVars.append(t);
400 }
401 }
402 List<Type> hibounds = hiboundsNoVars.toList();
403 Type hb = null;
404 if (hibounds.isEmpty())
405 hb = syms.objectType;
406 else if (hibounds.tail.isEmpty())
407 hb = hibounds.head;
408 else
409 hb = types.glb(hibounds);
410 if (hb == null || hb.isErroneous())
411 reportBoundError(uv, BoundErrorKind.BAD_UPPER);
412 }
414 enum BoundErrorKind {
415 BAD_UPPER() {
416 @Override
417 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
418 return ex.setMessage("incompatible.upper.bounds", uv.qtype,
419 uv.getBounds(InferenceBound.UPPER));
420 }
421 },
422 BAD_EQ_UPPER() {
423 @Override
424 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
425 return ex.setMessage("incompatible.eq.upper.bounds", uv.qtype,
426 uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.UPPER));
427 }
428 },
429 BAD_EQ_LOWER() {
430 @Override
431 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
432 return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype,
433 uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER));
434 }
435 },
436 UPPER() {
437 @Override
438 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
439 return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst,
440 uv.getBounds(InferenceBound.UPPER));
441 }
442 },
443 LOWER() {
444 @Override
445 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
446 return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst,
447 uv.getBounds(InferenceBound.LOWER));
448 }
449 },
450 EQ() {
451 @Override
452 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
453 return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst,
454 uv.getBounds(InferenceBound.EQ));
455 }
456 };
458 abstract InapplicableMethodException setMessage(InferenceException ex, UndetVar uv);
459 }
460 //where
461 void reportBoundError(UndetVar uv, BoundErrorKind bk) {
462 throw bk.setMessage(inferenceException, uv);
463 }
465 /**
466 * Compute a synthetic method type corresponding to the requested polymorphic
467 * method signature. The target return type is computed from the immediately
468 * enclosing scope surrounding the polymorphic-signature call.
469 */
470 Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
471 MethodSymbol spMethod, // sig. poly. method or null if none
472 List<Type> argtypes) {
473 final Type restype;
475 //The return type for a polymorphic signature call is computed from
476 //the enclosing tree E, as follows: if E is a cast, then use the
477 //target type of the cast expression as a return type; if E is an
478 //expression statement, the return type is 'void' - otherwise the
479 //return type is simply 'Object'. A correctness check ensures that
480 //env.next refers to the lexically enclosing environment in which
481 //the polymorphic signature call environment is nested.
483 switch (env.next.tree.getTag()) {
484 case TYPECAST:
485 JCTypeCast castTree = (JCTypeCast)env.next.tree;
486 restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ?
487 castTree.clazz.type :
488 syms.objectType;
489 break;
490 case EXEC:
491 JCTree.JCExpressionStatement execTree =
492 (JCTree.JCExpressionStatement)env.next.tree;
493 restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ?
494 syms.voidType :
495 syms.objectType;
496 break;
497 default:
498 restype = syms.objectType;
499 }
501 List<Type> paramtypes = Type.map(argtypes, implicitArgType);
502 List<Type> exType = spMethod != null ?
503 spMethod.getThrownTypes() :
504 List.of(syms.throwableType); // make it throw all exceptions
506 MethodType mtype = new MethodType(paramtypes,
507 restype,
508 exType,
509 syms.methodClass);
510 return mtype;
511 }
512 //where
513 Mapping implicitArgType = new Mapping ("implicitArgType") {
514 public Type apply(Type t) {
515 t = types.erasure(t);
516 if (t.tag == BOT)
517 // nulls type as the marker type Null (which has no instances)
518 // infer as java.lang.Void for now
519 t = types.boxedClass(syms.voidType).type;
520 return t;
521 }
522 };
524 /**
525 * Mapping that turns inference variables into undet vars
526 * (used by inference context)
527 */
528 Mapping fromTypeVarFun = new Mapping("fromTypeVarFun") {
529 public Type apply(Type t) {
530 if (t.tag == TYPEVAR) return new UndetVar((TypeVar)t, types);
531 else return t.map(this);
532 }
533 };
535 /**
536 * An inference context keeps track of the set of variables that are free
537 * in the current context. It provides utility methods for opening/closing
538 * types to their corresponding free/closed forms. It also provide hooks for
539 * attaching deferred post-inference action (see PendingCheck). Finally,
540 * it can be used as an entry point for performing upper/lower bound inference
541 * (see InferenceKind).
542 */
543 static class InferenceContext {
545 /**
546 * Single-method-interface for defining inference callbacks. Certain actions
547 * (i.e. subtyping checks) might need to be redone after all inference variables
548 * have been fixed.
549 */
550 interface FreeTypeListener {
551 void typesInferred(InferenceContext inferenceContext);
552 }
554 /** list of inference vars as undet vars */
555 List<Type> undetvars;
557 /** list of inference vars in this context */
558 List<Type> inferencevars;
560 java.util.Map<FreeTypeListener, List<Type>> freeTypeListeners =
561 new java.util.HashMap<FreeTypeListener, List<Type>>();
563 List<FreeTypeListener> freetypeListeners = List.nil();
565 public InferenceContext(List<Type> inferencevars, Infer infer) {
566 this.undetvars = Type.map(inferencevars, infer.fromTypeVarFun);
567 this.inferencevars = inferencevars;
568 }
570 /**
571 * returns the list of free variables (as type-variables) in this
572 * inference context
573 */
574 List<Type> inferenceVars() {
575 return inferencevars;
576 }
578 /**
579 * returns the list of uninstantiated variables (as type-variables) in this
580 * inference context (usually called after instantiate())
581 */
582 List<Type> restvars() {
583 List<Type> undetvars = this.undetvars;
584 ListBuffer<Type> restvars = ListBuffer.lb();
585 for (Type t : instTypes()) {
586 UndetVar uv = (UndetVar)undetvars.head;
587 if (uv.qtype == t) {
588 restvars.append(t);
589 }
590 undetvars = undetvars.tail;
591 }
592 return restvars.toList();
593 }
595 /**
596 * is this type free?
597 */
598 final boolean free(Type t) {
599 return t.containsAny(inferencevars);
600 }
602 final boolean free(List<Type> ts) {
603 for (Type t : ts) {
604 if (free(t)) return true;
605 }
606 return false;
607 }
609 /**
610 * Returns a list of free variables in a given type
611 */
612 final List<Type> freeVarsIn(Type t) {
613 ListBuffer<Type> buf = ListBuffer.lb();
614 for (Type iv : inferenceVars()) {
615 if (t.contains(iv)) {
616 buf.add(iv);
617 }
618 }
619 return buf.toList();
620 }
622 final List<Type> freeVarsIn(List<Type> ts) {
623 ListBuffer<Type> buf = ListBuffer.lb();
624 for (Type t : ts) {
625 buf.appendList(freeVarsIn(t));
626 }
627 ListBuffer<Type> buf2 = ListBuffer.lb();
628 for (Type t : buf) {
629 if (!buf2.contains(t)) {
630 buf2.add(t);
631 }
632 }
633 return buf2.toList();
634 }
636 /**
637 * Replace all free variables in a given type with corresponding
638 * undet vars (used ahead of subtyping/compatibility checks to allow propagation
639 * of inference constraints).
640 */
641 final Type asFree(Type t, Types types) {
642 return types.subst(t, inferencevars, undetvars);
643 }
645 final List<Type> asFree(List<Type> ts, Types types) {
646 ListBuffer<Type> buf = ListBuffer.lb();
647 for (Type t : ts) {
648 buf.append(asFree(t, types));
649 }
650 return buf.toList();
651 }
653 List<Type> instTypes() {
654 ListBuffer<Type> buf = ListBuffer.lb();
655 for (Type t : undetvars) {
656 UndetVar uv = (UndetVar)t;
657 buf.append(uv.inst != null ? uv.inst : uv.qtype);
658 }
659 return buf.toList();
660 }
662 /**
663 * Replace all free variables in a given type with corresponding
664 * instantiated types - if one or more free variable has not been
665 * fully instantiated, it will still be available in the resulting type.
666 */
667 Type asInstType(Type t, Types types) {
668 return types.subst(t, inferencevars, instTypes());
669 }
671 List<Type> asInstTypes(List<Type> ts, Types types) {
672 ListBuffer<Type> buf = ListBuffer.lb();
673 for (Type t : ts) {
674 buf.append(asInstType(t, types));
675 }
676 return buf.toList();
677 }
679 /**
680 * Add custom hook for performing post-inference action
681 */
682 void addFreeTypeListener(List<Type> types, FreeTypeListener ftl) {
683 freeTypeListeners.put(ftl, freeVarsIn(types));
684 }
686 /**
687 * Mark the inference context as complete and trigger evaluation
688 * of all deferred checks.
689 */
690 void notifyChange(Types types) {
691 InferenceException thrownEx = null;
692 for (Map.Entry<FreeTypeListener, List<Type>> entry :
693 new HashMap<FreeTypeListener, List<Type>>(freeTypeListeners).entrySet()) {
694 if (!Type.containsAny(entry.getValue(), restvars())) {
695 try {
696 entry.getKey().typesInferred(this);
697 freeTypeListeners.remove(entry.getKey());
698 } catch (InferenceException ex) {
699 if (thrownEx == null) {
700 thrownEx = ex;
701 }
702 }
703 }
704 }
705 //inference exception multiplexing - present any inference exception
706 //thrown when processing listeners as a single one
707 if (thrownEx != null) {
708 throw thrownEx;
709 }
710 }
711 }
713 final InferenceContext emptyContext = new InferenceContext(List.<Type>nil(), this);
714 }