| package org.checkerframework.framework.util.typeinference.constraint; |
| |
| import java.util.Objects; |
| import org.checkerframework.checker.nullness.qual.Nullable; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror; |
| import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable; |
| import org.checkerframework.framework.util.typeinference.TypeArgInferenceUtil; |
| |
| /** |
| * Subclasses of TUConstraint represent constraints between a type parameter, whose type arguments |
| * are being inferred, and the types used to do that inference. These constraints are used by the |
| * TASolver to infer arguments. |
| * |
| * <p>TU constraints come in the classic form of subtype, supertype, and equality constraints.<br> |
| * |
| * <ul> |
| * <li>{@code T <: U} -- implies T is a subtype of U, it is represented by TSubU <br> |
| * <li>{@code T >: U} -- implies T is a supertype of U, it is represented by TSuperU <br> |
| * <li>{@code T = U} -- implies T is equal to U, it is represented by TIsU <br> |
| * </ul> |
| * |
| * <p>Note, it is important that the type parameter is represented by an AnnotatedTypeVariable |
| * because if a use of the type parameter has a primary annotation, then the two types represented |
| * in by a TUConstraint are NOT constrained in the hierarchy of that annotation. e.g. |
| * |
| * <pre>{@code |
| * <T> void method(List<@NonNull T> t1, T t2) |
| * method(new ArrayList<@NonNull String>(), null); |
| * }</pre> |
| * |
| * The above method call would eventually be reduced to constraints: {@code [@NonNull String |
| * == @NonNull T, @Nullable null <: T]} |
| * |
| * <p>In this example, if we did not ignore the first constraint then the type argument would be |
| * exactly @NonNull String and the second argument would be invalid. However, the correct inference |
| * would be @Nullable String and both arguments would be valid. |
| */ |
| public abstract class TUConstraint { |
| /** |
| * An AnnotatedTypeVariable representing a target type parameter for which we are inferring a type |
| * argument. This is the T in the TUConstraints. |
| */ |
| public final AnnotatedTypeVariable typeVariable; |
| |
| /** |
| * A type used to infer an argument for the typeVariable T. This would be the U in the |
| * TUConstraints. |
| */ |
| public final AnnotatedTypeMirror relatedType; |
| |
| /** Whether or not U is a type from an argument to the method. */ |
| public final boolean uIsArg; |
| |
| protected TUConstraint( |
| final AnnotatedTypeVariable typeVariable, |
| final AnnotatedTypeMirror relatedType, |
| boolean uIsArg) { |
| this.typeVariable = typeVariable; |
| this.relatedType = relatedType; |
| this.uIsArg = uIsArg; |
| |
| TypeArgInferenceUtil.checkForUninferredTypes(relatedType); |
| } |
| |
| @Override |
| public boolean equals(@Nullable Object thatObject) { |
| if (this == thatObject) { |
| return true; |
| } // else |
| |
| if (thatObject == null || this.getClass() != thatObject.getClass()) { |
| return false; |
| } |
| |
| final TUConstraint that = (TUConstraint) thatObject; |
| |
| return this.typeVariable.equals(that.typeVariable) && this.relatedType.equals(that.relatedType); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(this.getClass(), typeVariable, relatedType); |
| } |
| } |