package org.checkerframework.framework.util.typeinference.constraint; import java.util.Map; import java.util.Set; import javax.lang.model.type.TypeVariable; import org.checkerframework.framework.type.AnnotatedTypeMirror; import org.checkerframework.framework.util.typeinference.TypeArgInferenceUtil; /** * AFConstraint represent the initial constraints used to infer type arguments for method * invocations and new class invocations. These constraints are simplified then converted to * TUConstraints during type argument inference.. * * <p>Subclasses of AFConstraint represent the following types of constraints found in * (http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2.7) * * <p>A 《 F and F 》 A both imply that A is convertible to F. F 《 A and A 》 F both imply that F is * convertible to A (this may happen due to wildcard/typevar bounds and recursive types) A = F * implies that A is exactly F * * <p>In the Checker Framework a type, A will be convertible to another type F, if * AnnotatedTypes.asSuper will return a non-null value when A is passed as a subtype and F the * supertype to the method. * * <p>In Java type A will be convertible to another type F if there exists a conversion * context/method that transforms the one type into the other. * * <p>A 《 F and F 》 A are represented by class A2F F 《 A and A 》 F are represented by class F2A F = * A is represented by class FIsA */ public abstract class AFConstraint { public final AnnotatedTypeMirror argument; public final AnnotatedTypeMirror formalParameter; /** * Used to compute hashcodes. This value should be unique for every subclass of AFConstraints. */ protected final int hashcodeBase; public AFConstraint( final AnnotatedTypeMirror argument, final AnnotatedTypeMirror formalParameter, int hashcodeBase) { this.argument = argument; this.formalParameter = formalParameter; this.hashcodeBase = hashcodeBase; } /** * @param targets the type parameters whose arguments we are trying to solve for * @return true if this constraint can't be broken up into other constraints or further * simplified In general, if either argument or formal parameter is a use of the type * parameters we are inferring over then the constraint cannot be reduced further */ public boolean isIrreducible(final Set<TypeVariable> targets) { return TypeArgInferenceUtil.isATarget(argument, targets) || TypeArgInferenceUtil.isATarget(formalParameter, targets); } @Override public boolean equals(Object thatObject) { if (this == thatObject) { return true; } // else if (thatObject == null || this.getClass() != thatObject.getClass()) { return false; } final AFConstraint that = (AFConstraint) thatObject; return this.argument.equals(that.argument) && this.formalParameter.equals(that.formalParameter); } @Override public int hashCode() { int result = formalParameter.hashCode(); result = hashcodeBase * result + argument.hashCode(); return result; } /** * Once AFConstraints are irreducible it can be converted to a TU constraint, constraints * between individual type parameters for which we are inferring an argument (T) and Java types * (U). * * @return a TUConstraint that represents this AFConstraint */ public abstract TUConstraint toTUConstraint(); /** * Given a partial solution to our type argument inference, replace any uses of type parameters * that have been solved with their arguments. * * <p>That is: Let S be a partial solution to our inference (i.e. we have inferred type * arguments for some types) Let S be a map {@code (T0 ⇒ A0, T1 ⇒ A1, ..., TN ⇒ * AN)} where Ti is a type parameter and Ai is its solved argument. For all uses of Ti in this * constraint, replace them with Ai. * * <p>For the mapping {@code (T0 ⇒ A0)}, the following constraint: {@code ArrayList<T0> << * List<T1>} * * <p>Becomes: {@code ArrayList<A0> << List<T1>} * * <p>A constraint: {@code T0 << T1} * * <p>Becomes: {@code A0 << T1} * * @param substitutions a mapping of target type parameter to the type argument to * @return a new constraint that contains no use of the keys in substitutions */ public AFConstraint substitute(final Map<TypeVariable, AnnotatedTypeMirror> substitutions) { final AnnotatedTypeMirror newArgument = TypeArgInferenceUtil.substitute(substitutions, argument); final AnnotatedTypeMirror newFormalParameter = TypeArgInferenceUtil.substitute(substitutions, formalParameter); return construct(newArgument, newFormalParameter); } /** Used to create a new constraint of the same subclass of AFConstraint. */ protected abstract AFConstraint construct( AnnotatedTypeMirror newArgument, AnnotatedTypeMirror newFormalParameter); }