Skip to content

Commit 3b775a9

Browse files
authored
Merge pull request #2095 from hvitved/csharp/type-unification
C#: Type unification library
2 parents f34025c + 05684b3 commit 3b775a9

File tree

14 files changed

+1378
-369
lines changed

14 files changed

+1378
-369
lines changed

change-notes/1.23/analysis-csharp.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,6 @@ The following changes in version 1.23 affect C# analysis in all applications.
4747
`TaintTracking::localExprTaint` predicate to make it easy to use the most
4848
common case of local data flow and taint: from one `Expr` to another.
4949
* Data is now tracked through null-coalescing expressions (`??`).
50+
* A new library `semmle.code.csharp.Unification` has been added. This library exposes two predicates `unifiable` and `subsumes` for calculating type unification and type subsumption, respectively.
5051

5152
## Changes to autobuilder

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,21 @@ module Stages {
7979
forceCachingInSameStageRev()
8080
}
8181
}
82+
83+
cached
84+
module UnificationStage {
85+
private import semmle.code.csharp.Unification
86+
87+
cached
88+
predicate forceCachingInSameStage() { any() }
89+
90+
cached
91+
private predicate forceCachingInSameStageRev() {
92+
exists(CompoundTypeKind k)
93+
or
94+
exists(Unification::UnconstrainedTypeParameter utp)
95+
or
96+
forceCachingInSameStageRev()
97+
}
98+
}
8299
}

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

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,48 @@ import Type
1515
private import semmle.code.csharp.frameworks.System
1616
private import semmle.code.csharp.frameworks.system.collections.Generic
1717

18-
/**
19-
* INTERNAL: Do not use.
20-
*
21-
* Holds if there exists an implicit conversion from `fromType` to `toType`.
22-
*
23-
* 6.1: Implicit type conversions.
24-
*
25-
* The following conversions are classified as implicit conversions:
26-
*
27-
* - Identity conversions
28-
* - Implicit numeric conversions
29-
* - Implicit nullable conversions
30-
* - Implicit reference conversions
31-
* - Boxing conversions
32-
* - User-defined implicit conversions
33-
*/
3418
cached
35-
predicate implicitConversion(Type fromType, Type toType) {
36-
implicitConversionNonNull(fromType, toType)
37-
or
38-
defaultNullConversion(fromType, toType)
19+
private module Cached {
20+
/**
21+
* INTERNAL: Do not use.
22+
*
23+
* Holds if there exists an implicit conversion from `fromType` to `toType`.
24+
*
25+
* 6.1: Implicit type conversions.
26+
*
27+
* The following conversions are classified as implicit conversions:
28+
*
29+
* - Identity conversions
30+
* - Implicit numeric conversions
31+
* - Implicit nullable conversions
32+
* - Implicit reference conversions
33+
* - Boxing conversions
34+
* - User-defined implicit conversions
35+
*/
36+
cached
37+
predicate implicitConversion(Type fromType, Type toType) {
38+
implicitConversionNonNull(fromType, toType)
39+
or
40+
defaultNullConversion(fromType, toType)
41+
}
42+
43+
/**
44+
* INTERNAL: Do not use.
45+
*
46+
* Holds if there is a constant expression conversion from `fromType` to `toType`.
47+
*
48+
* 6.1.9: Implicit constant expression conversions.
49+
*/
50+
cached
51+
predicate convConstantExpr(SignedIntegralConstantExpr e, SimpleType toType) {
52+
convConstantIntExpr(e, toType)
53+
or
54+
convConstantLongExpr(e) and toType instanceof ULongType
55+
}
3956
}
4057

58+
import Cached
59+
4160
private predicate implicitConversionNonNull(Type fromType, Type toType) {
4261
convIdentity(fromType, toType)
4362
or
@@ -477,7 +496,7 @@ predicate convNullableType(ValueOrRefType fromType, NullableType toType) {
477496
// This is a deliberate, small Cartesian product, so we have manually lifted it to force the
478497
// evaluator to evaluate it in its entirety, rather than trying to optimize it in context.
479498
pragma[noinline]
480-
private predicate defaultNullConversion(Type fromType, Type toType) {
499+
predicate defaultNullConversion(Type fromType, Type toType) {
481500
fromType instanceof NullType and convNullType(toType)
482501
}
483502

@@ -612,19 +631,6 @@ private predicate convBoxingValueType(ValueType fromType, Type toType) {
612631
toType = fromType.getABaseInterface+()
613632
}
614633

615-
/**
616-
* INTERNAL: Do not use.
617-
*
618-
* Holds if there is a constant expression conversion from `fromType` to `toType`.
619-
*
620-
* 6.1.9: Implicit constant expression conversions.
621-
*/
622-
predicate convConstantExpr(SignedIntegralConstantExpr e, SimpleType toType) {
623-
convConstantIntExpr(e, toType)
624-
or
625-
convConstantLongExpr(e) and toType instanceof ULongType
626-
}
627-
628634
private class SignedIntegralConstantExpr extends Expr {
629635
SignedIntegralConstantExpr() {
630636
this.getType() instanceof SignedIntegralType and

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

Lines changed: 43 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -236,63 +236,13 @@ private Type getArgumentOrReturnType(Method m, int i) {
236236
}
237237

238238
/**
239-
* INTERNAL: Do not use.
240-
*
241239
* Provides an implementation of Global Value Numbering for types
242240
* (see https://en.wikipedia.org/wiki/Global_value_numbering), where
243241
* types are considered equal modulo identity conversions and method
244242
* type parameters (at the same index).
245243
*/
246-
module Gvn {
247-
private newtype TCompoundTypeKind =
248-
TPointerTypeKind() or
249-
TNullableTypeKind() or
250-
TArrayTypeKind(int dim, int rnk) {
251-
exists(ArrayType at | dim = at.getDimension() and rnk = at.getRank())
252-
} or
253-
TConstructedType(UnboundGenericType ugt)
254-
255-
/** A type kind for a compound type. */
256-
class CompoundTypeKind extends TCompoundTypeKind {
257-
int getNumberOfTypeParameters() {
258-
this = TPointerTypeKind() and result = 1
259-
or
260-
this = TNullableTypeKind() and result = 1
261-
or
262-
this = TArrayTypeKind(_, _) and result = 1
263-
or
264-
exists(UnboundGenericType ugt | this = TConstructedType(ugt) |
265-
result = ugt.getNumberOfTypeParameters()
266-
)
267-
}
268-
269-
string toString() {
270-
this = TPointerTypeKind() and result = "*"
271-
or
272-
this = TNullableTypeKind() and result = "?"
273-
or
274-
exists(int dim, int rnk | this = TArrayTypeKind(dim, rnk) |
275-
result = "[" + dim + ", " + rnk + "]"
276-
)
277-
or
278-
exists(UnboundGenericType ugt | this = TConstructedType(ugt) |
279-
result = ugt.getNameWithoutBrackets()
280-
)
281-
}
282-
283-
Location getLocation() { result instanceof EmptyLocation }
284-
}
285-
286-
/** Gets the type kind for type `t`, if any. */
287-
CompoundTypeKind getTypeKind(Type t) {
288-
result = TPointerTypeKind() and t instanceof PointerType
289-
or
290-
result = TNullableTypeKind() and t instanceof NullableType
291-
or
292-
t = any(ArrayType at | result = TArrayTypeKind(at.getDimension(), at.getRank()))
293-
or
294-
result = TConstructedType(t.(ConstructedType).getUnboundGeneric())
295-
}
244+
private module Gvn {
245+
private import semmle.code.csharp.Unification
296246

297247
private class MethodTypeParameter extends TypeParameter {
298248
MethodTypeParameter() { this = any(UnboundGenericMethod ugm).getATypeParameter() }
@@ -310,38 +260,47 @@ module Gvn {
310260
private newtype TGvnType =
311261
TLeafGvnType(int i) { id(_, i) } or
312262
TMethodTypeParameterGvnType(int i) { i = any(MethodTypeParameter p).getIndex() } or
313-
TConstructedGvnType(TConstructedGvnType0 t)
263+
TConstructedGvnType(ConstructedGvnTypeList l)
314264

315-
private newtype TConstructedGvnType0 =
265+
private newtype TConstructedGvnTypeList =
316266
TConstructedGvnTypeNil(CompoundTypeKind k) or
317-
TConstructedGvnTypeCons(TGvnType head, TConstructedGvnType0 tail) {
318-
gvnConstructedCons(_, _, head, tail)
267+
TConstructedGvnTypeCons(GvnType head, ConstructedGvnTypeList tail) {
268+
gvnConstructedCons(_, _, _, head, tail)
319269
}
320270

321-
private TConstructedGvnType0 gvnConstructed(Type t, int i) {
322-
result = TConstructedGvnTypeNil(getTypeKind(t)) and i = -1
271+
private ConstructedGvnTypeList gvnConstructed(Type t, CompoundTypeKind k, int i) {
272+
result = TConstructedGvnTypeNil(k) and
273+
i = -1 and
274+
k = getTypeKind(t)
323275
or
324-
exists(TGvnType head, TConstructedGvnType0 tail | gvnConstructedCons(t, i, head, tail) |
276+
exists(GvnType head, ConstructedGvnTypeList tail | gvnConstructedCons(t, k, i, head, tail) |
325277
result = TConstructedGvnTypeCons(head, tail)
326278
)
327279
}
328280

329281
pragma[noinline]
330-
private TGvnType gvnTypeChild(Type t, int i) { result = getGlobalValueNumber(t.getChild(i)) }
282+
private GvnType gvnTypeChild(Type t, int i) { result = getGlobalValueNumber(t.getChild(i)) }
331283

332284
pragma[noinline]
333-
private predicate gvnConstructedCons(Type t, int i, TGvnType head, TConstructedGvnType0 tail) {
334-
tail = gvnConstructed(t, i - 1) and
285+
private predicate gvnConstructedCons(
286+
Type t, CompoundTypeKind k, int i, GvnType head, ConstructedGvnTypeList tail
287+
) {
288+
tail = gvnConstructed(t, k, i - 1) and
335289
head = gvnTypeChild(t, i)
336290
}
337291

338292
/** Gets the global value number for a given type. */
293+
pragma[nomagic]
339294
GvnType getGlobalValueNumber(Type t) {
340295
result = TLeafGvnType(any(int i | id(t, i)))
341296
or
342297
result = TMethodTypeParameterGvnType(t.(MethodTypeParameter).getIndex())
343298
or
344-
result = TConstructedGvnType(gvnConstructed(t, getTypeKind(t).getNumberOfTypeParameters() - 1))
299+
exists(ConstructedGvnTypeList l, CompoundTypeKind k, int i |
300+
l = gvnConstructed(t, k, i) and
301+
i = k.getNumberOfTypeParameters() - 1 and
302+
result = TConstructedGvnType(l)
303+
)
345304
}
346305

347306
/** A global value number for a type. */
@@ -351,40 +310,42 @@ module Gvn {
351310
or
352311
exists(int i | this = TMethodTypeParameterGvnType(i) | result = "M!" + i)
353312
or
354-
exists(GvnConstructedType t | this = TConstructedGvnType(t) | result = t.toString())
313+
exists(ConstructedGvnTypeList l | this = TConstructedGvnType(l) | result = l.toString())
355314
}
356315

357316
Location getLocation() { result instanceof EmptyLocation }
358317
}
359318

360-
/** A global value number for a constructed type. */
361-
class GvnConstructedType extends TConstructedGvnType0 {
362-
private CompoundTypeKind getKind() {
363-
this = TConstructedGvnTypeNil(result)
319+
private class ConstructedGvnTypeList extends TConstructedGvnTypeList {
320+
private int length() {
321+
this = TConstructedGvnTypeNil(_) and result = -1
364322
or
365-
exists(GvnConstructedType tail | this = TConstructedGvnTypeCons(_, tail) |
366-
result = tail.getKind()
323+
exists(ConstructedGvnTypeList tail | this = TConstructedGvnTypeCons(_, tail) |
324+
result = tail.length() + 1
367325
)
368326
}
369327

370328
private GvnType getArg(int i) {
371-
this = TConstructedGvnTypeCons(result, TConstructedGvnTypeNil(_)) and
372-
i = 0
373-
or
374-
exists(GvnConstructedType tail | this = TConstructedGvnTypeCons(result, tail) |
375-
exists(tail.getArg(i - 1))
329+
exists(GvnType head, ConstructedGvnTypeList tail |
330+
this = TConstructedGvnTypeCons(head, tail)
331+
|
332+
result = head and
333+
i = this.length()
334+
or
335+
result = tail.getArg(i)
376336
)
377337
}
378338

379339
language[monotonicAggregates]
380340
string toString() {
381-
exists(CompoundTypeKind k | k = this.getKind() |
382-
result = k + "<" +
383-
concat(int i |
384-
i in [0 .. k.getNumberOfTypeParameters() - 1]
385-
|
386-
this.getArg(i).toString(), ", "
387-
) + ">"
341+
exists(CompoundTypeKind k, string args |
342+
this = gvnConstructed(_, k, _) and
343+
args = concat(int i |
344+
i in [0 .. k.getNumberOfTypeParameters() - 1]
345+
|
346+
this.getArg(i).toString(), ", " order by i
347+
) and
348+
result = k.toString(args)
388349
)
389350
}
390351

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,8 @@ class TupleType extends ValueType, @tuple_type {
941941
}
942942

943943
override string getLabel() { result = getUnderlyingType().getLabel() }
944+
945+
override Type getChild(int i) { result = this.getUnderlyingType().getChild(i) }
944946
}
945947

946948
/**

0 commit comments

Comments
 (0)