Merge pull request #19501 from MathiasVP/as-expr-class-aggregate-literal

C++: Make `node.asExpr() instanceof ClassAggregateLiteral` satisfiable
This commit is contained in:
Mathias Vorreiter Pedersen
2025-05-16 13:49:03 +01:00
committed by GitHub
5 changed files with 71 additions and 8 deletions

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Fixed a problem where `asExpr()` on `DataFlow::Node` would never return `ClassAggregateLiteral`s.

View File

@@ -264,6 +264,41 @@ private module Cached {
e = getConvertedResultExpression(node.asInstruction(), n)
}
/**
* The IR doesn't have an instruction `i` for which this holds:
* ```
* i.getUnconvertedResultExpression() instanceof ClassAggregateLiteral
* ```
* and thus we don't automatically get a dataflow node for which:
* ```
* node.asExpr() instanceof ClassAggregateLiteral
* ```
* This is because the IR represents a `ClassAggregateLiteral` as a sequence
* of field writes. To work around this we map `asExpr` on the
* `PostUpdateNode` for the last field write to the class aggregate literal.
*/
private class ClassAggregateInitializerPostUpdateNode extends PostFieldUpdateNode {
ClassAggregateLiteral aggr;
ClassAggregateInitializerPostUpdateNode() {
exists(Node node1, FieldContent fc, int position, StoreInstruction store |
store.getSourceValue().getUnconvertedResultExpression() =
aggr.getFieldExpr(fc.getField(), position) and
node1.asInstruction() = store and
// This is the last field write from the aggregate initialization.
not exists(aggr.getFieldExpr(_, position + 1)) and
storeStep(node1, fc, this)
)
}
ClassAggregateLiteral getClassAggregateLiteral() { result = aggr }
}
private predicate exprNodeShouldBePostUpdateNode(Node node, Expr e, int n) {
node.(ClassAggregateInitializerPostUpdateNode).getClassAggregateLiteral() = e and
n = 0
}
/** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */
private predicate indirectExprNodeShouldBeIndirectInstruction(
IndirectInstruction node, Expr e, int n, int indirectionIndex
@@ -294,7 +329,8 @@ private module Cached {
exprNodeShouldBeInstruction(_, e, n) or
exprNodeShouldBeOperand(_, e, n) or
exprNodeShouldBeIndirectOutNode(_, e, n) or
exprNodeShouldBeIndirectOperand(_, e, n)
exprNodeShouldBeIndirectOperand(_, e, n) or
exprNodeShouldBePostUpdateNode(_, e, n)
}
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
@@ -442,6 +478,12 @@ private module Cached {
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOperand(this, result, n) }
}
private class PostUpdateExprNode extends ExprNodeBase instanceof PostUpdateNode {
PostUpdateExprNode() { exprNodeShouldBePostUpdateNode(this, _, _) }
final override Expr getConvertedExpr(int n) { exprNodeShouldBePostUpdateNode(this, result, n) }
}
/**
* An expression, viewed as a node in a data flow graph.
*/

View File

@@ -21,3 +21,20 @@ A& get_ref();
void test2() {
take_ref(get_ref()); // $ asExpr="call to get_ref" asIndirectExpr="call to get_ref"
}
struct S {
int a;
int b;
};
void test_aggregate_literal() {
S s1 = {1, 2}; // $ asExpr=1 asExpr=2 asExpr={...}
const S s2 = {3, 4}; // $ asExpr=3 asExpr=4 asExpr={...}
S s3 = (S){5, 6}; // $ asExpr=5 asExpr=6 asExpr={...}
const S s4 = (S){7, 8}; // $ asExpr=7 asExpr=8 asExpr={...}
S s5 = {.a = 1, .b = 2}; // $ asExpr=1 asExpr=2 asExpr={...}
int xs[] = {1, 2, 3}; // $ asExpr=1 asExpr=2 asExpr=3 MISSING: asExpr={...}
const int ys[] = {[0] = 4, [1] = 5, [0] = 6}; // $ asExpr=4 asExpr=5 asExpr=6 MISSING: asExpr={...}
}

View File

@@ -17,7 +17,6 @@
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
| example.c:17:11:17:16 | *definition of coords | example.c:24:13:24:18 | *coords |
| example.c:17:11:17:16 | *definition of coords [post update] | example.c:17:11:17:16 | *definition of coords |
| example.c:17:11:17:16 | *definition of coords [post update] | example.c:24:13:24:18 | *coords |
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | *definition of coords |
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | definition of coords |
@@ -27,6 +26,7 @@
| example.c:17:11:17:16 | definition of coords | example.c:24:13:24:18 | coords |
| example.c:17:11:17:16 | definition of coords [post update] | example.c:17:11:17:16 | definition of coords |
| example.c:17:11:17:16 | definition of coords [post update] | example.c:24:13:24:18 | coords |
| example.c:17:11:17:16 | {...} | example.c:17:11:17:16 | *definition of coords |
| example.c:17:19:17:22 | {...} | example.c:17:19:17:22 | {...} |
| example.c:17:21:17:21 | 0 | example.c:17:21:17:21 | 0 |
| example.c:19:6:19:6 | *b | example.c:15:37:15:37 | *b |

View File

@@ -863,12 +863,12 @@ edges
| struct_init.c:24:10:24:12 | absink output argument [a] | struct_init.c:28:5:28:7 | *& ... [a] | provenance | |
| struct_init.c:26:16:26:20 | *definition of outer [nestedAB, a] | struct_init.c:31:8:31:12 | *outer [nestedAB, a] | provenance | |
| struct_init.c:26:16:26:20 | *definition of outer [nestedAB, a] | struct_init.c:36:11:36:15 | *outer [nestedAB, a] | provenance | |
| struct_init.c:26:16:26:20 | *definition of outer [post update] [*pointerAB, a] | struct_init.c:33:8:33:12 | *outer [*pointerAB, a] | provenance | |
| struct_init.c:26:16:26:20 | *definition of outer [post update] [nestedAB, a] | struct_init.c:26:16:26:20 | *definition of outer [nestedAB, a] | provenance | |
| struct_init.c:26:16:26:20 | {...} [*pointerAB, a] | struct_init.c:33:8:33:12 | *outer [*pointerAB, a] | provenance | |
| struct_init.c:26:23:29:3 | *{...} [post update] [a] | struct_init.c:26:16:26:20 | *definition of outer [post update] [nestedAB, a] | provenance | |
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:26:23:29:3 | *{...} [post update] [a] | provenance | |
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:7:27:16 | call to user_input | provenance | |
| struct_init.c:28:5:28:7 | *& ... [a] | struct_init.c:26:16:26:20 | *definition of outer [post update] [*pointerAB, a] | provenance | |
| struct_init.c:28:5:28:7 | *& ... [a] | struct_init.c:26:16:26:20 | {...} [*pointerAB, a] | provenance | |
| struct_init.c:31:8:31:12 | *outer [nestedAB, a] | struct_init.c:31:14:31:21 | *nestedAB [a] | provenance | |
| struct_init.c:31:14:31:21 | *nestedAB [a] | struct_init.c:31:23:31:23 | a | provenance | |
| struct_init.c:33:8:33:12 | *outer [*pointerAB, a] | struct_init.c:33:14:33:22 | *pointerAB [a] | provenance | |
@@ -879,8 +879,8 @@ edges
| struct_init.c:40:13:40:14 | *definition of ab [post update] [a] | struct_init.c:40:13:40:14 | *definition of ab [a] | provenance | |
| struct_init.c:40:20:40:29 | call to user_input | struct_init.c:40:13:40:14 | *definition of ab [post update] [a] | provenance | |
| struct_init.c:40:20:40:29 | call to user_input | struct_init.c:40:20:40:29 | call to user_input | provenance | |
| struct_init.c:41:16:41:20 | *definition of outer [post update] [*pointerAB, a] | struct_init.c:46:10:46:14 | *outer [*pointerAB, a] | provenance | |
| struct_init.c:43:5:43:7 | *& ... [a] | struct_init.c:41:16:41:20 | *definition of outer [post update] [*pointerAB, a] | provenance | |
| struct_init.c:41:16:41:20 | {...} [*pointerAB, a] | struct_init.c:46:10:46:14 | *outer [*pointerAB, a] | provenance | |
| struct_init.c:43:5:43:7 | *& ... [a] | struct_init.c:41:16:41:20 | {...} [*pointerAB, a] | provenance | |
| struct_init.c:46:10:46:14 | *outer [*pointerAB, a] | struct_init.c:46:16:46:24 | *pointerAB [a] | provenance | |
| struct_init.c:46:16:46:24 | *pointerAB [a] | struct_init.c:14:24:14:25 | *ab [a] | provenance | |
nodes
@@ -1773,8 +1773,8 @@ nodes
| struct_init.c:24:10:24:12 | *& ... [a] | semmle.label | *& ... [a] |
| struct_init.c:24:10:24:12 | absink output argument [a] | semmle.label | absink output argument [a] |
| struct_init.c:26:16:26:20 | *definition of outer [nestedAB, a] | semmle.label | *definition of outer [nestedAB, a] |
| struct_init.c:26:16:26:20 | *definition of outer [post update] [*pointerAB, a] | semmle.label | *definition of outer [post update] [*pointerAB, a] |
| struct_init.c:26:16:26:20 | *definition of outer [post update] [nestedAB, a] | semmle.label | *definition of outer [post update] [nestedAB, a] |
| struct_init.c:26:16:26:20 | {...} [*pointerAB, a] | semmle.label | {...} [*pointerAB, a] |
| struct_init.c:26:23:29:3 | *{...} [post update] [a] | semmle.label | *{...} [post update] [a] |
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
@@ -1791,7 +1791,7 @@ nodes
| struct_init.c:40:13:40:14 | *definition of ab [post update] [a] | semmle.label | *definition of ab [post update] [a] |
| struct_init.c:40:20:40:29 | call to user_input | semmle.label | call to user_input |
| struct_init.c:40:20:40:29 | call to user_input | semmle.label | call to user_input |
| struct_init.c:41:16:41:20 | *definition of outer [post update] [*pointerAB, a] | semmle.label | *definition of outer [post update] [*pointerAB, a] |
| struct_init.c:41:16:41:20 | {...} [*pointerAB, a] | semmle.label | {...} [*pointerAB, a] |
| struct_init.c:43:5:43:7 | *& ... [a] | semmle.label | *& ... [a] |
| struct_init.c:46:10:46:14 | *outer [*pointerAB, a] | semmle.label | *outer [*pointerAB, a] |
| struct_init.c:46:16:46:24 | *pointerAB [a] | semmle.label | *pointerAB [a] |