diff --git a/cpp/ql/lib/semmle/code/cpp/PrintAST.qll b/cpp/ql/lib/semmle/code/cpp/PrintAST.qll index 12061db454d43..11244c756206c 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 c003ec4595ef8..c5292f45055ba 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,28 @@ 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.nodeBeforeDestructors(n2, s) + or + p1.nodeAfter(n1, s.getThen()) and + p2.nodeBeforeDestructors(n2, s) + or + p1.nodeAfter(n1, s.getElse()) and + p2.nodeBeforeDestructors(n2, s) + or + p1.nodeAfterDestructors(n1, s) 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 e0c7d625e81cc..41c1644c61833 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 b161da47620bf..8f39c24b6aaba 100644 --- a/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll +++ b/cpp/ql/lib/semmle/code/cpp/stmts/Stmt.qll @@ -437,6 +437,167 @@ 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 = "ConstevallIfStmt" } + + 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 = "NotConstevallIfStmt" } + 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 a01d8f91b8d49..1aa71a4a687fc 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 0000000000000..a1d6f2f9fa2d8 --- /dev/null +++ b/cpp/ql/test/library-tests/consteval_if/cfg.expected @@ -0,0 +1,48 @@ +| __va_list_tag::operator= | false | 66 | 66 | operator= | +| __va_list_tag::operator= | false | 72 | 72 | operator= | +| test | false | 142 | 142 | test | +| test | false | 147 | 147 | declaration | +| test | false | 152 | 152 | if consteval ... | +| test | false | 154 | 154 | ExprStmt | +| test | false | 156 | 156 | x | +| test | false | 160 | 160 | 1 | +| test | false | 161 | 161 | ... = ... | +| test | false | 163 | 163 | { ... } | +| test | false | 165 | 165 | ExprStmt | +| test | false | 167 | 167 | x | +| test | false | 171 | 171 | 2 | +| test | false | 172 | 172 | ... = ... | +| test | false | 174 | 174 | { ... } | +| test | false | 176 | 176 | if consteval ... | +| test | false | 178 | 178 | ExprStmt | +| test | false | 180 | 180 | x | +| test | false | 184 | 184 | 3 | +| test | false | 185 | 185 | ... = ... | +| test | false | 187 | 187 | { ... } | +| test | false | 189 | 189 | return ... | +| test | false | 191 | 191 | x | +| test | false | 193 | 193 | (bool)... | +| test | false | 194 | 194 | { ... } | +| test | true | 147 | 152 | | +| test | true | 152 | 163 | | +| test | true | 152 | 174 | | +| test | true | 154 | 160 | | +| test | true | 156 | 161 | | +| test | true | 160 | 156 | | +| test | true | 161 | 176 | | +| test | true | 163 | 154 | | +| test | true | 165 | 171 | | +| test | true | 167 | 172 | | +| test | true | 171 | 167 | | +| test | true | 172 | 176 | | +| test | true | 174 | 165 | | +| test | true | 176 | 187 | | +| test | true | 176 | 189 | | +| test | true | 178 | 184 | | +| test | true | 180 | 185 | | +| test | true | 184 | 180 | | +| test | true | 185 | 189 | | +| test | true | 187 | 178 | | +| test | true | 189 | 191 | | +| test | true | 191 | 142 | | +| test | true | 194 | 147 | | 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 0000000000000..0e1f45caf1930 --- /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 0000000000000..6145178477fab --- /dev/null +++ b/cpp/ql/test/library-tests/consteval_if/stmts.expected @@ -0,0 +1,11 @@ +| 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 ... | 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 0000000000000..0d77580b769f6 --- /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 0000000000000..d2304a57429d3 --- /dev/null +++ b/cpp/ql/test/library-tests/consteval_if/test.cpp @@ -0,0 +1,14 @@ +// semmle-extractor-options: -std=c++23 + +constexpr bool test() { + int x; + if consteval { + x = 1; + } else { + x = 2; + } + if consteval { + x = 3; + } + return x; +} diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index bfb123fa68439..4e8c69c0bf190 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): [ConstevallIfStmt] 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): [NotConstevallIfStmt] 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): [ConstevallIfStmt] 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): [NotConstevallIfStmt] 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): [NotConstevallIfStmt] 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): [ConstevallIfStmt] 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 4f340042cb9a1..9b491395a508d 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 0000000000000..607d28d0acc16 --- /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 31faa22d9fcd2..a71422d93f30c 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