Tue, 08 Jan 2013 10:15:30 +0100
8005243: Restructure method check code to allow pluggable checkers
Summary: Add interface to perform a method check - to be implemented by helper classes
Reviewed-by: jjg
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;
45 import static com.sun.tools.javac.code.TypeTag.*;
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 DeferredAttr deferredAttr;
66 Log log;
67 JCDiagnostic.Factory diags;
69 public static Infer instance(Context context) {
70 Infer instance = context.get(inferKey);
71 if (instance == null)
72 instance = new Infer(context);
73 return instance;
74 }
76 protected Infer(Context context) {
77 context.put(inferKey, this);
78 syms = Symtab.instance(context);
79 types = Types.instance(context);
80 rs = Resolve.instance(context);
81 deferredAttr = DeferredAttr.instance(context);
82 log = Log.instance(context);
83 chk = Check.instance(context);
84 diags = JCDiagnostic.Factory.instance(context);
85 inferenceException = new InferenceException(diags);
86 }
88 /**
89 * This exception class is design to store a list of diagnostics corresponding
90 * to inference errors that can arise during a method applicability check.
91 */
92 public static class InferenceException extends InapplicableMethodException {
93 private static final long serialVersionUID = 0;
95 List<JCDiagnostic> messages = List.nil();
97 InferenceException(JCDiagnostic.Factory diags) {
98 super(diags);
99 }
101 @Override
102 InapplicableMethodException setMessage(JCDiagnostic diag) {
103 messages = messages.append(diag);
104 return this;
105 }
107 @Override
108 public JCDiagnostic getDiagnostic() {
109 return messages.head;
110 }
112 void clear() {
113 messages = List.nil();
114 }
115 }
117 final InferenceException inferenceException;
119 /***************************************************************************
120 * Mini/Maximization of UndetVars
121 ***************************************************************************/
123 /** Instantiate undetermined type variable to its minimal upper bound.
124 * Throw a NoInstanceException if this not possible.
125 */
126 void maximizeInst(UndetVar that, Warner warn) throws InferenceException {
127 List<Type> hibounds = Type.filter(that.getBounds(InferenceBound.UPPER), boundFilter);
128 if (that.getBounds(InferenceBound.EQ).isEmpty()) {
129 if (hibounds.isEmpty())
130 that.inst = syms.objectType;
131 else if (hibounds.tail.isEmpty())
132 that.inst = hibounds.head;
133 else
134 that.inst = types.glb(hibounds);
135 } else {
136 that.inst = that.getBounds(InferenceBound.EQ).head;
137 }
138 if (that.inst == null ||
139 that.inst.isErroneous())
140 throw inferenceException
141 .setMessage("no.unique.maximal.instance.exists",
142 that.qtype, hibounds);
143 }
145 private Filter<Type> boundFilter = new Filter<Type>() {
146 @Override
147 public boolean accepts(Type t) {
148 return !t.isErroneous() && !t.hasTag(BOT);
149 }
150 };
152 /** Instantiate undetermined type variable to the lub of all its lower bounds.
153 * Throw a NoInstanceException if this not possible.
154 */
155 void minimizeInst(UndetVar that, Warner warn) throws InferenceException {
156 List<Type> lobounds = Type.filter(that.getBounds(InferenceBound.LOWER), boundFilter);
157 if (that.getBounds(InferenceBound.EQ).isEmpty()) {
158 if (lobounds.isEmpty()) {
159 //do nothing - the inference variable is under-constrained
160 return;
161 } else if (lobounds.tail.isEmpty())
162 that.inst = lobounds.head.isPrimitive() ? syms.errType : lobounds.head;
163 else {
164 that.inst = types.lub(lobounds);
165 }
166 if (that.inst == null || that.inst.hasTag(ERROR))
167 throw inferenceException
168 .setMessage("no.unique.minimal.instance.exists",
169 that.qtype, lobounds);
170 } else {
171 that.inst = that.getBounds(InferenceBound.EQ).head;
172 }
173 }
175 /***************************************************************************
176 * Exported Methods
177 ***************************************************************************/
179 /**
180 * Instantiate uninferred inference variables (JLS 15.12.2.8). First
181 * if the method return type is non-void, we derive constraints from the
182 * expected type - then we use declared bound well-formedness to derive additional
183 * constraints. If no instantiation exists, or if several incomparable
184 * best instantiations exist throw a NoInstanceException.
185 */
186 public void instantiateUninferred(DiagnosticPosition pos,
187 InferenceContext inferenceContext,
188 MethodType mtype,
189 Attr.ResultInfo resultInfo,
190 Warner warn) throws InferenceException {
191 Type to = resultInfo.pt;
192 if (to.hasTag(NONE) || resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) {
193 to = mtype.getReturnType().isPrimitiveOrVoid() ?
194 mtype.getReturnType() : syms.objectType;
195 }
196 Type qtype1 = inferenceContext.asFree(mtype.getReturnType(), types);
197 if (!types.isSubtype(qtype1,
198 qtype1.hasTag(UNDETVAR) ? types.boxedTypeOrType(to) : to)) {
199 throw inferenceException
200 .setMessage("infer.no.conforming.instance.exists",
201 inferenceContext.restvars(), mtype.getReturnType(), to);
202 }
204 while (true) {
205 boolean stuck = true;
206 for (Type t : inferenceContext.undetvars) {
207 UndetVar uv = (UndetVar)t;
208 if (uv.inst == null && (uv.getBounds(InferenceBound.EQ).nonEmpty() ||
209 !inferenceContext.free(uv.getBounds(InferenceBound.UPPER)))) {
210 maximizeInst((UndetVar)t, warn);
211 stuck = false;
212 }
213 }
214 if (inferenceContext.restvars().isEmpty()) {
215 //all variables have been instantiated - exit
216 break;
217 } else if (stuck) {
218 //some variables could not be instantiated because of cycles in
219 //upper bounds - provide a (possibly recursive) default instantiation
220 instantiateAsUninferredVars(inferenceContext);
221 break;
222 } else {
223 //some variables have been instantiated - replace newly instantiated
224 //variables in remaining upper bounds and continue
225 for (Type t : inferenceContext.undetvars) {
226 UndetVar uv = (UndetVar)t;
227 uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
228 }
229 }
230 }
231 }
233 /**
234 * Infer cyclic inference variables as described in 15.12.2.8.
235 */
236 private void instantiateAsUninferredVars(InferenceContext inferenceContext) {
237 ListBuffer<Type> todo = ListBuffer.lb();
238 //step 1 - create fresh tvars
239 for (Type t : inferenceContext.undetvars) {
240 UndetVar uv = (UndetVar)t;
241 if (uv.inst == null) {
242 TypeSymbol fresh_tvar = new TypeSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner);
243 fresh_tvar.type = new TypeVar(fresh_tvar, types.makeCompoundType(uv.getBounds(InferenceBound.UPPER)), null);
244 todo.append(uv);
245 uv.inst = fresh_tvar.type;
246 }
247 }
248 //step 2 - replace fresh tvars in their bounds
249 List<Type> formals = inferenceContext.inferenceVars();
250 for (Type t : todo) {
251 UndetVar uv = (UndetVar)t;
252 TypeVar ct = (TypeVar)uv.inst;
253 ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct), types));
254 if (ct.bound.isErroneous()) {
255 //report inference error if glb fails
256 reportBoundError(uv, BoundErrorKind.BAD_UPPER);
257 }
258 formals = formals.tail;
259 }
260 }
262 /** Instantiate a generic method type by finding instantiations for all its
263 * inference variables so that it can be applied to a given argument type list.
264 */
265 public Type instantiateMethod(Env<AttrContext> env,
266 List<Type> tvars,
267 MethodType mt,
268 Attr.ResultInfo resultInfo,
269 Symbol msym,
270 List<Type> argtypes,
271 boolean allowBoxing,
272 boolean useVarargs,
273 Resolve.MethodResolutionContext resolveContext,
274 Resolve.MethodCheck methodCheck,
275 Warner warn) throws InferenceException {
276 //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
277 final InferenceContext inferenceContext = new InferenceContext(tvars, this, true);
278 inferenceException.clear();
280 DeferredAttr.DeferredAttrContext deferredAttrContext =
281 resolveContext.deferredAttrContext(msym, inferenceContext);
283 try {
284 methodCheck.argumentsAcceptable(env, deferredAttrContext, argtypes, mt.getParameterTypes(), warn);
286 deferredAttrContext.complete();
288 // minimize as yet undetermined type variables
289 for (Type t : inferenceContext.undetvars) {
290 minimizeInst((UndetVar)t, warn);
291 }
293 checkWithinBounds(inferenceContext, warn);
295 mt = (MethodType)inferenceContext.asInstType(mt, types);
297 List<Type> restvars = inferenceContext.restvars();
299 if (!restvars.isEmpty()) {
300 if (resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
301 instantiateUninferred(env.tree.pos(), inferenceContext, mt, resultInfo, warn);
302 checkWithinBounds(inferenceContext, warn);
303 mt = (MethodType)inferenceContext.asInstType(mt, types);
304 if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
305 log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
306 }
307 }
308 }
310 // return instantiated version of method type
311 return mt;
312 } finally {
313 inferenceContext.notifyChange(types);
314 }
315 }
317 /** check that type parameters are within their bounds.
318 */
319 void checkWithinBounds(InferenceContext inferenceContext,
320 Warner warn) throws InferenceException {
321 //step 1 - check compatibility of instantiated type w.r.t. initial bounds
322 for (Type t : inferenceContext.undetvars) {
323 UndetVar uv = (UndetVar)t;
324 uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
325 checkCompatibleUpperBounds(uv, inferenceContext.inferenceVars());
326 if (!inferenceContext.restvars().contains(uv.qtype)) {
327 Type inst = inferenceContext.asInstType(t, types);
328 for (Type u : uv.getBounds(InferenceBound.UPPER)) {
329 if (!types.isSubtypeUnchecked(inst, inferenceContext.asFree(u, types), warn)) {
330 reportBoundError(uv, BoundErrorKind.UPPER);
331 }
332 }
333 for (Type l : uv.getBounds(InferenceBound.LOWER)) {
334 Assert.check(!inferenceContext.free(l));
335 if (!types.isSubtypeUnchecked(l, inst, warn)) {
336 reportBoundError(uv, BoundErrorKind.LOWER);
337 }
338 }
339 for (Type e : uv.getBounds(InferenceBound.EQ)) {
340 Assert.check(!inferenceContext.free(e));
341 if (!types.isSameType(inst, e)) {
342 reportBoundError(uv, BoundErrorKind.EQ);
343 }
344 }
345 }
346 }
348 //step 2 - check that eq bounds are consistent w.r.t. eq/lower bounds
349 for (Type t : inferenceContext.undetvars) {
350 UndetVar uv = (UndetVar)t;
351 //check eq bounds consistency
352 Type eq = null;
353 for (Type e : uv.getBounds(InferenceBound.EQ)) {
354 Assert.check(!inferenceContext.free(e));
355 if (eq != null && !types.isSameType(e, eq)) {
356 reportBoundError(uv, BoundErrorKind.EQ);
357 }
358 eq = e;
359 for (Type l : uv.getBounds(InferenceBound.LOWER)) {
360 Assert.check(!inferenceContext.free(l));
361 if (!types.isSubtypeUnchecked(l, e, warn)) {
362 reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER);
363 }
364 }
365 for (Type u : uv.getBounds(InferenceBound.UPPER)) {
366 if (inferenceContext.free(u)) continue;
367 if (!types.isSubtypeUnchecked(e, u, warn)) {
368 reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER);
369 }
370 }
371 }
372 }
373 }
375 void checkCompatibleUpperBounds(UndetVar uv, List<Type> tvars) {
376 // VGJ: sort of inlined maximizeInst() below. Adding
377 // bounds can cause lobounds that are above hibounds.
378 ListBuffer<Type> hiboundsNoVars = ListBuffer.lb();
379 for (Type t : Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter)) {
380 if (!t.containsAny(tvars)) {
381 hiboundsNoVars.append(t);
382 }
383 }
384 List<Type> hibounds = hiboundsNoVars.toList();
385 Type hb = null;
386 if (hibounds.isEmpty())
387 hb = syms.objectType;
388 else if (hibounds.tail.isEmpty())
389 hb = hibounds.head;
390 else
391 hb = types.glb(hibounds);
392 if (hb == null || hb.isErroneous())
393 reportBoundError(uv, BoundErrorKind.BAD_UPPER);
394 }
396 enum BoundErrorKind {
397 BAD_UPPER() {
398 @Override
399 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
400 return ex.setMessage("incompatible.upper.bounds", uv.qtype,
401 uv.getBounds(InferenceBound.UPPER));
402 }
403 },
404 BAD_EQ_UPPER() {
405 @Override
406 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
407 return ex.setMessage("incompatible.eq.upper.bounds", uv.qtype,
408 uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.UPPER));
409 }
410 },
411 BAD_EQ_LOWER() {
412 @Override
413 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
414 return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype,
415 uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER));
416 }
417 },
418 UPPER() {
419 @Override
420 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
421 return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst,
422 uv.getBounds(InferenceBound.UPPER));
423 }
424 },
425 LOWER() {
426 @Override
427 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
428 return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst,
429 uv.getBounds(InferenceBound.LOWER));
430 }
431 },
432 EQ() {
433 @Override
434 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
435 return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst,
436 uv.getBounds(InferenceBound.EQ));
437 }
438 };
440 abstract InapplicableMethodException setMessage(InferenceException ex, UndetVar uv);
441 }
442 //where
443 void reportBoundError(UndetVar uv, BoundErrorKind bk) {
444 throw bk.setMessage(inferenceException, uv);
445 }
447 // <editor-fold desc="functional interface instantiation">
448 /**
449 * This method is used to infer a suitable target functional interface in case
450 * the original parameterized interface contains wildcards. An inference process
451 * is applied so that wildcard bounds, as well as explicit lambda/method ref parameters
452 * (where applicable) are used to constraint the solution.
453 */
454 public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface,
455 List<Type> paramTypes, Check.CheckContext checkContext) {
456 if (types.capture(funcInterface) == funcInterface) {
457 //if capture doesn't change the type then return the target unchanged
458 //(this means the target contains no wildcards!)
459 return funcInterface;
460 } else {
461 Type formalInterface = funcInterface.tsym.type;
462 InferenceContext funcInterfaceContext =
463 new InferenceContext(funcInterface.tsym.type.getTypeArguments(), this, false);
464 if (paramTypes != null) {
465 //get constraints from explicit params (this is done by
466 //checking that explicit param types are equal to the ones
467 //in the functional interface descriptors)
468 List<Type> descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes();
469 if (descParameterTypes.size() != paramTypes.size()) {
470 checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda"));
471 return types.createErrorType(funcInterface);
472 }
473 for (Type p : descParameterTypes) {
474 if (!types.isSameType(funcInterfaceContext.asFree(p, types), paramTypes.head)) {
475 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
476 return types.createErrorType(funcInterface);
477 }
478 paramTypes = paramTypes.tail;
479 }
480 for (Type t : funcInterfaceContext.undetvars) {
481 UndetVar uv = (UndetVar)t;
482 minimizeInst(uv, types.noWarnings);
483 if (uv.inst == null &&
484 Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter).nonEmpty()) {
485 maximizeInst(uv, types.noWarnings);
486 }
487 }
489 formalInterface = funcInterfaceContext.asInstType(formalInterface, types);
490 }
491 ListBuffer<Type> typeargs = ListBuffer.lb();
492 List<Type> actualTypeargs = funcInterface.getTypeArguments();
493 //for remaining uninferred type-vars in the functional interface type,
494 //simply replace the wildcards with its bound
495 for (Type t : formalInterface.getTypeArguments()) {
496 if (actualTypeargs.head.hasTag(WILDCARD)) {
497 WildcardType wt = (WildcardType)actualTypeargs.head;
498 typeargs.append(wt.type);
499 } else {
500 typeargs.append(actualTypeargs.head);
501 }
502 actualTypeargs = actualTypeargs.tail;
503 }
504 Type owntype = types.subst(formalInterface, funcInterfaceContext.inferenceVars(), typeargs.toList());
505 if (!chk.checkValidGenericType(owntype)) {
506 //if the inferred functional interface type is not well-formed,
507 //or if it's not a subtype of the original target, issue an error
508 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
509 return types.createErrorType(funcInterface);
510 }
511 return owntype;
512 }
513 }
514 // </editor-fold>
516 /**
517 * Compute a synthetic method type corresponding to the requested polymorphic
518 * method signature. The target return type is computed from the immediately
519 * enclosing scope surrounding the polymorphic-signature call.
520 */
521 Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
522 MethodSymbol spMethod, // sig. poly. method or null if none
523 Resolve.MethodResolutionContext resolveContext,
524 List<Type> argtypes) {
525 final Type restype;
527 //The return type for a polymorphic signature call is computed from
528 //the enclosing tree E, as follows: if E is a cast, then use the
529 //target type of the cast expression as a return type; if E is an
530 //expression statement, the return type is 'void' - otherwise the
531 //return type is simply 'Object'. A correctness check ensures that
532 //env.next refers to the lexically enclosing environment in which
533 //the polymorphic signature call environment is nested.
535 switch (env.next.tree.getTag()) {
536 case TYPECAST:
537 JCTypeCast castTree = (JCTypeCast)env.next.tree;
538 restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ?
539 castTree.clazz.type :
540 syms.objectType;
541 break;
542 case EXEC:
543 JCTree.JCExpressionStatement execTree =
544 (JCTree.JCExpressionStatement)env.next.tree;
545 restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ?
546 syms.voidType :
547 syms.objectType;
548 break;
549 default:
550 restype = syms.objectType;
551 }
553 List<Type> paramtypes = Type.map(argtypes, new ImplicitArgType(spMethod, resolveContext.step));
554 List<Type> exType = spMethod != null ?
555 spMethod.getThrownTypes() :
556 List.of(syms.throwableType); // make it throw all exceptions
558 MethodType mtype = new MethodType(paramtypes,
559 restype,
560 exType,
561 syms.methodClass);
562 return mtype;
563 }
564 //where
565 class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
567 public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
568 deferredAttr.super(AttrMode.SPECULATIVE, msym, phase);
569 }
571 public Type apply(Type t) {
572 t = types.erasure(super.apply(t));
573 if (t.hasTag(BOT))
574 // nulls type as the marker type Null (which has no instances)
575 // infer as java.lang.Void for now
576 t = types.boxedClass(syms.voidType).type;
577 return t;
578 }
579 }
581 /**
582 * Mapping that turns inference variables into undet vars
583 * (used by inference context)
584 */
585 class FromTypeVarFun extends Mapping {
587 boolean includeBounds;
589 FromTypeVarFun(boolean includeBounds) {
590 super("fromTypeVarFunWithBounds");
591 this.includeBounds = includeBounds;
592 }
594 public Type apply(Type t) {
595 if (t.hasTag(TYPEVAR)) return new UndetVar((TypeVar)t, types, includeBounds);
596 else return t.map(this);
597 }
598 };
600 /**
601 * An inference context keeps track of the set of variables that are free
602 * in the current context. It provides utility methods for opening/closing
603 * types to their corresponding free/closed forms. It also provide hooks for
604 * attaching deferred post-inference action (see PendingCheck). Finally,
605 * it can be used as an entry point for performing upper/lower bound inference
606 * (see InferenceKind).
607 */
608 static class InferenceContext {
610 /**
611 * Single-method-interface for defining inference callbacks. Certain actions
612 * (i.e. subtyping checks) might need to be redone after all inference variables
613 * have been fixed.
614 */
615 interface FreeTypeListener {
616 void typesInferred(InferenceContext inferenceContext);
617 }
619 /** list of inference vars as undet vars */
620 List<Type> undetvars;
622 /** list of inference vars in this context */
623 List<Type> inferencevars;
625 java.util.Map<FreeTypeListener, List<Type>> freeTypeListeners =
626 new java.util.HashMap<FreeTypeListener, List<Type>>();
628 List<FreeTypeListener> freetypeListeners = List.nil();
630 public InferenceContext(List<Type> inferencevars, Infer infer, boolean includeBounds) {
631 this.undetvars = Type.map(inferencevars, infer.new FromTypeVarFun(includeBounds));
632 this.inferencevars = inferencevars;
633 }
635 /**
636 * returns the list of free variables (as type-variables) in this
637 * inference context
638 */
639 List<Type> inferenceVars() {
640 return inferencevars;
641 }
643 /**
644 * returns the list of uninstantiated variables (as type-variables) in this
645 * inference context (usually called after instantiate())
646 */
647 List<Type> restvars() {
648 List<Type> undetvars = this.undetvars;
649 ListBuffer<Type> restvars = ListBuffer.lb();
650 for (Type t : instTypes()) {
651 UndetVar uv = (UndetVar)undetvars.head;
652 if (uv.qtype == t) {
653 restvars.append(t);
654 }
655 undetvars = undetvars.tail;
656 }
657 return restvars.toList();
658 }
660 /**
661 * is this type free?
662 */
663 final boolean free(Type t) {
664 return t.containsAny(inferencevars);
665 }
667 final boolean free(List<Type> ts) {
668 for (Type t : ts) {
669 if (free(t)) return true;
670 }
671 return false;
672 }
674 /**
675 * Returns a list of free variables in a given type
676 */
677 final List<Type> freeVarsIn(Type t) {
678 ListBuffer<Type> buf = ListBuffer.lb();
679 for (Type iv : inferenceVars()) {
680 if (t.contains(iv)) {
681 buf.add(iv);
682 }
683 }
684 return buf.toList();
685 }
687 final List<Type> freeVarsIn(List<Type> ts) {
688 ListBuffer<Type> buf = ListBuffer.lb();
689 for (Type t : ts) {
690 buf.appendList(freeVarsIn(t));
691 }
692 ListBuffer<Type> buf2 = ListBuffer.lb();
693 for (Type t : buf) {
694 if (!buf2.contains(t)) {
695 buf2.add(t);
696 }
697 }
698 return buf2.toList();
699 }
701 /**
702 * Replace all free variables in a given type with corresponding
703 * undet vars (used ahead of subtyping/compatibility checks to allow propagation
704 * of inference constraints).
705 */
706 final Type asFree(Type t, Types types) {
707 return types.subst(t, inferencevars, undetvars);
708 }
710 final List<Type> asFree(List<Type> ts, Types types) {
711 ListBuffer<Type> buf = ListBuffer.lb();
712 for (Type t : ts) {
713 buf.append(asFree(t, types));
714 }
715 return buf.toList();
716 }
718 List<Type> instTypes() {
719 ListBuffer<Type> buf = ListBuffer.lb();
720 for (Type t : undetvars) {
721 UndetVar uv = (UndetVar)t;
722 buf.append(uv.inst != null ? uv.inst : uv.qtype);
723 }
724 return buf.toList();
725 }
727 /**
728 * Replace all free variables in a given type with corresponding
729 * instantiated types - if one or more free variable has not been
730 * fully instantiated, it will still be available in the resulting type.
731 */
732 Type asInstType(Type t, Types types) {
733 return types.subst(t, inferencevars, instTypes());
734 }
736 List<Type> asInstTypes(List<Type> ts, Types types) {
737 ListBuffer<Type> buf = ListBuffer.lb();
738 for (Type t : ts) {
739 buf.append(asInstType(t, types));
740 }
741 return buf.toList();
742 }
744 /**
745 * Add custom hook for performing post-inference action
746 */
747 void addFreeTypeListener(List<Type> types, FreeTypeListener ftl) {
748 freeTypeListeners.put(ftl, freeVarsIn(types));
749 }
751 /**
752 * Mark the inference context as complete and trigger evaluation
753 * of all deferred checks.
754 */
755 void notifyChange(Types types) {
756 InferenceException thrownEx = null;
757 for (Map.Entry<FreeTypeListener, List<Type>> entry :
758 new HashMap<FreeTypeListener, List<Type>>(freeTypeListeners).entrySet()) {
759 if (!Type.containsAny(entry.getValue(), restvars())) {
760 try {
761 entry.getKey().typesInferred(this);
762 freeTypeListeners.remove(entry.getKey());
763 } catch (InferenceException ex) {
764 if (thrownEx == null) {
765 thrownEx = ex;
766 }
767 }
768 }
769 }
770 //inference exception multiplexing - present any inference exception
771 //thrown when processing listeners as a single one
772 if (thrownEx != null) {
773 throw thrownEx;
774 }
775 }
777 void solveAny(List<Type> varsToSolve, Types types, Infer infer) {
778 boolean progress = false;
779 for (Type t : varsToSolve) {
780 UndetVar uv = (UndetVar)asFree(t, types);
781 if (uv.inst == null) {
782 infer.minimizeInst(uv, types.noWarnings);
783 if (uv.inst != null) {
784 progress = true;
785 }
786 }
787 }
788 if (!progress) {
789 throw infer.inferenceException.setMessage("cyclic.inference", varsToSolve);
790 }
791 }
792 }
794 final InferenceContext emptyContext = new InferenceContext(List.<Type>nil(), this, false);
795 }