Skip to content

Commit 86721ff

Browse files
committed
C#: Add more documentation to SuccSplits module
1 parent 50522ca commit 86721ff

File tree

3 files changed

+173
-89
lines changed

3 files changed

+173
-89
lines changed

csharp/ql/src/semmle/code/csharp/controlflow/ControlFlowGraph.qll

Lines changed: 137 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3749,74 +3749,98 @@ module ControlFlow {
37493749

37503750
/**
37513751
* Provides a predicate for the successor relation with split information,
3752-
* `succSplits()`, as well as logic used to construct the type `TSplits`
3753-
* representing sets of splits. Only sets of splits that can be reached
3754-
* are constructed, hence the predicates are mutually recursive.
3752+
* as well as logic used to construct the type `TSplits` representing sets
3753+
* of splits. Only sets of splits that can be reached are constructed, hence
3754+
* the predicates are mutually recursive.
37553755
*
3756-
* For the successor relation, we divide into two cases:
3756+
* For the successor relation
37573757
*
3758-
* 1. The set of splits for the successor is the same as the set of splits
3759-
* for the predecessor.
3760-
* 2. The set of splits for the successor is different from the set of splits
3761-
* for the predecessor.
3758+
* ```
3759+
* succSplits(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c)
3760+
* ```
37623761
*
3763-
* These are further divided into:
3762+
* the following invariants are maintained:
37643763
*
3765-
* 1a. The set of splits is the same because the successor is in the same
3766-
* `SameSplitsBlock` block as the predecessor.
3767-
* 1b. The set of splits is the same, but the successor is not in the same
3768-
* `SameSplitsBlock` block as the predecessor.
3769-
* 2a. The set of splits for the successor is *maybe* non-empty.
3770-
* 2b. The set of splits for the successor is *always* empty.
3764+
* 1. `pred` is reachable with split set `predSplits`.
3765+
* 2. For all `split` in `predSplits`:
3766+
* - If `split.hasSuccessor(pred, succ, c)` then `split` in `succSplits`.
3767+
* 3. For all `split` in `predSplits`:
3768+
* - If `split.hasExit(pred, succ, c)` then `split` not in `succSplits`.
3769+
* 4. For all `split` not in `predSplits`:
3770+
* - If `split.hasEntry(pred, succ, c)` then `split` in `succSplits`.
3771+
* 5. For all `split` in `succSplits`:
3772+
* - `split.hasSuccessor(pred, succ, c)` and `split` in `predSplits`, or
3773+
* - `split.hasEntry(pred, succ, c)` and `split` not in `predSplits`.
3774+
*
3775+
* The algorithm divides into four cases:
3776+
*
3777+
* 1. The set of splits for the successor is the same as the set of splits
3778+
* for the predecessor:
3779+
* a) The successor is in the same `SameSplitsBlock` as the predecessor.
3780+
* b) The successor is *not* in the same `SameSplitsBlock` as the predecessor.
3781+
* 2. The set of splits for the successor is different from the set of splits
3782+
* for the predecessor:
3783+
* a) The set of splits for the successor is *maybe* non-empty.
3784+
* b) The set of splits for the successor is *always* empty.
37713785
*
37723786
* Only case 2a may introduce new sets of splits, so only predicates from
3773-
* this case are used in the definition of `TSplits`.
3787+
* this case are used in the definition of `TSplits`.
37743788
*
37753789
* The predicates in this module are named after the cases above.
37763790
*/
37773791
private module SuccSplits {
3778-
private predicate succReachable(Reachability::SameSplitsBlock b, ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
3792+
private predicate succInvariant1(Reachability::SameSplitsBlock b, ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
37793793
pred = b.getAnElement() and
37803794
b.isReachable(predSplits) and
37813795
succ = succ(pred, c)
37823796
}
37833797

3784-
private predicate succSplits1b0(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
3798+
private predicate case1b0(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
37853799
exists(Reachability::SameSplitsBlock b |
3786-
succReachable(b, pred, predSplits, succ, c) |
3800+
// Invariant 1
3801+
succInvariant1(b, pred, predSplits, succ, c) |
37873802
(succ = b.getAnElement() implies succ = b) and
3803+
// Invariant 4
37883804
not exists(SplitInternal split | split.hasEntry(pred, succ, c))
37893805
)
37903806
}
37913807

3792-
// A `forall` over the splits in `predSplits`, but written using explicit recursion
3793-
// to avoid negative recursion compilation error.
3794-
private predicate succSplits1bForall(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits remaining) {
3795-
succSplits1b0(pred, predSplits, succ, c) and
3796-
remaining = predSplits
3808+
/**
3809+
* Case 1b.
3810+
*
3811+
* Invariants 1 and 4 hold in the base case, and invariants 2, 3, and 5 are
3812+
* maintained for all splits in `predSplits` (= `succSplits`), except
3813+
* possibly for the splits in `except`.
3814+
*
3815+
* The predicate is written using explicit recursion, as opposed to a `forall`,
3816+
* to avoid negative recursion.
3817+
*/
3818+
private predicate case1bForall(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits except) {
3819+
case1b0(pred, predSplits, succ, c) and
3820+
except = predSplits
37973821
or
37983822
exists(Splits mid, SplitInternal split |
3799-
succSplits1bForall(pred, predSplits, succ, c, mid) |
3800-
mid = TSplitsCons(split, remaining) and
3823+
case1bForall(pred, predSplits, succ, c, mid) |
3824+
mid = TSplitsCons(split, except) and
38013825
split.hasSuccessor(pred, succ, c)
38023826
)
38033827
}
38043828

3805-
private predicate succSplits1(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
3829+
private predicate case1(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
38063830
// Case 1a
38073831
exists(Reachability::SameSplitsBlock b |
3808-
succReachable(b, pred, predSplits, succ, c) |
3832+
succInvariant1(b, pred, predSplits, succ, c) |
38093833
succ = b.getAnElement() and
38103834
not succ = b
38113835
)
38123836
or
38133837
// Case 1b
3814-
succSplits1bForall(pred, predSplits, succ, c, TSplitsNil())
3838+
case1bForall(pred, predSplits, succ, c, TSplitsNil())
38153839
}
38163840

3817-
private predicate succSplits2aux(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
3841+
private predicate case2aux(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
38183842
exists(Reachability::SameSplitsBlock b |
3819-
succReachable(b, pred, predSplits, succ, c) and
3843+
succInvariant1(b, pred, predSplits, succ, c) and
38203844
(succ = b.getAnElement() implies succ = b)
38213845
|
38223846
predSplits.getASplit().hasExit(pred, succ, c)
@@ -3825,114 +3849,141 @@ module ControlFlow {
38253849
)
38263850
}
38273851

3828-
// A `forall` over the splits in `predSplits`, but written using explicit recursion
3829-
// to avoid negative recursion compilation error.
3830-
private predicate succSplits2aNoneOfKindForall(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk, Splits remaining) {
3831-
succSplits2aux(pred, predSplits, succ, c) and
3852+
/**
3853+
* Holds if `succSplits` should not inherit a split of kind `sk` from
3854+
* `predSplits, except possibly because of a split in `except`.
3855+
*
3856+
* The predicate is written using explicit recursion, as opposed to a `forall`,
3857+
* to avoid negative recursion.
3858+
*/
3859+
private predicate case2aNoneInheritedOfKindForall(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk, Splits except) {
3860+
case2aux(pred, predSplits, succ, c) and
38323861
sk.appliesTo(succ) and
3833-
remaining = predSplits
3862+
except = predSplits
38343863
or
38353864
exists(Splits mid, SplitInternal split |
3836-
succSplits2aNoneOfKindForall(pred, predSplits, succ, c, sk, mid) and
3837-
mid = TSplitsCons(split, remaining)
3865+
case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, mid) and
3866+
mid = TSplitsCons(split, except)
38383867
|
3839-
split.getKind() = any(SplitKind sk0 |
3840-
sk0 != sk and
3841-
(sk0.appliesTo(succ) or split.hasExit(pred, succ, c))
3842-
)
3868+
split.getKind() = any(SplitKind sk0 | sk0 != sk and sk0.appliesTo(succ))
38433869
or
38443870
split.hasExit(pred, succ, c)
38453871
)
38463872
}
38473873

3874+
/** Holds if `succSplits` should not have a split of kind `sk`. */
38483875
pragma[noinline]
3849-
private predicate succSplits2aNoneOfKind(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk) {
3850-
succSplits2aNoneOfKindForall(pred, predSplits, succ, c, sk, TSplitsNil()) and
3876+
private predicate case2aNoneOfKind(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk) {
3877+
// None inherited from predecessor
3878+
case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, TSplitsNil()) and
3879+
// None newly entered into
38513880
not exists(SplitInternal split | split.hasEntry(pred, succ, c) | split.getKind() = sk)
38523881
}
38533882

3883+
/** Holds if `succSplits` should not have a split of kind `sk` at rank `rnk`. */
38543884
pragma[noinline]
3855-
private predicate succSplits2aNoneAtRank(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk) {
3885+
private predicate case2aNoneAtRank(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk) {
38563886
exists(SplitKind sk |
3857-
succSplits2aNoneOfKind(pred, predSplits, succ, c, sk) |
3887+
case2aNoneOfKind(pred, predSplits, succ, c, sk) |
38583888
rnk = sk.getListRank(succ)
38593889
)
38603890
}
38613891

3892+
/** Gets a split that should be in `succSplits`. */
38623893
pragma[nomagic]
3863-
private SplitInternal succSplits2aSome(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
3864-
succSplits2aux(pred, predSplits, succ, c) and
3894+
private SplitInternal case2aSome(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c) {
3895+
case2aux(pred, predSplits, succ, c) and
38653896
(
38663897
result = predSplits.getASplit() and
38673898
result.hasSuccessor(pred, succ, c)
38683899
or
38693900
result.hasEntry(pred, succ, c) and
3870-
succSplits2aNoneOfKindForall(pred, predSplits, succ, c, result.getKind(), TSplitsNil())
3901+
case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, result.getKind(), TSplitsNil())
38713902
)
38723903
}
38733904

3874-
SplitInternal succSplits2aSomeAtRank(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk) {
3875-
result = succSplits2aSome(pred, predSplits, succ, c) and
3905+
/** Gets a split that should be in `succSplits` at rank `rnk`. */
3906+
SplitInternal case2aSomeAtRank(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk) {
3907+
result = case2aSome(pred, predSplits, succ, c) and
38763908
rnk = result.getKind().getListRank(succ)
38773909
}
38783910

3879-
predicate succSplits2a(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c, int rnk) {
3880-
succSplits2aux(pred, predSplits, succ, c) and
3911+
/**
3912+
* Case 2a.
3913+
*
3914+
* As opposed to the other cases, in this case we need to construct a new set
3915+
* of splits `succSplits`. Since this involves constructing the very IPA type,
3916+
* we cannot recurse directly over the structure of `succSplits`. Instead, we
3917+
* recurse over the ranks of all splits that *might* be in `succSplits`.
3918+
*
3919+
* - Invariant 1 holds in the base case,
3920+
* - invariant 2 holds for all splits with rank at least `rnk`,
3921+
* - invariant 3 holds for all splits in `predSplits`,
3922+
* - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`,
3923+
* and
3924+
* - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`.
3925+
*/
3926+
predicate case2aFromRank(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c, int rnk) {
3927+
case2aux(pred, predSplits, succ, c) and
38813928
succSplits = TSplitsNil() and
38823929
rnk = max(any(SplitKind sk).getListRank(succ)) + 1
38833930
or
3884-
succSplits2a(pred, predSplits, succ, succSplits, c, rnk + 1) and
3885-
succSplits2aNoneAtRank(pred, predSplits, succ, c, rnk)
3931+
case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and
3932+
case2aNoneAtRank(pred, predSplits, succ, c, rnk)
38863933
or
38873934
exists(Splits mid, SplitInternal split |
3888-
split = succSplits2aCons(pred, predSplits, succ, mid, c, rnk) |
3935+
split = case2aCons(pred, predSplits, succ, mid, c, rnk) |
38893936
succSplits = TSplitsCons(split, mid)
38903937
)
38913938
}
38923939

38933940
pragma[noinline]
3894-
private SplitInternal succSplits2aCons(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c, int rnk) {
3895-
succSplits2a(pred, predSplits, succ, succSplits, c, rnk + 1) and
3896-
result = succSplits2aSomeAtRank(pred, predSplits, succ, c, rnk)
3941+
private SplitInternal case2aCons(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c, int rnk) {
3942+
case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and
3943+
result = case2aSomeAtRank(pred, predSplits, succ, c, rnk)
38973944
}
38983945

3899-
// A `forall` over the splits in `predSplits`, but written using explicit recursion
3900-
// to avoid negative recursion compilation error.
3901-
private predicate succSplits2b(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits remaining) {
3902-
succSplits2aux(pred, predSplits, succ, c) and
3946+
/**
3947+
* Case 2b.
3948+
*
3949+
* Invariants 1, 4, and 5 hold in the base case, and invariants 2 and 3 are
3950+
* maintained for all splits in `predSplits`, except possibly for the splits
3951+
* in `except`.
3952+
*
3953+
* The predicate is written using explicit recursion, as opposed to a `forall`,
3954+
* to avoid negative recursion.
3955+
*/
3956+
private predicate case2bForall(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits except) {
3957+
// Invariant 1
3958+
case2aux(pred, predSplits, succ, c) and
3959+
// Invariants 4 and 5
39033960
not any(SplitKind sk).appliesTo(succ) and
3904-
remaining = predSplits
3961+
except = predSplits
39053962
or
39063963
exists(Splits mid, SplitInternal split |
3907-
succSplits2b(pred, predSplits, succ, c, mid) |
3908-
mid = TSplitsCons(split, remaining) and
3964+
case2bForall(pred, predSplits, succ, c, mid) |
3965+
mid = TSplitsCons(split, except) and
3966+
// Invariants 2 and 3
39093967
split.hasExit(pred, succ, c)
39103968
)
39113969
}
39123970

3913-
private predicate succSplits2(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c) {
3914-
// Case 2a
3915-
succSplits2a(pred, predSplits, succ, succSplits, c, 1)
3971+
private predicate case2(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c) {
3972+
case2aFromRank(pred, predSplits, succ, succSplits, c, 1)
39163973
or
3917-
// Case 2b
3918-
succSplits2b(pred, predSplits, succ, c, TSplitsNil()) and
3974+
case2bForall(pred, predSplits, succ, c, TSplitsNil()) and
39193975
succSplits = TSplitsNil()
39203976
}
39213977

39223978
/**
39233979
* Holds if `succ` with splits `succSplits` is a successor of type `t` for `pred`
39243980
* with splits `predSplits`.
39253981
*/
3926-
predicate succSplits(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, SuccessorType t) {
3927-
exists(Completion c |
3928-
t.matchesCompletion(c) |
3929-
// Case 1
3930-
succSplits1(pred, predSplits, succ, c) and
3931-
succSplits = predSplits
3932-
or
3933-
// Case 2
3934-
succSplits2(pred, predSplits, succ, succSplits, c)
3935-
)
3982+
predicate succSplits(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c) {
3983+
case1(pred, predSplits, succ, c) and
3984+
succSplits = predSplits
3985+
or
3986+
case2(pred, predSplits, succ, succSplits, c)
39363987
}
39373988
}
39383989
import SuccSplits
@@ -4069,8 +4120,8 @@ module ControlFlow {
40694120
or
40704121
TSplitsCons(SplitInternal head, Splits tail) {
40714122
exists(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk |
4072-
succSplits2a(pred, predSplits, succ, tail, c, rnk + 1) |
4073-
head = succSplits2aSomeAtRank(pred, predSplits, succ, c, rnk)
4123+
case2aFromRank(pred, predSplits, succ, tail, c, rnk + 1) |
4124+
head = case2aSomeAtRank(pred, predSplits, succ, c, rnk)
40744125
)
40754126
}
40764127

@@ -4152,9 +4203,10 @@ module ControlFlow {
41524203
succExitSplits(predElement, predSplits, result.(Nodes::ExitNode).getCallable(), t)
41534204
or
41544205
// Element node -> element node
4155-
exists(ControlFlowElement succElement, Splits succSplits |
4206+
exists(ControlFlowElement succElement, Splits succSplits, Completion c |
41564207
result = TElementNode(succElement, succSplits) |
4157-
succSplits(predElement, predSplits, succElement, succSplits, t)
4208+
succSplits(predElement, predSplits, succElement, succSplits, c) and
4209+
t.matchesCompletion(c)
41584210
)
41594211
)
41604212
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
nonUniqueSetRepresentation
2+
breakInvariant2
3+
breakInvariant3
4+
breakInvariant4
5+
breakInvariant5
Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,38 @@
11
import csharp
2+
import semmle.code.csharp.controlflow.Completion
23
import ControlFlow
34
import ControlFlow::Internal
45

5-
// Should not have any output; no set of splits should have two representations
6-
from Splits s1, Splits s2
7-
where
6+
query predicate nonUniqueSetRepresentation(Splits s1, Splits s2) {
87
forex(Nodes::Split s | s = s1.getASplit() | s = s2.getASplit()) and
98
forex(Nodes::Split s | s = s2.getASplit() | s = s1.getASplit()) and
109
s1 != s2
11-
select s1, s2
10+
}
11+
12+
query predicate breakInvariant2(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, SplitInternal split, Completion c) {
13+
succSplits(pred, predSplits, succ, succSplits, c) and
14+
split = predSplits.getASplit() and
15+
split.hasSuccessor(pred, succ, c) and
16+
not split = succSplits.getASplit()
17+
}
18+
19+
query predicate breakInvariant3(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, SplitInternal split, Completion c) {
20+
succSplits(pred, predSplits, succ, succSplits, c) and
21+
split = predSplits.getASplit() and
22+
split.hasExit(pred, succ, c) and
23+
split = succSplits.getASplit()
24+
}
25+
26+
query predicate breakInvariant4(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, SplitInternal split, Completion c) {
27+
succSplits(pred, predSplits, succ, succSplits, c) and
28+
split.hasEntry(pred, succ, c) and
29+
not split = predSplits.getASplit() and
30+
not split = succSplits.getASplit()
31+
}
32+
33+
query predicate breakInvariant5(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, SplitInternal split, Completion c) {
34+
succSplits(pred, predSplits, succ, succSplits, c) and
35+
split = succSplits.getASplit() and
36+
not (split.hasSuccessor(pred, succ, c) and split = predSplits.getASplit()) and
37+
not (split.hasEntry(pred, succ, c) and not split = predSplits.getASplit())
38+
}

0 commit comments

Comments
 (0)