blob: 8595cb9d7cc4618c7e2143b68fd5924d69a022e8 [file] [log] [blame]
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);
}
}