Fri, 05 Oct 2012 14:35:24 +0100
7177385: Add attribution support for lambda expressions
Summary: Add support for function descriptor lookup, functional interface inference and lambda expression type-checking
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, true);
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 // <editor-fold desc="functional interface instantiation">
471 /**
472 * This method is used to infer a suitable target functional interface in case
473 * the original parameterized interface contains wildcards. An inference process
474 * is applied so that wildcard bounds, as well as explicit lambda/method ref parameters
475 * (where applicable) are used to constraint the solution.
476 */
477 public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface,
478 List<Type> paramTypes, Check.CheckContext checkContext) {
479 if (types.capture(funcInterface) == funcInterface) {
480 //if capture doesn't change the type then return the target unchanged
481 //(this means the target contains no wildcards!)
482 return funcInterface;
483 } else {
484 Type formalInterface = funcInterface.tsym.type;
485 InferenceContext funcInterfaceContext =
486 new InferenceContext(funcInterface.tsym.type.getTypeArguments(), this, false);
487 if (paramTypes != null) {
488 //get constraints from explicit params (this is done by
489 //checking that explicit param types are equal to the ones
490 //in the functional interface descriptors)
491 List<Type> descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes();
492 if (descParameterTypes.size() != paramTypes.size()) {
493 checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda"));
494 return types.createErrorType(funcInterface);
495 }
496 for (Type p : descParameterTypes) {
497 if (!types.isSameType(funcInterfaceContext.asFree(p, types), paramTypes.head)) {
498 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
499 return types.createErrorType(funcInterface);
500 }
501 paramTypes = paramTypes.tail;
502 }
503 for (Type t : funcInterfaceContext.undetvars) {
504 UndetVar uv = (UndetVar)t;
505 minimizeInst(uv, Warner.noWarnings);
506 if (uv.inst == null &&
507 Type.filter(uv.getBounds(InferenceBound.UPPER), boundFilter).nonEmpty()) {
508 maximizeInst(uv, Warner.noWarnings);
509 }
510 }
512 formalInterface = funcInterfaceContext.asInstType(formalInterface, types);
513 }
514 ListBuffer<Type> typeargs = ListBuffer.lb();
515 List<Type> actualTypeargs = funcInterface.getTypeArguments();
516 //for remaining uninferred type-vars in the functional interface type,
517 //simply replace the wildcards with its bound
518 for (Type t : formalInterface.getTypeArguments()) {
519 if (actualTypeargs.head.tag == WILDCARD) {
520 WildcardType wt = (WildcardType)actualTypeargs.head;
521 typeargs.append(wt.type);
522 } else {
523 typeargs.append(actualTypeargs.head);
524 }
525 actualTypeargs = actualTypeargs.tail;
526 }
527 Type owntype = types.subst(formalInterface, funcInterfaceContext.inferenceVars(), typeargs.toList());
528 if (!chk.checkValidGenericType(owntype)) {
529 //if the inferred functional interface type is not well-formed,
530 //or if it's not a subtype of the original target, issue an error
531 checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface));
532 return types.createErrorType(funcInterface);
533 }
534 return owntype;
535 }
536 }
537 // </editor-fold>
539 /**
540 * Compute a synthetic method type corresponding to the requested polymorphic
541 * method signature. The target return type is computed from the immediately
542 * enclosing scope surrounding the polymorphic-signature call.
543 */
544 Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env,
545 MethodSymbol spMethod, // sig. poly. method or null if none
546 Resolve.MethodResolutionContext resolveContext,
547 List<Type> argtypes) {
548 final Type restype;
550 //The return type for a polymorphic signature call is computed from
551 //the enclosing tree E, as follows: if E is a cast, then use the
552 //target type of the cast expression as a return type; if E is an
553 //expression statement, the return type is 'void' - otherwise the
554 //return type is simply 'Object'. A correctness check ensures that
555 //env.next refers to the lexically enclosing environment in which
556 //the polymorphic signature call environment is nested.
558 switch (env.next.tree.getTag()) {
559 case TYPECAST:
560 JCTypeCast castTree = (JCTypeCast)env.next.tree;
561 restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ?
562 castTree.clazz.type :
563 syms.objectType;
564 break;
565 case EXEC:
566 JCTree.JCExpressionStatement execTree =
567 (JCTree.JCExpressionStatement)env.next.tree;
568 restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ?
569 syms.voidType :
570 syms.objectType;
571 break;
572 default:
573 restype = syms.objectType;
574 }
576 List<Type> paramtypes = Type.map(argtypes, new ImplicitArgType(spMethod, resolveContext.step));
577 List<Type> exType = spMethod != null ?
578 spMethod.getThrownTypes() :
579 List.of(syms.throwableType); // make it throw all exceptions
581 MethodType mtype = new MethodType(paramtypes,
582 restype,
583 exType,
584 syms.methodClass);
585 return mtype;
586 }
587 //where
588 class ImplicitArgType extends DeferredAttr.DeferredTypeMap {
590 public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) {
591 deferredAttr.super(AttrMode.SPECULATIVE, msym, phase);
592 }
594 public Type apply(Type t) {
595 t = types.erasure(super.apply(t));
596 if (t.tag == BOT)
597 // nulls type as the marker type Null (which has no instances)
598 // infer as java.lang.Void for now
599 t = types.boxedClass(syms.voidType).type;
600 return t;
601 }
602 }
604 /**
605 * Mapping that turns inference variables into undet vars
606 * (used by inference context)
607 */
608 class FromTypeVarFun extends Mapping {
610 boolean includeBounds;
612 FromTypeVarFun(boolean includeBounds) {
613 super("fromTypeVarFunWithBounds");
614 this.includeBounds = includeBounds;
615 }
617 public Type apply(Type t) {
618 if (t.tag == TYPEVAR) return new UndetVar((TypeVar)t, types, includeBounds);
619 else return t.map(this);
620 }
621 };
623 /**
624 * An inference context keeps track of the set of variables that are free
625 * in the current context. It provides utility methods for opening/closing
626 * types to their corresponding free/closed forms. It also provide hooks for
627 * attaching deferred post-inference action (see PendingCheck). Finally,
628 * it can be used as an entry point for performing upper/lower bound inference
629 * (see InferenceKind).
630 */
631 static class InferenceContext {
633 /**
634 * Single-method-interface for defining inference callbacks. Certain actions
635 * (i.e. subtyping checks) might need to be redone after all inference variables
636 * have been fixed.
637 */
638 interface FreeTypeListener {
639 void typesInferred(InferenceContext inferenceContext);
640 }
642 /** list of inference vars as undet vars */
643 List<Type> undetvars;
645 /** list of inference vars in this context */
646 List<Type> inferencevars;
648 java.util.Map<FreeTypeListener, List<Type>> freeTypeListeners =
649 new java.util.HashMap<FreeTypeListener, List<Type>>();
651 List<FreeTypeListener> freetypeListeners = List.nil();
653 public InferenceContext(List<Type> inferencevars, Infer infer, boolean includeBounds) {
654 this.undetvars = Type.map(inferencevars, infer.new FromTypeVarFun(includeBounds));
655 this.inferencevars = inferencevars;
656 }
658 /**
659 * returns the list of free variables (as type-variables) in this
660 * inference context
661 */
662 List<Type> inferenceVars() {
663 return inferencevars;
664 }
666 /**
667 * returns the list of uninstantiated variables (as type-variables) in this
668 * inference context (usually called after instantiate())
669 */
670 List<Type> restvars() {
671 List<Type> undetvars = this.undetvars;
672 ListBuffer<Type> restvars = ListBuffer.lb();
673 for (Type t : instTypes()) {
674 UndetVar uv = (UndetVar)undetvars.head;
675 if (uv.qtype == t) {
676 restvars.append(t);
677 }
678 undetvars = undetvars.tail;
679 }
680 return restvars.toList();
681 }
683 /**
684 * is this type free?
685 */
686 final boolean free(Type t) {
687 return t.containsAny(inferencevars);
688 }
690 final boolean free(List<Type> ts) {
691 for (Type t : ts) {
692 if (free(t)) return true;
693 }
694 return false;
695 }
697 /**
698 * Returns a list of free variables in a given type
699 */
700 final List<Type> freeVarsIn(Type t) {
701 ListBuffer<Type> buf = ListBuffer.lb();
702 for (Type iv : inferenceVars()) {
703 if (t.contains(iv)) {
704 buf.add(iv);
705 }
706 }
707 return buf.toList();
708 }
710 final List<Type> freeVarsIn(List<Type> ts) {
711 ListBuffer<Type> buf = ListBuffer.lb();
712 for (Type t : ts) {
713 buf.appendList(freeVarsIn(t));
714 }
715 ListBuffer<Type> buf2 = ListBuffer.lb();
716 for (Type t : buf) {
717 if (!buf2.contains(t)) {
718 buf2.add(t);
719 }
720 }
721 return buf2.toList();
722 }
724 /**
725 * Replace all free variables in a given type with corresponding
726 * undet vars (used ahead of subtyping/compatibility checks to allow propagation
727 * of inference constraints).
728 */
729 final Type asFree(Type t, Types types) {
730 return types.subst(t, inferencevars, undetvars);
731 }
733 final List<Type> asFree(List<Type> ts, Types types) {
734 ListBuffer<Type> buf = ListBuffer.lb();
735 for (Type t : ts) {
736 buf.append(asFree(t, types));
737 }
738 return buf.toList();
739 }
741 List<Type> instTypes() {
742 ListBuffer<Type> buf = ListBuffer.lb();
743 for (Type t : undetvars) {
744 UndetVar uv = (UndetVar)t;
745 buf.append(uv.inst != null ? uv.inst : uv.qtype);
746 }
747 return buf.toList();
748 }
750 /**
751 * Replace all free variables in a given type with corresponding
752 * instantiated types - if one or more free variable has not been
753 * fully instantiated, it will still be available in the resulting type.
754 */
755 Type asInstType(Type t, Types types) {
756 return types.subst(t, inferencevars, instTypes());
757 }
759 List<Type> asInstTypes(List<Type> ts, Types types) {
760 ListBuffer<Type> buf = ListBuffer.lb();
761 for (Type t : ts) {
762 buf.append(asInstType(t, types));
763 }
764 return buf.toList();
765 }
767 /**
768 * Add custom hook for performing post-inference action
769 */
770 void addFreeTypeListener(List<Type> types, FreeTypeListener ftl) {
771 freeTypeListeners.put(ftl, freeVarsIn(types));
772 }
774 /**
775 * Mark the inference context as complete and trigger evaluation
776 * of all deferred checks.
777 */
778 void notifyChange(Types types) {
779 InferenceException thrownEx = null;
780 for (Map.Entry<FreeTypeListener, List<Type>> entry :
781 new HashMap<FreeTypeListener, List<Type>>(freeTypeListeners).entrySet()) {
782 if (!Type.containsAny(entry.getValue(), restvars())) {
783 try {
784 entry.getKey().typesInferred(this);
785 freeTypeListeners.remove(entry.getKey());
786 } catch (InferenceException ex) {
787 if (thrownEx == null) {
788 thrownEx = ex;
789 }
790 }
791 }
792 }
793 //inference exception multiplexing - present any inference exception
794 //thrown when processing listeners as a single one
795 if (thrownEx != null) {
796 throw thrownEx;
797 }
798 }
800 void solveAny(List<Type> varsToSolve, Types types, Infer infer) {
801 boolean progress = false;
802 for (Type t : varsToSolve) {
803 UndetVar uv = (UndetVar)asFree(t, types);
804 if (uv.inst == null) {
805 infer.minimizeInst(uv, Warner.noWarnings);
806 if (uv.inst != null) {
807 progress = true;
808 }
809 }
810 }
811 if (!progress) {
812 throw infer.inferenceException.setMessage("cyclic.inference", varsToSolve);
813 }
814 }
815 }
817 final InferenceContext emptyContext = new InferenceContext(List.<Type>nil(), this, false);
818 }