Skip to content

Commit eb893fb

Browse files
authored
Merge pull request github#2024 from hvitved/csharp/conversion-unbound
C#: Handle unbound types in conversion library
2 parents 39f550b + 7f18f35 commit eb893fb

File tree

5 files changed

+101
-88
lines changed

5 files changed

+101
-88
lines changed

csharp/ql/src/semmle/code/csharp/Conversion.qll

Lines changed: 68 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,28 @@ private predicate implicitConversionNonNull(Type fromType, Type toType) {
5454
fromType instanceof DynamicType // 6.1.8
5555
}
5656

57-
private Type getTypeArgument(UnboundGenericType ugt, ConstructedType ct, int i, TypeParameter tp) {
58-
ct.getUnboundGeneric() = ugt and
57+
/**
58+
* A generic type. This includes both constructed generic types and unbound
59+
* generic types (which correspond to constructed generic types where the
60+
* type arguments equal the type parameters).
61+
*/
62+
private class GenericType extends Generic, Type {
63+
/** Gets the `i`th type argument. */
64+
Type getTypeArgument(int i) { result = this.getChild(i) }
65+
66+
/** Gets the unbound generic type. */
67+
UnboundGenericType getUnboundGeneric() {
68+
result = this.(ConstructedType).getUnboundGeneric()
69+
or
70+
result = this
71+
}
72+
}
73+
74+
private Type getTypeArgument(UnboundGenericType ugt, GenericType gt, int i, TypeParameter tp) {
75+
gt.getUnboundGeneric() = ugt and
5976
not ugt instanceof AnonymousClass and
6077
tp = ugt.getTypeParameter(i) and
61-
result = ct.getTypeArgument(i)
78+
result = gt.getTypeArgument(i)
6279
}
6380

6481
/** A type that is an element type of an array type. */
@@ -68,7 +85,7 @@ private class ArrayElementType extends Type {
6885

6986
/** A type that is an argument in a constructed type. */
7087
private class TypeArgument extends Type {
71-
TypeArgument() { this = any(ConstructedType ct).getATypeArgument() }
88+
TypeArgument() { this = any(GenericType gt).getTypeArgument(_) }
7289
}
7390

7491
/**
@@ -95,8 +112,7 @@ private module Identity {
95112

96113
private class IdentityConvertibleArrayType extends IdentityConvertibleType, ArrayType { }
97114

98-
private class IdentityConvertibleConstructedType extends IdentityConvertibleType, ConstructedType {
99-
}
115+
private class IdentityConvertibleGenericType extends IdentityConvertibleType, GenericType { }
100116

101117
/**
102118
* A type is (strictly) identity convertible if it contains at least one `object`
@@ -109,7 +125,7 @@ private module Identity {
109125
or
110126
isIdentityConvertible(t.(ArrayType).getElementType())
111127
or
112-
isIdentityConvertible(t.(ConstructedType).getATypeArgument())
128+
isIdentityConvertible(t.(GenericType).getTypeArgument(_))
113129
}
114130

115131
predicate convIdentityStrict(IdentityConvertibleType fromType, IdentityConvertibleType toType) {
@@ -119,7 +135,7 @@ private module Identity {
119135
or
120136
convIdentityStrictArrayType(fromType, toType)
121137
or
122-
convIdentityStrictConstructedType(fromType, toType)
138+
convIdentityStrictGenericType(fromType, toType)
123139
}
124140

125141
private predicate convIdentityObjectDynamic(ObjectType fromType, DynamicType toType) { any() }
@@ -151,7 +167,7 @@ private module Identity {
151167
*/
152168
private int getTypeArgumentCount(UnboundGenericType ugt, int i) {
153169
result = strictcount(Type arg |
154-
exists(IdentityConvertibleConstructedType ct | ct.getUnboundGeneric() = ugt |
170+
exists(IdentityConvertibleGenericType ct | ct.getUnboundGeneric() = ugt |
155171
arg = ct.getTypeArgument(i)
156172
)
157173
)
@@ -162,9 +178,7 @@ private module Identity {
162178
}
163179

164180
/** Gets the 'i'th type argument, ranked by size, of constructed type `t`. */
165-
private Type getTypeArgumentRanked(
166-
UnboundGenericType ugt, IdentityConvertibleConstructedType t, int i
167-
) {
181+
private Type getTypeArgumentRanked(UnboundGenericType ugt, IdentityConvertibleGenericType t, int i) {
168182
result = getTypeArgument(ugt, t, rnk(ugt, i), _)
169183
}
170184

@@ -207,8 +221,8 @@ private module Identity {
207221

208222
pragma[nomagic]
209223
private predicate convIdentitySingle0(
210-
UnboundGenericType ugt, IdentityConvertibleConstructedType toType,
211-
TypeArgument fromTypeArgument, TypeArgument toTypeArgument
224+
UnboundGenericType ugt, IdentityConvertibleGenericType toType, TypeArgument fromTypeArgument,
225+
TypeArgument toTypeArgument
212226
) {
213227
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, 0) and
214228
toTypeArgument = getTypeArgumentRanked(ugt, toType, 0) and
@@ -220,8 +234,8 @@ private module Identity {
220234
* convertible, and the number of type arguments is 1.
221235
*/
222236
predicate convIdentitySingle(
223-
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
224-
IdentityConvertibleConstructedType toType
237+
UnboundGenericType ugt, IdentityConvertibleGenericType fromType,
238+
IdentityConvertibleGenericType toType
225239
) {
226240
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
227241
convIdentitySingle0(ugt, toType, fromTypeArgument, toTypeArgument)
@@ -232,8 +246,8 @@ private module Identity {
232246

233247
pragma[nomagic]
234248
private predicate convIdentityMultiple01Aux0(
235-
UnboundGenericType ugt, IdentityConvertibleConstructedType toType,
236-
TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1
249+
UnboundGenericType ugt, IdentityConvertibleGenericType toType, TypeArgument fromTypeArgument0,
250+
TypeArgument toTypeArgument0, TypeArgument toTypeArgument1
237251
) {
238252
convTypeArgumentsSameUnbound(ugt, fromTypeArgument0, toTypeArgument0, 0) and
239253
toTypeArgument0 = getTypeArgumentRanked(ugt, toType, 0) and
@@ -242,8 +256,8 @@ private module Identity {
242256

243257
pragma[nomagic]
244258
private predicate convIdentityMultiple01Aux1(
245-
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
246-
TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1
259+
UnboundGenericType ugt, IdentityConvertibleGenericType fromType, TypeArgument fromTypeArgument0,
260+
TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1
247261
) {
248262
convTypeArgumentsSameUnbound(ugt, fromTypeArgument1, toTypeArgument1, 1) and
249263
fromTypeArgument0 = getTypeArgumentRanked(ugt, fromType, 0) and
@@ -255,8 +269,8 @@ private module Identity {
255269
* are identity convertible.
256270
*/
257271
private predicate convIdentityMultiple01(
258-
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
259-
IdentityConvertibleConstructedType toType
272+
UnboundGenericType ugt, IdentityConvertibleGenericType fromType,
273+
IdentityConvertibleGenericType toType
260274
) {
261275
exists(
262276
Type fromTypeArgument0, Type toTypeArgument0, Type fromTypeArgument1, Type toTypeArgument1
@@ -270,7 +284,7 @@ private module Identity {
270284

271285
pragma[nomagic]
272286
private predicate convIdentityMultiple2Aux(
273-
UnboundGenericType ugt, IdentityConvertibleConstructedType toType, int i,
287+
UnboundGenericType ugt, IdentityConvertibleGenericType toType, int i,
274288
TypeArgument fromTypeArgument, TypeArgument toTypeArgument
275289
) {
276290
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
@@ -279,8 +293,8 @@ private module Identity {
279293
}
280294

281295
private predicate convIdentityMultiple2(
282-
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
283-
IdentityConvertibleConstructedType toType, int i
296+
UnboundGenericType ugt, IdentityConvertibleGenericType fromType,
297+
IdentityConvertibleGenericType toType, int i
284298
) {
285299
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
286300
convIdentityMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument)
@@ -295,17 +309,17 @@ private module Identity {
295309
*/
296310
pragma[nomagic]
297311
predicate convIdentityMultiple(
298-
UnboundGenericType ugt, IdentityConvertibleConstructedType fromType,
299-
IdentityConvertibleConstructedType toType, int i
312+
UnboundGenericType ugt, IdentityConvertibleGenericType fromType,
313+
IdentityConvertibleGenericType toType, int i
300314
) {
301315
convIdentityMultiple01(ugt, fromType, toType) and i = 1
302316
or
303317
convIdentityMultiple(ugt, fromType, toType, i - 1) and
304318
convIdentityMultiple2(ugt, fromType, toType, i)
305319
}
306320

307-
private predicate convIdentityStrictConstructedType(
308-
IdentityConvertibleConstructedType fromType, IdentityConvertibleConstructedType toType
321+
private predicate convIdentityStrictGenericType(
322+
IdentityConvertibleGenericType fromType, IdentityConvertibleGenericType toType
309323
) {
310324
// Semantically equivalent with
311325
// ```
@@ -730,7 +744,7 @@ predicate convConversionOperator(Type fromType, Type toType) {
730744
}
731745

732746
/** 13.1.3.2: Variance conversion. */
733-
private predicate convVariance(ConstructedType fromType, ConstructedType toType) {
747+
private predicate convVariance(GenericType fromType, GenericType toType) {
734748
// Semantically equivalent with
735749
// ```
736750
// ugt = fromType.getUnboundGeneric()
@@ -758,34 +772,14 @@ private predicate convVariance(ConstructedType fromType, ConstructedType toType)
758772
}
759773

760774
private module Variance {
761-
/**
762-
* Holds if constructed type `ct` is potentially variance convertible to
763-
* or from another constructed type, as a result of the `i`th type
764-
* argument being potentially convertible.
765-
*/
766-
private predicate isVarianceConvertible(ConstructedType ct, int i) {
767-
exists(TypeParameter tp, Type t |
768-
tp = ct.getUnboundGeneric().getTypeParameter(i) and
769-
t = ct.getTypeArgument(i)
770-
|
771-
// Anything that is not a type parameter is potentially convertible
772-
// to/from another type; if the `i`th type parameter is invariant,
773-
// `t` must be strictly identity convertible
774-
not t instanceof TypeParameter and
775-
(tp.isIn() or tp.isOut() or Identity::convIdentityStrict(t, _))
776-
or
777-
exists(TypeParameter s | s = t |
778-
// A type parameter with implicit reference conversion
779-
exists(convTypeParameterBase(s)) and s.isRefType() and tp.isOut()
775+
private class VarianceConvertibleGenericType extends GenericType {
776+
VarianceConvertibleGenericType() {
777+
exists(TypeParameter tp | tp = this.getUnboundGeneric().getATypeParameter() |
778+
tp.isIn()
780779
or
781-
// A type parameter convertible from another type parameter
782-
exists(TypeParameter u | s = convTypeParameterBase(u) and u.isRefType() and tp.isIn())
780+
tp.isOut()
783781
)
784-
)
785-
}
786-
787-
private class VarianceConvertibleConstructedType extends ConstructedType {
788-
VarianceConvertibleConstructedType() { isVarianceConvertible(this, _) }
782+
}
789783
}
790784

791785
/**
@@ -794,8 +788,8 @@ private module Variance {
794788
*/
795789
private int getTypeArgumentCount(UnboundGenericType ugt, int i) {
796790
result = strictcount(Type arg |
797-
exists(VarianceConvertibleConstructedType ct | ct.getUnboundGeneric() = ugt |
798-
arg = ct.getTypeArgument(i)
791+
exists(VarianceConvertibleGenericType gt | gt.getUnboundGeneric() = ugt |
792+
arg = gt.getTypeArgument(i)
799793
)
800794
)
801795
}
@@ -806,7 +800,7 @@ private module Variance {
806800

807801
/** Gets the 'i'th type argument, ranked by size, of constructed type `t`. */
808802
private Type getTypeArgumentRanked(
809-
UnboundGenericType ugt, VarianceConvertibleConstructedType t, int i, TypeParameter tp
803+
UnboundGenericType ugt, VarianceConvertibleGenericType t, int i, TypeParameter tp
810804
) {
811805
result = getTypeArgument(ugt, t, rnk(ugt, i), tp)
812806
}
@@ -889,8 +883,8 @@ private module Variance {
889883

890884
pragma[nomagic]
891885
private predicate convVarianceSingle0(
892-
UnboundGenericType ugt, VarianceConvertibleConstructedType toType,
893-
TypeArgument fromTypeArgument, TypeArgument toTypeArgument
886+
UnboundGenericType ugt, VarianceConvertibleGenericType toType, TypeArgument fromTypeArgument,
887+
TypeArgument toTypeArgument
894888
) {
895889
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, 0) and
896890
toTypeArgument = getTypeArgumentRanked(ugt, toType, 0, _) and
@@ -902,8 +896,8 @@ private module Variance {
902896
* convertible, and the number of type arguments is 1.
903897
*/
904898
predicate convVarianceSingle(
905-
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
906-
VarianceConvertibleConstructedType toType
899+
UnboundGenericType ugt, VarianceConvertibleGenericType fromType,
900+
VarianceConvertibleGenericType toType
907901
) {
908902
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
909903
convVarianceSingle0(ugt, toType, fromTypeArgument, toTypeArgument)
@@ -914,8 +908,8 @@ private module Variance {
914908

915909
pragma[nomagic]
916910
private predicate convVarianceMultiple01Aux0(
917-
UnboundGenericType ugt, VarianceConvertibleConstructedType toType,
918-
TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument toTypeArgument1
911+
UnboundGenericType ugt, VarianceConvertibleGenericType toType, TypeArgument fromTypeArgument0,
912+
TypeArgument toTypeArgument0, TypeArgument toTypeArgument1
919913
) {
920914
convTypeArgumentsSameUnbound(ugt, fromTypeArgument0, toTypeArgument0, 0) and
921915
toTypeArgument0 = getTypeArgumentRanked(ugt, toType, 0, _) and
@@ -924,8 +918,8 @@ private module Variance {
924918

925919
pragma[nomagic]
926920
private predicate convVarianceMultiple01Aux1(
927-
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
928-
TypeArgument fromTypeArgument0, TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1
921+
UnboundGenericType ugt, VarianceConvertibleGenericType fromType, TypeArgument fromTypeArgument0,
922+
TypeArgument fromTypeArgument1, TypeArgument toTypeArgument1
929923
) {
930924
convTypeArgumentsSameUnbound(ugt, fromTypeArgument1, toTypeArgument1, 1) and
931925
fromTypeArgument0 = getTypeArgumentRanked(ugt, fromType, 0, _) and
@@ -937,8 +931,8 @@ private module Variance {
937931
* are variance convertible.
938932
*/
939933
private predicate convVarianceMultiple01(
940-
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
941-
VarianceConvertibleConstructedType toType
934+
UnboundGenericType ugt, VarianceConvertibleGenericType fromType,
935+
VarianceConvertibleGenericType toType
942936
) {
943937
exists(
944938
TypeArgument fromTypeArgument0, TypeArgument toTypeArgument0, TypeArgument fromTypeArgument1,
@@ -953,7 +947,7 @@ private module Variance {
953947

954948
pragma[nomagic]
955949
private predicate convVarianceMultiple2Aux(
956-
UnboundGenericType ugt, VarianceConvertibleConstructedType toType, int i,
950+
UnboundGenericType ugt, VarianceConvertibleGenericType toType, int i,
957951
TypeArgument fromTypeArgument, TypeArgument toTypeArgument
958952
) {
959953
convTypeArgumentsSameUnbound(ugt, fromTypeArgument, toTypeArgument, i) and
@@ -962,8 +956,8 @@ private module Variance {
962956
}
963957

964958
private predicate convVarianceMultiple2(
965-
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
966-
VarianceConvertibleConstructedType toType, int i
959+
UnboundGenericType ugt, VarianceConvertibleGenericType fromType,
960+
VarianceConvertibleGenericType toType, int i
967961
) {
968962
exists(TypeArgument fromTypeArgument, TypeArgument toTypeArgument |
969963
convVarianceMultiple2Aux(ugt, toType, i, fromTypeArgument, toTypeArgument)
@@ -978,8 +972,8 @@ private module Variance {
978972
*/
979973
pragma[nomagic]
980974
predicate convVarianceMultiple(
981-
UnboundGenericType ugt, VarianceConvertibleConstructedType fromType,
982-
VarianceConvertibleConstructedType toType, int i
975+
UnboundGenericType ugt, VarianceConvertibleGenericType fromType,
976+
VarianceConvertibleGenericType toType, int i
983977
) {
984978
convVarianceMultiple01(ugt, fromType, toType) and i = 1
985979
or
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
1-
| comments2.cs:118:5:118:21 | // ... | comments2.cs:119:11:119:25 | GenericClass<> | NestedType |
2-
| comments2.cs:118:5:118:21 | // ... | comments2.cs:119:11:119:25 | GenericClass<> | UnboundGenericClass |
3-
| comments2.cs:124:5:124:16 | // ... | comments2.cs:125:9:125:20 | GenericFn | CallableOrCFE |
4-
| comments2.cs:124:5:124:16 | // ... | comments2.cs:125:9:125:20 | GenericFn | InstanceCallable |
5-
| comments2.cs:124:5:124:16 | // ... | comments2.cs:125:9:125:20 | GenericFn | UnboundGenericMethod |
1+
| comments2.cs:118:5:118:21 | // ... | comments2.cs:119:11:119:25 | GenericClass<> |
2+
| comments2.cs:124:5:124:16 | // ... | comments2.cs:125:9:125:20 | GenericFn |
Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
import csharp
22

3-
from CommentBlock b, Element e, string s
3+
from CommentBlock b, Element e
44
where
55
b.getElement() = e and
66
(
77
e instanceof ConstructedMethod or
88
e instanceof ConstructedClass or
99
e instanceof UnboundGenericClass or
1010
e instanceof UnboundGenericMethod
11-
) and
12-
s = e.getAQlClass() and
13-
not s = "SourceDeclarationType" and
14-
not s = "SourceDeclarationCallable" and
15-
not s = "SourceDeclarationMethod" and
16-
not s = "NonConstructedMethod" and
17-
not s = "RuntimeInstanceMethod"
18-
select b, e, s
11+
)
12+
select b, e

csharp/ql/test/library-tests/conversion/reftype/RefType.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
interface I1<in T> { }
55
interface I2<out T> { }
66
interface I3<in T1, out T2> : I1<T1>, I2<T2> { }
7+
interface I4<in T3, T4> where T3 : C1 { I4<T3, T4> M(I4<C1, T4> x); }
78

89
class C1 { }
910

@@ -86,4 +87,9 @@ void M<T3, T4, T5, T6, T7>(T3 p1, T4 p2, T5 p3, T6 p4, T7 p5)
8687
}
8788
}
8889

90+
class C3<T5, T6> where T5 : C1
91+
{
92+
public I4<T5, T6> M(I4<C1, T6> x) => x;
93+
}
94+
8995
// semmle-extractor-options: /r:System.Dynamic.Runtime.dll /r:System.Linq.Expressions.dll

0 commit comments

Comments
 (0)