diff --git a/cpp/ql/lib/semmle/code/cpp/PrintAST.qll b/cpp/ql/lib/semmle/code/cpp/PrintAST.qll index 12061db454d4..11244c756206 100644 --- a/cpp/ql/lib/semmle/code/cpp/PrintAST.qll +++ b/cpp/ql/lib/semmle/code/cpp/PrintAST.qll @@ -912,6 +912,10 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred) or s.(ConstexprIfStmt).getElse() = e and pred = "getElse()" or + s.(ConstevalOrNotConstevalIfStmt).getThen() = e and pred = "getThen()" + or + s.(ConstevalOrNotConstevalIfStmt).getElse() = e and pred = "getElse()" + or s.(Handler).getParameter() = e and pred = "getParameter()" or s.(IfStmt).getInitialization() = e and pred = "getInitialization()" diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll index c003ec4595ef..19b0d48fdfdb 100644 --- a/cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll +++ b/cpp/ql/lib/semmle/code/cpp/controlflow/internal/CFG.qll @@ -876,6 +876,25 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) { p2.nodeAfter(n2, s) ) or + // ConstevalOrNotConstevalIfStmt -> { then, else } -> + exists(ConstevalOrNotConstevalIfStmt s | + p1.nodeAt(n1, s) and + p2.nodeBefore(n2, s.getThen()) + or + p1.nodeAt(n1, s) and + p2.nodeBefore(n2, s.getElse()) + or + p1.nodeAt(n1, s) and + not exists(s.getElse()) and + p2.nodeAfter(n2, s) + or + p1.nodeAfter(n1, s.getThen()) and + p2.nodeAfter(n2, s) + or + p1.nodeAfter(n1, s.getElse()) and + p2.nodeAfter(n2, s) + ) + or // WhileStmt -> condition ; body -> condition ; after dtors -> after exists(WhileStmt s | p1.nodeAt(n1, s) and diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll index e0c7d625e81c..41c1644c6183 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll @@ -1098,6 +1098,61 @@ class TranslatedConstExprIfStmt extends TranslatedIfLikeStmt { override predicate hasElse() { exists(stmt.getElse()) } } +class TranslatedConstevalOrNotConstevalIfStmt extends TranslatedStmt { + override ConstevalOrNotConstevalIfStmt stmt; + + override Instruction getFirstInstruction(EdgeKind kind) { + if not this.hasEvaluatedBranch() + then + kind instanceof GotoEdge and + result = this.getInstruction(OnlyInstructionTag()) + else result = this.getEvaluatedBranch().getFirstInstruction(kind) + } + + override TranslatedElement getChildInternal(int id) { + id = 0 and + result = this.getThen() + or + id = 1 and + result = this.getElse() + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + not this.hasEvaluatedBranch() and + opcode instanceof Opcode::NoOp and + tag = OnlyInstructionTag() and + resultType = getVoidType() + } + + override Instruction getALastInstructionInternal() { + if not this.hasEvaluatedBranch() + then result = this.getInstruction(OnlyInstructionTag()) + else result = this.getEvaluatedBranch().getALastInstruction() + } + + override TranslatedElement getLastChild() { result = this.getEvaluatedBranch() } + + override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { + tag = OnlyInstructionTag() and + result = this.getParent().getChildSuccessor(this, kind) + } + + override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) { + (child = this.getThen() or child = this.getElse()) and + result = this.getParent().getChildSuccessor(this, kind) + } + + TranslatedStmt getEvaluatedBranch() { + result = getTranslatedStmt(stmt.getRuntimeEvaluatedBranch()) + } + + predicate hasEvaluatedBranch() { stmt.hasRuntimeEvaluatedBranch() } + + TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) } + + TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) } +} + abstract class TranslatedLoop extends TranslatedStmt, ConditionContext { override Loop stmt; diff --git a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll index b161da47620b..8958811453c1 100644 --- a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll @@ -437,6 +437,168 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if { } } +/** + * A C/C++ '(not) consteval if'. For example, the `if consteval` statement + * in the following code: + * ```cpp + * if consteval { + * ... + * } + * ``` + */ +class ConstevalOrNotConstevalIfStmt extends Stmt, @stmt_consteval_or_not_consteval_if { + /** + * Gets the 'then' statement of this '(not) consteval if' statement. + * + * For example, for + * ```cpp + * if consteval { return true; } + * ``` + * the result is the `BlockStmt` `{ return true; }`. + */ + Stmt getThen() { consteval_if_then(underlyingElement(this), unresolveElement(result)) } + + /** + * Gets the 'else' statement of this '(not) constexpr if' statement, if any. + * + * For example, for + * ```cpp + * if consteval { return true; } else { return false; } + * ``` + * the result is the `BlockStmt` `{ return false; }`, and for + * ```cpp + * if consteval { return true; } + * ``` + * there is no result. + */ + Stmt getElse() { consteval_if_else(underlyingElement(this), unresolveElement(result)) } + + /** + * Holds if this '(not) constexpr if' statement has an 'else' statement. + * + * For example, this holds for + * ```cpp + * if consteval { return true; } else { return false; } + * ``` + * but not for + * ```cpp + * if consteval { return true; } + * ``` + */ + predicate hasElse() { exists(this.getElse()) } + + override predicate mayBeImpure() { + this.getThen().mayBeImpure() or + this.getElse().mayBeImpure() + } + + override predicate mayBeGloballyImpure() { + this.getThen().mayBeGloballyImpure() or + this.getElse().mayBeGloballyImpure() + } + + override MacroInvocation getGeneratingMacro() { + this.getThen().getGeneratingMacro() = result and + (this.hasElse() implies this.getElse().getGeneratingMacro() = result) + } + + /** + * Gets the statement of this '(not) consteval if' statement evaluated during compile time, if any. + * + * For example, for + * ```cpp + * if ! consteval { return true; } else { return false; } + * ``` + * the result is the `BlockStmt` `{ return false; }`, and for + * ```cpp + * if ! consteval { return true; } + * ``` + * there is no result. + */ + Stmt getCompileTimeEvaluatedBranch() { none() } + + /** + * Holds if this '(not) constexpr if' statement has a compile time evaluated statement. + * + * For example, this holds for + * ```cpp + * if ! consteval { return true; } else { return false; } + * ``` + * but not for + * ```cpp + * if ! consteval { return true; } + * ``` + */ + predicate hasCompileTimeEvaluatedBranch() { exists(this.getCompileTimeEvaluatedBranch()) } + + /** + * Gets the statement of this '(not) consteval if' statement evaluated during runtime, if any. + * + * For example, for + * ```cpp + * if consteval { return true; } else { return false; } + * ``` + * the result is the `BlockStmt` `{ return false; }`, and for + * ```cpp + * if consteval { return true; } + * ``` + * there is no result. + */ + Stmt getRuntimeEvaluatedBranch() { none() } + + /** + * Holds if this '(not) constexpr if' statement has a runtime evaluated statement. + * + * For example, this holds for + * ```cpp + * if consteval { return true; } else { return false; } + * ``` + * but not for + * ```cpp + * if consteval { return true; } + * ``` + */ + predicate hasRuntimeEvaluatedBranch() { exists(this.getRuntimeEvaluatedBranch()) } +} + +/** + * A C/C++ 'consteval if'. For example, the `if consteval` statement + * in the following code: + * ```cpp + * if consteval { + * ... + * } + * ``` + */ +class ConstevalIfStmt extends ConstevalOrNotConstevalIfStmt, @stmt_consteval_if { + override string getAPrimaryQlClass() { result = "ConstevalIfStmt" } + + override string toString() { result = "if consteval ..." } + + override Stmt getCompileTimeEvaluatedBranch() { result = this.getThen() } + + override Stmt getRuntimeEvaluatedBranch() { result = this.getElse() } +} + +/** + * A C/C++ 'not consteval if'. For example, the `if ! consteval` statement + * in the following code: + * ```cpp + * if ! consteval { + * ... + * } + * ``` + */ +class NotConstevalIfStmt extends ConstevalOrNotConstevalIfStmt, @stmt_not_consteval_if { + override string getAPrimaryQlClass() { result = "NotConstevalIfStmt" } + + override string toString() { result = "if ! consteval ..." } + + override Stmt getCompileTimeEvaluatedBranch() { result = this.getElse() } + + override Stmt getRuntimeEvaluatedBranch() { result = this.getThen() } +} + private class TLoop = @stmt_while or @stmt_end_test_while or @stmt_range_based_for or @stmt_for; /** diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme index a01d8f91b8d4..1aa71a4a687f 100644 --- a/cpp/ql/lib/semmlecode.cpp.dbscheme +++ b/cpp/ql/lib/semmlecode.cpp.dbscheme @@ -2152,6 +2152,8 @@ case @stmt.kind of // ... 34 @stmt_finally_end deprecated | 35 = @stmt_constexpr_if | 37 = @stmt_co_return +| 38 = @stmt_consteval_if +| 39 = @stmt_not_consteval_if ; type_vla( @@ -2194,6 +2196,18 @@ constexpr_if_else( int else_id: @stmt ref ); +@stmt_consteval_or_not_consteval_if = @stmt_consteval_if | @stmt_not_consteval_if; + +consteval_if_then( + unique int constexpr_if_stmt: @stmt_consteval_or_not_consteval_if ref, + int then_id: @stmt ref +); + +consteval_if_else( + unique int constexpr_if_stmt: @stmt_consteval_or_not_consteval_if ref, + int else_id: @stmt ref +); + while_body( unique int while_stmt: @stmt_while ref, int body_id: @stmt ref diff --git a/cpp/ql/test/library-tests/consteval_if/cfg.expected b/cpp/ql/test/library-tests/consteval_if/cfg.expected new file mode 100644 index 000000000000..f17d207aa598 --- /dev/null +++ b/cpp/ql/test/library-tests/consteval_if/cfg.expected @@ -0,0 +1,124 @@ +| ClassWithDestructor::ClassWithDestructor | false | 157 | 157 | ClassWithDestructor | +| ClassWithDestructor::ClassWithDestructor | false | 296 | 296 | ClassWithDestructor | +| ClassWithDestructor::ClassWithDestructor | false | 302 | 302 | ClassWithDestructor | +| ClassWithDestructor::operator bool | false | 173 | 173 | operator bool | +| ClassWithDestructor::operator= | false | 288 | 288 | operator= | +| ClassWithDestructor::~ClassWithDestructor | false | 190 | 190 | ~ClassWithDestructor | +| __va_list_tag::operator= | false | 66 | 66 | operator= | +| __va_list_tag::operator= | false | 72 | 72 | operator= | +| destruction_on_consteval | false | 193 | 193 | destruction_on_consteval | +| destruction_on_consteval | false | 198 | 198 | if consteval ... | +| destruction_on_consteval | false | 200 | 200 | return ... | +| destruction_on_consteval | false | 204 | 204 | 1 | +| destruction_on_consteval | false | 205 | 205 | { ... } | +| destruction_on_consteval | false | 208 | 208 | call to ClassWithDestructor | +| destruction_on_consteval | false | 210 | 210 | initializer for cwd | +| destruction_on_consteval | false | 213 | 213 | declaration | +| destruction_on_consteval | false | 215 | 215 | return ... | +| destruction_on_consteval | false | 218 | 218 | call to operator bool | +| destruction_on_consteval | false | 219 | 219 | cwd | +| destruction_on_consteval | false | 221 | 221 | (const ClassWithDestructor)... | +| destruction_on_consteval | false | 222 | 222 | { ... } | +| destruction_on_consteval | false | 224 | 224 | { ... } | +| destruction_on_consteval | false | 226 | 226 | cwd | +| destruction_on_consteval | false | 228 | 228 | call to cwd.~ClassWithDestructor | +| destruction_on_consteval | false | 229 | 229 | cwd | +| destruction_on_consteval | false | 230 | 230 | call to cwd.~ClassWithDestructor | +| destruction_on_consteval | true | 198 | 205 | | +| destruction_on_consteval | true | 198 | 222 | | +| destruction_on_consteval | true | 200 | 204 | | +| destruction_on_consteval | true | 204 | 193 | | +| destruction_on_consteval | true | 205 | 200 | | +| destruction_on_consteval | true | 208 | 215 | | +| destruction_on_consteval | true | 210 | 208 | | +| destruction_on_consteval | true | 213 | 210 | | +| destruction_on_consteval | true | 215 | 219 | | +| destruction_on_consteval | true | 218 | 226 | | +| destruction_on_consteval | true | 219 | 218 | | +| destruction_on_consteval | true | 222 | 213 | | +| destruction_on_consteval | true | 224 | 198 | | +| destruction_on_consteval | true | 226 | 228 | | +| destruction_on_consteval | true | 228 | 193 | | +| destruction_on_consteval | true | 229 | 230 | | +| destruction_on_consteval | true | 230 | 193 | | +| destruction_on_consteval2 | false | 147 | 147 | destruction_on_consteval2 | +| destruction_on_consteval2 | false | 152 | 152 | declaration | +| destruction_on_consteval2 | false | 155 | 155 | call to ClassWithDestructor | +| destruction_on_consteval2 | false | 158 | 158 | initializer for cwd | +| destruction_on_consteval2 | false | 161 | 161 | if consteval ... | +| destruction_on_consteval2 | false | 163 | 163 | return ... | +| destruction_on_consteval2 | false | 167 | 167 | 1 | +| destruction_on_consteval2 | false | 168 | 168 | { ... } | +| destruction_on_consteval2 | false | 170 | 170 | return ... | +| destruction_on_consteval2 | false | 178 | 178 | call to operator bool | +| destruction_on_consteval2 | false | 179 | 179 | cwd | +| destruction_on_consteval2 | false | 182 | 182 | (const ClassWithDestructor)... | +| destruction_on_consteval2 | false | 183 | 183 | { ... } | +| destruction_on_consteval2 | false | 185 | 185 | { ... } | +| destruction_on_consteval2 | false | 187 | 187 | cwd | +| destruction_on_consteval2 | false | 189 | 189 | call to cwd.~ClassWithDestructor | +| destruction_on_consteval2 | false | 191 | 191 | cwd | +| destruction_on_consteval2 | false | 192 | 192 | call to cwd.~ClassWithDestructor | +| destruction_on_consteval2 | true | 152 | 158 | | +| destruction_on_consteval2 | true | 155 | 161 | | +| destruction_on_consteval2 | true | 158 | 155 | | +| destruction_on_consteval2 | true | 161 | 168 | | +| destruction_on_consteval2 | true | 161 | 183 | | +| destruction_on_consteval2 | true | 163 | 167 | | +| destruction_on_consteval2 | true | 167 | 187 | | +| destruction_on_consteval2 | true | 168 | 163 | | +| destruction_on_consteval2 | true | 170 | 179 | | +| destruction_on_consteval2 | true | 178 | 191 | | +| destruction_on_consteval2 | true | 179 | 178 | | +| destruction_on_consteval2 | true | 183 | 170 | | +| destruction_on_consteval2 | true | 185 | 152 | | +| destruction_on_consteval2 | true | 187 | 189 | | +| destruction_on_consteval2 | true | 189 | 147 | | +| destruction_on_consteval2 | true | 191 | 192 | | +| destruction_on_consteval2 | true | 192 | 147 | | +| test | false | 231 | 231 | test | +| test | false | 236 | 236 | declaration | +| test | false | 241 | 241 | if consteval ... | +| test | false | 243 | 243 | ExprStmt | +| test | false | 245 | 245 | x | +| test | false | 249 | 249 | 1 | +| test | false | 250 | 250 | ... = ... | +| test | false | 252 | 252 | { ... } | +| test | false | 254 | 254 | ExprStmt | +| test | false | 256 | 256 | x | +| test | false | 260 | 260 | 2 | +| test | false | 261 | 261 | ... = ... | +| test | false | 263 | 263 | { ... } | +| test | false | 265 | 265 | if consteval ... | +| test | false | 267 | 267 | ExprStmt | +| test | false | 269 | 269 | x | +| test | false | 273 | 273 | 3 | +| test | false | 274 | 274 | ... = ... | +| test | false | 276 | 276 | { ... } | +| test | false | 278 | 278 | return ... | +| test | false | 280 | 280 | x | +| test | false | 282 | 282 | (bool)... | +| test | false | 283 | 283 | { ... } | +| test | true | 236 | 241 | | +| test | true | 241 | 252 | | +| test | true | 241 | 263 | | +| test | true | 243 | 249 | | +| test | true | 245 | 250 | | +| test | true | 249 | 245 | | +| test | true | 250 | 265 | | +| test | true | 252 | 243 | | +| test | true | 254 | 260 | | +| test | true | 256 | 261 | | +| test | true | 260 | 256 | | +| test | true | 261 | 265 | | +| test | true | 263 | 254 | | +| test | true | 265 | 276 | | +| test | true | 265 | 278 | | +| test | true | 267 | 273 | | +| test | true | 269 | 274 | | +| test | true | 273 | 269 | | +| test | true | 274 | 278 | | +| test | true | 276 | 267 | | +| test | true | 278 | 280 | | +| test | true | 280 | 231 | | +| test | true | 283 | 236 | | diff --git a/cpp/ql/test/library-tests/consteval_if/cfg.ql b/cpp/ql/test/library-tests/consteval_if/cfg.ql new file mode 100644 index 000000000000..0e1f45caf193 --- /dev/null +++ b/cpp/ql/test/library-tests/consteval_if/cfg.ql @@ -0,0 +1,42 @@ +/** + * query-type: graph + * + * @kind graph-equivalence-test + */ + +import cpp + +class DestructorCallEnhanced extends DestructorCall { + override string toString() { + if exists(this.getQualifier().(VariableAccess).getTarget().getName()) + then + result = + "call to " + this.getQualifier().(VariableAccess).getTarget().getName() + "." + + this.getTarget().getName() + else result = super.toString() + } +} + +string scope(ControlFlowNode x) { + if exists(x.getControlFlowScope().getQualifiedName()) + then result = x.getControlFlowScope().getQualifiedName() + else result = "" +} + +predicate isNode(boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label) { + isEdge = false and x = y and label = x.toString() +} + +predicate isSuccessor(boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label) { + exists(string truelabel, string falselabel | + isEdge = true and + x.getASuccessor() = y and + (if x.getATrueSuccessor() = y then truelabel = "T" else truelabel = "") and + (if x.getAFalseSuccessor() = y then falselabel = "F" else falselabel = "") and + label = truelabel + falselabel + ) +} + +from boolean isEdge, ControlFlowNode x, ControlFlowNode y, string label +where isNode(isEdge, x, y, label) or isSuccessor(isEdge, x, y, label) +select scope(x), isEdge, x, y, label diff --git a/cpp/ql/test/library-tests/consteval_if/stmts.expected b/cpp/ql/test/library-tests/consteval_if/stmts.expected new file mode 100644 index 000000000000..40233f9e014d --- /dev/null +++ b/cpp/ql/test/library-tests/consteval_if/stmts.expected @@ -0,0 +1,25 @@ +| test.cpp:3:23:14:1 | { ... } | +| test.cpp:4:5:4:10 | declaration | +| test.cpp:5:5:9:5 | if consteval ... | +| test.cpp:5:18:7:5 | { ... } | +| test.cpp:6:9:6:14 | ExprStmt | +| test.cpp:7:12:9:5 | { ... } | +| test.cpp:8:9:8:14 | ExprStmt | +| test.cpp:10:5:12:5 | if consteval ... | +| test.cpp:10:18:12:5 | { ... } | +| test.cpp:11:9:11:14 | ExprStmt | +| test.cpp:13:5:13:13 | return ... | +| test.cpp:24:33:31:1 | { ... } | +| test.cpp:25:3:30:3 | if consteval ... | +| test.cpp:25:16:27:3 | { ... } | +| test.cpp:26:5:26:16 | return ... | +| test.cpp:27:10:30:3 | { ... } | +| test.cpp:28:5:28:28 | declaration | +| test.cpp:29:5:29:15 | return ... | +| test.cpp:33:34:40:1 | { ... } | +| test.cpp:34:3:34:26 | declaration | +| test.cpp:35:3:39:3 | if consteval ... | +| test.cpp:35:16:37:3 | { ... } | +| test.cpp:36:5:36:16 | return ... | +| test.cpp:37:10:39:3 | { ... } | +| test.cpp:38:5:38:15 | return ... | diff --git a/cpp/ql/test/library-tests/consteval_if/stmts.ql b/cpp/ql/test/library-tests/consteval_if/stmts.ql new file mode 100644 index 000000000000..0d77580b769f --- /dev/null +++ b/cpp/ql/test/library-tests/consteval_if/stmts.ql @@ -0,0 +1,4 @@ +import cpp + +from Stmt s +select s diff --git a/cpp/ql/test/library-tests/consteval_if/test.cpp b/cpp/ql/test/library-tests/consteval_if/test.cpp new file mode 100644 index 000000000000..7d6c2243aeb5 --- /dev/null +++ b/cpp/ql/test/library-tests/consteval_if/test.cpp @@ -0,0 +1,40 @@ +// semmle-extractor-options: -std=c++23 + +constexpr bool test() { + int x; + if consteval { + x = 1; + } else { + x = 2; + } + if consteval { + x = 3; + } + return x; +} + +struct ClassWithDestructor +{ + ClassWithDestructor(); + ClassWithDestructor(const char*); + ~ClassWithDestructor(); + operator bool() const; +}; + +bool destruction_on_consteval() { + if consteval { + return true; + } else { + ClassWithDestructor cwd; + return cwd; + } +} + +bool destruction_on_consteval2() { + ClassWithDestructor cwd; + if consteval { + return true; + } else { + return cwd; + } +} diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index bfb123fa6843..530bb5bb12f6 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -24118,6 +24118,125 @@ ir.cpp: # 2725| getExpr().getFullyConverted(): [ReferenceDereferenceExpr] (reference dereference) # 2725| Type = [PlainCharType] char # 2725| ValueCategory = prvalue(load) +ir23.cpp: +# 1| [TopLevelFunction] bool consteval_1() +# 1| : +# 2| getEntryPoint(): [BlockStmt] { ... } +# 3| getStmt(0): [ConstevalIfStmt] if consteval ... +# 3| getThen(): [BlockStmt] { ... } +# 4| getStmt(0): [ReturnStmt] return ... +# 4| getExpr(): [Literal] 1 +# 4| Type = [BoolType] bool +# 4| Value = [Literal] 1 +# 4| ValueCategory = prvalue +# 5| getElse(): [BlockStmt] { ... } +# 6| getStmt(0): [ReturnStmt] return ... +# 6| getExpr(): [Literal] 0 +# 6| Type = [BoolType] bool +# 6| Value = [Literal] 0 +# 6| ValueCategory = prvalue +# 10| [TopLevelFunction] bool consteval_2() +# 10| : +# 11| getEntryPoint(): [BlockStmt] { ... } +# 12| getStmt(0): [NotConstevalIfStmt] if ! consteval ... +# 12| getThen(): [BlockStmt] { ... } +# 13| getStmt(0): [ReturnStmt] return ... +# 13| getExpr(): [Literal] 1 +# 13| Type = [BoolType] bool +# 13| Value = [Literal] 1 +# 13| ValueCategory = prvalue +# 14| getElse(): [BlockStmt] { ... } +# 15| getStmt(0): [ReturnStmt] return ... +# 15| getExpr(): [Literal] 0 +# 15| Type = [BoolType] bool +# 15| Value = [Literal] 0 +# 15| ValueCategory = prvalue +# 19| [TopLevelFunction] bool consteval_3() +# 19| : +# 20| getEntryPoint(): [BlockStmt] { ... } +# 21| getStmt(0): [ConstevalIfStmt] if consteval ... +# 21| getThen(): [BlockStmt] { ... } +# 22| getStmt(0): [ReturnStmt] return ... +# 22| getExpr(): [Literal] 1 +# 22| Type = [BoolType] bool +# 22| Value = [Literal] 1 +# 22| ValueCategory = prvalue +# 25| getStmt(1): [ReturnStmt] return ... +# 25| getExpr(): [Literal] 0 +# 25| Type = [BoolType] bool +# 25| Value = [Literal] 0 +# 25| ValueCategory = prvalue +# 28| [TopLevelFunction] bool consteval_4() +# 28| : +# 29| getEntryPoint(): [BlockStmt] { ... } +# 30| getStmt(0): [NotConstevalIfStmt] if ! consteval ... +# 30| getThen(): [BlockStmt] { ... } +# 31| getStmt(0): [ReturnStmt] return ... +# 31| getExpr(): [Literal] 1 +# 31| Type = [BoolType] bool +# 31| Value = [Literal] 1 +# 31| ValueCategory = prvalue +# 34| getStmt(1): [ReturnStmt] return ... +# 34| getExpr(): [Literal] 0 +# 34| Type = [BoolType] bool +# 34| Value = [Literal] 0 +# 34| ValueCategory = prvalue +# 37| [TopLevelFunction] bool consteval_5() +# 37| : +# 38| getEntryPoint(): [BlockStmt] { ... } +# 39| getStmt(0): [DeclStmt] declaration +# 39| getDeclarationEntry(0): [VariableDeclarationEntry] definition of r +# 39| Type = [BoolType] bool +# 39| getVariable().getInitializer(): [Initializer] initializer for r +# 39| getExpr(): [Literal] 1 +# 39| Type = [BoolType] bool +# 39| Value = [Literal] 1 +# 39| ValueCategory = prvalue +# 41| getStmt(1): [NotConstevalIfStmt] if ! consteval ... +# 41| getThen(): [BlockStmt] { ... } +# 42| getStmt(0): [ExprStmt] ExprStmt +# 42| getExpr(): [AssignExpr] ... = ... +# 42| Type = [BoolType] bool +# 42| ValueCategory = lvalue +# 42| getLValue(): [VariableAccess] r +# 42| Type = [BoolType] bool +# 42| ValueCategory = lvalue +# 42| getRValue(): [Literal] 0 +# 42| Type = [BoolType] bool +# 42| Value = [Literal] 0 +# 42| ValueCategory = prvalue +# 45| getStmt(2): [ReturnStmt] return ... +# 45| getExpr(): [VariableAccess] r +# 45| Type = [BoolType] bool +# 45| ValueCategory = prvalue(load) +# 48| [TopLevelFunction] bool consteval_6() +# 48| : +# 49| getEntryPoint(): [BlockStmt] { ... } +# 50| getStmt(0): [DeclStmt] declaration +# 50| getDeclarationEntry(0): [VariableDeclarationEntry] definition of r +# 50| Type = [BoolType] bool +# 50| getVariable().getInitializer(): [Initializer] initializer for r +# 50| getExpr(): [Literal] 1 +# 50| Type = [BoolType] bool +# 50| Value = [Literal] 1 +# 50| ValueCategory = prvalue +# 52| getStmt(1): [ConstevalIfStmt] if consteval ... +# 52| getThen(): [BlockStmt] { ... } +# 53| getStmt(0): [ExprStmt] ExprStmt +# 53| getExpr(): [AssignExpr] ... = ... +# 53| Type = [BoolType] bool +# 53| ValueCategory = lvalue +# 53| getLValue(): [VariableAccess] r +# 53| Type = [BoolType] bool +# 53| ValueCategory = lvalue +# 53| getRValue(): [Literal] 0 +# 53| Type = [BoolType] bool +# 53| Value = [Literal] 0 +# 53| ValueCategory = prvalue +# 56| getStmt(2): [ReturnStmt] return ... +# 56| getExpr(): [VariableAccess] r +# 56| Type = [BoolType] bool +# 56| ValueCategory = prvalue(load) many-defs-per-use.cpp: # 34| [TopLevelFunction] void many_defs_per_use() # 34| : diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ir.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ir.expected index 4f340042cb9a..9b491395a508 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ir.expected @@ -19740,6 +19740,104 @@ ir.cpp: # 2724| v2724_12(void) = AliasedUse : ~m2725_8 # 2724| v2724_13(void) = ExitFunction : +ir23.cpp: +# 1| bool consteval_1() +# 1| Block 0 +# 1| v1_1(void) = EnterFunction : +# 1| m1_2(unknown) = AliasedDefinition : +# 1| m1_3(unknown) = InitializeNonLocal : +# 1| m1_4(unknown) = Chi : total:m1_2, partial:m1_3 +# 6| r6_1(glval) = VariableAddress[#return] : +# 6| r6_2(bool) = Constant[0] : +# 6| m6_3(bool) = Store[#return] : &:r6_1, r6_2 +# 1| r1_5(glval) = VariableAddress[#return] : +# 1| v1_6(void) = ReturnValue : &:r1_5, m6_3 +# 1| v1_7(void) = AliasedUse : m1_3 +# 1| v1_8(void) = ExitFunction : + +# 10| bool consteval_2() +# 10| Block 0 +# 10| v10_1(void) = EnterFunction : +# 10| m10_2(unknown) = AliasedDefinition : +# 10| m10_3(unknown) = InitializeNonLocal : +# 10| m10_4(unknown) = Chi : total:m10_2, partial:m10_3 +# 13| r13_1(glval) = VariableAddress[#return] : +# 13| r13_2(bool) = Constant[1] : +# 13| m13_3(bool) = Store[#return] : &:r13_1, r13_2 +# 10| r10_5(glval) = VariableAddress[#return] : +# 10| v10_6(void) = ReturnValue : &:r10_5, m13_3 +# 10| v10_7(void) = AliasedUse : m10_3 +# 10| v10_8(void) = ExitFunction : + +# 19| bool consteval_3() +# 19| Block 0 +# 19| v19_1(void) = EnterFunction : +# 19| m19_2(unknown) = AliasedDefinition : +# 19| m19_3(unknown) = InitializeNonLocal : +# 19| m19_4(unknown) = Chi : total:m19_2, partial:m19_3 +# 21| v21_1(void) = NoOp : +# 25| r25_1(glval) = VariableAddress[#return] : +# 25| r25_2(bool) = Constant[0] : +# 25| m25_3(bool) = Store[#return] : &:r25_1, r25_2 +# 19| r19_5(glval) = VariableAddress[#return] : +# 19| v19_6(void) = ReturnValue : &:r19_5, m25_3 +# 19| v19_7(void) = AliasedUse : m19_3 +# 19| v19_8(void) = ExitFunction : + +# 28| bool consteval_4() +# 28| Block 0 +# 28| v28_1(void) = EnterFunction : +# 28| m28_2(unknown) = AliasedDefinition : +# 28| m28_3(unknown) = InitializeNonLocal : +# 28| m28_4(unknown) = Chi : total:m28_2, partial:m28_3 +# 31| r31_1(glval) = VariableAddress[#return] : +# 31| r31_2(bool) = Constant[1] : +# 31| m31_3(bool) = Store[#return] : &:r31_1, r31_2 +# 28| r28_5(glval) = VariableAddress[#return] : +# 28| v28_6(void) = ReturnValue : &:r28_5, m31_3 +# 28| v28_7(void) = AliasedUse : m28_3 +# 28| v28_8(void) = ExitFunction : + +# 37| bool consteval_5() +# 37| Block 0 +# 37| v37_1(void) = EnterFunction : +# 37| m37_2(unknown) = AliasedDefinition : +# 37| m37_3(unknown) = InitializeNonLocal : +# 37| m37_4(unknown) = Chi : total:m37_2, partial:m37_3 +# 39| r39_1(glval) = VariableAddress[r] : +# 39| r39_2(bool) = Constant[1] : +# 39| m39_3(bool) = Store[r] : &:r39_1, r39_2 +# 42| r42_1(bool) = Constant[0] : +# 42| r42_2(glval) = VariableAddress[r] : +# 42| m42_3(bool) = Store[r] : &:r42_2, r42_1 +# 45| r45_1(glval) = VariableAddress[#return] : +# 45| r45_2(glval) = VariableAddress[r] : +# 45| r45_3(bool) = Load[r] : &:r45_2, m42_3 +# 45| m45_4(bool) = Store[#return] : &:r45_1, r45_3 +# 37| r37_5(glval) = VariableAddress[#return] : +# 37| v37_6(void) = ReturnValue : &:r37_5, m45_4 +# 37| v37_7(void) = AliasedUse : m37_3 +# 37| v37_8(void) = ExitFunction : + +# 48| bool consteval_6() +# 48| Block 0 +# 48| v48_1(void) = EnterFunction : +# 48| m48_2(unknown) = AliasedDefinition : +# 48| m48_3(unknown) = InitializeNonLocal : +# 48| m48_4(unknown) = Chi : total:m48_2, partial:m48_3 +# 50| r50_1(glval) = VariableAddress[r] : +# 50| r50_2(bool) = Constant[1] : +# 50| m50_3(bool) = Store[r] : &:r50_1, r50_2 +# 52| v52_1(void) = NoOp : +# 56| r56_1(glval) = VariableAddress[#return] : +# 56| r56_2(glval) = VariableAddress[r] : +# 56| r56_3(bool) = Load[r] : &:r56_2, m50_3 +# 56| m56_4(bool) = Store[#return] : &:r56_1, r56_3 +# 48| r48_5(glval) = VariableAddress[#return] : +# 48| v48_6(void) = ReturnValue : &:r48_5, m56_4 +# 48| v48_7(void) = AliasedUse : m48_3 +# 48| v48_8(void) = ExitFunction : + many-defs-per-use.cpp: # 34| void many_defs_per_use() # 34| Block 0 diff --git a/cpp/ql/test/library-tests/ir/ir/ir23.cpp b/cpp/ql/test/library-tests/ir/ir/ir23.cpp new file mode 100644 index 000000000000..607d28d0acc1 --- /dev/null +++ b/cpp/ql/test/library-tests/ir/ir/ir23.cpp @@ -0,0 +1,59 @@ +constexpr bool consteval_1() noexcept +{ + if consteval { + return true; + } else { + return false; + } +} + +constexpr bool consteval_2() noexcept +{ + if ! consteval { + return true; + } else { + return false; + } +} + +constexpr bool consteval_3() noexcept +{ + if consteval { + return true; + } + + return false; +} + +constexpr bool consteval_4() noexcept +{ + if ! consteval { + return true; + } + + return false; +} + +constexpr bool consteval_5() noexcept +{ + bool r = true; + + if ! consteval { + r = false; + } + + return r; +} + +constexpr bool consteval_6() noexcept +{ + bool r = true; + + if consteval { + r = false; + } + + return r; +} + +// semmle-extractor-options: -std=c++23 diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index 31faa22d9fcd..a71422d93f30 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -18055,6 +18055,143 @@ ir.cpp: # 2724| v2724_10(void) = AliasedUse : ~m? # 2724| v2724_11(void) = ExitFunction : +ir23.cpp: +# 1| bool consteval_1() +# 1| Block 0 +# 1| v1_1(void) = EnterFunction : +# 1| mu1_2(unknown) = AliasedDefinition : +# 1| mu1_3(unknown) = InitializeNonLocal : +# 6| r6_1(glval) = VariableAddress[#return] : +# 6| r6_2(bool) = Constant[0] : +# 6| mu6_3(bool) = Store[#return] : &:r6_1, r6_2 +#-----| Goto -> Block 1 + +# 1| Block 1 +# 1| r1_4(glval) = VariableAddress[#return] : +# 1| v1_5(void) = ReturnValue : &:r1_4, ~m? +# 1| v1_6(void) = AliasedUse : ~m? +# 1| v1_7(void) = ExitFunction : + +# 4| Block 2 +# 4| r4_1(glval) = VariableAddress[#return] : +# 4| r4_2(bool) = Constant[1] : +# 4| mu4_3(bool) = Store[#return] : &:r4_1, r4_2 +#-----| Goto -> Block 1 + +# 10| bool consteval_2() +# 10| Block 0 +# 10| v10_1(void) = EnterFunction : +# 10| mu10_2(unknown) = AliasedDefinition : +# 10| mu10_3(unknown) = InitializeNonLocal : +# 13| r13_1(glval) = VariableAddress[#return] : +# 13| r13_2(bool) = Constant[1] : +# 13| mu13_3(bool) = Store[#return] : &:r13_1, r13_2 +#-----| Goto -> Block 1 + +# 10| Block 1 +# 10| r10_4(glval) = VariableAddress[#return] : +# 10| v10_5(void) = ReturnValue : &:r10_4, ~m? +# 10| v10_6(void) = AliasedUse : ~m? +# 10| v10_7(void) = ExitFunction : + +# 15| Block 2 +# 15| r15_1(glval) = VariableAddress[#return] : +# 15| r15_2(bool) = Constant[0] : +# 15| mu15_3(bool) = Store[#return] : &:r15_1, r15_2 +#-----| Goto -> Block 1 + +# 19| bool consteval_3() +# 19| Block 0 +# 19| v19_1(void) = EnterFunction : +# 19| mu19_2(unknown) = AliasedDefinition : +# 19| mu19_3(unknown) = InitializeNonLocal : +# 21| v21_1(void) = NoOp : +# 25| r25_1(glval) = VariableAddress[#return] : +# 25| r25_2(bool) = Constant[0] : +# 25| mu25_3(bool) = Store[#return] : &:r25_1, r25_2 +#-----| Goto -> Block 1 + +# 19| Block 1 +# 19| r19_4(glval) = VariableAddress[#return] : +# 19| v19_5(void) = ReturnValue : &:r19_4, ~m? +# 19| v19_6(void) = AliasedUse : ~m? +# 19| v19_7(void) = ExitFunction : + +# 22| Block 2 +# 22| r22_1(glval) = VariableAddress[#return] : +# 22| r22_2(bool) = Constant[1] : +# 22| mu22_3(bool) = Store[#return] : &:r22_1, r22_2 +#-----| Goto -> Block 1 + +# 28| bool consteval_4() +# 28| Block 0 +# 28| v28_1(void) = EnterFunction : +# 28| mu28_2(unknown) = AliasedDefinition : +# 28| mu28_3(unknown) = InitializeNonLocal : +# 31| r31_1(glval) = VariableAddress[#return] : +# 31| r31_2(bool) = Constant[1] : +# 31| mu31_3(bool) = Store[#return] : &:r31_1, r31_2 +#-----| Goto -> Block 1 + +# 28| Block 1 +# 28| r28_4(glval) = VariableAddress[#return] : +# 28| v28_5(void) = ReturnValue : &:r28_4, ~m? +# 28| v28_6(void) = AliasedUse : ~m? +# 28| v28_7(void) = ExitFunction : + +# 34| Block 2 +# 34| r34_1(glval) = VariableAddress[#return] : +# 34| r34_2(bool) = Constant[0] : +# 34| mu34_3(bool) = Store[#return] : &:r34_1, r34_2 +#-----| Goto -> Block 1 + +# 37| bool consteval_5() +# 37| Block 0 +# 37| v37_1(void) = EnterFunction : +# 37| mu37_2(unknown) = AliasedDefinition : +# 37| mu37_3(unknown) = InitializeNonLocal : +# 39| r39_1(glval) = VariableAddress[r] : +# 39| r39_2(bool) = Constant[1] : +# 39| mu39_3(bool) = Store[r] : &:r39_1, r39_2 +# 42| r42_1(bool) = Constant[0] : +# 42| r42_2(glval) = VariableAddress[r] : +# 42| mu42_3(bool) = Store[r] : &:r42_2, r42_1 +# 45| r45_1(glval) = VariableAddress[#return] : +# 45| r45_2(glval) = VariableAddress[r] : +# 45| r45_3(bool) = Load[r] : &:r45_2, ~m? +# 45| mu45_4(bool) = Store[#return] : &:r45_1, r45_3 +# 37| r37_4(glval) = VariableAddress[#return] : +# 37| v37_5(void) = ReturnValue : &:r37_4, ~m? +# 37| v37_6(void) = AliasedUse : ~m? +# 37| v37_7(void) = ExitFunction : + +# 48| bool consteval_6() +# 48| Block 0 +# 48| v48_1(void) = EnterFunction : +# 48| mu48_2(unknown) = AliasedDefinition : +# 48| mu48_3(unknown) = InitializeNonLocal : +# 50| r50_1(glval) = VariableAddress[r] : +# 50| r50_2(bool) = Constant[1] : +# 50| mu50_3(bool) = Store[r] : &:r50_1, r50_2 +# 52| v52_1(void) = NoOp : +#-----| Goto -> Block 2 + +# 53| Block 1 +# 53| r53_1(bool) = Constant[0] : +# 53| r53_2(glval) = VariableAddress[r] : +# 53| mu53_3(bool) = Store[r] : &:r53_2, r53_1 +#-----| Goto -> Block 2 + +# 56| Block 2 +# 56| r56_1(glval) = VariableAddress[#return] : +# 56| r56_2(glval) = VariableAddress[r] : +# 56| r56_3(bool) = Load[r] : &:r56_2, ~m? +# 56| mu56_4(bool) = Store[#return] : &:r56_1, r56_3 +# 48| r48_4(glval) = VariableAddress[#return] : +# 48| v48_5(void) = ReturnValue : &:r48_4, ~m? +# 48| v48_6(void) = AliasedUse : ~m? +# 48| v48_7(void) = ExitFunction : + many-defs-per-use.cpp: # 34| void many_defs_per_use() # 34| Block 0