Skip to content

Commit 471f536

Browse files
authored
Merge pull request github#3307 from dbartol/dbartol/BinaryConditional
C++: IR translation for binary conditional operator
2 parents 5a2dcc5 + 66381e8 commit 471f536

File tree

9 files changed

+696
-210
lines changed

9 files changed

+696
-210
lines changed

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,11 @@ private predicate usedAsCondition(Expr expr) {
200200
or
201201
exists(IfStmt ifStmt | ifStmt.getCondition().getFullyConverted() = expr)
202202
or
203-
exists(ConditionalExpr condExpr | condExpr.getCondition().getFullyConverted() = expr)
203+
exists(ConditionalExpr condExpr |
204+
// The two-operand form of `ConditionalExpr` treats its condition as a value, since it needs to
205+
// be reused as a value if the condition is true.
206+
condExpr.getCondition().getFullyConverted() = expr and not condExpr.isTwoOperand()
207+
)
204208
or
205209
exists(NotExpr notExpr |
206210
notExpr.getOperand().getFullyConverted() = expr and

cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll

Lines changed: 135 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,20 +1758,20 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, St
17581758
private TranslatedExpr getDestructorCall() { result = getTranslatedExpr(expr.getExpr()) }
17591759
}
17601760

1761-
class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionContext {
1761+
/**
1762+
* The IR translation of the `?:` operator. This class has the portions of the implementation that
1763+
* are shared between the standard three-operand form (`a ? b : c`) and the GCC-extension
1764+
* two-operand form (`a ?: c`).
1765+
*/
1766+
abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
17621767
override ConditionalExpr expr;
17631768

1764-
final override TranslatedElement getChild(int id) {
1765-
id = 0 and result = getCondition()
1766-
or
1767-
id = 1 and result = getThen()
1768-
or
1769-
id = 2 and result = getElse()
1770-
}
1771-
1772-
override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
1773-
17741769
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
1770+
// Note that the ternary flavor needs no explicit `ConditionalBranch` instruction here, because
1771+
// the condition is a `TranslatedCondition`, which will simply connect the successor edges of
1772+
// the condition directly to the appropriate then/else block via
1773+
// `getChild[True|False]Successor()`.
1774+
// The binary flavor will override this predicate to add the `ConditionalBranch`.
17751775
not resultIsVoid() and
17761776
(
17771777
(
@@ -1866,13 +1866,13 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
18661866
)
18671867
}
18681868

1869-
override predicate hasTempVariable(TempVariableTag tag, CppType type) {
1869+
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
18701870
not resultIsVoid() and
18711871
tag = ConditionValueTempVar() and
18721872
type = getResultType()
18731873
}
18741874

1875-
override IRVariable getInstructionVariable(InstructionTag tag) {
1875+
final override IRVariable getInstructionVariable(InstructionTag tag) {
18761876
not resultIsVoid() and
18771877
(
18781878
tag = ConditionValueTrueTempAddressTag() or
@@ -1882,25 +1882,75 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
18821882
result = getTempVariable(ConditionValueTempVar())
18831883
}
18841884

1885-
override Instruction getResult() {
1885+
final override Instruction getResult() {
18861886
not resultIsVoid() and
18871887
result = getInstruction(ConditionValueResultLoadTag())
18881888
}
18891889

18901890
override Instruction getChildSuccessor(TranslatedElement child) {
1891+
child = getElse() and
1892+
if elseIsVoid()
1893+
then result = getParent().getChildSuccessor(this)
1894+
else result = getInstruction(ConditionValueFalseTempAddressTag())
1895+
}
1896+
1897+
/**
1898+
* Gets the `TranslatedExpr` for the "then" result. Note that nothing in the base implementation
1899+
* of this class assumes that `getThen()` is disjoint from `getCondition()`.
1900+
*/
1901+
abstract TranslatedExpr getThen();
1902+
1903+
/**
1904+
* Gets the `TranslatedExpr` for the "else" result.
1905+
*/
1906+
final TranslatedExpr getElse() { result = getTranslatedExpr(expr.getElse().getFullyConverted()) }
1907+
1908+
final predicate thenIsVoid() {
1909+
getThen().getResultType().getIRType() instanceof IRVoidType
1910+
or
1911+
// A `ThrowExpr.getType()` incorrectly returns the type of exception being
1912+
// thrown, rather than `void`. Handle that case here.
1913+
expr.getThen() instanceof ThrowExpr
1914+
}
1915+
1916+
private predicate elseIsVoid() {
1917+
getElse().getResultType().getIRType() instanceof IRVoidType
1918+
or
1919+
// A `ThrowExpr.getType()` incorrectly returns the type of exception being
1920+
// thrown, rather than `void`. Handle that case here.
1921+
expr.getElse() instanceof ThrowExpr
1922+
}
1923+
1924+
private predicate resultIsVoid() { getResultType().getIRType() instanceof IRVoidType }
1925+
}
1926+
1927+
/**
1928+
* The IR translation of the ternary conditional operator (`a ? b : c`).
1929+
* For this version, we expand the condition as a `TranslatedCondition`, rather than a
1930+
* `TranslatedExpr`, to simplify the control flow in the presence of short-ciruit logical operators.
1931+
*/
1932+
class TranslatedTernaryConditionalExpr extends TranslatedConditionalExpr, ConditionContext {
1933+
TranslatedTernaryConditionalExpr() { not expr.isTwoOperand() }
1934+
1935+
final override TranslatedElement getChild(int id) {
1936+
id = 0 and result = getCondition()
1937+
or
1938+
id = 1 and result = getThen()
1939+
or
1940+
id = 2 and result = getElse()
1941+
}
1942+
1943+
override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
1944+
1945+
override Instruction getChildSuccessor(TranslatedElement child) {
1946+
result = TranslatedConditionalExpr.super.getChildSuccessor(child)
1947+
or
18911948
(
18921949
child = getThen() and
18931950
if thenIsVoid()
18941951
then result = getParent().getChildSuccessor(this)
18951952
else result = getInstruction(ConditionValueTrueTempAddressTag())
18961953
)
1897-
or
1898-
(
1899-
child = getElse() and
1900-
if elseIsVoid()
1901-
then result = getParent().getChildSuccessor(this)
1902-
else result = getInstruction(ConditionValueFalseTempAddressTag())
1903-
)
19041954
}
19051955

19061956
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
@@ -1917,31 +1967,81 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont
19171967
result = getTranslatedCondition(expr.getCondition().getFullyConverted())
19181968
}
19191969

1920-
private TranslatedExpr getThen() {
1970+
final override TranslatedExpr getThen() {
19211971
result = getTranslatedExpr(expr.getThen().getFullyConverted())
19221972
}
1973+
}
1974+
1975+
/**
1976+
* The IR translation of a two-operand conditional operator (`a ?: b`). This is a GCC language
1977+
* extension.
1978+
* This version of the conditional expression returns its first operand (the condition) if that
1979+
* condition is non-zero. Since we'll be reusing the value of the condition, we'll compute that
1980+
* value directly before branching, even if that value was a short-circuit logical expression.
1981+
*/
1982+
class TranslatedBinaryConditionalExpr extends TranslatedConditionalExpr {
1983+
TranslatedBinaryConditionalExpr() { expr.isTwoOperand() }
19231984

1924-
private TranslatedExpr getElse() {
1925-
result = getTranslatedExpr(expr.getElse().getFullyConverted())
1985+
final override TranslatedElement getChild(int id) {
1986+
// We only truly have two children, because our "condition" and "then" are the same as far as
1987+
// the extractor is concerned.
1988+
id = 0 and result = getCondition()
1989+
or
1990+
id = 1 and result = getElse()
19261991
}
19271992

1928-
private predicate thenIsVoid() {
1929-
getThen().getResultType().getIRType() instanceof IRVoidType
1993+
override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() }
1994+
1995+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
1996+
super.hasInstruction(opcode, tag, resultType)
19301997
or
1931-
// A `ThrowExpr.getType()` incorrectly returns the type of exception being
1932-
// thrown, rather than `void`. Handle that case here.
1933-
expr.getThen() instanceof ThrowExpr
1998+
// For the binary variant, we create our own conditional branch.
1999+
tag = ValueConditionConditionalBranchTag() and
2000+
opcode instanceof Opcode::ConditionalBranch and
2001+
resultType = getVoidType()
19342002
}
19352003

1936-
private predicate elseIsVoid() {
1937-
getElse().getResultType().getIRType() instanceof IRVoidType
2004+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
2005+
result = super.getInstructionSuccessor(tag, kind)
19382006
or
1939-
// A `ThrowExpr.getType()` incorrectly returns the type of exception being
1940-
// thrown, rather than `void`. Handle that case here.
1941-
expr.getElse() instanceof ThrowExpr
2007+
tag = ValueConditionConditionalBranchTag() and
2008+
(
2009+
kind instanceof TrueEdge and
2010+
result = getInstruction(ConditionValueTrueTempAddressTag())
2011+
or
2012+
kind instanceof FalseEdge and
2013+
result = getElse().getFirstInstruction()
2014+
)
19422015
}
19432016

1944-
private predicate resultIsVoid() { getResultType().getIRType() instanceof IRVoidType }
2017+
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
2018+
result = super.getInstructionOperand(tag, operandTag)
2019+
or
2020+
tag = ValueConditionConditionalBranchTag() and
2021+
operandTag instanceof ConditionOperandTag and
2022+
result = getCondition().getResult()
2023+
}
2024+
2025+
override Instruction getChildSuccessor(TranslatedElement child) {
2026+
result = super.getChildSuccessor(child)
2027+
or
2028+
child = getCondition() and result = getInstruction(ValueConditionConditionalBranchTag())
2029+
}
2030+
2031+
private TranslatedExpr getCondition() {
2032+
result = getTranslatedExpr(expr.getCondition().getFullyConverted())
2033+
}
2034+
2035+
final override TranslatedExpr getThen() {
2036+
// The extractor returns the exact same expression for `ConditionalExpr::getCondition()` and
2037+
// `ConditionalExpr::getThen()`, even though the condition may have been converted to `bool`,
2038+
// and the "then" may have been converted to the result type. We'll strip the top-level implicit
2039+
// conversions from this, to skip any conversion to `bool`. We don't have enough information to
2040+
// know how to convert the result to the destination type, especially in the class pointer case,
2041+
// so we'll still sometimes wind up with one operand as the wrong type. This is better than
2042+
// always converting the "then" operand to `bool`, which is almost always the wrong type.
2043+
result = getTranslatedExpr(expr.getThen().getExplicitlyConverted())
2044+
}
19452045
}
19462046

19472047
/**

0 commit comments

Comments
 (0)