Wed, 23 Jan 2013 15:08:03 +0000
8006692: jdk/test/java/util/Collections/BigBinarySearch.java fails to compile
Summary: Missing boxing cause spurious inference failure
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 /** Should we inject return-type constraints earlier? */
70 boolean allowEarlyReturnConstraints;
72 public static Infer instance(Context context) {
73 Infer instance = context.get(inferKey);
74 if (instance == null)
75 instance = new Infer(context);
76 return instance;
77 }
79 protected Infer(Context context) {
80 context.put(inferKey, this);
81 syms = Symtab.instance(context);
82 types = Types.instance(context);
83 rs = Resolve.instance(context);
84 deferredAttr = DeferredAttr.instance(context);
85 log = Log.instance(context);
86 chk = Check.instance(context);
87 diags = JCDiagnostic.Factory.instance(context);
88 inferenceException = new InferenceException(diags);
89 allowEarlyReturnConstraints = Source.instance(context).allowEarlyReturnConstraints();
90 }
92 /**
93 * This exception class is design to store a list of diagnostics corresponding
94 * to inference errors that can arise during a method applicability check.
95 */
96 public static class InferenceException extends InapplicableMethodException {
97 private static final long serialVersionUID = 0;
99 List<JCDiagnostic> messages = List.nil();
101 InferenceException(JCDiagnostic.Factory diags) {
102 super(diags);
103 }
105 @Override
106 InapplicableMethodException setMessage(JCDiagnostic diag) {
107 messages = messages.append(diag);
108 return this;
109 }
111 @Override
112 public JCDiagnostic getDiagnostic() {
113 return messages.head;
114 }
116 void clear() {
117 messages = List.nil();
118 }
119 }
121 final InferenceException inferenceException;
123 /***************************************************************************
124 * Mini/Maximization of UndetVars
125 ***************************************************************************/
127 /** Instantiate undetermined type variable to its minimal upper bound.
128 * Throw a NoInstanceException if this not possible.
129 */
130 void maximizeInst(UndetVar that, Warner warn) throws InferenceException {
131 List<Type> hibounds = Type.filter(that.getBounds(InferenceBound.UPPER), boundFilter);
132 if (that.getBounds(InferenceBound.EQ).isEmpty()) {
133 if (hibounds.isEmpty())
134 that.inst = syms.objectType;
135 else if (hibounds.tail.isEmpty())
136 that.inst = hibounds.head;
137 else
138 that.inst = types.glb(hibounds);
139 } else {
140 that.inst = that.getBounds(InferenceBound.EQ).head;
141 }
142 if (that.inst == null ||
143 that.inst.isErroneous())
144 throw inferenceException
145 .setMessage("no.unique.maximal.instance.exists",
146 that.qtype, hibounds);
147 }
149 private Filter<Type> boundFilter = new Filter<Type>() {
150 @Override
151 public boolean accepts(Type t) {
152 return !t.isErroneous() && !t.hasTag(BOT);
153 }
154 };
156 /** Instantiate undetermined type variable to the lub of all its lower bounds.
157 * Throw a NoInstanceException if this not possible.
158 */
159 void minimizeInst(UndetVar that, Warner warn) throws InferenceException {
160 List<Type> lobounds = Type.filter(that.getBounds(InferenceBound.LOWER), boundFilter);
161 if (that.getBounds(InferenceBound.EQ).isEmpty()) {
162 if (lobounds.isEmpty()) {
163 //do nothing - the inference variable is under-constrained
164 return;
165 } else if (lobounds.tail.isEmpty())
166 that.inst = lobounds.head.isPrimitive() ? syms.errType : lobounds.head;
167 else {
168 that.inst = types.lub(lobounds);
169 }
170 if (that.inst == null || that.inst.hasTag(ERROR))
171 throw inferenceException
172 .setMessage("no.unique.minimal.instance.exists",
173 that.qtype, lobounds);
174 } else {
175 that.inst = that.getBounds(InferenceBound.EQ).head;
176 }
177 }
179 /***************************************************************************
180 * Exported Methods
181 ***************************************************************************/
183 /**
184 * Instantiate uninferred inference variables (JLS 15.12.2.8). First
185 * if the method return type is non-void, we derive constraints from the
186 * expected type - then we use declared bound well-formedness to derive additional
187 * constraints. If no instantiation exists, or if several incomparable
188 * best instantiations exist throw a NoInstanceException.
189 */
190 public void instantiateUninferred(DiagnosticPosition pos,
191 InferenceContext inferenceContext,
192 MethodType mtype,
193 Attr.ResultInfo resultInfo,
194 Warner warn) throws InferenceException {
195 while (true) {
196 boolean stuck = true;
197 for (Type t : inferenceContext.undetvars) {
198 UndetVar uv = (UndetVar)t;
199 if (uv.inst == null && (uv.getBounds(InferenceBound.EQ).nonEmpty() ||
200 !inferenceContext.free(uv.getBounds(InferenceBound.UPPER)))) {
201 maximizeInst((UndetVar)t, warn);
202 stuck = false;
203 }
204 }
205 if (inferenceContext.restvars().isEmpty()) {
206 //all variables have been instantiated - exit
207 break;
208 } else if (stuck) {
209 //some variables could not be instantiated because of cycles in
210 //upper bounds - provide a (possibly recursive) default instantiation
211 instantiateAsUninferredVars(inferenceContext);
212 break;
213 } else {
214 //some variables have been instantiated - replace newly instantiated
215 //variables in remaining upper bounds and continue
216 for (Type t : inferenceContext.undetvars) {
217 UndetVar uv = (UndetVar)t;
218 uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
219 }
220 }
221 }
222 }
224 /**
225 * Infer cyclic inference variables as described in 15.12.2.8.
226 */
227 private void instantiateAsUninferredVars(InferenceContext inferenceContext) {
228 ListBuffer<Type> todo = ListBuffer.lb();
229 //step 1 - create fresh tvars
230 for (Type t : inferenceContext.undetvars) {
231 UndetVar uv = (UndetVar)t;
232 if (uv.inst == null) {
233 TypeSymbol fresh_tvar = new TypeSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner);
234 fresh_tvar.type = new TypeVar(fresh_tvar, types.makeCompoundType(uv.getBounds(InferenceBound.UPPER)), null);
235 todo.append(uv);
236 uv.inst = fresh_tvar.type;
237 }
238 }
239 //step 2 - replace fresh tvars in their bounds
240 List<Type> formals = inferenceContext.inferenceVars();
241 for (Type t : todo) {
242 UndetVar uv = (UndetVar)t;
243 TypeVar ct = (TypeVar)uv.inst;
244 ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct), types));
245 if (ct.bound.isErroneous()) {
246 //report inference error if glb fails
247 reportBoundError(uv, BoundErrorKind.BAD_UPPER);
248 }
249 formals = formals.tail;
250 }
251 }
253 /** Instantiate a generic method type by finding instantiations for all its
254 * inference variables so that it can be applied to a given argument type list.
255 */
256 public Type instantiateMethod(Env<AttrContext> env,
257 List<Type> tvars,
258 MethodType mt,
259 Attr.ResultInfo resultInfo,
260 Symbol msym,
261 List<Type> argtypes,
262 boolean allowBoxing,
263 boolean useVarargs,
264 Resolve.MethodResolutionContext resolveContext,
265 Resolve.MethodCheck methodCheck,
266 Warner warn) throws InferenceException {
267 //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
268 final InferenceContext inferenceContext = new InferenceContext(tvars, this, true);
269 inferenceException.clear();
271 DeferredAttr.DeferredAttrContext deferredAttrContext =
272 resolveContext.deferredAttrContext(msym, inferenceContext);
274 try {
275 methodCheck.argumentsAcceptable(env, deferredAttrContext, argtypes, mt.getParameterTypes(), warn);
277 if (resultInfo != null && allowEarlyReturnConstraints &&
278 !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
279 generateReturnConstraints(mt, inferenceContext, resultInfo);
280 }
282 deferredAttrContext.complete();
284 // minimize as yet undetermined type variables
285 for (Type t : inferenceContext.undetvars) {
286 minimizeInst((UndetVar)t, warn);
287 }
289 checkWithinBounds(inferenceContext, warn);
291 mt = (MethodType)inferenceContext.asInstType(mt, types);
293 List<Type> restvars = inferenceContext.restvars();
295 if (!restvars.isEmpty()) {
296 if (resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) {
297 if (!allowEarlyReturnConstraints) {
298 generateReturnConstraints(mt, inferenceContext, resultInfo);
299 }
300 instantiateUninferred(env.tree.pos(), inferenceContext, mt, resultInfo, warn);
301 checkWithinBounds(inferenceContext, warn);
302 mt = (MethodType)inferenceContext.asInstType(mt, types);
303 if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
304 log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
305 }
306 }
307 }
309 // return instantiated version of method type
310 return mt;
311 } finally {
312 inferenceContext.notifyChange(types);
313 }
314 }
315 //where
316 void generateReturnConstraints(Type mt, InferenceContext inferenceContext, Attr.ResultInfo resultInfo) {
317 if (resultInfo != null) {
318 Type to = resultInfo.pt;
319 if (to.hasTag(NONE) || resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) {
320 to = mt.getReturnType().isPrimitiveOrVoid() ?
321 mt.getReturnType() : syms.objectType;
322 }
323 Type qtype1 = inferenceContext.asFree(mt.getReturnType(), types);
324 Warner retWarn = new Warner();
325 if (!resultInfo.checkContext.compatible(qtype1, qtype1.hasTag(UNDETVAR) ? types.boxedTypeOrType(to) : to, retWarn) ||
326 //unchecked conversion is not allowed
327 retWarn.hasLint(Lint.LintCategory.UNCHECKED)) {
328 throw inferenceException
329 .setMessage("infer.no.conforming.instance.exists",
330 inferenceContext.restvars(), mt.getReturnType(), to);
331 }
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 // <editor-fold desc="functional interface instantiation">
466 /**
467 * This method is used to infer a suitable target functional interface in case
468 * the original parameterized interface contains wildcards. An inference process
469 * is applied so that wildcard bounds, as well as explicit lambda/method ref parameters
470 * (where applicable) are used to constraint the solution.
471 */
472 public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface,
473 List<Type> paramTypes, Check.CheckContext checkContext) {
474 if (types.capture(funcInterface) == funcInterface) {
475 //if capture doesn't change the type then return the target unchanged
476 //(this means the target contains no wildcards!)
477 return funcInterface;
478 } else {
479 Type formalInterface = funcInterface.tsym.type;
480 InferenceContext funcInterfaceContext =
481 new InferenceContext(funcInterface.tsym.type.getTypeArguments(), this, false);
482 Assert.check(paramTypes != null);
483 //get constraints from explicit params (this is done by
484 //checking that explicit param types are equal to the ones
485 //in the functional interface descriptors)
486 List<Type> descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes();
487 if (descParameterTypes.size() != paramTypes.size()) {
488 checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda"));
489 return types.createErrorType(funcInterface);
490 }
491 for (Type p : descParameterTypes) {
492 if (!types.isSameType(funcInterfaceContext.asFree(p, types), paramTypes.head)) {
493 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
494 return types.createErrorType(funcInterface);
495 }
496 paramTypes = paramTypes.tail;
497 }
498 List<Type> actualTypeargs = funcInterface.getTypeArguments();
499 for (Type t : funcInterfaceContext.undetvars) {
500 UndetVar uv = (UndetVar)t;
501 minimizeInst(uv, types.noWarnings);
502 if (uv.inst == null &&
503 Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter).nonEmpty()) {
504 maximizeInst(uv, types.noWarnings);
505 }
506 if (uv.inst == null) {
507 uv.inst = actualTypeargs.head;
508 }
509 actualTypeargs = actualTypeargs.tail;
510 }
511 Type owntype = funcInterfaceContext.asInstType(formalInterface, types);
512 if (!chk.checkValidGenericType(owntype)) {
513 //if the inferred functional interface type is not well-formed,
514 //or if it's not a subtype of the original target, issue an error
515 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
516 }
517 return owntype;
518 }
519 }
520 // </editor-fold>
522 /**
523 * Compute a synthetic method type corresponding to the requested polymorphic
524 * method signature. The target return type is computed from the immediately
525 * enclosing scope surrounding the polymorphic-signature call.
526 */
527 Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
528 MethodSymbol spMethod, // sig. poly. method or null if none
529 Resolve.MethodResolutionContext resolveContext,
530 List<Type> argtypes) {
531 final Type restype;
533 //The return type for a polymorphic signature call is computed from
534 //the enclosing tree E, as follows: if E is a cast, then use the
535 //target type of the cast expression as a return type; if E is an
536 //expression statement, the return type is 'void' - otherwise the
537 //return type is simply 'Object'. A correctness check ensures that
538 //env.next refers to the lexically enclosing environment in which
539 //the polymorphic signature call environment is nested.
541 switch (env.next.tree.getTag()) {
542 case TYPECAST:
543 JCTypeCast castTree = (JCTypeCast)env.next.tree;
544 restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ?
545 castTree.clazz.type :
546 syms.objectType;
547 break;
548 case EXEC:
549 JCTree.JCExpressionStatement execTree =
550 (JCTree.JCExpressionStatement)env.next.tree;
551 restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ?
552 syms.voidType :
553 syms.objectType;
554 break;
555 default:
556 restype = syms.objectType;
557 }
559 List<Type> paramtypes = Type.map(argtypes, new ImplicitArgType(spMethod, resolveContext.step));
560 List<Type> exType = spMethod != null ?
561 spMethod.getThrownTypes() :
562 List.of(syms.throwableType); // make it throw all exceptions
564 MethodType mtype = new MethodType(paramtypes,
565 restype,
566 exType,
567 syms.methodClass);
568 return mtype;
569 }
570 //where
571 class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
573 public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
574 deferredAttr.super(AttrMode.SPECULATIVE, msym, phase);
575 }
577 public Type apply(Type t) {
578 t = types.erasure(super.apply(t));
579 if (t.hasTag(BOT))
580 // nulls type as the marker type Null (which has no instances)
581 // infer as java.lang.Void for now
582 t = types.boxedClass(syms.voidType).type;
583 return t;
584 }
585 }
587 /**
588 * Mapping that turns inference variables into undet vars
589 * (used by inference context)
590 */
591 class FromTypeVarFun extends Mapping {
593 boolean includeBounds;
595 FromTypeVarFun(boolean includeBounds) {
596 super("fromTypeVarFunWithBounds");
597 this.includeBounds = includeBounds;
598 }
600 public Type apply(Type t) {
601 if (t.hasTag(TYPEVAR)) return new UndetVar((TypeVar)t, types, includeBounds);
602 else return t.map(this);
603 }
604 };
606 /**
607 * An inference context keeps track of the set of variables that are free
608 * in the current context. It provides utility methods for opening/closing
609 * types to their corresponding free/closed forms. It also provide hooks for
610 * attaching deferred post-inference action (see PendingCheck). Finally,
611 * it can be used as an entry point for performing upper/lower bound inference
612 * (see InferenceKind).
613 */
614 static class InferenceContext {
616 /**
617 * Single-method-interface for defining inference callbacks. Certain actions
618 * (i.e. subtyping checks) might need to be redone after all inference variables
619 * have been fixed.
620 */
621 interface FreeTypeListener {
622 void typesInferred(InferenceContext inferenceContext);
623 }
625 /** list of inference vars as undet vars */
626 List<Type> undetvars;
628 /** list of inference vars in this context */
629 List<Type> inferencevars;
631 java.util.Map<FreeTypeListener, List<Type>> freeTypeListeners =
632 new java.util.HashMap<FreeTypeListener, List<Type>>();
634 List<FreeTypeListener> freetypeListeners = List.nil();
636 public InferenceContext(List<Type> inferencevars, Infer infer, boolean includeBounds) {
637 this.undetvars = Type.map(inferencevars, infer.new FromTypeVarFun(includeBounds));
638 this.inferencevars = inferencevars;
639 }
641 /**
642 * returns the list of free variables (as type-variables) in this
643 * inference context
644 */
645 List<Type> inferenceVars() {
646 return inferencevars;
647 }
649 /**
650 * returns the list of uninstantiated variables (as type-variables) in this
651 * inference context (usually called after instantiate())
652 */
653 List<Type> restvars() {
654 List<Type> undetvars = this.undetvars;
655 ListBuffer<Type> restvars = ListBuffer.lb();
656 for (Type t : instTypes()) {
657 UndetVar uv = (UndetVar)undetvars.head;
658 if (uv.qtype == t) {
659 restvars.append(t);
660 }
661 undetvars = undetvars.tail;
662 }
663 return restvars.toList();
664 }
666 /**
667 * is this type free?
668 */
669 final boolean free(Type t) {
670 return t.containsAny(inferencevars);
671 }
673 final boolean free(List<Type> ts) {
674 for (Type t : ts) {
675 if (free(t)) return true;
676 }
677 return false;
678 }
680 /**
681 * Returns a list of free variables in a given type
682 */
683 final List<Type> freeVarsIn(Type t) {
684 ListBuffer<Type> buf = ListBuffer.lb();
685 for (Type iv : inferenceVars()) {
686 if (t.contains(iv)) {
687 buf.add(iv);
688 }
689 }
690 return buf.toList();
691 }
693 final List<Type> freeVarsIn(List<Type> ts) {
694 ListBuffer<Type> buf = ListBuffer.lb();
695 for (Type t : ts) {
696 buf.appendList(freeVarsIn(t));
697 }
698 ListBuffer<Type> buf2 = ListBuffer.lb();
699 for (Type t : buf) {
700 if (!buf2.contains(t)) {
701 buf2.add(t);
702 }
703 }
704 return buf2.toList();
705 }
707 /**
708 * Replace all free variables in a given type with corresponding
709 * undet vars (used ahead of subtyping/compatibility checks to allow propagation
710 * of inference constraints).
711 */
712 final Type asFree(Type t, Types types) {
713 return types.subst(t, inferencevars, undetvars);
714 }
716 final List<Type> asFree(List<Type> ts, Types types) {
717 ListBuffer<Type> buf = ListBuffer.lb();
718 for (Type t : ts) {
719 buf.append(asFree(t, types));
720 }
721 return buf.toList();
722 }
724 List<Type> instTypes() {
725 ListBuffer<Type> buf = ListBuffer.lb();
726 for (Type t : undetvars) {
727 UndetVar uv = (UndetVar)t;
728 buf.append(uv.inst != null ? uv.inst : uv.qtype);
729 }
730 return buf.toList();
731 }
733 /**
734 * Replace all free variables in a given type with corresponding
735 * instantiated types - if one or more free variable has not been
736 * fully instantiated, it will still be available in the resulting type.
737 */
738 Type asInstType(Type t, Types types) {
739 return types.subst(t, inferencevars, instTypes());
740 }
742 List<Type> asInstTypes(List<Type> ts, Types types) {
743 ListBuffer<Type> buf = ListBuffer.lb();
744 for (Type t : ts) {
745 buf.append(asInstType(t, types));
746 }
747 return buf.toList();
748 }
750 /**
751 * Add custom hook for performing post-inference action
752 */
753 void addFreeTypeListener(List<Type> types, FreeTypeListener ftl) {
754 freeTypeListeners.put(ftl, freeVarsIn(types));
755 }
757 /**
758 * Mark the inference context as complete and trigger evaluation
759 * of all deferred checks.
760 */
761 void notifyChange(Types types) {
762 InferenceException thrownEx = null;
763 for (Map.Entry<FreeTypeListener, List<Type>> entry :
764 new HashMap<FreeTypeListener, List<Type>>(freeTypeListeners).entrySet()) {
765 if (!Type.containsAny(entry.getValue(), restvars())) {
766 try {
767 entry.getKey().typesInferred(this);
768 freeTypeListeners.remove(entry.getKey());
769 } catch (InferenceException ex) {
770 if (thrownEx == null) {
771 thrownEx = ex;
772 }
773 }
774 }
775 }
776 //inference exception multiplexing - present any inference exception
777 //thrown when processing listeners as a single one
778 if (thrownEx != null) {
779 throw thrownEx;
780 }
781 }
783 void solveAny(List<Type> varsToSolve, Types types, Infer infer) {
784 boolean progress = false;
785 for (Type t : varsToSolve) {
786 UndetVar uv = (UndetVar)asFree(t, types);
787 if (uv.inst == null) {
788 infer.minimizeInst(uv, types.noWarnings);
789 if (uv.inst != null) {
790 progress = true;
791 }
792 }
793 }
794 if (!progress) {
795 throw infer.inferenceException.setMessage("cyclic.inference", varsToSolve);
796 }
797 }
798 }
800 final InferenceContext emptyContext = new InferenceContext(List.<Type>nil(), this, false);
801 }