Skip to content

Commit 3049e95

Browse files
committed
C++: Support if consteval and if ! consteval
1 parent 84c674b commit 3049e95

File tree

14 files changed

+789
-0
lines changed

14 files changed

+789
-0
lines changed

cpp/ql/lib/semmle/code/cpp/PrintAST.qll

+4
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,10 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
912912
or
913913
s.(ConstexprIfStmt).getElse() = e and pred = "getElse()"
914914
or
915+
s.(ConstevalOrNotConstevalIfStmt).getThen() = e and pred = "getThen()"
916+
or
917+
s.(ConstevalOrNotConstevalIfStmt).getElse() = e and pred = "getElse()"
918+
or
915919
s.(Handler).getParameter() = e and pred = "getParameter()"
916920
or
917921
s.(IfStmt).getInitialization() = e and pred = "getInitialization()"

cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll

+22
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,28 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
876876
p2.nodeAfter(n2, s)
877877
)
878878
or
879+
// ConstevalOrNotConstevalIfStmt -> { then, else } ->
880+
exists(ConstevalOrNotConstevalIfStmt s |
881+
p1.nodeAt(n1, s) and
882+
p2.nodeBefore(n2, s.getThen())
883+
or
884+
p1.nodeAt(n1, s) and
885+
p2.nodeBefore(n2, s.getElse())
886+
or
887+
p1.nodeAt(n1, s) and
888+
not exists(s.getElse()) and
889+
p2.nodeBeforeDestructors(n2, s)
890+
or
891+
p1.nodeAfter(n1, s.getThen()) and
892+
p2.nodeBeforeDestructors(n2, s)
893+
or
894+
p1.nodeAfter(n1, s.getElse()) and
895+
p2.nodeBeforeDestructors(n2, s)
896+
or
897+
p1.nodeAfterDestructors(n1, s) and
898+
p2.nodeAfter(n2, s)
899+
)
900+
or
879901
// WhileStmt -> condition ; body -> condition ; after dtors -> after
880902
exists(WhileStmt s |
881903
p1.nodeAt(n1, s) and

cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll

+55
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,61 @@ class TranslatedConstExprIfStmt extends TranslatedIfLikeStmt {
10981098
override predicate hasElse() { exists(stmt.getElse()) }
10991099
}
11001100

1101+
class TranslatedConstevalOrNotConstevalIfStmt extends TranslatedStmt {
1102+
override ConstevalOrNotConstevalIfStmt stmt;
1103+
1104+
override Instruction getFirstInstruction(EdgeKind kind) {
1105+
if not this.hasEvaluatedBranch()
1106+
then
1107+
kind instanceof GotoEdge and
1108+
result = this.getInstruction(OnlyInstructionTag())
1109+
else result = this.getEvaluatedBranch().getFirstInstruction(kind)
1110+
}
1111+
1112+
override TranslatedElement getChildInternal(int id) {
1113+
id = 0 and
1114+
result = this.getThen()
1115+
or
1116+
id = 1 and
1117+
result = this.getElse()
1118+
}
1119+
1120+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
1121+
not this.hasEvaluatedBranch() and
1122+
opcode instanceof Opcode::NoOp and
1123+
tag = OnlyInstructionTag() and
1124+
resultType = getVoidType()
1125+
}
1126+
1127+
override Instruction getALastInstructionInternal() {
1128+
if not this.hasEvaluatedBranch()
1129+
then result = this.getInstruction(OnlyInstructionTag())
1130+
else result = this.getEvaluatedBranch().getALastInstruction()
1131+
}
1132+
1133+
override TranslatedElement getLastChild() { result = this.getEvaluatedBranch() }
1134+
1135+
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
1136+
tag = OnlyInstructionTag() and
1137+
result = this.getParent().getChildSuccessor(this, kind)
1138+
}
1139+
1140+
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
1141+
(child = this.getThen() or child = this.getElse()) and
1142+
result = this.getParent().getChildSuccessor(this, kind)
1143+
}
1144+
1145+
TranslatedStmt getEvaluatedBranch() {
1146+
result = getTranslatedStmt(stmt.getRuntimeEvaluatedBranch())
1147+
}
1148+
1149+
predicate hasEvaluatedBranch() { stmt.hasRuntimeEvaluatedBranch() }
1150+
1151+
TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
1152+
1153+
TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
1154+
}
1155+
11011156
abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
11021157
override Loop stmt;
11031158

cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll

+162
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,168 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
437437
}
438438
}
439439

440+
/**
441+
* A C/C++ '(not) consteval if'. For example, the `if consteval` statement
442+
* in the following code:
443+
* ```cpp
444+
* if consteval {
445+
* ...
446+
* }
447+
* ```
448+
*/
449+
class ConstevalOrNotConstevalIfStmt extends Stmt, @stmt_consteval_or_not_consteval_if {
450+
/**
451+
* Gets the 'then' statement of this '(not) consteval if' statement.
452+
*
453+
* For example, for
454+
* ```cpp
455+
* if consteval { return true; }
456+
* ```
457+
* the result is the `BlockStmt` `{ return true; }`.
458+
*/
459+
Stmt getThen() { consteval_if_then(underlyingElement(this), unresolveElement(result)) }
460+
461+
/**
462+
* Gets the 'else' statement of this '(not) constexpr if' statement, if any.
463+
*
464+
* For example, for
465+
* ```cpp
466+
* if consteval { return true; } else { return false; }
467+
* ```
468+
* the result is the `BlockStmt` `{ return false; }`, and for
469+
* ```cpp
470+
* if consteval { return true; }
471+
* ```
472+
* there is no result.
473+
*/
474+
Stmt getElse() { consteval_if_else(underlyingElement(this), unresolveElement(result)) }
475+
476+
/**
477+
* Holds if this '(not) constexpr if' statement has an 'else' statement.
478+
*
479+
* For example, this holds for
480+
* ```cpp
481+
* if consteval { return true; } else { return false; }
482+
* ```
483+
* but not for
484+
* ```cpp
485+
* if consteval { return true; }
486+
* ```
487+
*/
488+
predicate hasElse() { exists(this.getElse()) }
489+
490+
override predicate mayBeImpure() {
491+
this.getThen().mayBeImpure() or
492+
this.getElse().mayBeImpure()
493+
}
494+
495+
override predicate mayBeGloballyImpure() {
496+
this.getThen().mayBeGloballyImpure() or
497+
this.getElse().mayBeGloballyImpure()
498+
}
499+
500+
override MacroInvocation getGeneratingMacro() {
501+
this.getThen().getGeneratingMacro() = result and
502+
(this.hasElse() implies this.getElse().getGeneratingMacro() = result)
503+
}
504+
505+
/**
506+
* Gets the statement of this '(not) consteval if' statement evaluated during compile time, if any.
507+
*
508+
* For example, for
509+
* ```cpp
510+
* if ! consteval { return true; } else { return false; }
511+
* ```
512+
* the result is the `BlockStmt` `{ return false; }`, and for
513+
* ```cpp
514+
* if ! consteval { return true; }
515+
* ```
516+
* there is no result.
517+
*/
518+
Stmt getCompileTimeEvaluatedBranch() { none() }
519+
520+
/**
521+
* Holds if this '(not) constexpr if' statement has a compile time evaluated statement.
522+
*
523+
* For example, this holds for
524+
* ```cpp
525+
* if ! consteval { return true; } else { return false; }
526+
* ```
527+
* but not for
528+
* ```cpp
529+
* if ! consteval { return true; }
530+
* ```
531+
*/
532+
predicate hasCompileTimeEvaluatedBranch() { exists(this.getCompileTimeEvaluatedBranch()) }
533+
534+
/**
535+
* Gets the statement of this '(not) consteval if' statement evaluated during runtime, if any.
536+
*
537+
* For example, for
538+
* ```cpp
539+
* if consteval { return true; } else { return false; }
540+
* ```
541+
* the result is the `BlockStmt` `{ return false; }`, and for
542+
* ```cpp
543+
* if consteval { return true; }
544+
* ```
545+
* there is no result.
546+
*/
547+
Stmt getRuntimeEvaluatedBranch() { none() }
548+
549+
/**
550+
* Holds if this '(not) constexpr if' statement has a runtime evaluated statement.
551+
*
552+
* For example, this holds for
553+
* ```cpp
554+
* if consteval { return true; } else { return false; }
555+
* ```
556+
* but not for
557+
* ```cpp
558+
* if consteval { return true; }
559+
* ```
560+
*/
561+
predicate hasRuntimeEvaluatedBranch() { exists(this.getRuntimeEvaluatedBranch()) }
562+
}
563+
564+
/**
565+
* A C/C++ 'consteval if'. For example, the `if consteval` statement
566+
* in the following code:
567+
* ```cpp
568+
* if consteval {
569+
* ...
570+
* }
571+
* ```
572+
*/
573+
class ConstevalIfStmt extends ConstevalOrNotConstevalIfStmt, @stmt_consteval_if {
574+
override string getAPrimaryQlClass() { result = "ConstevallIfStmt" }
575+
576+
override string toString() { result = "if consteval ..." }
577+
578+
override Stmt getCompileTimeEvaluatedBranch() { result = this.getThen() }
579+
580+
override Stmt getRuntimeEvaluatedBranch() { result = this.getElse() }
581+
}
582+
583+
/**
584+
* A C/C++ 'not consteval if'. For example, the `if ! consteval` statement
585+
* in the following code:
586+
* ```cpp
587+
* if ! consteval {
588+
* ...
589+
* }
590+
* ```
591+
*/
592+
class NotConstevalIfStmt extends ConstevalOrNotConstevalIfStmt, @stmt_not_consteval_if {
593+
override string getAPrimaryQlClass() { result = "NotConstevallIfStmt" }
594+
595+
override string toString() { result = "if ! consteval ..." }
596+
597+
override Stmt getCompileTimeEvaluatedBranch() { result = this.getElse() }
598+
599+
override Stmt getRuntimeEvaluatedBranch() { result = this.getThen() }
600+
}
601+
440602
private class TLoop = @stmt_while or @stmt_end_test_while or @stmt_range_based_for or @stmt_for;
441603

442604
/**

cpp/ql/lib/semmlecode.cpp.dbscheme

+14
Original file line numberDiff line numberDiff line change
@@ -2152,6 +2152,8 @@ case @stmt.kind of
21522152
// ... 34 @stmt_finally_end deprecated
21532153
| 35 = @stmt_constexpr_if
21542154
| 37 = @stmt_co_return
2155+
| 38 = @stmt_consteval_if
2156+
| 39 = @stmt_not_consteval_if
21552157
;
21562158

21572159
type_vla(
@@ -2194,6 +2196,18 @@ constexpr_if_else(
21942196
int else_id: @stmt ref
21952197
);
21962198

2199+
@stmt_consteval_or_not_consteval_if = @stmt_consteval_if | @stmt_not_consteval_if;
2200+
2201+
consteval_if_then(
2202+
unique int constexpr_if_stmt: @stmt_consteval_or_not_consteval_if ref,
2203+
int then_id: @stmt ref
2204+
);
2205+
2206+
consteval_if_else(
2207+
unique int constexpr_if_stmt: @stmt_consteval_or_not_consteval_if ref,
2208+
int else_id: @stmt ref
2209+
);
2210+
21972211
while_body(
21982212
unique int while_stmt: @stmt_while ref,
21992213
int body_id: @stmt ref
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
| __va_list_tag::operator= | false | 66 | 66 | operator= |
2+
| __va_list_tag::operator= | false | 72 | 72 | operator= |
3+
| test | false | 142 | 142 | test |
4+
| test | false | 147 | 147 | declaration |
5+
| test | false | 152 | 152 | if consteval ... |
6+
| test | false | 154 | 154 | ExprStmt |
7+
| test | false | 156 | 156 | x |
8+
| test | false | 160 | 160 | 1 |
9+
| test | false | 161 | 161 | ... = ... |
10+
| test | false | 163 | 163 | { ... } |
11+
| test | false | 165 | 165 | ExprStmt |
12+
| test | false | 167 | 167 | x |
13+
| test | false | 171 | 171 | 2 |
14+
| test | false | 172 | 172 | ... = ... |
15+
| test | false | 174 | 174 | { ... } |
16+
| test | false | 176 | 176 | if consteval ... |
17+
| test | false | 178 | 178 | ExprStmt |
18+
| test | false | 180 | 180 | x |
19+
| test | false | 184 | 184 | 3 |
20+
| test | false | 185 | 185 | ... = ... |
21+
| test | false | 187 | 187 | { ... } |
22+
| test | false | 189 | 189 | return ... |
23+
| test | false | 191 | 191 | x |
24+
| test | false | 193 | 193 | (bool)... |
25+
| test | false | 194 | 194 | { ... } |
26+
| test | true | 147 | 152 | |
27+
| test | true | 152 | 163 | |
28+
| test | true | 152 | 174 | |
29+
| test | true | 154 | 160 | |
30+
| test | true | 156 | 161 | |
31+
| test | true | 160 | 156 | |
32+
| test | true | 161 | 176 | |
33+
| test | true | 163 | 154 | |
34+
| test | true | 165 | 171 | |
35+
| test | true | 167 | 172 | |
36+
| test | true | 171 | 167 | |
37+
| test | true | 172 | 176 | |
38+
| test | true | 174 | 165 | |
39+
| test | true | 176 | 187 | |
40+
| test | true | 176 | 189 | |
41+
| test | true | 178 | 184 | |
42+
| test | true | 180 | 185 | |
43+
| test | true | 184 | 180 | |
44+
| test | true | 185 | 189 | |
45+
| test | true | 187 | 178 | |
46+
| test | true | 189 | 191 | |
47+
| test | true | 191 | 142 | |
48+
| test | true | 194 | 147 | |
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/**
2+
* query-type: graph
3+
*
4+
* @kind graph-equivalence-test
5+
*/
6+
7+
import cpp
8+
9+
class DestructorCallEnhanced extends DestructorCall {
10+
override string toString() {
11+
if exists(this.getQualifier().(VariableAccess).getTarget().getName())
12+
then
13+
result =
14+
"call to " + this.getQualifier().(VariableAccess).getTarget().getName() + "." +
15+
this.getTarget().getName()
16+
else result = super.toString()
17+
}
18+
}
19+
20+
string scope(ControlFlowNode x) {
21+
if exists(x.getControlFlowScope().getQualifiedName())
22+
then result = x.getControlFlowScope().getQualifiedName()
23+
else result = "<no scope>"
24+
}
25+
26+
predicate isNode(boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label) {
27+
isEdge = false and x = y and label = x.toString()
28+
}
29+
30+
predicate isSuccessor(boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label) {
31+
exists(string truelabel, string falselabel |
32+
isEdge = true and
33+
x.getASuccessor() = y and
34+
(if x.getATrueSuccessor() = y then truelabel = "T" else truelabel = "") and
35+
(if x.getAFalseSuccessor() = y then falselabel = "F" else falselabel = "") and
36+
label = truelabel + falselabel
37+
)
38+
}
39+
40+
from boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label
41+
where isNode(isEdge, x, y, label) or isSuccessor(isEdge, x, y, label)
42+
select scope(x), isEdge, x, y, label

0 commit comments

Comments
 (0)