|
Java example source code file (Infer.java)
The Infer.java Java example source code/* * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.javac.comp; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCTypeCast; import com.sun.tools.javac.tree.TreeInfo; import com.sun.tools.javac.util.*; import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; import com.sun.tools.javac.util.List; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Type.*; import com.sun.tools.javac.code.Type.UndetVar.InferenceBound; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.comp.DeferredAttr.AttrMode; import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph; import com.sun.tools.javac.comp.Infer.GraphSolver.InferenceGraph.Node; import com.sun.tools.javac.comp.Resolve.InapplicableMethodException; import com.sun.tools.javac.comp.Resolve.VerboseResolutionMode; import com.sun.tools.javac.util.GraphUtils.TarjanNode; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import static com.sun.tools.javac.code.TypeTag.*; /** Helper class for type parameter inference, used by the attribution phase. * * <p>This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice.</b> */ public class Infer { protected static final Context.Key<Infer> inferKey = new Context.Key<Infer>(); Resolve rs; Check chk; Symtab syms; Types types; JCDiagnostic.Factory diags; Log log; /** should the graph solver be used? */ boolean allowGraphInference; public static Infer instance(Context context) { Infer instance = context.get(inferKey); if (instance == null) instance = new Infer(context); return instance; } protected Infer(Context context) { context.put(inferKey, this); rs = Resolve.instance(context); chk = Check.instance(context); syms = Symtab.instance(context); types = Types.instance(context); diags = JCDiagnostic.Factory.instance(context); log = Log.instance(context); inferenceException = new InferenceException(diags); Options options = Options.instance(context); allowGraphInference = Source.instance(context).allowGraphInference() && options.isUnset("useLegacyInference"); } /** A value for prototypes that admit any type, including polymorphic ones. */ public static final Type anyPoly = new JCNoType(); /** * This exception class is design to store a list of diagnostics corresponding * to inference errors that can arise during a method applicability check. */ public static class InferenceException extends InapplicableMethodException { private static final long serialVersionUID = 0; List<JCDiagnostic> messages = List.nil(); InferenceException(JCDiagnostic.Factory diags) { super(diags); } @Override InapplicableMethodException setMessage() { //no message to set return this; } @Override InapplicableMethodException setMessage(JCDiagnostic diag) { messages = messages.append(diag); return this; } @Override public JCDiagnostic getDiagnostic() { return messages.head; } void clear() { messages = List.nil(); } } protected final InferenceException inferenceException; // <editor-fold defaultstate="collapsed" desc="Inference routines"> /** * Main inference entry point - instantiate a generic method type * using given argument types and (possibly) an expected target-type. */ public Type instantiateMethod(Env<AttrContext> env, List<Type> tvars, MethodType mt, Attr.ResultInfo resultInfo, Symbol msym, List<Type> argtypes, boolean allowBoxing, boolean useVarargs, Resolve.MethodResolutionContext resolveContext, Warner warn) throws InferenceException { //-System.err.println("instantiateMethod(" + tvars + ", " + mt + ", " + argtypes + ")"); //DEBUG final InferenceContext inferenceContext = new InferenceContext(tvars); inferenceException.clear(); try { DeferredAttr.DeferredAttrContext deferredAttrContext = resolveContext.deferredAttrContext(msym, inferenceContext, resultInfo, warn); resolveContext.methodCheck.argumentsAcceptable(env, deferredAttrContext, argtypes, mt.getParameterTypes(), warn); if (allowGraphInference && resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { //inject return constraints earlier checkWithinBounds(inferenceContext, warn); //propagation Type newRestype = generateReturnConstraints(resultInfo, mt, inferenceContext); mt = (MethodType)types.createMethodTypeWithReturn(mt, newRestype); //propagate outwards if needed if (resultInfo.checkContext.inferenceContext().free(resultInfo.pt)) { //propagate inference context outwards and exit inferenceContext.dupTo(resultInfo.checkContext.inferenceContext()); deferredAttrContext.complete(); return mt; } } deferredAttrContext.complete(); // minimize as yet undetermined type variables if (allowGraphInference) { inferenceContext.solve(warn); } else { inferenceContext.solveLegacy(true, warn, LegacyInferenceSteps.EQ_LOWER.steps); //minimizeInst } mt = (MethodType)inferenceContext.asInstType(mt); if (!allowGraphInference && inferenceContext.restvars().nonEmpty() && resultInfo != null && !warn.hasNonSilentLint(Lint.LintCategory.UNCHECKED)) { generateReturnConstraints(resultInfo, mt, inferenceContext); inferenceContext.solveLegacy(false, warn, LegacyInferenceSteps.EQ_UPPER.steps); //maximizeInst mt = (MethodType)inferenceContext.asInstType(mt); } if (resultInfo != null && rs.verboseResolutionMode.contains(VerboseResolutionMode.DEFERRED_INST)) { log.note(env.tree.pos, "deferred.method.inst", msym, mt, resultInfo.pt); } // return instantiated version of method type return mt; } finally { if (resultInfo != null || !allowGraphInference) { inferenceContext.notifyChange(); } else { inferenceContext.notifyChange(inferenceContext.boundedVars()); } } } /** * Generate constraints from the generic method's return type. If the method * call occurs in a context where a type T is expected, use the expected * type to derive more constraints on the generic method inference variables. */ Type generateReturnConstraints(Attr.ResultInfo resultInfo, MethodType mt, InferenceContext inferenceContext) { Type from = mt.getReturnType(); if (mt.getReturnType().containsAny(inferenceContext.inferencevars) && resultInfo.checkContext.inferenceContext() != emptyContext) { from = types.capture(from); //add synthetic captured ivars for (Type t : from.getTypeArguments()) { if (t.hasTag(TYPEVAR) && ((TypeVar)t).isCaptured()) { inferenceContext.addVar((TypeVar)t); } } } Type qtype1 = inferenceContext.asFree(from); Type to = returnConstraintTarget(qtype1, resultInfo.pt); Assert.check(allowGraphInference || !resultInfo.checkContext.inferenceContext().free(to), "legacy inference engine cannot handle constraints on both sides of a subtyping assertion"); //we need to skip capture? Warner retWarn = new Warner(); if (!resultInfo.checkContext.compatible(qtype1, resultInfo.checkContext.inferenceContext().asFree(to), retWarn) || //unchecked conversion is not allowed in source 7 mode (!allowGraphInference && retWarn.hasLint(Lint.LintCategory.UNCHECKED))) { throw inferenceException .setMessage("infer.no.conforming.instance.exists", inferenceContext.restvars(), mt.getReturnType(), to); } return from; } Type returnConstraintTarget(Type from, Type to) { if (from.hasTag(VOID)) { return syms.voidType; } else if (to.hasTag(NONE)) { return from.isPrimitive() ? from : syms.objectType; } else if (from.hasTag(UNDETVAR) && to.isPrimitive()) { if (!allowGraphInference) { //if legacy, just return boxed type return types.boxedClass(to).type; } //if graph inference we need to skip conflicting boxed bounds... UndetVar uv = (UndetVar)from; for (Type t : uv.getBounds(InferenceBound.EQ, InferenceBound.LOWER)) { Type boundAsPrimitive = types.unboxedType(t); if (boundAsPrimitive == null) continue; if (types.isConvertible(boundAsPrimitive, to)) { //effectively skip return-type constraint generation (compatibility) return syms.objectType; } } return types.boxedClass(to).type; } else { return to; } } /** * Infer cyclic inference variables as described in 15.12.2.8. */ private void instantiateAsUninferredVars(List<Type> vars, InferenceContext inferenceContext) { ListBuffer<Type> todo = new ListBuffer<>(); //step 1 - create fresh tvars for (Type t : vars) { UndetVar uv = (UndetVar)inferenceContext.asFree(t); List<Type> upperBounds = uv.getBounds(InferenceBound.UPPER); if (Type.containsAny(upperBounds, vars)) { TypeSymbol fresh_tvar = new TypeVariableSymbol(Flags.SYNTHETIC, uv.qtype.tsym.name, null, uv.qtype.tsym.owner); fresh_tvar.type = new TypeVar(fresh_tvar, types.makeCompoundType(uv.getBounds(InferenceBound.UPPER)), null); todo.append(uv); uv.inst = fresh_tvar.type; } else if (upperBounds.nonEmpty()) { uv.inst = types.glb(upperBounds); } else { uv.inst = syms.objectType; } } //step 2 - replace fresh tvars in their bounds List<Type> formals = vars; for (Type t : todo) { UndetVar uv = (UndetVar)t; TypeVar ct = (TypeVar)uv.inst; ct.bound = types.glb(inferenceContext.asInstTypes(types.getBounds(ct))); if (ct.bound.isErroneous()) { //report inference error if glb fails reportBoundError(uv, BoundErrorKind.BAD_UPPER); } formals = formals.tail; } } /** * Compute a synthetic method type corresponding to the requested polymorphic * method signature. The target return type is computed from the immediately * enclosing scope surrounding the polymorphic-signature call. */ Type instantiatePolymorphicSignatureInstance(Env<AttrContext> env, MethodSymbol spMethod, // sig. poly. method or null if none Resolve.MethodResolutionContext resolveContext, List<Type> argtypes) { final Type restype; //The return type for a polymorphic signature call is computed from //the enclosing tree E, as follows: if E is a cast, then use the //target type of the cast expression as a return type; if E is an //expression statement, the return type is 'void' - otherwise the //return type is simply 'Object'. A correctness check ensures that //env.next refers to the lexically enclosing environment in which //the polymorphic signature call environment is nested. switch (env.next.tree.getTag()) { case TYPECAST: JCTypeCast castTree = (JCTypeCast)env.next.tree; restype = (TreeInfo.skipParens(castTree.expr) == env.tree) ? castTree.clazz.type : syms.objectType; break; case EXEC: JCTree.JCExpressionStatement execTree = (JCTree.JCExpressionStatement)env.next.tree; restype = (TreeInfo.skipParens(execTree.expr) == env.tree) ? syms.voidType : syms.objectType; break; default: restype = syms.objectType; } List<Type> paramtypes = Type.map(argtypes, new ImplicitArgType(spMethod, resolveContext.step)); List<Type> exType = spMethod != null ? spMethod.getThrownTypes() : List.of(syms.throwableType); // make it throw all exceptions MethodType mtype = new MethodType(paramtypes, restype, exType, syms.methodClass); return mtype; } //where class ImplicitArgType extends DeferredAttr.DeferredTypeMap { public ImplicitArgType(Symbol msym, Resolve.MethodResolutionPhase phase) { rs.deferredAttr.super(AttrMode.SPECULATIVE, msym, phase); } public Type apply(Type t) { t = types.erasure(super.apply(t)); if (t.hasTag(BOT)) // nulls type as the marker type Null (which has no instances) // infer as java.lang.Void for now t = types.boxedClass(syms.voidType).type; return t; } } /** * This method is used to infer a suitable target SAM in case the original * SAM type contains one or more wildcards. An inference process is applied * so that wildcard bounds, as well as explicit lambda/method ref parameters * (where applicable) are used to constraint the solution. */ public Type instantiateFunctionalInterface(DiagnosticPosition pos, Type funcInterface, List<Type> paramTypes, Check.CheckContext checkContext) { if (types.capture(funcInterface) == funcInterface) { //if capture doesn't change the type then return the target unchanged //(this means the target contains no wildcards!) return funcInterface; } else { Type formalInterface = funcInterface.tsym.type; InferenceContext funcInterfaceContext = new InferenceContext(funcInterface.tsym.type.getTypeArguments()); Assert.check(paramTypes != null); //get constraints from explicit params (this is done by //checking that explicit param types are equal to the ones //in the functional interface descriptors) List<Type> descParameterTypes = types.findDescriptorType(formalInterface).getParameterTypes(); if (descParameterTypes.size() != paramTypes.size()) { checkContext.report(pos, diags.fragment("incompatible.arg.types.in.lambda")); return types.createErrorType(funcInterface); } for (Type p : descParameterTypes) { if (!types.isSameType(funcInterfaceContext.asFree(p), paramTypes.head)) { checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface)); return types.createErrorType(funcInterface); } paramTypes = paramTypes.tail; } try { funcInterfaceContext.solve(funcInterfaceContext.boundedVars(), types.noWarnings); } catch (InferenceException ex) { checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface)); } List<Type> actualTypeargs = funcInterface.getTypeArguments(); for (Type t : funcInterfaceContext.undetvars) { UndetVar uv = (UndetVar)t; if (uv.inst == null) { uv.inst = actualTypeargs.head; } actualTypeargs = actualTypeargs.tail; } Type owntype = funcInterfaceContext.asInstType(formalInterface); if (!chk.checkValidGenericType(owntype)) { //if the inferred functional interface type is not well-formed, //or if it's not a subtype of the original target, issue an error checkContext.report(pos, diags.fragment("no.suitable.functional.intf.inst", funcInterface)); } return owntype; } } // </editor-fold> // <editor-fold defaultstate="collapsed" desc="Bound checking"> /** * Check bounds and perform incorporation */ void checkWithinBounds(InferenceContext inferenceContext, Warner warn) throws InferenceException { MultiUndetVarListener mlistener = new MultiUndetVarListener(inferenceContext.undetvars); List<Type> saved_undet = inferenceContext.save(); try { while (true) { mlistener.reset(); if (!allowGraphInference) { //in legacy mode we lack of transitivity, so bound check //cannot be run in parallel with other incoprporation rounds for (Type t : inferenceContext.undetvars) { UndetVar uv = (UndetVar)t; IncorporationStep.CHECK_BOUNDS.apply(uv, inferenceContext, warn); } } for (Type t : inferenceContext.undetvars) { UndetVar uv = (UndetVar)t; //bound incorporation EnumSet<IncorporationStep> incorporationSteps = allowGraphInference ? incorporationStepsGraph : incorporationStepsLegacy; for (IncorporationStep is : incorporationSteps) { if (is.accepts(uv, inferenceContext)) { is.apply(uv, inferenceContext, warn); } } } if (!mlistener.changed || !allowGraphInference) break; } } finally { mlistener.detach(); if (incorporationCache.size() == MAX_INCORPORATION_STEPS) { inferenceContext.rollback(saved_undet); } incorporationCache.clear(); } } //where /** * This listener keeps track of changes on a group of inference variable * bounds. Note: the listener must be detached (calling corresponding * method) to make sure that the underlying inference variable is * left in a clean state. */ class MultiUndetVarListener implements UndetVar.UndetVarListener { boolean changed; List<Type> undetvars; public MultiUndetVarListener(List<Type> undetvars) { this.undetvars = undetvars; for (Type t : undetvars) { UndetVar uv = (UndetVar)t; uv.listener = this; } } public void varChanged(UndetVar uv, Set<InferenceBound> ibs) { //avoid non-termination if (incorporationCache.size() < MAX_INCORPORATION_STEPS) { changed = true; } } void reset() { changed = false; } void detach() { for (Type t : undetvars) { UndetVar uv = (UndetVar)t; uv.listener = null; } } }; /** max number of incorporation rounds */ static final int MAX_INCORPORATION_STEPS = 100; /** * This enumeration defines an entry point for doing inference variable * bound incorporation - it can be used to inject custom incorporation * logic into the basic bound checking routine */ enum IncorporationStep { /** * Performs basic bound checking - i.e. is the instantiated type for a given * inference variable compatible with its bounds? */ CHECK_BOUNDS() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); uv.substBounds(inferenceContext.inferenceVars(), inferenceContext.instTypes(), infer.types); infer.checkCompatibleUpperBounds(uv, inferenceContext); if (uv.inst != null) { Type inst = uv.inst; for (Type u : uv.getBounds(InferenceBound.UPPER)) { if (!isSubtype(inst, inferenceContext.asFree(u), warn, infer)) { infer.reportBoundError(uv, BoundErrorKind.UPPER); } } for (Type l : uv.getBounds(InferenceBound.LOWER)) { if (!isSubtype(inferenceContext.asFree(l), inst, warn, infer)) { infer.reportBoundError(uv, BoundErrorKind.LOWER); } } for (Type e : uv.getBounds(InferenceBound.EQ)) { if (!isSameType(inst, inferenceContext.asFree(e), infer)) { infer.reportBoundError(uv, BoundErrorKind.EQ); } } } } @Override boolean accepts(UndetVar uv, InferenceContext inferenceContext) { //applies to all undetvars return true; } }, /** * Check consistency of equality constraints. This is a slightly more aggressive * inference routine that is designed as to maximize compatibility with JDK 7. * Note: this is not used in graph mode. */ EQ_CHECK_LEGACY() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); Type eq = null; for (Type e : uv.getBounds(InferenceBound.EQ)) { Assert.check(!inferenceContext.free(e)); if (eq != null && !isSameType(e, eq, infer)) { infer.reportBoundError(uv, BoundErrorKind.EQ); } eq = e; for (Type l : uv.getBounds(InferenceBound.LOWER)) { Assert.check(!inferenceContext.free(l)); if (!isSubtype(l, e, warn, infer)) { infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER); } } for (Type u : uv.getBounds(InferenceBound.UPPER)) { if (inferenceContext.free(u)) continue; if (!isSubtype(e, u, warn, infer)) { infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER); } } } } }, /** * Check consistency of equality constraints. */ EQ_CHECK() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); for (Type e : uv.getBounds(InferenceBound.EQ)) { if (e.containsAny(inferenceContext.inferenceVars())) continue; for (Type u : uv.getBounds(InferenceBound.UPPER)) { if (!isSubtype(e, inferenceContext.asFree(u), warn, infer)) { infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_UPPER); } } for (Type l : uv.getBounds(InferenceBound.LOWER)) { if (!isSubtype(inferenceContext.asFree(l), e, warn, infer)) { infer.reportBoundError(uv, BoundErrorKind.BAD_EQ_LOWER); } } } } }, /** * Given a bound set containing {@code alpha <: T} and {@code alpha :> S} * perform {@code S <: T} (which could lead to new bounds). */ CROSS_UPPER_LOWER() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); for (Type b1 : uv.getBounds(InferenceBound.UPPER)) { for (Type b2 : uv.getBounds(InferenceBound.LOWER)) { isSubtype(inferenceContext.asFree(b2), inferenceContext.asFree(b1), warn , infer); } } } }, /** * Given a bound set containing {@code alpha <: T} and {@code alpha == S} * perform {@code S <: T} (which could lead to new bounds). */ CROSS_UPPER_EQ() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); for (Type b1 : uv.getBounds(InferenceBound.UPPER)) { for (Type b2 : uv.getBounds(InferenceBound.EQ)) { isSubtype(inferenceContext.asFree(b2), inferenceContext.asFree(b1), warn, infer); } } } }, /** * Given a bound set containing {@code alpha :> S} and {@code alpha == T} * perform {@code S <: T} (which could lead to new bounds). */ CROSS_EQ_LOWER() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); for (Type b1 : uv.getBounds(InferenceBound.EQ)) { for (Type b2 : uv.getBounds(InferenceBound.LOWER)) { isSubtype(inferenceContext.asFree(b2), inferenceContext.asFree(b1), warn, infer); } } } }, /** * Given a bound set containing {@code alpha == S} and {@code alpha == T} * perform {@code S == T} (which could lead to new bounds). */ CROSS_EQ_EQ() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); for (Type b1 : uv.getBounds(InferenceBound.EQ)) { for (Type b2 : uv.getBounds(InferenceBound.EQ)) { if (b1 != b2) { isSameType(inferenceContext.asFree(b2), inferenceContext.asFree(b1), infer); } } } } }, /** * Given a bound set containing {@code alpha <: beta} propagate lower bounds * from alpha to beta; also propagate upper bounds from beta to alpha. */ PROP_UPPER() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); for (Type b : uv.getBounds(InferenceBound.UPPER)) { if (inferenceContext.inferenceVars().contains(b)) { UndetVar uv2 = (UndetVar)inferenceContext.asFree(b); if (uv2.isCaptured()) continue; //alpha <: beta //0. set beta :> alpha addBound(InferenceBound.LOWER, uv2, inferenceContext.asInstType(uv.qtype), infer); //1. copy alpha's lower to beta's for (Type l : uv.getBounds(InferenceBound.LOWER)) { addBound(InferenceBound.LOWER, uv2, inferenceContext.asInstType(l), infer); } //2. copy beta's upper to alpha's for (Type u : uv2.getBounds(InferenceBound.UPPER)) { addBound(InferenceBound.UPPER, uv, inferenceContext.asInstType(u), infer); } } } } }, /** * Given a bound set containing {@code alpha :> beta} propagate lower bounds * from beta to alpha; also propagate upper bounds from alpha to beta. */ PROP_LOWER() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); for (Type b : uv.getBounds(InferenceBound.LOWER)) { if (inferenceContext.inferenceVars().contains(b)) { UndetVar uv2 = (UndetVar)inferenceContext.asFree(b); if (uv2.isCaptured()) continue; //alpha :> beta //0. set beta <: alpha addBound(InferenceBound.UPPER, uv2, inferenceContext.asInstType(uv.qtype), infer); //1. copy alpha's upper to beta's for (Type u : uv.getBounds(InferenceBound.UPPER)) { addBound(InferenceBound.UPPER, uv2, inferenceContext.asInstType(u), infer); } //2. copy beta's lower to alpha's for (Type l : uv2.getBounds(InferenceBound.LOWER)) { addBound(InferenceBound.LOWER, uv, inferenceContext.asInstType(l), infer); } } } } }, /** * Given a bound set containing {@code alpha == beta} propagate lower/upper * bounds from alpha to beta and back. */ PROP_EQ() { public void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn) { Infer infer = inferenceContext.infer(); for (Type b : uv.getBounds(InferenceBound.EQ)) { if (inferenceContext.inferenceVars().contains(b)) { UndetVar uv2 = (UndetVar)inferenceContext.asFree(b); if (uv2.isCaptured()) continue; //alpha == beta //0. set beta == alpha addBound(InferenceBound.EQ, uv2, inferenceContext.asInstType(uv.qtype), infer); //1. copy all alpha's bounds to beta's for (InferenceBound ib : InferenceBound.values()) { for (Type b2 : uv.getBounds(ib)) { if (b2 != uv2) { addBound(ib, uv2, inferenceContext.asInstType(b2), infer); } } } //2. copy all beta's bounds to alpha's for (InferenceBound ib : InferenceBound.values()) { for (Type b2 : uv2.getBounds(ib)) { if (b2 != uv) { addBound(ib, uv, inferenceContext.asInstType(b2), infer); } } } } } } }; abstract void apply(UndetVar uv, InferenceContext inferenceContext, Warner warn); boolean accepts(UndetVar uv, InferenceContext inferenceContext) { return !uv.isCaptured(); } boolean isSubtype(Type s, Type t, Warner warn, Infer infer) { return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn, infer); } boolean isSameType(Type s, Type t, Infer infer) { return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null, infer); } void addBound(InferenceBound ib, UndetVar uv, Type b, Infer infer) { doIncorporationOp(opFor(ib), uv, b, null, infer); } IncorporationBinaryOpKind opFor(InferenceBound boundKind) { switch (boundKind) { case EQ: return IncorporationBinaryOpKind.ADD_EQ_BOUND; case LOWER: return IncorporationBinaryOpKind.ADD_LOWER_BOUND; case UPPER: return IncorporationBinaryOpKind.ADD_UPPER_BOUND; default: Assert.error("Can't get here!"); return null; } } boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn, Infer infer) { IncorporationBinaryOp newOp = infer.new IncorporationBinaryOp(opKind, op1, op2); Boolean res = infer.incorporationCache.get(newOp); if (res == null) { infer.incorporationCache.put(newOp, res = newOp.apply(warn)); } return res; } } /** incorporation steps to be executed when running in legacy mode */ EnumSet<IncorporationStep> incorporationStepsLegacy = EnumSet.of(IncorporationStep.EQ_CHECK_LEGACY); /** incorporation steps to be executed when running in graph mode */ EnumSet<IncorporationStep> incorporationStepsGraph = EnumSet.complementOf(EnumSet.of(IncorporationStep.EQ_CHECK_LEGACY)); /** * Three kinds of basic operation are supported as part of an incorporation step: * (i) subtype check, (ii) same type check and (iii) bound addition (either * upper/lower/eq bound). */ enum IncorporationBinaryOpKind { IS_SUBTYPE() { @Override boolean apply(Type op1, Type op2, Warner warn, Types types) { return types.isSubtypeUnchecked(op1, op2, warn); } }, IS_SAME_TYPE() { @Override boolean apply(Type op1, Type op2, Warner warn, Types types) { return types.isSameType(op1, op2); } }, ADD_UPPER_BOUND() { @Override boolean apply(Type op1, Type op2, Warner warn, Types types) { UndetVar uv = (UndetVar)op1; uv.addBound(InferenceBound.UPPER, op2, types); return true; } }, ADD_LOWER_BOUND() { @Override boolean apply(Type op1, Type op2, Warner warn, Types types) { UndetVar uv = (UndetVar)op1; uv.addBound(InferenceBound.LOWER, op2, types); return true; } }, ADD_EQ_BOUND() { @Override boolean apply(Type op1, Type op2, Warner warn, Types types) { UndetVar uv = (UndetVar)op1; uv.addBound(InferenceBound.EQ, op2, types); return true; } }; abstract boolean apply(Type op1, Type op2, Warner warn, Types types); } /** * This class encapsulates a basic incorporation operation; incorporation * operations takes two type operands and a kind. Each operation performed * during an incorporation round is stored in a cache, so that operations * are not executed unnecessarily (which would potentially lead to adding * same bounds over and over). */ class IncorporationBinaryOp { IncorporationBinaryOpKind opKind; Type op1; Type op2; IncorporationBinaryOp(IncorporationBinaryOpKind opKind, Type op1, Type op2) { this.opKind = opKind; this.op1 = op1; this.op2 = op2; } @Override public boolean equals(Object o) { if (!(o instanceof IncorporationBinaryOp)) { return false; } else { IncorporationBinaryOp that = (IncorporationBinaryOp)o; return opKind == that.opKind && types.isSameType(op1, that.op1, true) && types.isSameType(op2, that.op2, true); } } @Override public int hashCode() { int result = opKind.hashCode(); result *= 127; result += types.hashCode(op1); result *= 127; result += types.hashCode(op2); return result; } boolean apply(Warner warn) { return opKind.apply(op1, op2, warn, types); } } /** an incorporation cache keeps track of all executed incorporation-related operations */ Map<IncorporationBinaryOp, Boolean> incorporationCache = new HashMap<IncorporationBinaryOp, Boolean>(); /** * Make sure that the upper bounds we got so far lead to a solvable inference * variable by making sure that a glb exists. */ void checkCompatibleUpperBounds(UndetVar uv, InferenceContext inferenceContext) { List<Type> hibounds = Type.filter(uv.getBounds(InferenceBound.UPPER), new BoundFilter(inferenceContext)); Type hb = null; if (hibounds.isEmpty()) hb = syms.objectType; else if (hibounds.tail.isEmpty()) hb = hibounds.head; else hb = types.glb(hibounds); if (hb == null || hb.isErroneous()) reportBoundError(uv, BoundErrorKind.BAD_UPPER); } //where protected static class BoundFilter implements Filter<Type> { InferenceContext inferenceContext; public BoundFilter(InferenceContext inferenceContext) { this.inferenceContext = inferenceContext; } @Override public boolean accepts(Type t) { return !t.isErroneous() && !inferenceContext.free(t) && !t.hasTag(BOT); } }; /** * This enumeration defines all possible bound-checking related errors. */ enum BoundErrorKind { /** * The (uninstantiated) inference variable has incompatible upper bounds. */ BAD_UPPER() { @Override InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { return ex.setMessage("incompatible.upper.bounds", uv.qtype, uv.getBounds(InferenceBound.UPPER)); } }, /** * An equality constraint is not compatible with an upper bound. */ BAD_EQ_UPPER() { @Override InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { return ex.setMessage("incompatible.eq.upper.bounds", uv.qtype, uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.UPPER)); } }, /** * An equality constraint is not compatible with a lower bound. */ BAD_EQ_LOWER() { @Override InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { return ex.setMessage("incompatible.eq.lower.bounds", uv.qtype, uv.getBounds(InferenceBound.EQ), uv.getBounds(InferenceBound.LOWER)); } }, /** * Instantiated inference variable is not compatible with an upper bound. */ UPPER() { @Override InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { return ex.setMessage("inferred.do.not.conform.to.upper.bounds", uv.inst, uv.getBounds(InferenceBound.UPPER)); } }, /** * Instantiated inference variable is not compatible with a lower bound. */ LOWER() { @Override InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { return ex.setMessage("inferred.do.not.conform.to.lower.bounds", uv.inst, uv.getBounds(InferenceBound.LOWER)); } }, /** * Instantiated inference variable is not compatible with an equality constraint. */ EQ() { @Override InapplicableMethodException setMessage(InferenceException ex, UndetVar uv) { return ex.setMessage("inferred.do.not.conform.to.eq.bounds", uv.inst, uv.getBounds(InferenceBound.EQ)); } }; abstract InapplicableMethodException setMessage(InferenceException ex, UndetVar uv); } /** * Report a bound-checking error of given kind */ void reportBoundError(UndetVar uv, BoundErrorKind bk) { throw bk.setMessage(inferenceException, uv); } // </editor-fold> // <editor-fold defaultstate="collapsed" desc="Inference engine"> /** * Graph inference strategy - act as an input to the inference solver; a strategy is * composed of two ingredients: (i) find a node to solve in the inference graph, * and (ii) tell th engine when we are done fixing inference variables */ interface GraphStrategy { /** * A NodeNotFoundException is thrown whenever an inference strategy fails * to pick the next node to solve in the inference graph. */ public static class NodeNotFoundException extends RuntimeException { private static final long serialVersionUID = 0; InferenceGraph graph; public NodeNotFoundException(InferenceGraph graph) { this.graph = graph; } } /** * Pick the next node (leaf) to solve in the graph */ Node pickNode(InferenceGraph g) throws NodeNotFoundException; /** * Is this the last step? */ boolean done(); } /** * Simple solver strategy class that locates all leaves inside a graph * and picks the first leaf as the next node to solve */ abstract class LeafSolver implements GraphStrategy { public Node pickNode(InferenceGraph g) { if (g.nodes.isEmpty()) { //should not happen throw new NodeNotFoundException(g); }; return g.nodes.get(0); } boolean isSubtype(Type s, Type t, Warner warn, Infer infer) { return doIncorporationOp(IncorporationBinaryOpKind.IS_SUBTYPE, s, t, warn, infer); } boolean isSameType(Type s, Type t, Infer infer) { return doIncorporationOp(IncorporationBinaryOpKind.IS_SAME_TYPE, s, t, null, infer); } void addBound(InferenceBound ib, UndetVar uv, Type b, Infer infer) { doIncorporationOp(opFor(ib), uv, b, null, infer); } IncorporationBinaryOpKind opFor(InferenceBound boundKind) { switch (boundKind) { case EQ: return IncorporationBinaryOpKind.ADD_EQ_BOUND; case LOWER: return IncorporationBinaryOpKind.ADD_LOWER_BOUND; case UPPER: return IncorporationBinaryOpKind.ADD_UPPER_BOUND; default: Assert.error("Can't get here!"); return null; } } boolean doIncorporationOp(IncorporationBinaryOpKind opKind, Type op1, Type op2, Warner warn, Infer infer) { IncorporationBinaryOp newOp = infer.new IncorporationBinaryOp(opKind, op1, op2); Boolean res = infer.incorporationCache.get(newOp); if (res == null) { infer.incorporationCache.put(newOp, res = newOp.apply(warn)); } return res; } } /** * This solver uses an heuristic to pick the best leaf - the heuristic * tries to select the node that has maximal probability to contain one * or more inference variables in a given list */ abstract class BestLeafSolver extends LeafSolver { /** list of ivars of which at least one must be solved */ List<Type> varsToSolve; BestLeafSolver(List<Type> varsToSolve) { this.varsToSolve = varsToSolve; } /** * Computes a path that goes from a given node to the leafs in the graph. * Typically this will start from a node containing a variable in * {@code varsToSolve}. For any given path, the cost is computed as the total * number of type-variables that should be eagerly instantiated across that path. */ Pair<List Other Java examples (source code examples)Here is a short list of links related to this Java Infer.java source code file: |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.