Skip to content

Commit 4b85ea3

Browse files
authored
Merge pull request #18502 from jketema/consteval
C++: Support `if consteval` and `if ! consteval`
2 parents ef034bc + a9e0f20 commit 4b85ea3

File tree

25 files changed

+11929
-1216
lines changed

25 files changed

+11929
-1216
lines changed

cpp/downgrades/1aa71a4a687fc93f807d4dfeeef70feceeced242/old.dbscheme

+2,429
Large diffs are not rendered by default.

cpp/downgrades/1aa71a4a687fc93f807d4dfeeef70feceeced242/semmlecode.cpp.dbscheme

+2,415
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
class Stmt extends @stmt {
2+
string toString() { none() }
3+
}
4+
5+
class Location extends @location_stmt {
6+
string toString() { none() }
7+
}
8+
9+
predicate isConstevalIf(Stmt stmt) {
10+
exists(int kind | stmts(stmt, kind, _) | kind = 38 or kind = 39)
11+
}
12+
13+
from Stmt stmt, int kind, int kind_new, Location location
14+
where
15+
stmts(stmt, kind, location) and
16+
if isConstevalIf(stmt) then kind_new = 7 else kind_new = kind // Turns consteval if into a block with two block statements in it
17+
select stmt, kind_new, location
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
description: Support (not) consteval if
2+
compatibility: full
3+
consteval_if_then.rel: delete
4+
consteval_if_else.rel: delete
5+
stmts.rel: run stmts.qlo
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: feature
3+
---
4+
* A new class `ConstevalIfStmt` was introduced, which represents the C++23 `if consteval` and `if ! consteval` statements.

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.(ConstevalIfStmt).getThen() = e and pred = "getThen()"
916+
or
917+
s.(ConstevalIfStmt).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

+19
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,25 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
876876
p2.nodeAfter(n2, s)
877877
)
878878
or
879+
// NotConstevalIfStmt -> { then, else } ->
880+
exists(ConstevalIfStmt 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.nodeAfter(n2, s)
890+
or
891+
p1.nodeAfter(n1, s.getThen()) and
892+
p2.nodeAfter(n2, s)
893+
or
894+
p1.nodeAfter(n1, s.getElse()) and
895+
p2.nodeAfter(n2, s)
896+
)
897+
or
879898
// WhileStmt -> condition ; body -> condition ; after dtors -> after
880899
exists(WhileStmt s |
881900
p1.nodeAt(n1, s) and

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ abstract class TranslatedFlexibleCondition extends TranslatedCondition, Conditio
4747
{
4848
TranslatedFlexibleCondition() { this = TTranslatedFlexibleCondition(expr) }
4949

50-
final override predicate handlesDestructorsExplicitly() { none() } // TODO: this needs to be revisted when we get unnamed destructors
50+
final override predicate handlesDestructorsExplicitly() { none() } // TODO: this needs to be revisited when we get unnamed destructors
5151

5252
final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() }
5353

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ private predicate ignoreExpr(Expr expr) {
166166
}
167167

168168
/**
169-
* Holds if the side effects of `expr` should be ignoredf for the purposes of IR generation.
169+
* Holds if the side effects of `expr` should be ignored for the purposes of IR generation.
170170
*
171171
* In cases involving `constexpr`, a call can wind up as a constant expression. `ignoreExpr()` will
172172
* not hold for such a call, since we do need to translate the call (as a constant), but we need to

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 TranslatedConstevalIfStmt extends TranslatedStmt {
1102+
override ConstevalIfStmt 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

+148
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,154 @@ 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 ConstevalIfStmt extends Stmt, @stmt_consteval_or_not_consteval_if {
450+
override string getAPrimaryQlClass() { result = "ConstevalIfStmt" }
451+
452+
override string toString() {
453+
if this.isNot() then result = "if ! consteval ..." else result = "if consteval ..."
454+
}
455+
456+
/**
457+
* Holds if this is a 'not consteval if' statement.
458+
*
459+
* For example, this holds for
460+
* ```cpp
461+
* if ! consteval { return true; }
462+
* ```
463+
* but not for
464+
* ```cpp
465+
* if consteval { return true; }
466+
* ```
467+
*/
468+
predicate isNot() { this instanceof @stmt_not_consteval_if }
469+
470+
/**
471+
* Gets the 'then' statement of this '(not) consteval if' statement.
472+
*
473+
* For example, for
474+
* ```cpp
475+
* if consteval { return true; }
476+
* ```
477+
* the result is the `BlockStmt` `{ return true; }`.
478+
*/
479+
Stmt getThen() { consteval_if_then(underlyingElement(this), unresolveElement(result)) }
480+
481+
/**
482+
* Gets the 'else' statement of this '(not) constexpr if' statement, if any.
483+
*
484+
* For example, for
485+
* ```cpp
486+
* if consteval { return true; } else { return false; }
487+
* ```
488+
* the result is the `BlockStmt` `{ return false; }`, and for
489+
* ```cpp
490+
* if consteval { return true; }
491+
* ```
492+
* there is no result.
493+
*/
494+
Stmt getElse() { consteval_if_else(underlyingElement(this), unresolveElement(result)) }
495+
496+
/**
497+
* Holds if this '(not) constexpr if' statement has an 'else' statement.
498+
*
499+
* For example, this holds for
500+
* ```cpp
501+
* if consteval { return true; } else { return false; }
502+
* ```
503+
* but not for
504+
* ```cpp
505+
* if consteval { return true; }
506+
* ```
507+
*/
508+
predicate hasElse() { exists(this.getElse()) }
509+
510+
override predicate mayBeImpure() {
511+
this.getThen().mayBeImpure() or
512+
this.getElse().mayBeImpure()
513+
}
514+
515+
override predicate mayBeGloballyImpure() {
516+
this.getThen().mayBeGloballyImpure() or
517+
this.getElse().mayBeGloballyImpure()
518+
}
519+
520+
override MacroInvocation getGeneratingMacro() {
521+
this.getThen().getGeneratingMacro() = result and
522+
(this.hasElse() implies this.getElse().getGeneratingMacro() = result)
523+
}
524+
525+
/**
526+
* Gets the statement of this '(not) consteval if' statement evaluated during compile time, if any.
527+
*
528+
* For example, for
529+
* ```cpp
530+
* if ! consteval { return true; } else { return false; }
531+
* ```
532+
* the result is the `BlockStmt` `{ return false; }`, and for
533+
* ```cpp
534+
* if ! consteval { return true; }
535+
* ```
536+
* there is no result.
537+
*/
538+
Stmt getCompileTimeEvaluatedBranch() {
539+
if this.isNot() then result = this.getElse() else result = this.getThen()
540+
}
541+
542+
/**
543+
* Holds if this '(not) constexpr if' statement has a compile time evaluated statement.
544+
*
545+
* For example, this holds for
546+
* ```cpp
547+
* if ! consteval { return true; } else { return false; }
548+
* ```
549+
* but not for
550+
* ```cpp
551+
* if ! consteval { return true; }
552+
* ```
553+
*/
554+
predicate hasCompileTimeEvaluatedBranch() { exists(this.getCompileTimeEvaluatedBranch()) }
555+
556+
/**
557+
* Gets the statement of this '(not) consteval if' statement evaluated during runtime, if any.
558+
*
559+
* For example, for
560+
* ```cpp
561+
* if consteval { return true; } else { return false; }
562+
* ```
563+
* the result is the `BlockStmt` `{ return false; }`, and for
564+
* ```cpp
565+
* if consteval { return true; }
566+
* ```
567+
* there is no result.
568+
*/
569+
Stmt getRuntimeEvaluatedBranch() {
570+
if this.isNot() then result = this.getThen() else result = this.getElse()
571+
}
572+
573+
/**
574+
* Holds if this '(not) constexpr if' statement has a runtime evaluated statement.
575+
*
576+
* For example, this holds for
577+
* ```cpp
578+
* if consteval { return true; } else { return false; }
579+
* ```
580+
* but not for
581+
* ```cpp
582+
* if consteval { return true; }
583+
* ```
584+
*/
585+
predicate hasRuntimeEvaluatedBranch() { exists(this.getRuntimeEvaluatedBranch()) }
586+
}
587+
440588
private class TLoop = @stmt_while or @stmt_end_test_while or @stmt_range_based_for or @stmt_for;
441589

442590
/**

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

0 commit comments

Comments
 (0)