C#: Improve performance of type conversion library

This commit is contained in:
Tom Hvitved
2018-10-02 12:48:57 +02:00
parent ae9b492b33
commit 19215d0868

View File

@@ -64,6 +64,20 @@ private Type getTypeArgument(UnboundGenericType ugt, ConstructedType ct, int i,
result = ct.getTypeArgument(i)
}
/** A type that is an element type of an array type. */
private class ArrayElementType extends Type {
ArrayElementType() {
this = any(ArrayType at).getElementType()
}
}
/** A type that is an argument in a constructed type. */
private class TypeArgument extends Type {
TypeArgument() {
this = any(ConstructedType ct).getATypeArgument()
}
}
/**
* INTERNAL: Do not use.
*
@@ -78,125 +92,213 @@ private Type getTypeArgument(UnboundGenericType ugt, ConstructedType ct, int i,
predicate convIdentity(Type fromType, Type toType) {
fromType = toType
or
convIdentityStrict(fromType, toType)
Identity::convIdentityStrict(fromType, toType)
}
private class IdentityConvertibleType extends Type {
IdentityConvertibleType() {
isIdentityConvertible(this)
private module Identity {
private class IdentityConvertibleType extends Type {
IdentityConvertibleType() {
isIdentityConvertible(this)
}
}
}
private class IdentityConvertibleArrayType extends IdentityConvertibleType, ArrayType { }
private class IdentityConvertibleArrayType extends IdentityConvertibleType, ArrayType { }
private class IdentityConvertibleConstructedType extends IdentityConvertibleType {
IdentityConvertibleConstructedType() {
this instanceof ConstructedType
}
}
private class IdentityConvertibleConstructedType extends IdentityConvertibleType, ConstructedType { }
/**
* A type is (strictly) identity convertible if it contains at least one `object`
* or one `dynamic` sub term.
*/
private predicate isIdentityConvertible(Type t) {
t instanceof ObjectType
or
t instanceof DynamicType
or
isIdentityConvertible(t.(ArrayType).getElementType())
or
isIdentityConvertible(t.(ConstructedType).getATypeArgument())
}
private predicate convIdentityStrict(IdentityConvertibleType fromType, IdentityConvertibleType toType) {
convIdentityObjectDynamic(fromType, toType)
or
convIdentityObjectDynamic(toType, fromType)
or
convIdentityStrictArrayType(fromType, toType)
or
convIdentityStrictConstructedType(fromType, toType)
}
private predicate convIdentityObjectDynamic(ObjectType fromType, DynamicType toType) {
any()
}
private predicate convIdentityStrictArrayType(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType) {
convIdentityStrictArrayTypeJoin(fromType, toType, toType.getDimension(), toType.getRank())
}
pragma [noinline]
private predicate convIdentityStrictArrayTypeJoin(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType, int dim, int rnk) {
convIdentityStrictArrayElementType(fromType, toType.getElementType(), dim, rnk)
}
private predicate convIdentityStrictArrayElementType(IdentityConvertibleArrayType fromType, ArrayElementType aet, int dim, int rnk) {
convIdentityStrict(fromType.getElementType(), aet) and
dim = fromType.getDimension() and
rnk = fromType.getRank()
}
/** A type that is an element type of an array type. */
private class ArrayElementType extends Type {
ArrayElementType() {
this = any(ArrayType at).getElementType()
}
}
private predicate convIdentityStrictConstructedType(IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
/* Semantically equivalent with
* ```
* ugt = fromType.getUnboundGeneric()
* and
* forex(int i |
* i in [0 .. ugt.getNumberOfTypeParameters() - 1] |
* exists(Type t1, Type t2 |
* t1 = getTypeArgument(ugt, fromType, i, _) and
* t2 = getTypeArgument(ugt, toType, i, _) |
* convIdentity(t1, t2)
* )
* )
* ```
* but performance is improved by explicitly evaluating the `i`th argument
* only when all preceding arguments are convertible.
/**
* A type is (strictly) identity convertible if it contains at least one `object`
* or one `dynamic` sub term.
*/
exists(UnboundGenericType ugt |
convIdentityStrictConstructedTypeFromZero(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
)
}
/**
* Holds if the type arguments 0 through `i` of `fromType` and `toType` are identity convertible.
*/
private predicate convIdentityStrictConstructedTypeFromZero(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType, int i) {
exists(Type toTypeArgument |
convIdentityStrictConstructedTypeFromZeroAux(ugt, fromType, i, toTypeArgument) and
toTypeArgument = getTypeArgument(ugt, toType, i, _) and
fromType != toType |
i = 0
private predicate isIdentityConvertible(Type t) {
t instanceof ObjectType
or
convIdentityStrictConstructedTypeFromZero(ugt, fromType, toType, i - 1)
)
}
t instanceof DynamicType
or
isIdentityConvertible(t.(ArrayType).getElementType())
or
isIdentityConvertible(t.(ConstructedType).getATypeArgument())
}
pragma [nomagic]
private predicate convIdentityStrictConstructedTypeFromZeroAux(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, int i, Type toTypeArgument) {
exists(Type fromTypeArgument |
fromTypeArgument = getTypeArgument(ugt, fromType, i, _) and
convIdentityTypeArgument(fromTypeArgument, toTypeArgument)
)
}
predicate convIdentityStrict(IdentityConvertibleType fromType, IdentityConvertibleType toType) {
convIdentityObjectDynamic(fromType, toType)
or
convIdentityObjectDynamic(toType, fromType)
or
convIdentityStrictArrayType(fromType, toType)
or
convIdentityStrictConstructedType(fromType, toType)
}
private predicate convIdentityTypeArgument(TypeArgument fromType, TypeArgument toType) {
convIdentity(fromType, toType)
}
private predicate convIdentityObjectDynamic(ObjectType fromType, DynamicType toType) {
any()
}
/** A type that is an argument in a constructed type. */
private class TypeArgument extends Type {
TypeArgument() {
this = any(ConstructedType ct).getATypeArgument()
private predicate convIdentityStrictArrayType(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType) {
convIdentityStrictArrayTypeJoin(fromType, toType, toType.getDimension(), toType.getRank())
}
pragma[noinline]
private predicate convIdentityStrictArrayTypeJoin(IdentityConvertibleArrayType fromType, IdentityConvertibleArrayType toType, int dim, int rnk) {
convIdentityStrictArrayElementType(fromType, toType.getElementType(), dim, rnk)
}
private predicate convIdentityStrictArrayElementType(IdentityConvertibleArrayType fromType, ArrayElementType aet, int dim, int rnk) {
convIdentityStrict(fromType.getElementType(), aet) and
dim = fromType.getDimension() and
rnk = fromType.getRank()
}
/**
* Gets the number of different type arguments supplied for the type
* parameter at index `i` in unbound generic type `ugt`.
*/
private int getTypeArgumentCount(UnboundGenericType ugt, int i) {
result = strictcount(Type arg |
exists(IdentityConvertibleConstructedType ct |
ct.getUnboundGeneric() = ugt |
arg = ct.getTypeArgument(i)
)
)
}
private int rnk(UnboundGenericType ugt, int i) {
result = rank[i + 1](int j, int k |
j = getTypeArgumentCount(ugt, k) |
k order by j, k
)
}
private Type getTypeArgumentRanked(UnboundGenericType ugt, IdentityConvertibleConstructedType t, int i) {
result = getTypeArgument(ugt, t, rnk(ugt, i), _)
}
/**
* Holds if `fromTypeArgument` is identity convertible to `toTypeArgument`, and
* both types are the `i`th type argument in _some_ constructed type.
*/
pragma[nomagic]
private predicate convTypeArguments(Type fromTypeArgument, Type toTypeArgument, int i) {
exists(int j |
fromTypeArgument = getTypeArgumentRanked(_, _, i) and
toTypeArgument = getTypeArgumentRanked(_, _, j) and
i <= j and j <= i
|
convIdentity(fromTypeArgument, toTypeArgument)
)
}
pragma[nomagic]
private predicate convTypeArgumentsSomeUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
convTypeArguments(fromTypeArgument, toTypeArgument, i) and
fromTypeArgument = getTypeArgumentRanked(ugt, _, i)
}
/**
* Holds if `fromTypeArgument` is identity convertible to `toTypeArgument` and
* both types are the `i`th type argument in _some_ constructed type
* based on unbound generic type `ugt`.
*/
pragma[noinline]
private predicate convTypeArgumentsSameUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
convTypeArgumentsSomeUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
toTypeArgument = getTypeArgumentRanked(ugt, _, i)
}
pragma[nomagic]
private predicate convIdentitySingle0(UnboundGenericType ugt, IdentityConvertibleConstructedType toType, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, 0) and
toTypeArgument = getTypeArgumentRanked(ugt, toType, 0) and
ugt.getNumberOfTypeParameters() = 1
}
/**
* Holds if the type arguments of types `fromType` and `toType` are identity
* convertible, and the number of type arguments is 1.
*/
predicate convIdentitySingle(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
convIdentitySingle0(ugt, toType, fromTypeArgument, toTypeArgument) |
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, 0)
)
}
pragma[nomagic]
private predicate convIdentityMultiple01Aux0(UnboundGenericType ugt, IdentityConvertibleConstructedType toType, TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument0, toTypeArgument0, 0) and
toTypeArgument0 = getTypeArgumentRanked(ugt, toType, 0) and
toTypeArgument1 = getTypeArgumentRanked(ugt, toType, 1)
}
pragma[nomagic]
private predicate convIdentityMultiple01Aux1(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument1, toTypeArgument1, 1) and
fromTypeArgument0 = getTypeArgumentRanked(ugt, fromType, 0) and
fromTypeArgument1 = getTypeArgumentRanked(ugt, fromType, 1)
}
/**
* Holds if the first two ranked type arguments of types `fromType` and `toType`
* are identity convertible.
*/
private predicate convIdentityMultiple01(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
exists(Type fromTypeArgument0, Type toTypeArgument0, Type fromTypeArgument1, Type toTypeArgument1 |
convIdentityMultiple01Aux0(ugt, toType, fromTypeArgument0, toTypeArgument0, toTypeArgument1) |
convIdentityMultiple01Aux1(ugt, fromType, fromTypeArgument0, fromTypeArgument1, toTypeArgument1)
)
}
pragma[nomagic]
private predicate convIdentityMultiple2Aux(UnboundGenericType ugt, IdentityConvertibleConstructedType toType, int i, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
toTypeArgument = getTypeArgumentRanked(ugt, toType, i) and
i >= 2
}
private predicate convIdentityMultiple2(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType, int i) {
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
convIdentityMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument) |
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, i)
)
}
/**
* Holds if the ranked type arguments 0 through `i` (with `i >= 1`) of types
* `fromType` and `toType` are identity convertible.
*/
pragma[nomagic]
predicate convIdentityMultiple(UnboundGenericType ugt, IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType, int i) {
convIdentityMultiple01(ugt, fromType, toType) and i = 1
or
convIdentityMultiple(ugt, fromType, toType, i - 1) and
convIdentityMultiple2(ugt, fromType, toType, i)
}
private predicate convIdentityStrictConstructedType(IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType) {
/* Semantically equivalent with
* ```
* ugt = fromType.getUnboundGeneric()
* and
* forex(int i |
* i in [0 .. ugt.getNumberOfTypeParameters() - 1] |
* exists(Type t1, Type t2 |
* t1 = getTypeArgument(ugt, fromType, i, _) and
* t2 = getTypeArgument(ugt, toType, i, _) |
* convIdentity(t1, t2)
* )
* )
* ```
* but performance is improved by explicitly evaluating the `i`th argument
* only when all preceding arguments are convertible.
*/
fromType != toType and
(
convIdentitySingle(_, fromType, toType)
or
exists(UnboundGenericType ugt |
convIdentityMultiple(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
)
)
}
}
@@ -363,9 +465,8 @@ private predicate convRefTypeNonNull(Type fromType, Type toType) {
* This is a deliberate, small cartesian product, so we have manually lifted it to force the
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
*/
pragma [noinline]
private
predicate defaultDynamicConversion(Type fromType, Type toType) {
pragma[noinline]
private predicate defaultDynamicConversion(Type fromType, Type toType) {
fromType instanceof RefType and toType instanceof DynamicType
}
@@ -373,9 +474,8 @@ predicate defaultDynamicConversion(Type fromType, Type toType) {
* This is a deliberate, small cartesian product, so we have manually lifted it to force the
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
*/
pragma [noinline]
private
predicate defaultDelegateConversion(RefType fromType, RefType toType) {
pragma[noinline]
private predicate defaultDelegateConversion(RefType fromType, RefType toType) {
fromType instanceof DelegateType and toType = any(SystemDelegateClass c).getABaseType*()
}
@@ -398,9 +498,8 @@ private predicate convRefTypeRefType(RefType fromType, RefType toType) {
* This is a deliberate, small cartesian product, so we have manually lifted it to force the
* evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
*/
pragma [noinline]
private
predicate defaultArrayConversion(Type fromType, RefType toType) {
pragma[noinline]
private predicate defaultArrayConversion(Type fromType, RefType toType) {
fromType instanceof ArrayType and toType = any(SystemArrayClass c).getABaseType*()
}
@@ -427,7 +526,7 @@ private predicate convArrayTypeCovariance(ArrayType fromType, ArrayType toType)
convArrayTypeCovarianceJoin(fromType, toType, toType.getDimension(), toType.getRank())
}
pragma [noinline]
pragma[noinline]
private predicate convArrayTypeCovarianceJoin(ArrayType fromType, ArrayType toType, int dim, int rnk) {
convArrayElementType(fromType, toType.getElementType(), dim, rnk)
}
@@ -616,7 +715,7 @@ predicate convConversionOperator(Type fromType, Type toType) {
}
/** 13.1.3.2: Variance conversion. */
private predicate convVariance(VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType) {
private predicate convVariance(ConstructedType fromType, ConstructedType toType) {
/* Semantically equivalent with
* ```
* ugt = fromType.getUnboundGeneric()
@@ -637,95 +736,204 @@ private predicate convVariance(VarianceConvertibleConstructedType fromType, Vari
* but performance is improved by explicitly evaluating the `i`th argument
* only when all preceding arguments are convertible.
*/
exists(UnboundGenericType ugt |
convVarianceFromZero(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
)
}
/**
* Holds if the type arguments 0 through `i` of types `fromType` and `toType`
* are variance convertible.
*/
private predicate convVarianceFromZero(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType, int i) {
exists(Type toTypeArgument |
convVarianceFromZeroAux(ugt, fromType, i, toTypeArgument) |
toTypeArgument = getTypeArgument(ugt, toType, i, _) and
i = 0 and
fromType != toType
)
Variance::convVarianceSingle(_, fromType, toType)
or
exists(Type toTypeArgument |
convVarianceFromZero(ugt, fromType, toType, i - 1) |
toTypeArgument = getTypeArgument(ugt, toType, i, _) and
convVarianceFromZeroAux(ugt, fromType, i, toTypeArgument)
exists(UnboundGenericType ugt |
Variance::convVarianceMultiple(ugt, fromType, toType, ugt.getNumberOfTypeParameters() - 1)
)
}
pragma [nomagic]
private predicate convVarianceFromZeroAux(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, int i, Type toTypeArgument) {
exists(Type fromTypeArgument, TypeParameter tp |
fromTypeArgument = getTypeArgument(ugt, fromType, i, tp) |
convIdentity(fromTypeArgument, toTypeArgument)
private module Variance {
/**
* Holds if constructed type `ct` is potentially variance convertible to
* or from another constructed type, as a result of the `i`th type
* argument being potentially convertible.
*/
private predicate isVarianceConvertible(ConstructedType ct, int i) {
exists(TypeParameter tp, Type t |
tp = ct.getUnboundGeneric().getTypeParameter(i) and
t = ct.getTypeArgument(i) |
(
// Anything that is not a type parameter is potentially convertible
// to/from another type; if the `i`th type parameter is invariant,
// `t` must be strictly identity convertible
not t instanceof TypeParameter
and
(tp.isIn() or tp.isOut() or Identity::convIdentityStrict(t, _))
)
or
exists(TypeParameter s |
s = t |
// A type parameter with implicit reference conversion
exists(convTypeParameterBase(s)) and s.isRefType() and tp.isOut()
or
// A type parameter convertible from another type parameter
exists(TypeParameter u | s = convTypeParameterBase(u) and u.isRefType() and tp.isIn())
)
)
}
private class VarianceConvertibleConstructedType extends ConstructedType {
VarianceConvertibleConstructedType() {
isVarianceConvertible(this, _)
}
}
/**
* Gets the number of different type arguments supplied for the type
* parameter at index `i` in unbound generic type `ugt`.
*/
private int getTypeArgumentCount(UnboundGenericType ugt, int i) {
result = strictcount(Type arg |
exists(VarianceConvertibleConstructedType ct |
ct.getUnboundGeneric() = ugt |
arg = ct.getTypeArgument(i)
)
)
}
private int rnk(UnboundGenericType ugt, int i) {
result = rank[i + 1](int j, int k |
j = getTypeArgumentCount(ugt, k) |
k order by j, k
)
}
private Type getTypeArgumentRanked(UnboundGenericType ugt, VarianceConvertibleConstructedType t, int i, TypeParameter tp) {
result = getTypeArgument(ugt, t, rnk(ugt, i), tp)
}
pragma[noinline]
private Type getATypeArgumentRankedOut(int i) {
result = getTypeArgumentRanked(_, _, i, any(TypeParameter tp | tp.isOut()))
}
pragma[noinline]
private Type getATypeArgumentRankedIn(int i) {
result = getTypeArgumentRanked(_, _, i, any(TypeParameter tp | tp.isIn()))
}
private predicate convRefTypeTypeArgumentOut(TypeArgument fromType, TypeArgument toType, int i) {
convRefTypeNonNull(fromType, toType) and
toType = getATypeArgumentRankedOut(i)
}
private predicate convRefTypeTypeArgumentIn(TypeArgument toType, TypeArgument fromType, int i) {
convRefTypeNonNull(toType, fromType) and
toType = getATypeArgumentRankedIn(i)
}
private newtype TVariance = TNone() or TIn() or TOut()
/**
* Holds if `fromTypeArgument` is convertible to `toTypeArgument`, with variance
* `v`, and both types are the `i`th type argument in _some_ constructed type.
*/
pragma[nomagic]
private predicate convTypeArguments(TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i, TVariance v) {
exists(int j |
fromTypeArgument = getTypeArgumentRanked(_, _, i, _) and
toTypeArgument = getTypeArgumentRanked(_, _, j, _) and
i <= j and j <= i
|
convIdentity(fromTypeArgument, toTypeArgument) and
v = TNone()
or
convRefTypeTypeArgumentOut(fromTypeArgument, toTypeArgument, j) and
v = TOut()
or
convRefTypeTypeArgumentIn(toTypeArgument, fromTypeArgument, j) and
v = TIn()
)
}
pragma[nomagic]
private predicate convTypeArgumentsSomeUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
exists(TypeParameter tp, TVariance v |
convTypeArguments(fromTypeArgument, toTypeArgument, i, v) |
fromTypeArgument = getTypeArgumentRanked(ugt, _, i, tp) and
(v = TIn() implies tp.isIn()) and
(v = TOut() implies tp.isOut())
)
}
/**
* Holds if `fromTypeArgument` is convertible to `toTypeArgument` and
* both types are the `i`th type argument in _some_ constructed type
* based on unbound generic type `ugt`.
*/
pragma[noinline]
private predicate convTypeArgumentsSameUnbound(UnboundGenericType ugt, TypeArgument fromTypeArgument, TypeArgument toTypeArgument, int i) {
convTypeArgumentsSomeUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
toTypeArgument = getTypeArgumentRanked(ugt, _, i, _)
}
pragma[nomagic]
private predicate convVarianceSingle0(UnboundGenericType ugt, VarianceConvertibleConstructedType toType, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, 0) and
toTypeArgument = getTypeArgumentRanked(ugt, toType, 0, _) and
ugt.getNumberOfTypeParameters() = 1
}
/**
* Holds if the type arguments of types `fromType` and `toType` are variance
* convertible, and the number of type arguments is 1.
*/
predicate convVarianceSingle(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType) {
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
convVarianceSingle0(ugt, toType, fromTypeArgument, toTypeArgument) |
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, 0, _)
)
}
pragma[nomagic]
private predicate convVarianceMultiple01Aux0(UnboundGenericType ugt, VarianceConvertibleConstructedType toType, TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument0, toTypeArgument0, 0) and
toTypeArgument0 = getTypeArgumentRanked(ugt, toType, 0, _) and
toTypeArgument1 = getTypeArgumentRanked(ugt, toType, 1, _)
}
pragma[nomagic]
private predicate convVarianceMultiple01Aux1(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument1, toTypeArgument1, 1) and
fromTypeArgument0 = getTypeArgumentRanked(ugt, fromType, 0, _) and
fromTypeArgument1 = getTypeArgumentRanked(ugt, fromType, 1, _)
}
/**
* Holds if the first two ranked type arguments of types `fromType` and `toType`
* are variance convertible.
*/
private predicate convVarianceMultiple01(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType) {
exists(TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1 |
convVarianceMultiple01Aux0(ugt, toType, fromTypeArgument0, toTypeArgument0, toTypeArgument1) |
convVarianceMultiple01Aux1(ugt, fromType, fromTypeArgument0, fromTypeArgument1, toTypeArgument1)
)
}
pragma[nomagic]
private predicate convVarianceMultiple2Aux(UnboundGenericType ugt, VarianceConvertibleConstructedType toType, int i, TypeArgument fromTypeArgument, TypeArgument toTypeArgument) {
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
toTypeArgument = getTypeArgumentRanked(ugt, toType, i, _) and
i >= 2
}
private predicate convVarianceMultiple2(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType, int i) {
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
convVarianceMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument) |
fromTypeArgument = getTypeArgumentRanked(ugt, fromType, i, _)
)
}
/**
* Holds if the ranked type arguments 0 through `i` (with `i >= 1`) of types
* `fromType` and `toType` are variance convertible.
*/
pragma[nomagic]
predicate convVarianceMultiple(UnboundGenericType ugt, VarianceConvertibleConstructedType fromType, VarianceConvertibleConstructedType toType, int i) {
convVarianceMultiple01(ugt, fromType, toType) and i = 1
or
convRefTypeTypeArgumentOut(fromTypeArgument, toTypeArgument, i) and
tp.isOut()
or
convRefTypeTypeArgumentIn(toTypeArgument, fromTypeArgument, i) and
tp.isIn()
)
}
pragma [nomagic]
private predicate convRefTypeTypeArgumentOut(TypeArgument fromType, TypeArgument toType, int i) {
exists(TypeParameter tp |
convRefTypeNonNull(fromType, toType) |
toType = getTypeArgument(_, _, i, tp) and
tp.isOut()
)
}
pragma [nomagic]
private predicate convRefTypeTypeArgumentIn(TypeArgument toType, TypeArgument fromType, int i) {
exists(TypeParameter tp |
convRefTypeNonNull(toType, fromType) |
toType = getTypeArgument(_, _, i, tp) and
tp.isIn()
)
}
private class VarianceConvertibleConstructedType extends ConstructedType {
VarianceConvertibleConstructedType() {
isVarianceConvertible(this, _)
convVarianceMultiple(ugt, fromType, toType, i - 1) and
convVarianceMultiple2(ugt, fromType, toType, i)
}
}
/**
* Holds if constructed type `ct` is potentially variance convertible to
* or from another constructed type, as a result of the `i`th type
* argument being potentially convertible.
*/
private predicate isVarianceConvertible(ConstructedType ct, int i) {
exists(TypeParameter tp, Type t |
tp = ct.getUnboundGeneric().getTypeParameter(i)
and
t = ct.getTypeArgument(i)
|
(
// Anything that is not a type parameter is potentially convertible
// to/from another type; if the `i`th type parameter is invariant,
// `t` must be strictly identity convertible
not t instanceof TypeParameter
and
(tp.isIn() or tp.isOut() or convIdentityStrict(t, _))
)
or
exists(TypeParameter s |
s = t |
// A type parameter with implicit reference conversion
exists(convTypeParameterBase(s)) and s.isRefType() and tp.isOut()
or
// A type parameter convertible from another type parameter
exists(TypeParameter u | s = convTypeParameterBase(u) and u.isRefType() and tp.isIn())
)
)
}