-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rust: More CFG modelling #17633
Rust: More CFG modelling #17633
Changes from all commits
5444a5b
f3e3734
8c1fd8f
a507854
17770af
bbd0aa9
26c69b8
0710768
8595776
e8cb349
8b66dc1
c4eafb2
8f0b7f0
69e0ad0
9c7216f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,14 +84,18 @@ class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion { | |
private predicate isValidForSpecific0(AstNode e) { | ||
e = any(IfExpr c).getCondition() | ||
or | ||
any(MatchArm arm).getGuard() = e | ||
e = any(WhileExpr c).getCondition() | ||
or | ||
any(MatchGuard guard).getCondition() = e | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess we should drop /hide |
||
or | ||
exists(BinaryExpr expr | | ||
expr.getOperatorName() = ["&&", "||"] and | ||
e = expr.getLhs() | ||
) | ||
or | ||
exists(Expr parent | this.isValidForSpecific0(parent) | | ||
e = parent.(ParenExpr).getExpr() | ||
aibaars marked this conversation as resolved.
Show resolved
Hide resolved
|
||
or | ||
parent = | ||
any(PrefixExpr expr | | ||
expr.getOperatorName() = "!" and | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,6 +61,10 @@ import CfgImpl | |
/** Holds if `p` is a trivial pattern that is always guaranteed to match. */ | ||
predicate trivialPat(Pat p) { p instanceof WildcardPat or p instanceof IdentPat } | ||
|
||
class ArrayExprTree extends StandardPostOrderTree, ArrayExpr { | ||
override AstNode getChildNode(int i) { result = this.getExpr(i) } | ||
} | ||
|
||
class AsmExprTree extends LeafTree instanceof AsmExpr { } | ||
|
||
class AwaitExprTree extends StandardPostOrderTree instanceof AwaitExpr { | ||
|
@@ -132,14 +136,40 @@ class LogicalAndBinaryOpExprTree extends PreOrderTree, LogicalAndExpr { | |
} | ||
} | ||
|
||
class BlockExprBaseTree extends StandardPostOrderTree instanceof BlockExpr { | ||
class BlockExprTree extends StandardPostOrderTree, BlockExpr { | ||
override AstNode getChildNode(int i) { | ||
result = super.getStmtList().getStatement(i) | ||
or | ||
not exists(super.getStmtList().getStatement(i)) and | ||
(exists(super.getStmtList().getStatement(i - 1)) or i = 0) and | ||
result = super.getStmtList().getTailExpr() | ||
} | ||
|
||
override predicate propagatesAbnormal(AstNode child) { none() } | ||
|
||
/** Holds if this block captures the break completion `c`. */ | ||
private predicate capturesBreakCompletion(LoopJumpCompletion c) { | ||
c.isBreak() and | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you also handle shadowing of labels? It's a silly thing to do, and the rust compiler will warn about it, but shadowed labels are allowed. For example: 'a: loop {
'a: loop {
'a: {
break 'a;
}
break 'a;
}
break 'a;
} You might want to add a test case, if you don't already have one; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, wait, I we don't have one that shadows; I'll add. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have added one now, and the CFG is correct: flowchart TD
1["enter test_loop_label_shadowing"]
10["b"]
11["ContinueExpr"]
12["ExprStmt"]
13["ContinueExpr"]
14["ExprStmt"]
2["1"]
3["ExprStmt"]
4["ExprStmt"]
5["IfExpr"]
6["b"]
7["ContinueExpr"]
8["ExprStmt"]
9["IfExpr"]
1 --> 3
2 --> 4
3 --> 2
4 --> 6
5 --> 14
6 -- false --> 10
6 -- true --> 8
7 -- continue --> 4
8 --> 7
9 --> 5
10 -- false --> 9
10 -- true --> 12
11 -- continue('loop) --> 4
12 --> 11
13 -- continue('loop) --> 4
14 --> 13
|
||
c.getLabelName() = this.getLabel().getLifetime().getText() | ||
} | ||
|
||
override predicate succ(AstNode pred, AstNode succ, Completion c) { | ||
super.succ(pred, succ, c) | ||
or | ||
// Edge for exiting the block with a break expressions | ||
last(this.getChildNode(_), pred, c) and | ||
this.capturesBreakCompletion(c) and | ||
succ = this | ||
} | ||
|
||
override predicate last(AstNode last, Completion c) { | ||
super.last(last, c) | ||
or | ||
// Any abnormal completions that this block does not capture should propagate | ||
last(this.getChildNode(_), last, c) and | ||
not completionIsNormal(c) and | ||
not this.capturesBreakCompletion(c) | ||
} | ||
} | ||
|
||
class BreakExprTree extends PostOrderTree instanceof BreakExpr { | ||
|
@@ -189,7 +219,7 @@ class IfExprTree extends PostOrderTree instanceof IfExpr { | |
child = [super.getCondition(), super.getThen(), super.getElse()] | ||
} | ||
|
||
ConditionalCompletion conditionCompletion(Completion c) { | ||
private ConditionalCompletion conditionCompletion(Completion c) { | ||
if super.getCondition() instanceof LetExpr | ||
then result = c.(MatchCompletion) | ||
else result = c.(BooleanCompletion) | ||
|
@@ -226,6 +256,8 @@ class IndexExprTree extends StandardPostOrderTree instanceof IndexExpr { | |
} | ||
} | ||
|
||
class ItemTree extends LeafTree, Item { } | ||
|
||
// `LetExpr` is a pre-order tree such that the pattern itself ends up | ||
// dominating successors in the graph in the same way that patterns do in | ||
// `match` expressions. | ||
|
@@ -277,45 +309,124 @@ class LetStmtTree extends PreOrderTree instanceof LetStmt { | |
|
||
class LiteralExprTree extends LeafTree instanceof LiteralExpr { } | ||
|
||
class LoopExprTree extends PostOrderTree instanceof LoopExpr { | ||
abstract class LoopingExprTree extends PostOrderTree { | ||
override predicate propagatesAbnormal(AstNode child) { none() } | ||
|
||
override predicate first(AstNode node) { first(super.getLoopBody(), node) } | ||
abstract BlockExpr getLoopBody(); | ||
|
||
abstract Label getLabel(); | ||
|
||
/** | ||
* Gets the node to execute when continuing the loop; either after | ||
* executing the last node in the body or after an explicit `continue`. | ||
*/ | ||
abstract AstNode getLoopContinue(); | ||
|
||
/** Whether this `LoopExpr` captures the `c` completion. */ | ||
/** Holds if this loop captures the `c` completion. */ | ||
private predicate capturesLoopJumpCompletion(LoopJumpCompletion c) { | ||
not c.hasLabel() | ||
or | ||
c.getLabelName() = super.getLabel().getLifetime().getText() | ||
c.getLabelName() = this.getLabel().getLifetime().getText() | ||
} | ||
|
||
override predicate succ(AstNode pred, AstNode succ, Completion c) { | ||
// Edge for exiting the loop with a break expressions | ||
last(this.getLoopBody(), pred, c) and | ||
c.(LoopJumpCompletion).isBreak() and | ||
this.capturesLoopJumpCompletion(c) and | ||
succ = this | ||
or | ||
// Edge back to the start for final expression and continue expressions | ||
last(super.getLoopBody(), pred, c) and | ||
last(this.getLoopBody(), pred, c) and | ||
( | ||
completionIsNormal(c) | ||
or | ||
c.(LoopJumpCompletion).isContinue() and this.capturesLoopJumpCompletion(c) | ||
) and | ||
this.first(succ) | ||
or | ||
// Edge for exiting the loop with a break expressions | ||
last(super.getLoopBody(), pred, c) and | ||
c.(LoopJumpCompletion).isBreak() and | ||
this.capturesLoopJumpCompletion(c) and | ||
succ = this | ||
first(this.getLoopContinue(), succ) | ||
} | ||
|
||
override predicate last(AstNode last, Completion c) { | ||
super.last(last, c) | ||
or | ||
// Any abnormal completions that this loop does not capture should propagate | ||
last(super.getLoopBody(), last, c) and | ||
last(this.getLoopBody(), last, c) and | ||
not completionIsNormal(c) and | ||
not this.capturesLoopJumpCompletion(c) | ||
} | ||
} | ||
|
||
class LoopExprTree extends LoopingExprTree instanceof LoopExpr { | ||
override BlockExpr getLoopBody() { result = LoopExpr.super.getLoopBody() } | ||
|
||
override Label getLabel() { result = LoopExpr.super.getLabel() } | ||
|
||
override AstNode getLoopContinue() { result = this.getLoopBody() } | ||
|
||
override predicate first(AstNode node) { first(this.getLoopBody(), node) } | ||
} | ||
|
||
class WhileExprTree extends LoopingExprTree instanceof WhileExpr { | ||
override BlockExpr getLoopBody() { result = WhileExpr.super.getLoopBody() } | ||
|
||
override Label getLabel() { result = WhileExpr.super.getLabel() } | ||
|
||
override AstNode getLoopContinue() { result = super.getCondition() } | ||
|
||
override predicate propagatesAbnormal(AstNode child) { child = super.getCondition() } | ||
|
||
override predicate first(AstNode node) { first(super.getCondition(), node) } | ||
|
||
private ConditionalCompletion conditionCompletion(Completion c) { | ||
if super.getCondition() instanceof LetExpr | ||
then result = c.(MatchCompletion) | ||
else result = c.(BooleanCompletion) | ||
} | ||
|
||
override predicate succ(AstNode pred, AstNode succ, Completion c) { | ||
super.succ(pred, succ, c) | ||
or | ||
last(super.getCondition(), pred, c) and | ||
this.conditionCompletion(c).succeeded() and | ||
first(this.getLoopBody(), succ) | ||
or | ||
last(super.getCondition(), pred, c) and | ||
this.conditionCompletion(c).failed() and | ||
succ = this | ||
} | ||
} | ||
|
||
class ForExprTree extends LoopingExprTree instanceof ForExpr { | ||
override BlockExpr getLoopBody() { result = ForExpr.super.getLoopBody() } | ||
|
||
override Label getLabel() { result = ForExpr.super.getLabel() } | ||
|
||
override AstNode getLoopContinue() { result = super.getPat() } | ||
|
||
override predicate propagatesAbnormal(AstNode child) { child = super.getIterable() } | ||
|
||
override predicate first(AstNode node) { first(super.getIterable(), node) } | ||
|
||
override predicate succ(AstNode pred, AstNode succ, Completion c) { | ||
super.succ(pred, succ, c) | ||
or | ||
last(super.getIterable(), pred, c) and | ||
first(super.getPat(), succ) and | ||
completionIsNormal(c) | ||
or | ||
last(super.getPat(), pred, c) and | ||
c.(MatchCompletion).succeeded() and | ||
first(this.getLoopBody(), succ) | ||
or | ||
last(super.getPat(), pred, c) and | ||
c.(MatchCompletion).failed() and | ||
succ = this | ||
Comment on lines
+421
to
+423
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks a little weird. The pattern in a for-loop can never fail, it is actually the implicit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be even better if we could desugar |
||
} | ||
} | ||
|
||
// TODO: replace with expanded macro once the extractor supports it | ||
class MacroExprTree extends LeafTree, MacroExpr { } | ||
|
||
class MatchArmTree extends ControlFlowTree instanceof MatchArm { | ||
override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() } | ||
|
||
|
@@ -376,10 +487,24 @@ class MethodCallExprTree extends StandardPostOrderTree instanceof MethodCallExpr | |
} | ||
} | ||
|
||
class NameTree extends LeafTree, Name { } | ||
|
||
class NameRefTree extends LeafTree, NameRef { } | ||
|
||
class OffsetOfExprTree extends LeafTree instanceof OffsetOfExpr { } | ||
|
||
class ParenExprTree extends StandardPostOrderTree, ParenExpr { | ||
override AstNode getChildNode(int i) { i = 0 and result = super.getExpr() } | ||
class ParenExprTree extends ControlFlowTree, ParenExpr { | ||
private ControlFlowTree expr; | ||
|
||
ParenExprTree() { expr = super.getExpr() } | ||
|
||
override predicate propagatesAbnormal(AstNode child) { expr.propagatesAbnormal(child) } | ||
|
||
override predicate first(AstNode first) { expr.first(first) } | ||
|
||
override predicate last(AstNode last, Completion c) { expr.last(last, c) } | ||
|
||
override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } | ||
} | ||
|
||
// This covers all patterns as they all extend `Pat` | ||
|
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks fine for now, we don't really have a way to identify "panic"-like statements. A real panic wouldn't complete normally, it would abort the program. When we extract types, we may refine this to hold if the type of the expression is
!
.