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