Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ee18db7

Browse files
author
Dave Bartolomeo
committed
Fix IR for member accesses on prvalues
This fixes the IR generation for member accesses where the qualifier is a prvalue that is _not_ the load of a `TemporaryObjectExpr`. We synthesize a temporary variable during IR generation instead. It fits into the IR construction code at the same spot as `TranslatedLoad`, since it's basically the opposite of `TranslatedLoad` (prvalue->glvalue instead of vice versa). Note that array prvalues require special treatment. This fixes some consistency errors in the `syntax-zoo`. It introduces three new ones in `dataflow-ir-consistency.expected`, but those are along the same lines as tons of existing failures.
1 parent 98e0ae4 commit ee18db7

14 files changed

Lines changed: 326 additions & 53 deletions

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

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,49 @@ private predicate usedAsCondition(Expr expr) {
227227
)
228228
}
229229

230+
/**
231+
* Holds if `expr` is the result of a field access whose qualifier was a prvalue and whose result is
232+
* a prvalue. These accesses are not marked as having loads, but we do need a load in the IR.
233+
*/
234+
private predicate isPRValueFieldAccessWithImplicitLoad(Expr expr) {
235+
expr instanceof ValueFieldAccess and
236+
expr.isPRValueCategory() and
237+
// No need to do a load if we're replacing the result with a constant anyway.
238+
not isIRConstant(expr) and
239+
// Model an array prvalue as the address of the array, just like an array glvalue.
240+
not expr.getUnspecifiedType() instanceof ArrayType
241+
}
242+
243+
/**
244+
* Holds if `expr` is a prvalue of class type that is used directly as the qualifier for a member
245+
* access, or is used after undergoing a prvalue adjustment conversion.
246+
*/
247+
private predicate isClassPRValueForMemberAccessQualifier(Expr expr) {
248+
exists(Expr qualifier |
249+
exists(FieldAccess access | qualifier = access.getQualifier().getFullyConverted())
250+
or
251+
exists(Call call | qualifier = call.getQualifier().getFullyConverted())
252+
|
253+
// The qualifier is a prvalue of class type.
254+
qualifier.getUnspecifiedType() instanceof Class and
255+
qualifier.isPRValueCategory() and
256+
(
257+
expr = qualifier and not expr instanceof PrvalueAdjustmentConversion
258+
or
259+
// If the qualifier is a prvalue adjustment conversion, the actual object with be provided by
260+
// the operand of that conversion. For example:
261+
// ```c++
262+
// std::string("s").c_str();
263+
// ```
264+
// The object for the qualifier is a prvalue(load) of type `std::string`, but the actual
265+
// fully-converted qualifier of the call to `c_str()` is a prvalue adjustment conversion that
266+
// converts the type to `const std::string` to match the type of the `this` pointer of the
267+
// member function.
268+
expr = qualifier.(PrvalueAdjustmentConversion).getExpr()
269+
)
270+
)
271+
}
272+
230273
/**
231274
* Holds if `expr` has an lvalue-to-rvalue conversion that should be ignored
232275
* when generating IR. This occurs for conversion from an lvalue of function type
@@ -249,30 +292,9 @@ predicate ignoreLoad(Expr expr) {
249292
or
250293
// The extractor represents the qualifier of a field access or member function call as a load of
251294
// the temporary object if the original qualifier was a prvalue. For IR purposes, we always want
252-
// to use the address of the temporary object as the base of a field access or the `this`
295+
// to use the address of the temporary object as the qualifier of a field access or the `this`
253296
// argument to a member function call.
254-
exists(Expr qualifier |
255-
exists(FieldAccess access | qualifier = access.getQualifier().getFullyConverted())
256-
or
257-
exists(Call call | qualifier = call.getQualifier().getFullyConverted())
258-
|
259-
// The qualifier has a class type.
260-
qualifier.getUnspecifiedType() instanceof Class and
261-
(
262-
expr = qualifier
263-
or
264-
// If the qualifier is a prvalue adjustment conversion, the load will be on the operand of
265-
// that conversion. For example:
266-
// ```c++
267-
// std::string("s").c_str();
268-
// ```
269-
// The temporary object for the qualifier is a prvalue(load) of type `std::string`, but the
270-
// actual fully converted qualifier of the call to `c_str()` is a prvalue adjustment
271-
// conversion that converts the type to `const std::string` to match the type of the `this`
272-
// pointer of the member function.
273-
expr = qualifier.(PrvalueAdjustmentConversion).getExpr()
274-
)
275-
)
297+
isClassPRValueForMemberAccessQualifier(expr)
276298
)
277299
}
278300

@@ -308,13 +330,25 @@ predicate hasTranslatedLoad(Expr expr) {
308330
expr.hasLValueToRValueConversion()
309331
or
310332
needsLoadForParentExpr(expr)
333+
or
334+
isPRValueFieldAccessWithImplicitLoad(expr)
311335
) and
312336
not ignoreExpr(expr) and
313337
not isNativeCondition(expr) and
314338
not isFlexibleCondition(expr) and
315339
not ignoreLoad(expr)
316340
}
317341

342+
/**
343+
* Holds if `expr` should have a `TranslatedSyntheticTemporaryObject` on it.
344+
*/
345+
predicate hasTranslatedSyntheticTemporaryObject(Expr expr) {
346+
not ignoreExpr(expr) and
347+
isClassPRValueForMemberAccessQualifier(expr) and
348+
// If it's a load, we'll just ignore the load in `ignoreLoad()`.
349+
not expr.hasLValueToRValueConversion()
350+
}
351+
318352
/**
319353
* Holds if the specified `DeclarationEntry` needs an IR translation. An IR translation is only
320354
* necessary for automatic local variables, or for static local variables with dynamic
@@ -345,6 +379,9 @@ newtype TTranslatedElement =
345379
// A separate element to handle the lvalue-to-rvalue conversion step of an
346380
// expression.
347381
TTranslatedLoad(Expr expr) { hasTranslatedLoad(expr) } or
382+
// A temporary object that we had to synthesize ourselves, so that we could do a field access or
383+
// method call on a prvalue.
384+
TTranslatedSyntheticTemporaryObject(Expr expr) { hasTranslatedSyntheticTemporaryObject(expr) } or
348385
// For expressions that would not otherwise generate an instruction.
349386
TTranslatedResultCopy(Expr expr) {
350387
not ignoreExpr(expr) and

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

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,10 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
110110
}
111111

112112
final override predicate producesExprResult() {
113-
// If there's no load, then this is the only TranslatedExpr for this
113+
// If there's no load or temp object, then this is the only TranslatedExpr for this
114114
// expression.
115115
not hasTranslatedLoad(expr) and
116+
not hasTranslatedSyntheticTemporaryObject(expr) and
116117
// If there's a result copy, then this expression's result is the copy.
117118
not exprNeedsCopyIfNotLoaded(expr)
118119
}
@@ -246,19 +247,35 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext,
246247
private TranslatedCondition getCondition() { result = getTranslatedCondition(expr) }
247248
}
248249

250+
/**
251+
* The IR translation of a node synthesized to adjust the value category of its operand.
252+
* One of:
253+
* - `TranslatedLoad` - Convert from glvalue to prvalue by loading from the location.
254+
* - `TranslatedSyntheticTemporaryObject` - Convert from prvalue to glvalue by storing to a
255+
* temporary variable.
256+
*/
257+
abstract class TranslatedValueCategoryAdjustment extends TranslatedExpr {
258+
final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
259+
260+
final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
261+
262+
final override predicate producesExprResult() {
263+
// A temp object always produces the result of the expression.
264+
any()
265+
}
266+
267+
final TranslatedCoreExpr getOperand() { result.getExpr() = expr }
268+
}
269+
249270
/**
250271
* IR translation of an implicit lvalue-to-rvalue conversion on the result of
251272
* an expression.
252273
*/
253-
class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
274+
class TranslatedLoad extends TranslatedValueCategoryAdjustment, TTranslatedLoad {
254275
TranslatedLoad() { this = TTranslatedLoad(expr) }
255276

256277
override string toString() { result = "Load of " + expr.toString() }
257278

258-
override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
259-
260-
override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
261-
262279
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
263280
tag = LoadTag() and
264281
opcode instanceof Opcode::Load and
@@ -286,18 +303,75 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
286303
result = getOperand().getResult()
287304
)
288305
}
306+
}
289307

290-
final override predicate producesExprResult() {
291-
// A load always produces the result of the expression.
292-
any()
308+
/**
309+
* The IR translation of a temporary object synthesized by the IR to hold a class prvalue on which
310+
* a member access is going to be performed. This differs from `TranslatedTemporaryObjectExpr` in
311+
* that instances of `TranslatedSyntheticTemporaryObject` are synthesized during IR construction,
312+
* whereas `TranslatedTemporaryObjectExpr` instances are created from `TemporaryObjectExpr` nodes
313+
* from the AST.
314+
*/
315+
class TranslatedSyntheticTemporaryObject extends TranslatedValueCategoryAdjustment,
316+
TTranslatedSyntheticTemporaryObject {
317+
TranslatedSyntheticTemporaryObject() { this = TTranslatedSyntheticTemporaryObject(expr) }
318+
319+
override string toString() { result = "Temporary materialization of " + expr.toString() }
320+
321+
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
322+
tag = InitializerVariableAddressTag() and
323+
opcode instanceof Opcode::VariableAddress and
324+
resultType = getTypeForGLValue(expr.getType())
325+
or
326+
tag = InitializerStoreTag() and
327+
opcode instanceof Opcode::Store and
328+
resultType = getTypeForPRValue(expr.getType())
293329
}
294330

295-
TranslatedCoreExpr getOperand() { result.getExpr() = expr }
331+
override predicate isResultGLValue() { any() }
332+
333+
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
334+
tag = InitializerVariableAddressTag() and
335+
result = getInstruction(InitializerStoreTag()) and
336+
kind instanceof GotoEdge
337+
or
338+
tag = InitializerStoreTag() and
339+
result = getParent().getChildSuccessor(this) and
340+
kind instanceof GotoEdge
341+
}
342+
343+
override Instruction getChildSuccessor(TranslatedElement child) {
344+
child = getOperand() and result = getInstruction(InitializerVariableAddressTag())
345+
}
346+
347+
override Instruction getResult() { result = getInstruction(InitializerVariableAddressTag()) }
348+
349+
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
350+
tag = InitializerStoreTag() and
351+
(
352+
operandTag instanceof AddressOperandTag and
353+
result = getInstruction(InitializerVariableAddressTag())
354+
or
355+
operandTag instanceof StoreValueOperandTag and
356+
result = getOperand().getResult()
357+
)
358+
}
359+
360+
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
361+
tag = TempObjectTempVar() and
362+
type = getTypeForPRValue(expr.getType())
363+
}
364+
365+
final override IRVariable getInstructionVariable(InstructionTag tag) {
366+
tag = InitializerVariableAddressTag() and
367+
result = getIRTempVariable(expr, TempObjectTempVar())
368+
}
296369
}
297370

298371
/**
299-
* IR translation of an implicit lvalue-to-rvalue conversion on the result of
300-
* an expression.
372+
* IR translation of an expression that simply returns its result. We generate an otherwise useless
373+
* `CopyValue` instruction for these expressions so that there is at least one instruction
374+
* associated with the expression.
301375
*/
302376
class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy {
303377
TranslatedResultCopy() { this = TTranslatedResultCopy(expr) }

cpp/ql/test/library-tests/ir/ir/PrintAST.expected

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10656,6 +10656,8 @@ ir.cpp:
1065610656
# 1363| params:
1065710657
# 1363| [TemplateFunction,TopLevelFunction] T returnValue<T>()
1065810658
# 1363| params:
10659+
# 1363| [FunctionTemplateInstantiation,TopLevelFunction] UnusualFields returnValue<UnusualFields>()
10660+
# 1363| params:
1065910661
# 1363| [FunctionTemplateInstantiation,TopLevelFunction] copy_constructor returnValue<copy_constructor>()
1066010662
# 1363| params:
1066110663
# 1363| [FunctionTemplateInstantiation,TopLevelFunction] destructor_only returnValue<destructor_only>()
@@ -11061,6 +11063,104 @@ ir.cpp:
1106111063
# 1413| Type = [Struct] Point
1106211064
# 1413| ValueCategory = prvalue
1106311065
# 1414| 7: [ReturnStmt] return ...
11066+
# 1416| [CopyAssignmentOperator] UnusualFields& UnusualFields::operator=(UnusualFields const&)
11067+
# 1416| params:
11068+
#-----| 0: [Parameter] (unnamed parameter 0)
11069+
#-----| Type = [LValueReferenceType] const UnusualFields &
11070+
# 1416| [Constructor] void UnusualFields::UnusualFields()
11071+
# 1416| params:
11072+
# 1416| [CopyConstructor] void UnusualFields::UnusualFields(UnusualFields const&)
11073+
# 1416| params:
11074+
#-----| 0: [Parameter] (unnamed parameter 0)
11075+
#-----| Type = [LValueReferenceType] const UnusualFields &
11076+
# 1416| [MoveConstructor] void UnusualFields::UnusualFields(UnusualFields&&)
11077+
# 1416| params:
11078+
#-----| 0: [Parameter] (unnamed parameter 0)
11079+
#-----| Type = [RValueReferenceType] UnusualFields &&
11080+
# 1421| [TopLevelFunction] void temporary_unusual_fields()
11081+
# 1421| params:
11082+
# 1421| body: [BlockStmt] { ... }
11083+
# 1422| 0: [DeclStmt] declaration
11084+
# 1422| 0: [VariableDeclarationEntry] definition of rx
11085+
# 1422| Type = [LValueReferenceType] const int &
11086+
# 1422| init: [Initializer] initializer for rx
11087+
# 1422| expr: [ReferenceToExpr] (reference to)
11088+
# 1422| Type = [LValueReferenceType] const int &
11089+
# 1422| ValueCategory = prvalue
11090+
# 1422| expr: [CStyleCast] (const int)...
11091+
# 1422| Conversion = [GlvalueConversion] glvalue conversion
11092+
# 1422| Type = [SpecifiedType] const int
11093+
# 1422| ValueCategory = lvalue
11094+
# 1422| expr: [ReferenceDereferenceExpr] (reference dereference)
11095+
# 1422| Type = [IntType] int
11096+
# 1422| ValueCategory = lvalue
11097+
# 1422| expr: [ValueFieldAccess] r
11098+
# 1422| Type = [LValueReferenceType] int &
11099+
# 1422| ValueCategory = prvalue
11100+
# 1422| -1: [FunctionCall] call to returnValue
11101+
# 1422| Type = [Struct] UnusualFields
11102+
# 1422| ValueCategory = prvalue
11103+
# 1423| 1: [DeclStmt] declaration
11104+
# 1423| 0: [VariableDeclarationEntry] definition of x
11105+
# 1423| Type = [IntType] int
11106+
# 1423| init: [Initializer] initializer for x
11107+
# 1423| expr: [ReferenceDereferenceExpr] (reference dereference)
11108+
# 1423| Type = [IntType] int
11109+
# 1423| ValueCategory = prvalue(load)
11110+
# 1423| expr: [ValueFieldAccess] r
11111+
# 1423| Type = [LValueReferenceType] int &
11112+
# 1423| ValueCategory = prvalue
11113+
# 1423| -1: [FunctionCall] call to returnValue
11114+
# 1423| Type = [Struct] UnusualFields
11115+
# 1423| ValueCategory = prvalue
11116+
# 1425| 2: [DeclStmt] declaration
11117+
# 1425| 0: [VariableDeclarationEntry] definition of rf
11118+
# 1425| Type = [LValueReferenceType] const float &
11119+
# 1425| init: [Initializer] initializer for rf
11120+
# 1425| expr: [ReferenceToExpr] (reference to)
11121+
# 1425| Type = [LValueReferenceType] const float &
11122+
# 1425| ValueCategory = prvalue
11123+
# 1425| expr: [CStyleCast] (const float)...
11124+
# 1425| Conversion = [GlvalueConversion] glvalue conversion
11125+
# 1425| Type = [SpecifiedType] const float
11126+
# 1425| ValueCategory = lvalue
11127+
# 1425| expr: [ArrayExpr] access to array
11128+
# 1425| Type = [FloatType] float
11129+
# 1425| ValueCategory = lvalue
11130+
# 1425| 0: [ArrayToPointerConversion] array to pointer conversion
11131+
# 1425| Type = [PointerType] float *
11132+
# 1425| ValueCategory = prvalue
11133+
# 1425| expr: [ValueFieldAccess] a
11134+
# 1425| Type = [ArrayType] float[10]
11135+
# 1425| ValueCategory = prvalue
11136+
# 1425| -1: [FunctionCall] call to returnValue
11137+
# 1425| Type = [Struct] UnusualFields
11138+
# 1425| ValueCategory = prvalue
11139+
# 1425| 1: [Literal] 3
11140+
# 1425| Type = [IntType] int
11141+
# 1425| Value = [Literal] 3
11142+
# 1425| ValueCategory = prvalue
11143+
# 1426| 3: [DeclStmt] declaration
11144+
# 1426| 0: [VariableDeclarationEntry] definition of f
11145+
# 1426| Type = [FloatType] float
11146+
# 1426| init: [Initializer] initializer for f
11147+
# 1426| expr: [ArrayExpr] access to array
11148+
# 1426| Type = [FloatType] float
11149+
# 1426| ValueCategory = prvalue(load)
11150+
# 1426| 0: [ArrayToPointerConversion] array to pointer conversion
11151+
# 1426| Type = [PointerType] float *
11152+
# 1426| ValueCategory = prvalue
11153+
# 1426| expr: [ValueFieldAccess] a
11154+
# 1426| Type = [ArrayType] float[10]
11155+
# 1426| ValueCategory = prvalue
11156+
# 1426| -1: [FunctionCall] call to returnValue
11157+
# 1426| Type = [Struct] UnusualFields
11158+
# 1426| ValueCategory = prvalue
11159+
# 1426| 1: [Literal] 5
11160+
# 1426| Type = [IntType] int
11161+
# 1426| Value = [Literal] 5
11162+
# 1426| ValueCategory = prvalue
11163+
# 1427| 4: [ReturnStmt] return ...
1106411164
perf-regression.cpp:
1106511165
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
1106611166
# 4| params:

cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ wronglyMarkedAsConflated
2222
invalidOverlap
2323
nonUniqueEnclosingIRFunction
2424
fieldAddressOnNonPointer
25-
| ir.cpp:1411:34:1411:34 | FieldAddress: y | FieldAddress instruction 'FieldAddress: y' has an object address operand that is not an address, in function '$@'. | ir.cpp:1404:6:1404:20 | void temporary_point() | void temporary_point() |
2625
thisArgumentIsNonPointer
2726
missingCanonicalLanguageType
2827
multipleCanonicalLanguageTypes

cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ wronglyMarkedAsConflated
2222
invalidOverlap
2323
nonUniqueEnclosingIRFunction
2424
fieldAddressOnNonPointer
25-
| ir.cpp:1411:34:1411:34 | FieldAddress: y | FieldAddress instruction 'FieldAddress: y' has an object address operand that is not an address, in function '$@'. | ir.cpp:1404:6:1404:20 | void temporary_point() | void temporary_point() |
2625
thisArgumentIsNonPointer
2726
missingCanonicalLanguageType
2827
multipleCanonicalLanguageTypes

0 commit comments

Comments
 (0)