mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
C#: Improve performance of type conversion library
This commit is contained in:
@@ -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())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user