mirror of
https://github.com/github/codeql.git
synced 2026-04-27 17:55:19 +02:00
Merge pull request #18502 from jketema/consteval
C++: Support `if consteval` and `if ! consteval`
This commit is contained in:
4
cpp/ql/lib/change-notes/2024-01-16-consteval-if.md
Normal file
4
cpp/ql/lib/change-notes/2024-01-16-consteval-if.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* A new class `ConstevalIfStmt` was introduced, which represents the C++23 `if consteval` and `if ! consteval` statements.
|
||||
@@ -912,6 +912,10 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
|
||||
or
|
||||
s.(ConstexprIfStmt).getElse() = e and pred = "getElse()"
|
||||
or
|
||||
s.(ConstevalIfStmt).getThen() = e and pred = "getThen()"
|
||||
or
|
||||
s.(ConstevalIfStmt).getElse() = e and pred = "getElse()"
|
||||
or
|
||||
s.(Handler).getParameter() = e and pred = "getParameter()"
|
||||
or
|
||||
s.(IfStmt).getInitialization() = e and pred = "getInitialization()"
|
||||
|
||||
@@ -876,6 +876,25 @@ private predicate subEdge(Pos p1, Node n1, Node n2, Pos p2) {
|
||||
p2.nodeAfter(n2, s)
|
||||
)
|
||||
or
|
||||
// NotConstevalIfStmt -> { then, else } ->
|
||||
exists(ConstevalIfStmt 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
|
||||
|
||||
@@ -47,7 +47,7 @@ abstract class TranslatedFlexibleCondition extends TranslatedCondition, Conditio
|
||||
{
|
||||
TranslatedFlexibleCondition() { this = TTranslatedFlexibleCondition(expr) }
|
||||
|
||||
final override predicate handlesDestructorsExplicitly() { none() } // TODO: this needs to be revisted when we get unnamed destructors
|
||||
final override predicate handlesDestructorsExplicitly() { none() } // TODO: this needs to be revisited when we get unnamed destructors
|
||||
|
||||
final override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() }
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ private predicate ignoreExpr(Expr expr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the side effects of `expr` should be ignoredf for the purposes of IR generation.
|
||||
* Holds if the side effects of `expr` should be ignored for the purposes of IR generation.
|
||||
*
|
||||
* In cases involving `constexpr`, a call can wind up as a constant expression. `ignoreExpr()` will
|
||||
* not hold for such a call, since we do need to translate the call (as a constant), but we need to
|
||||
|
||||
@@ -1098,6 +1098,61 @@ class TranslatedConstExprIfStmt extends TranslatedIfLikeStmt {
|
||||
override predicate hasElse() { exists(stmt.getElse()) }
|
||||
}
|
||||
|
||||
class TranslatedConstevalIfStmt extends TranslatedStmt {
|
||||
override ConstevalIfStmt 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;
|
||||
|
||||
|
||||
@@ -437,6 +437,154 @@ 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 ConstevalIfStmt extends Stmt, @stmt_consteval_or_not_consteval_if {
|
||||
override string getAPrimaryQlClass() { result = "ConstevalIfStmt" }
|
||||
|
||||
override string toString() {
|
||||
if this.isNot() then result = "if ! consteval ..." else result = "if consteval ..."
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is a 'not consteval if' statement.
|
||||
*
|
||||
* For example, this holds for
|
||||
* ```cpp
|
||||
* if ! consteval { return true; }
|
||||
* ```
|
||||
* but not for
|
||||
* ```cpp
|
||||
* if consteval { return true; }
|
||||
* ```
|
||||
*/
|
||||
predicate isNot() { this instanceof @stmt_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() {
|
||||
if this.isNot() then result = this.getElse() else result = this.getThen()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
if this.isNot() then result = this.getThen() else result = this.getElse()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()) }
|
||||
}
|
||||
|
||||
private class TLoop = @stmt_while or @stmt_end_test_while or @stmt_range_based_for or @stmt_for;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Support (not) consteval if
|
||||
compatibility: partial
|
||||
Reference in New Issue
Block a user