Skip to content

Commit f4a0600

Browse files
C++: Handle casts to void in IR
Casts to `void` did not have a semantic conversion type in the AST, so they also weren't getting generated correctly in the IR. I've added a `VoidConversion` class to the AST, along with tests. I've also added IR translation for such conversions, using a new `ConvertToVoid` opcode. I'm not sure if it's really necessary to generate an instruction to represent this, but it may be useful for detecting values that are explicitly unused (e.g. return value from a call). I added two new sanity queries for the IR to detect the following: - IR blocks with no successors, which usually indicates bad IR translation - Phi instruction without an operand for one of the predecessor blocks. These sanity queries found another subtle IR translation bug. If an expression that is normally translated as a condition (e.g. `&&`, `||`, or parens in certain contexts) has a constant value, we were not creating a `TranslatedExpr` for the expression at all. I changed it to always treat a constant condition as a non-condition expression.
1 parent bea298f commit f4a0600

File tree

18 files changed

+362
-2
lines changed

18 files changed

+362
-2
lines changed

cpp/ql/src/semmle/code/cpp/exprs/Cast.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,20 @@ class BoolConversion extends Cast {
292292
}
293293
}
294294

295+
/**
296+
* A conversion to `void`.
297+
*/
298+
class VoidConversion extends Cast {
299+
VoidConversion() {
300+
conversionkinds(this, 0) and
301+
getType().getUnspecifiedType() instanceof VoidType
302+
}
303+
304+
override string getSemanticConversionString() {
305+
result = "conversion to void"
306+
}
307+
}
308+
295309
/**
296310
* A conversion between two pointers or glvalues related by inheritance. The
297311
* base class will always be either a direct base class of the derived class,

cpp/ql/src/semmle/code/cpp/ir/internal/Instruction.qll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,31 @@ module InstructionSanity {
6767
not tag instanceof UnmodeledUseOperand
6868
}
6969

70+
/**
71+
* Holds if `Phi` instruction `instr` has fewer than two operands.
72+
*/
73+
query predicate missingPhiOperands(PhiInstruction instr, int predIndex, Location predLoc) {
74+
exists(IRBlock pred |
75+
pred = instr.getBlock().getAPredecessor() and
76+
predLoc = pred.getLocation() and
77+
predIndex = pred.getDisplayIndex() and
78+
not exists(PhiOperand operand |
79+
exists(instr.getOperand(operand)) and
80+
operand.getPredecessorBlock() = pred
81+
)
82+
)
83+
}
84+
85+
/**
86+
* Holds if an instruction, other than `ExitFunction`, has no successors.
87+
*/
88+
query predicate instructionWithoutSuccessor(Instruction instr) {
89+
not exists(instr.getASuccessor()) and
90+
not instr instanceof ExitFunctionInstruction and
91+
// Phi instructions aren't linked into the instruction-level flow graph.
92+
not instr instanceof PhiInstruction
93+
}
94+
7095
query predicate operandAcrossFunctions(
7196
Instruction op, Instruction operand, OperandTag tag
7297
) {

cpp/ql/src/semmle/code/cpp/ir/internal/Opcode.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ private newtype TOpcode =
3333
TPointerSub() or
3434
TPointerDiff() or
3535
TConvert() or
36+
TConvertToVoid() or
3637
TConvertToBase() or
3738
TConvertToVirtualBase() or
3839
TConvertToDerived() or
@@ -125,6 +126,7 @@ module Opcode {
125126
class PointerSub extends PointerOffsetOpcode, TPointerSub { override final string toString() { result = "PointerSub" } }
126127
class PointerDiff extends PointerArithmeticOpcode, TPointerDiff { override final string toString() { result = "PointerDiff" } }
127128
class Convert extends UnaryOpcode, TConvert { override final string toString() { result = "Convert" } }
129+
class ConvertToVoid extends UnaryOpcode, TConvertToVoid { override final string toString() { result = "ConvertToVoid" } }
128130
class ConvertToBase extends UnaryOpcode, TConvertToBase { override final string toString() { result = "ConvertToBase" } }
129131
class ConvertToVirtualBase extends UnaryOpcode, TConvertToVirtualBase { override final string toString() { result = "ConvertToVirtualBase" } }
130132
class ConvertToDerived extends UnaryOpcode, TConvertToDerived { override final string toString() { result = "ConvertToDerived" } }

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ private predicate ignoreElement(Element element) {
8989
* a value.
9090
*/
9191
private predicate isNativeCondition(Expr expr) {
92-
expr instanceof BinaryLogicalOperation
92+
expr instanceof BinaryLogicalOperation and
93+
not expr.isConstant()
9394
}
9495

9596
/**
@@ -101,7 +102,8 @@ private predicate isFlexibleCondition(Expr expr) {
101102
expr instanceof ParenthesisExpr or
102103
expr instanceof NotExpr
103104
) and
104-
usedAsCondition(expr)
105+
usedAsCondition(expr) and
106+
not expr.isConstant()
105107
}
106108

107109
/**

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,19 @@ abstract class TranslatedSingleInstructionConversion extends TranslatedConversio
11261126
abstract Opcode getOpcode();
11271127
}
11281128

1129+
/**
1130+
* The translation of an explicit cast to `void`.
1131+
*/
1132+
class TranslatedVoidConversion extends TranslatedSingleInstructionConversion {
1133+
TranslatedVoidConversion() {
1134+
conv instanceof VoidConversion
1135+
}
1136+
1137+
override Opcode getOpcode() {
1138+
result instanceof Opcode::ConvertToVoid
1139+
}
1140+
}
1141+
11291142
/**
11301143
* Represents the translation of a conversion expression that generates a
11311144
* `Convert` instruction.

cpp/ql/src/semmle/code/cpp/ssa/internal/aliased_ssa/Instruction.qll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,31 @@ module InstructionSanity {
6767
not tag instanceof UnmodeledUseOperand
6868
}
6969

70+
/**
71+
* Holds if `Phi` instruction `instr` has fewer than two operands.
72+
*/
73+
query predicate missingPhiOperand(PhiInstruction instr, int predIndex, Location predLoc) {
74+
exists(IRBlock pred |
75+
pred = instr.getBlock().getAPredecessor() and
76+
predLoc = pred.getLocation() and
77+
predIndex = pred.getDisplayIndex() and
78+
not exists(PhiOperand operand |
79+
exists(instr.getOperand(operand)) and
80+
operand.getPredecessorBlock() = pred
81+
)
82+
)
83+
}
84+
85+
/**
86+
* Holds if an instruction, other than `ExitFunction`, has no successors.
87+
*/
88+
query predicate instructionWithoutSuccessor(Instruction instr) {
89+
not exists(instr.getASuccessor()) and
90+
not instr instanceof ExitFunctionInstruction and
91+
// Phi instructions aren't linked into the instruction-level flow graph.
92+
not instr instanceof PhiInstruction
93+
}
94+
7095
query predicate operandAcrossFunctions(
7196
Instruction op, Instruction operand, OperandTag tag
7297
) {

cpp/ql/src/semmle/code/cpp/ssa/internal/ssa/Instruction.qll

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,31 @@ module InstructionSanity {
6767
not tag instanceof UnmodeledUseOperand
6868
}
6969

70+
/**
71+
* Holds if `Phi` instruction `instr` has fewer than two operands.
72+
*/
73+
query predicate missingPhiOperands(PhiInstruction instr, int predIndex, Location predLoc) {
74+
exists(IRBlock pred |
75+
pred = instr.getBlock().getAPredecessor() and
76+
predLoc = pred.getLocation() and
77+
predIndex = pred.getDisplayIndex() and
78+
not exists(PhiOperand operand |
79+
exists(instr.getOperand(operand)) and
80+
operand.getPredecessorBlock() = pred
81+
)
82+
)
83+
}
84+
85+
/**
86+
* Holds if an instruction, other than `ExitFunction`, has no successors.
87+
*/
88+
query predicate instructionWithoutSuccessor(Instruction instr) {
89+
not exists(instr.getASuccessor()) and
90+
not instr instanceof ExitFunctionInstruction and
91+
// Phi instructions aren't linked into the instruction-level flow graph.
92+
not instr instanceof PhiInstruction
93+
}
94+
7095
query predicate operandAcrossFunctions(
7196
Instruction op, Instruction operand, OperandTag tag
7297
) {

cpp/ql/test/library-tests/conversions/conversions.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,16 @@ void FuncPtrConversions(int(*pfn)(int), void* p) {
235235
p = (void*)pfn;
236236
pfn = (int(*)(int))p;
237237
}
238+
239+
int Func();
240+
241+
void ConversionsToVoid() {
242+
int x;
243+
(void)x;
244+
static_cast<void>(x);
245+
(void)Func();
246+
static_cast<void>(Func());
247+
(void)1;
248+
static_cast<void>(1);
249+
}
250+

cpp/ql/test/library-tests/conversions/conversions.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,9 @@
143143
| conversions.cpp:231:28:231:63 | dynamic_cast<PolymorphicDerived>... | dynamic_cast | lval | PolymorphicDerived | PolymorphicBase |
144144
| conversions.cpp:235:7:235:16 | (void *)... | pointer conversion | prval | void * | ..(*)(..) |
145145
| conversions.cpp:236:9:236:22 | (..(*)(..))... | pointer conversion | prval | ..(*)(..) | void * |
146+
| conversions.cpp:243:3:243:9 | (void)... | conversion to void | prval | void | int |
147+
| conversions.cpp:244:3:244:22 | static_cast<void>... | conversion to void | prval | void | int |
148+
| conversions.cpp:245:3:245:14 | (void)... | conversion to void | prval | void | int |
149+
| conversions.cpp:246:3:246:27 | static_cast<void>... | conversion to void | prval | void | int |
150+
| conversions.cpp:247:3:247:9 | (void)... | conversion to void | prval | void | int |
151+
| conversions.cpp:248:3:248:22 | static_cast<void>... | conversion to void | prval | void | int |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
missingOperand
22
unexpectedOperand
33
duplicateOperand
4+
missingPhiOperand
5+
instructionWithoutSuccessor
46
operandAcrossFunctions

0 commit comments

Comments
 (0)