Mon, 17 Dec 2012 07:47:05 -0800
8004832: Add new doclint package
Reviewed-by: mcimadamore
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 private 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 Warner warn) throws InferenceException {
275 //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG
276 final InferenceContext inferenceContext = new InferenceContext(tvars, this, true);
277 inferenceException.clear();
279 try {
280 rs.checkRawArgumentsAcceptable(env, msym, resolveContext.attrMode(), inferenceContext,
281 argtypes, mt.getParameterTypes(), allowBoxing, useVarargs, warn,
282 new InferenceCheckHandler(inferenceContext));
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 instantiateUninferred(env.tree.pos(), inferenceContext, mt, resultInfo, warn);
298 checkWithinBounds(inferenceContext, warn);
299 mt = (MethodType)inferenceContext.asInstType(mt, types);
300 if (rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) {
301 log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt);
302 }
303 }
304 }
306 // return instantiated version of method type
307 return mt;
308 } finally {
309 inferenceContext.notifyChange(types);
310 }
311 }
312 //where
314 /** inference check handler **/
315 class InferenceCheckHandler implements Resolve.MethodCheckHandler {
317 InferenceContext inferenceContext;
319 public InferenceCheckHandler(InferenceContext inferenceContext) {
320 this.inferenceContext = inferenceContext;
321 }
323 public InapplicableMethodException arityMismatch() {
324 return inferenceException.setMessage("infer.arg.length.mismatch", inferenceContext.inferenceVars());
325 }
326 public InapplicableMethodException argumentMismatch(boolean varargs, JCDiagnostic details) {
327 String key = varargs ?
328 "infer.varargs.argument.mismatch" :
329 "infer.no.conforming.assignment.exists";
330 return inferenceException.setMessage(key,
331 inferenceContext.inferenceVars(), details);
332 }
333 public InapplicableMethodException inaccessibleVarargs(Symbol location, Type expected) {
334 return inferenceException.setMessage("inaccessible.varargs.type",
335 expected, Kinds.kindName(location), location);
336 }
337 }
339 /** check that type parameters are within their bounds.
340 */
341 void checkWithinBounds(InferenceContext inferenceContext,
342 Warner warn) throws InferenceException {
343 //step 1 - check compatibility of instantiated type w.r.t. initial bounds
344 for (Type t : inferenceContext.undetvars) {
345 UndetVar uv = (UndetVar)t;
346 uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), types);
347 checkCompatibleUpperBounds(uv, inferenceContext.inferenceVars());
348 if (!inferenceContext.restvars().contains(uv.qtype)) {
349 Type inst = inferenceContext.asInstType(t, types);
350 for (Type u : uv.getBounds(InferenceBound.UPPER)) {
351 if (!types.isSubtypeUnchecked(inst, inferenceContext.asFree(u, types), warn)) {
352 reportBoundError(uv, BoundErrorKind.UPPER);
353 }
354 }
355 for (Type l : uv.getBounds(InferenceBound.LOWER)) {
356 Assert.check(!inferenceContext.free(l));
357 if (!types.isSubtypeUnchecked(l, inst, warn)) {
358 reportBoundError(uv, BoundErrorKind.LOWER);
359 }
360 }
361 for (Type e : uv.getBounds(InferenceBound.EQ)) {
362 Assert.check(!inferenceContext.free(e));
363 if (!types.isSameType(inst, e)) {
364 reportBoundError(uv, BoundErrorKind.EQ);
365 }
366 }
367 }
368 }
370 //step 2 - check that eq bounds are consistent w.r.t. eq/lower bounds
371 for (Type t : inferenceContext.undetvars) {
372 UndetVar uv = (UndetVar)t;
373 //check eq bounds consistency
374 Type eq = null;
375 for (Type e : uv.getBounds(InferenceBound.EQ)) {
376 Assert.check(!inferenceContext.free(e));
377 if (eq != null && !types.isSameType(e, eq)) {
378 reportBoundError(uv, BoundErrorKind.EQ);
379 }
380 eq = e;
381 for (Type l : uv.getBounds(InferenceBound.LOWER)) {
382 Assert.check(!inferenceContext.free(l));
383 if (!types.isSubtypeUnchecked(l, e, warn)) {
384 reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER);
385 }
386 }
387 for (Type u : uv.getBounds(InferenceBound.UPPER)) {
388 if (inferenceContext.free(u)) continue;
389 if (!types.isSubtypeUnchecked(e, u, warn)) {
390 reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER);
391 }
392 }
393 }
394 }
395 }
397 void checkCompatibleUpperBounds(UndetVar uv, List<Type> tvars) {
398 // VGJ: sort of inlined maximizeInst() below. Adding
399 // bounds can cause lobounds that are above hibounds.
400 ListBuffer<Type> hiboundsNoVars = ListBuffer.lb();
401 for (Type t : Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter)) {
402 if (!t.containsAny(tvars)) {
403 hiboundsNoVars.append(t);
404 }
405 }
406 List<Type> hibounds = hiboundsNoVars.toList();
407 Type hb = null;
408 if (hibounds.isEmpty())
409 hb = syms.objectType;
410 else if (hibounds.tail.isEmpty())
411 hb = hibounds.head;
412 else
413 hb = types.glb(hibounds);
414 if (hb == null || hb.isErroneous())
415 reportBoundError(uv, BoundErrorKind.BAD_UPPER);
416 }
418 enum BoundErrorKind {
419 BAD_UPPER() {
420 @Override
421 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
422 return ex.setMessage("incompatible.upper.bounds", uv.qtype,
423 uv.getBounds(InferenceBound.UPPER));
424 }
425 },
426 BAD_EQ_UPPER() {
427 @Override
428 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
429 return ex.setMessage("incompatible.eq.upper.bounds", uv.qtype,
430 uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.UPPER));
431 }
432 },
433 BAD_EQ_LOWER() {
434 @Override
435 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
436 return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype,
437 uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER));
438 }
439 },
440 UPPER() {
441 @Override
442 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
443 return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst,
444 uv.getBounds(InferenceBound.UPPER));
445 }
446 },
447 LOWER() {
448 @Override
449 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
450 return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst,
451 uv.getBounds(InferenceBound.LOWER));
452 }
453 },
454 EQ() {
455 @Override
456 InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) {
457 return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst,
458 uv.getBounds(InferenceBound.EQ));
459 }
460 };
462 abstract InapplicableMethodException setMessage(InferenceException ex, UndetVar uv);
463 }
464 //where
465 void reportBoundError(UndetVar uv, BoundErrorKind bk) {
466 throw bk.setMessage(inferenceException, uv);
467 }
469 // <editor-fold desc="functional interface instantiation">
470 /**
471 * This method is used to infer a suitable target functional interface in case
472 * the original parameterized interface contains wildcards. An inference process
473 * is applied so that wildcard bounds, as well as explicit lambda/method ref parameters
474 * (where applicable) are used to constraint the solution.
475 */
476 public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface,
477 List<Type> paramTypes, Check.CheckContext checkContext) {
478 if (types.capture(funcInterface) == funcInterface) {
479 //if capture doesn't change the type then return the target unchanged
480 //(this means the target contains no wildcards!)
481 return funcInterface;
482 } else {
483 Type formalInterface = funcInterface.tsym.type;
484 InferenceContext funcInterfaceContext =
485 new InferenceContext(funcInterface.tsym.type.getTypeArguments(), this, false);
486 if (paramTypes != null) {
487 //get constraints from explicit params (this is done by
488 //checking that explicit param types are equal to the ones
489 //in the functional interface descriptors)
490 List<Type> descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes();
491 if (descParameterTypes.size() != paramTypes.size()) {
492 checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda"));
493 return types.createErrorType(funcInterface);
494 }
495 for (Type p : descParameterTypes) {
496 if (!types.isSameType(funcInterfaceContext.asFree(p, types), paramTypes.head)) {
497 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
498 return types.createErrorType(funcInterface);
499 }
500 paramTypes = paramTypes.tail;
501 }
502 for (Type t : funcInterfaceContext.undetvars) {
503 UndetVar uv = (UndetVar)t;
504 minimizeInst(uv, types.noWarnings);
505 if (uv.inst == null &&
506 Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter).nonEmpty()) {
507 maximizeInst(uv, types.noWarnings);
508 }
509 }
511 formalInterface = funcInterfaceContext.asInstType(formalInterface, types);
512 }
513 ListBuffer<Type> typeargs = ListBuffer.lb();
514 List<Type> actualTypeargs = funcInterface.getTypeArguments();
515 //for remaining uninferred type-vars in the functional interface type,
516 //simply replace the wildcards with its bound
517 for (Type t : formalInterface.getTypeArguments()) {
518 if (actualTypeargs.head.hasTag(WILDCARD)) {
519 WildcardType wt = (WildcardType)actualTypeargs.head;
520 typeargs.append(wt.type);
521 } else {
522 typeargs.append(actualTypeargs.head);
523 }
524 actualTypeargs = actualTypeargs.tail;
525 }
526 Type owntype = types.subst(formalInterface, funcInterfaceContext.inferenceVars(), typeargs.toList());
527 if (!chk.checkValidGenericType(owntype)) {
528 //if the inferred functional interface type is not well-formed,
529 //or if it's not a subtype of the original target, issue an error
530 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
531 return types.createErrorType(funcInterface);
532 }
533 return owntype;
534 }
535 }
536 // </editor-fold>
538 /**
539 * Compute a synthetic method type corresponding to the requested polymorphic
540 * method signature. The target return type is computed from the immediately
541 * enclosing scope surrounding the polymorphic-signature call.
542 */
543 Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
544 MethodSymbol spMethod, // sig. poly. method or null if none
545 Resolve.MethodResolutionContext resolveContext,
546 List<Type> argtypes) {
547 final Type restype;
549 //The return type for a polymorphic signature call is computed from
550 //the enclosing tree E, as follows: if E is a cast, then use the
551 //target type of the cast expression as a return type; if E is an
552 //expression statement, the return type is 'void' - otherwise the
553 //return type is simply 'Object'. A correctness check ensures that
554 //env.next refers to the lexically enclosing environment in which
555 //the polymorphic signature call environment is nested.
557 switch (env.next.tree.getTag()) {
558 case TYPECAST:
559 JCTypeCast castTree = (JCTypeCast)env.next.tree;
560 restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ?
561 castTree.clazz.type :
562 syms.objectType;
563 break;
564 case EXEC:
565 JCTree.JCExpressionStatement execTree =
566 (JCTree.JCExpressionStatement)env.next.tree;
567 restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ?
568 syms.voidType :
569 syms.objectType;
570 break;
571 default:
572 restype = syms.objectType;
573 }
575 List<Type> paramtypes = Type.map(argtypes, new ImplicitArgType(spMethod, resolveContext.step));
576 List<Type> exType = spMethod != null ?
577 spMethod.getThrownTypes() :
578 List.of(syms.throwableType); // make it throw all exceptions
580 MethodType mtype = new MethodType(paramtypes,
581 restype,
582 exType,
583 syms.methodClass);
584 return mtype;
585 }
586 //where
587 class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
589 public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
590 deferredAttr.super(AttrMode.SPECULATIVE, msym, phase);
591 }
593 public Type apply(Type t) {
594 t = types.erasure(super.apply(t));
595 if (t.hasTag(BOT))
596 // nulls type as the marker type Null (which has no instances)
597 // infer as java.lang.Void for now
598 t = types.boxedClass(syms.voidType).type;
599 return t;
600 }
601 }
603 /**
604 * Mapping that turns inference variables into undet vars
605 * (used by inference context)
606 */
607 class FromTypeVarFun extends Mapping {
609 boolean includeBounds;
611 FromTypeVarFun(boolean includeBounds) {
612 super("fromTypeVarFunWithBounds");
613 this.includeBounds = includeBounds;
614 }
616 public Type apply(Type t) {
617 if (t.hasTag(TYPEVAR)) return new UndetVar((TypeVar)t, types, includeBounds);
618 else return t.map(this);
619 }
620 };
622 /**
623 * An inference context keeps track of the set of variables that are free
624 * in the current context. It provides utility methods for opening/closing
625 * types to their corresponding free/closed forms. It also provide hooks for
626 * attaching deferred post-inference action (see PendingCheck). Finally,
627 * it can be used as an entry point for performing upper/lower bound inference
628 * (see InferenceKind).
629 */
630 static class InferenceContext {
632 /**
633 * Single-method-interface for defining inference callbacks. Certain actions
634 * (i.e. subtyping checks) might need to be redone after all inference variables
635 * have been fixed.
636 */
637 interface FreeTypeListener {
638 void typesInferred(InferenceContext inferenceContext);
639 }
641 /** list of inference vars as undet vars */
642 List<Type> undetvars;
644 /** list of inference vars in this context */
645 List<Type> inferencevars;
647 java.util.Map<FreeTypeListener, List<Type>> freeTypeListeners =
648 new java.util.HashMap<FreeTypeListener, List<Type>>();
650 List<FreeTypeListener> freetypeListeners = List.nil();
652 public InferenceContext(List<Type> inferencevars, Infer infer, boolean includeBounds) {
653 this.undetvars = Type.map(inferencevars, infer.new FromTypeVarFun(includeBounds));
654 this.inferencevars = inferencevars;
655 }
657 /**
658 * returns the list of free variables (as type-variables) in this
659 * inference context
660 */
661 List<Type> inferenceVars() {
662 return inferencevars;
663 }
665 /**
666 * returns the list of uninstantiated variables (as type-variables) in this
667 * inference context (usually called after instantiate())
668 */
669 List<Type> restvars() {
670 List<Type> undetvars = this.undetvars;
671 ListBuffer<Type> restvars = ListBuffer.lb();
672 for (Type t : instTypes()) {
673 UndetVar uv = (UndetVar)undetvars.head;
674 if (uv.qtype == t) {
675 restvars.append(t);
676 }
677 undetvars = undetvars.tail;
678 }
679 return restvars.toList();
680 }
682 /**
683 * is this type free?
684 */
685 final boolean free(Type t) {
686 return t.containsAny(inferencevars);
687 }
689 final boolean free(List<Type> ts) {
690 for (Type t : ts) {
691 if (free(t)) return true;
692 }
693 return false;
694 }
696 /**
697 * Returns a list of free variables in a given type
698 */
699 final List<Type> freeVarsIn(Type t) {
700 ListBuffer<Type> buf = ListBuffer.lb();
701 for (Type iv : inferenceVars()) {
702 if (t.contains(iv)) {
703 buf.add(iv);
704 }
705 }
706 return buf.toList();
707 }
709 final List<Type> freeVarsIn(List<Type> ts) {
710 ListBuffer<Type> buf = ListBuffer.lb();
711 for (Type t : ts) {
712 buf.appendList(freeVarsIn(t));
713 }
714 ListBuffer<Type> buf2 = ListBuffer.lb();
715 for (Type t : buf) {
716 if (!buf2.contains(t)) {
717 buf2.add(t);
718 }
719 }
720 return buf2.toList();
721 }
723 /**
724 * Replace all free variables in a given type with corresponding
725 * undet vars (used ahead of subtyping/compatibility checks to allow propagation
726 * of inference constraints).
727 */
728 final Type asFree(Type t, Types types) {
729 return types.subst(t, inferencevars, undetvars);
730 }
732 final List<Type> asFree(List<Type> ts, Types types) {
733 ListBuffer<Type> buf = ListBuffer.lb();
734 for (Type t : ts) {
735 buf.append(asFree(t, types));
736 }
737 return buf.toList();
738 }
740 List<Type> instTypes() {
741 ListBuffer<Type> buf = ListBuffer.lb();
742 for (Type t : undetvars) {
743 UndetVar uv = (UndetVar)t;
744 buf.append(uv.inst != null ? uv.inst : uv.qtype);
745 }
746 return buf.toList();
747 }
749 /**
750 * Replace all free variables in a given type with corresponding
751 * instantiated types - if one or more free variable has not been
752 * fully instantiated, it will still be available in the resulting type.
753 */
754 Type asInstType(Type t, Types types) {
755 return types.subst(t, inferencevars, instTypes());
756 }
758 List<Type> asInstTypes(List<Type> ts, Types types) {
759 ListBuffer<Type> buf = ListBuffer.lb();
760 for (Type t : ts) {
761 buf.append(asInstType(t, types));
762 }
763 return buf.toList();
764 }
766 /**
767 * Add custom hook for performing post-inference action
768 */
769 void addFreeTypeListener(List<Type> types, FreeTypeListener ftl) {
770 freeTypeListeners.put(ftl, freeVarsIn(types));
771 }
773 /**
774 * Mark the inference context as complete and trigger evaluation
775 * of all deferred checks.
776 */
777 void notifyChange(Types types) {
778 InferenceException thrownEx = null;
779 for (Map.Entry<FreeTypeListener, List<Type>> entry :
780 new HashMap<FreeTypeListener, List<Type>>(freeTypeListeners).entrySet()) {
781 if (!Type.containsAny(entry.getValue(), restvars())) {
782 try {
783 entry.getKey().typesInferred(this);
784 freeTypeListeners.remove(entry.getKey());
785 } catch (InferenceException ex) {
786 if (thrownEx == null) {
787 thrownEx = ex;
788 }
789 }
790 }
791 }
792 //inference exception multiplexing - present any inference exception
793 //thrown when processing listeners as a single one
794 if (thrownEx != null) {
795 throw thrownEx;
796 }
797 }
799 void solveAny(List<Type> varsToSolve, Types types, Infer infer) {
800 boolean progress = false;
801 for (Type t : varsToSolve) {
802 UndetVar uv = (UndetVar)asFree(t, types);
803 if (uv.inst == null) {
804 infer.minimizeInst(uv, types.noWarnings);
805 if (uv.inst != null) {
806 progress = true;
807 }
808 }
809 }
810 if (!progress) {
811 throw infer.inferenceException.setMessage("cyclic.inference", varsToSolve);
812 }
813 }
814 }
816 final InferenceContext emptyContext = new InferenceContext(List.<Type>nil(), this, false);
817 }