Skip to content

Commit 1a47115

Browse files
committed
Exclude flows through non constexpr variables
Before the analysis only considered whether the source of an argument passed to a function was computed at compile time. Now we consider whether intermediate variables are also constexpr even though their values are compile time constants, because otherwise the compiler will accept the variable receiving the compiled time constant to be a constexpr variable.
1 parent fe630e9 commit 1a47115

File tree

3 files changed

+98
-46
lines changed

3 files changed

+98
-46
lines changed

cpp/autosar/src/rules/A7-1-2/VariableMissingConstexpr.ql

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import cpp
1717
import codingstandards.cpp.autosar
1818
import codingstandards.cpp.TrivialType
1919
import codingstandards.cpp.SideEffect
20+
import semmle.code.cpp.controlflow.SSA
2021

2122
predicate isZeroInitializable(Variable v) {
2223
not exists(v.getInitializer().getExpr()) and
@@ -33,6 +34,36 @@ predicate isTypeZeroInitializable(Type t) {
3334
t.getUnderlyingType() instanceof ArrayType
3435
}
3536

37+
/**
38+
* An optimized set of expressions used to determine the flow through constexpr variables.
39+
*/
40+
class VariableAccessOrCallOrLiteral extends Expr {
41+
VariableAccessOrCallOrLiteral() {
42+
this instanceof VariableAccess or
43+
this instanceof Call or
44+
this instanceof Literal
45+
}
46+
}
47+
48+
/**
49+
* Holds if the value of source flows through compile time evaluated variables to target.
50+
*/
51+
predicate flowsThroughConstExprVariables(VariableAccessOrCallOrLiteral source, VariableAccessOrCallOrLiteral target) {
52+
(
53+
source = target
54+
or
55+
source != target and
56+
exists(SsaDefinition intermediateDef, StackVariable intermediate |
57+
intermediateDef.getAVariable().getFunction() = source.getEnclosingFunction() and
58+
intermediateDef.getAVariable().getFunction() = target.getEnclosingFunction() and
59+
intermediateDef.getAVariable() = intermediate and intermediate.isConstexpr()
60+
|
61+
DataFlow::localExprFlow(source, intermediateDef.getDefiningValue(intermediate)) and
62+
flowsThroughConstExprVariables(intermediateDef.getAUse(intermediate), target)
63+
)
64+
)
65+
}
66+
3667
/*
3768
* Returns true if the given call may be evaluated at compile time and is compile time evaluated because
3869
* all its arguments are compile time evaluated and its default values are compile time evaluated.
@@ -42,13 +73,23 @@ predicate isCompileTimeEvaluated(Call call) {
4273
// 1. The call may be evaluated at compile time, because it is constexpr, and
4374
call.getTarget().isConstexpr() and
4475
// 2. all its arguments are compile time evaluated, and
45-
forall(DataFlow::Node ultimateArgSource |
46-
DataFlow::localFlow(ultimateArgSource, DataFlow::exprNode(call.getAnArgument())) and
76+
forall(DataFlow::Node ultimateArgSource, DataFlow::Node argSource |
77+
argSource = DataFlow::exprNode(call.getAnArgument()) and
78+
DataFlow::localFlow(ultimateArgSource, argSource) and
4779
not DataFlow::localFlowStep(_, ultimateArgSource)
4880
|
49-
ultimateArgSource.asExpr() instanceof Literal
50-
or
51-
any(Call c | isCompileTimeEvaluated(c)) = ultimateArgSource.asExpr()
81+
(
82+
ultimateArgSource.asExpr() instanceof Literal
83+
or
84+
any(Call c | isCompileTimeEvaluated(c)) = ultimateArgSource.asExpr()
85+
) and
86+
// If the ultimate argument source is not the same as the argument source, then it must flow through
87+
// constexpr variables.
88+
(
89+
ultimateArgSource != argSource
90+
implies
91+
flowsThroughConstExprVariables(ultimateArgSource.asExpr(), argSource.asExpr())
92+
)
5293
) and
5394
// 3. all the default values used are compile time evaluated.
5495
forall(Expr defaultValue, Parameter parameterUsingDefaultValue, int idx |

cpp/autosar/test/rules/A7-1-2/VariableMissingConstexpr.expected

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
| test.cpp:55:7:55:8 | m2 | Variable m2 could be marked 'constexpr'. |
1111
| test.cpp:130:7:130:8 | m1 | Variable m1 could be marked 'constexpr'. |
1212
| test.cpp:141:7:141:8 | m1 | Variable m1 could be marked 'constexpr'. |
13-
| test.cpp:221:7:221:7 | x | Variable x could be marked 'constexpr'. |
14-
| test.cpp:234:7:234:7 | v | Variable v could be marked 'constexpr'. |
15-
| test.cpp:235:7:235:7 | w | Variable w could be marked 'constexpr'. |
16-
| test.cpp:237:7:237:7 | a | Variable a could be marked 'constexpr'. |
17-
| test.cpp:239:7:239:7 | b | Variable b could be marked 'constexpr'. |
18-
| test.cpp:242:7:242:7 | e | Variable e could be marked 'constexpr'. |
19-
| test.cpp:244:7:244:7 | f | Variable f could be marked 'constexpr'. |
20-
| test.cpp:245:7:245:7 | g | Variable g could be marked 'constexpr'. |
21-
| test.cpp:248:7:248:7 | j | Variable j could be marked 'constexpr'. |
22-
| test.cpp:252:7:252:7 | m | Variable m could be marked 'constexpr'. |
23-
| test.cpp:253:7:253:7 | n | Variable n could be marked 'constexpr'. |
13+
| test.cpp:221:7:221:8 | l1 | Variable l1 could be marked 'constexpr'. |
14+
| test.cpp:235:7:235:8 | l6 | Variable l6 could be marked 'constexpr'. |
15+
| test.cpp:237:7:237:8 | l8 | Variable l8 could be marked 'constexpr'. |
16+
| test.cpp:240:7:240:9 | l10 | Variable l10 could be marked 'constexpr'. |
17+
| test.cpp:243:7:243:9 | l12 | Variable l12 could be marked 'constexpr'. |
18+
| test.cpp:248:7:248:9 | l15 | Variable l15 could be marked 'constexpr'. |
19+
| test.cpp:250:7:250:9 | l16 | Variable l16 could be marked 'constexpr'. |
20+
| test.cpp:251:7:251:9 | l17 | Variable l17 could be marked 'constexpr'. |
21+
| test.cpp:257:7:257:9 | l21 | Variable l21 could be marked 'constexpr'. |
22+
| test.cpp:262:7:262:9 | l24 | Variable l24 could be marked 'constexpr'. |
23+
| test.cpp:263:7:263:9 | l25 | Variable l25 could be marked 'constexpr'. |

cpp/autosar/test/rules/A7-1-2/test.cpp

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -218,39 +218,50 @@ constexpr int add3(int x, int y = random()) { return x + y; }
218218
constexpr int add4(int x = 1, int y = 2) { return x + y; }
219219

220220
constexpr void fp_reported_in_466(int p) {
221-
int x = add(1, 2); // NON_COMPLIANT
222-
int y = add(1, p); // COMPLIANT
221+
int l1 = add(1, 2); // NON_COMPLIANT
222+
int l2 = add(1, p); // COMPLIANT
223223

224-
int z = 0;
224+
int l3 = 0;
225225
if (p > 0) {
226-
z = 1;
226+
l3 = 1;
227227
} else {
228-
z = p;
228+
l3 = p;
229229
}
230230

231-
constexpr int t = add(1, 2); // COMPLIANT
232-
233-
int u = add(z, 2); // COMPLIANT - z is not compile time constant on all paths
234-
int v = add(t, 2); // NON_COMPLIANT
235-
int w =
236-
add1(t, 2); // NON_COMPLIANT - all arguments are compile time constants
237-
int a = add1(t); // NON_COMPLIANT - s and the default value of the second
238-
// argument are compile time constants
239-
int b = add1(1); // NON_COMPLIANT
240-
int c = add1(1, z); // COMPLIANT - z is not compile time constant on all paths
241-
int d = add1(z); // COMPLIANT - z is not compile time constant on all paths
242-
int e = add2(1); // NON_COMPLIANT - provided argument and default value are
243-
// compile time constants
244-
int f = add2(1, 2); // NON_COMPLIANT
245-
int g = add2(t, 2); // NON_COMPLIANT
246-
int h = add2(z); // COMPLIANT - z is not compile time constant on all paths
247-
int i = add2(z, 1); // COMPLIANT - z is not compile time constant on all paths
248-
int j = add3(1, 1); // NON_COMPLIANT
249-
int k = add3(1); // COMPLIANT - default value for second argument is not a
250-
// compile time constant
251-
int l = add3(1, z); // COMPLIANT - z is not compile time constant on all paths
252-
int m = add4(); // NON_COMPLIANT - default values are compile time constants
253-
int n = add4(1); // NON_COMPLIANT - default value for second argument is a
254-
// compile time constant
255-
int o = add4(1, z); // COMPLIANT - z is not compile time constant on all paths
231+
constexpr int l4 = add(1, 2); // COMPLIANT
232+
233+
int l5 =
234+
add(l3, 2); // COMPLIANT - l3 is not compile time constant on all paths
235+
int l6 = add(l4, 2); // NON_COMPLIANT
236+
int l7 = add(l1, 2); // COMPLIANT - l1 is not constexpr
237+
int l8 =
238+
add1(l4, 2); // NON_COMPLIANT - all arguments are compile time constants
239+
int l9 = add1(l1, 2); // COMPLIANT - l1 is not constexpr
240+
int l10 = add1(l4); // NON_COMPLIANT - s and the default value of the second
241+
// argument are compile time constants
242+
int l11 = add1(l1); // COMPLIANT - l1 is not constexpr
243+
int l12 = add1(1); // NON_COMPLIANT
244+
int l13 =
245+
add1(1, l3); // COMPLIANT - l3 is not compile time constant on all paths
246+
int l14 =
247+
add1(l3); // COMPLIANT - l3 is not compile time constant on all paths
248+
int l15 = add2(1); // NON_COMPLIANT - provided argument and default value are
249+
// compile time constants
250+
int l16 = add2(1, 2); // NON_COMPLIANT
251+
int l17 = add2(l4, 2); // NON_COMPLIANT
252+
int l18 = add2(l1, 2); // COMPLIANT - l1 is not constexpr
253+
int l19 =
254+
add2(l3); // COMPLIANT - l3 is not compile time constant on all paths
255+
int l20 =
256+
add2(l3, 1); // COMPLIANT - l3 is not compile time constant on all paths
257+
int l21 = add3(1, 1); // NON_COMPLIANT
258+
int l22 = add3(1); // COMPLIANT - default value for second argument is not a
259+
// compile time constant
260+
int l23 =
261+
add3(1, l3); // COMPLIANT - l3 is not compile time constant on all paths
262+
int l24 = add4(); // NON_COMPLIANT - default values are compile time constants
263+
int l25 = add4(1); // NON_COMPLIANT - default value for second argument is a
264+
// compile time constant
265+
int l26 =
266+
add4(1, l3); // COMPLIANT - l3 is not compile time constant on all paths
256267
}

0 commit comments

Comments
 (0)