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

Skip to content

Commit 4ba2817

Browse files
author
Dave Bartolomeo
committed
Fix IR generation for member access with a prvalue on the RHS
For historical reasons, the extractor marks the temporary object expression used as the qualifier of a member access as a prvalue(load), even though the current C++ standard says that the temporary object materialization results in a glvalue. Added some special handling to ignore the load for both field accesses and member function calls. This fixes all of the consistency failures in our regular tests, and all of the related failures in `syntax-zoo` other than the ones that deal with pointers-to-member, which aren't really supported yet anyway.
1 parent 735c657 commit 4ba2817

9 files changed

Lines changed: 76 additions & 82 deletions

File tree

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

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,15 +234,45 @@ private predicate usedAsCondition(Expr expr) {
234234
* AST as an lvalue-to-rvalue conversion, but the IR represents both a function
235235
* lvalue and a function pointer prvalue the same.
236236
*/
237-
private predicate ignoreLoad(Expr expr) {
237+
predicate ignoreLoad(Expr expr) {
238238
expr.hasLValueToRValueConversion() and
239239
(
240-
expr instanceof ThisExpr or
241-
expr instanceof FunctionAccess or
240+
expr instanceof ThisExpr
241+
or
242+
expr instanceof FunctionAccess
243+
or
242244
expr.(PointerDereferenceExpr).getOperand().getFullyConverted().getType().getUnspecifiedType()
243-
instanceof FunctionPointerType or
245+
instanceof FunctionPointerType
246+
or
244247
expr.(ReferenceDereferenceExpr).getExpr().getType().getUnspecifiedType() instanceof
245248
FunctionReferenceType
249+
or
250+
// The extractor represents the qualifier of a field access or member function call as a load of
251+
// 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`
253+
// 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+
)
246276
)
247277
}
248278

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,14 @@ class TranslatedSimpleConversion extends TranslatedSingleInstructionConversion {
997997
}
998998

999999
override Opcode getOpcode() { result instanceof Opcode::Convert }
1000+
1001+
override predicate isResultGLValue() {
1002+
super.isResultGLValue()
1003+
or
1004+
// If this is a prvalue adjustment, the result is a treated as a glvalue if the source was also
1005+
// treated as a glvalue. See the comment in `ignoreLoad()` for more details.
1006+
expr instanceof PrvalueAdjustmentConversion and ignoreLoad(expr.getExpr())
1007+
}
10001008
}
10011009

10021010
/**

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,7 @@ wronglyMarkedAsConflated
2222
invalidOverlap
2323
nonUniqueEnclosingIRFunction
2424
fieldAddressOnNonPointer
25-
| ir.cpp:1401:45:1401:45 | FieldAddress: y | FieldAddress instruction 'FieldAddress: y' has an object address operand that is not an address, in function '$@'. | ir.cpp:1391:6:1391:31 | void temporary_copy_constructor() | void temporary_copy_constructor() |
2625
thisArgumentIsNonPointer
27-
| ir.cpp:1373:14:1373:18 | Call: call to c_str | Call instruction 'Call: call to c_str' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1365:6:1365:21 | void temporary_string() | void temporary_string() |
28-
| ir.cpp:1374:27:1374:31 | Call: call to c_str | Call instruction 'Call: call to c_str' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1365:6:1365:21 | void temporary_string() | void temporary_string() |
29-
| ir.cpp:1385:23:1385:28 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1379:6:1379:30 | void temporary_destructor_only() | void temporary_destructor_only() |
30-
| ir.cpp:1386:36:1386:41 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1379:6:1379:30 | void temporary_destructor_only() | void temporary_destructor_only() |
31-
| ir.cpp:1397:24:1397:29 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1391:6:1391:31 | void temporary_copy_constructor() | void temporary_copy_constructor() |
32-
| ir.cpp:1398:37:1398:42 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1391:6:1391:31 | void temporary_copy_constructor() | void temporary_copy_constructor() |
3326
missingCanonicalLanguageType
3427
multipleCanonicalLanguageTypes
3528
missingIRType

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

Lines changed: 34 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7564,25 +7564,23 @@ ir.cpp:
75647564
# 1373| v1373_4(void) = Call[String] : func:r1373_3, this:r1373_1
75657565
# 1373| mu1373_5(unknown) = ^CallSideEffect : ~m?
75667566
# 1373| mu1373_6(String) = ^IndirectMayWriteSideEffect[-1] : &:r1373_1
7567-
# 1373| r1373_7(String) = Load[#temp1373:5] : &:r1373_1, ~m?
7568-
# 1373| r1373_8(String) = Convert : r1373_7
7569-
# 1373| r1373_9(glval<unknown>) = FunctionAddress[c_str] :
7570-
# 1373| r1373_10(char *) = Call[c_str] : func:r1373_9, this:r1373_8
7571-
# 1373| mu1373_11(unknown) = ^CallSideEffect : ~m?
7572-
# 1373| v1373_12(void) = ^BufferReadSideEffect[-1] : &:r1373_8, ~m?
7573-
# 1373| mu1373_13(String) = ^IndirectMayWriteSideEffect[-1] : &:r1373_8
7567+
# 1373| r1373_7(glval<String>) = Convert : r1373_1
7568+
# 1373| r1373_8(glval<unknown>) = FunctionAddress[c_str] :
7569+
# 1373| r1373_9(char *) = Call[c_str] : func:r1373_8, this:r1373_7
7570+
# 1373| mu1373_10(unknown) = ^CallSideEffect : ~m?
7571+
# 1373| v1373_11(void) = ^BufferReadSideEffect[-1] : &:r1373_7, ~m?
7572+
# 1373| mu1373_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r1373_7
75747573
# 1374| r1374_1(glval<String>) = VariableAddress[#temp1374:5] :
75757574
# 1374| r1374_2(glval<unknown>) = FunctionAddress[returnValue] :
75767575
# 1374| r1374_3(String) = Call[returnValue] : func:r1374_2
75777576
# 1374| mu1374_4(unknown) = ^CallSideEffect : ~m?
75787577
# 1374| mu1374_5(String) = Store[#temp1374:5] : &:r1374_1, r1374_3
7579-
# 1374| r1374_6(String) = Load[#temp1374:5] : &:r1374_1, ~m?
7580-
# 1374| r1374_7(String) = Convert : r1374_6
7581-
# 1374| r1374_8(glval<unknown>) = FunctionAddress[c_str] :
7582-
# 1374| r1374_9(char *) = Call[c_str] : func:r1374_8, this:r1374_7
7583-
# 1374| mu1374_10(unknown) = ^CallSideEffect : ~m?
7584-
# 1374| v1374_11(void) = ^BufferReadSideEffect[-1] : &:r1374_7, ~m?
7585-
# 1374| mu1374_12(String) = ^IndirectMayWriteSideEffect[-1] : &:r1374_7
7578+
# 1374| r1374_6(glval<String>) = Convert : r1374_1
7579+
# 1374| r1374_7(glval<unknown>) = FunctionAddress[c_str] :
7580+
# 1374| r1374_8(char *) = Call[c_str] : func:r1374_7, this:r1374_6
7581+
# 1374| mu1374_9(unknown) = ^CallSideEffect : ~m?
7582+
# 1374| v1374_10(void) = ^BufferReadSideEffect[-1] : &:r1374_6, ~m?
7583+
# 1374| mu1374_11(String) = ^IndirectMayWriteSideEffect[-1] : &:r1374_6
75867584
# 1376| r1376_1(glval<String>) = VariableAddress[#temp1376:5] :
75877585
# 1376| r1376_2(glval<unknown>) = FunctionAddress[defaultConstruct] :
75887586
# 1376| r1376_3(String) = Call[defaultConstruct] : func:r1376_2
@@ -7634,23 +7632,21 @@ ir.cpp:
76347632
# 1385| r1385_1(glval<destructor_only>) = VariableAddress[#temp1385:5] :
76357633
# 1385| r1385_2(destructor_only) = Constant[0] :
76367634
# 1385| mu1385_3(destructor_only) = Store[#temp1385:5] : &:r1385_1, r1385_2
7637-
# 1385| r1385_4(destructor_only) = Load[#temp1385:5] : &:r1385_1, ~m?
7638-
# 1385| r1385_5(glval<unknown>) = FunctionAddress[method] :
7639-
# 1385| v1385_6(void) = Call[method] : func:r1385_5, this:r1385_4
7640-
# 1385| mu1385_7(unknown) = ^CallSideEffect : ~m?
7641-
# 1385| v1385_8(void) = ^BufferReadSideEffect[-1] : &:r1385_4, ~m?
7642-
# 1385| mu1385_9(destructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1385_4
7635+
# 1385| r1385_4(glval<unknown>) = FunctionAddress[method] :
7636+
# 1385| v1385_5(void) = Call[method] : func:r1385_4, this:r1385_1
7637+
# 1385| mu1385_6(unknown) = ^CallSideEffect : ~m?
7638+
# 1385| v1385_7(void) = ^BufferReadSideEffect[-1] : &:r1385_1, ~m?
7639+
# 1385| mu1385_8(destructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1385_1
76437640
# 1386| r1386_1(glval<destructor_only>) = VariableAddress[#temp1386:5] :
76447641
# 1386| r1386_2(glval<unknown>) = FunctionAddress[returnValue] :
76457642
# 1386| r1386_3(destructor_only) = Call[returnValue] : func:r1386_2
76467643
# 1386| mu1386_4(unknown) = ^CallSideEffect : ~m?
76477644
# 1386| mu1386_5(destructor_only) = Store[#temp1386:5] : &:r1386_1, r1386_3
7648-
# 1386| r1386_6(destructor_only) = Load[#temp1386:5] : &:r1386_1, ~m?
7649-
# 1386| r1386_7(glval<unknown>) = FunctionAddress[method] :
7650-
# 1386| v1386_8(void) = Call[method] : func:r1386_7, this:r1386_6
7651-
# 1386| mu1386_9(unknown) = ^CallSideEffect : ~m?
7652-
# 1386| v1386_10(void) = ^BufferReadSideEffect[-1] : &:r1386_6, ~m?
7653-
# 1386| mu1386_11(destructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1386_6
7645+
# 1386| r1386_6(glval<unknown>) = FunctionAddress[method] :
7646+
# 1386| v1386_7(void) = Call[method] : func:r1386_6, this:r1386_1
7647+
# 1386| mu1386_8(unknown) = ^CallSideEffect : ~m?
7648+
# 1386| v1386_9(void) = ^BufferReadSideEffect[-1] : &:r1386_1, ~m?
7649+
# 1386| mu1386_10(destructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1386_1
76547650
# 1388| r1388_1(glval<destructor_only>) = VariableAddress[#temp1388:5] :
76557651
# 1388| r1388_2(glval<unknown>) = FunctionAddress[defaultConstruct] :
76567652
# 1388| r1388_3(destructor_only) = Call[defaultConstruct] : func:r1388_2
@@ -7716,23 +7712,21 @@ ir.cpp:
77167712
# 1397| v1397_4(void) = Call[copy_constructor] : func:r1397_3, this:r1397_1
77177713
# 1397| mu1397_5(unknown) = ^CallSideEffect : ~m?
77187714
# 1397| mu1397_6(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1397_1
7719-
# 1397| r1397_7(copy_constructor) = Load[#temp1397:5] : &:r1397_1, ~m?
7720-
# 1397| r1397_8(glval<unknown>) = FunctionAddress[method] :
7721-
# 1397| v1397_9(void) = Call[method] : func:r1397_8, this:r1397_7
7722-
# 1397| mu1397_10(unknown) = ^CallSideEffect : ~m?
7723-
# 1397| v1397_11(void) = ^BufferReadSideEffect[-1] : &:r1397_7, ~m?
7724-
# 1397| mu1397_12(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1397_7
7715+
# 1397| r1397_7(glval<unknown>) = FunctionAddress[method] :
7716+
# 1397| v1397_8(void) = Call[method] : func:r1397_7, this:r1397_1
7717+
# 1397| mu1397_9(unknown) = ^CallSideEffect : ~m?
7718+
# 1397| v1397_10(void) = ^BufferReadSideEffect[-1] : &:r1397_1, ~m?
7719+
# 1397| mu1397_11(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1397_1
77257720
# 1398| r1398_1(glval<copy_constructor>) = VariableAddress[#temp1398:5] :
77267721
# 1398| r1398_2(glval<unknown>) = FunctionAddress[returnValue] :
77277722
# 1398| r1398_3(copy_constructor) = Call[returnValue] : func:r1398_2
77287723
# 1398| mu1398_4(unknown) = ^CallSideEffect : ~m?
77297724
# 1398| mu1398_5(copy_constructor) = Store[#temp1398:5] : &:r1398_1, r1398_3
7730-
# 1398| r1398_6(copy_constructor) = Load[#temp1398:5] : &:r1398_1, ~m?
7731-
# 1398| r1398_7(glval<unknown>) = FunctionAddress[method] :
7732-
# 1398| v1398_8(void) = Call[method] : func:r1398_7, this:r1398_6
7733-
# 1398| mu1398_9(unknown) = ^CallSideEffect : ~m?
7734-
# 1398| v1398_10(void) = ^BufferReadSideEffect[-1] : &:r1398_6, ~m?
7735-
# 1398| mu1398_11(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1398_6
7725+
# 1398| r1398_6(glval<unknown>) = FunctionAddress[method] :
7726+
# 1398| v1398_7(void) = Call[method] : func:r1398_6, this:r1398_1
7727+
# 1398| mu1398_8(unknown) = ^CallSideEffect : ~m?
7728+
# 1398| v1398_9(void) = ^BufferReadSideEffect[-1] : &:r1398_1, ~m?
7729+
# 1398| mu1398_10(copy_constructor) = ^IndirectMayWriteSideEffect[-1] : &:r1398_1
77367730
# 1399| r1399_1(glval<copy_constructor>) = VariableAddress[#temp1399:5] :
77377731
# 1399| r1399_2(glval<unknown>) = FunctionAddress[defaultConstruct] :
77387732
# 1399| r1399_3(copy_constructor) = Call[defaultConstruct] : func:r1399_2
@@ -7745,9 +7739,8 @@ ir.cpp:
77457739
# 1401| r1401_4(copy_constructor) = Call[returnValue] : func:r1401_3
77467740
# 1401| mu1401_5(unknown) = ^CallSideEffect : ~m?
77477741
# 1401| mu1401_6(copy_constructor) = Store[#temp1401:13] : &:r1401_2, r1401_4
7748-
# 1401| r1401_7(copy_constructor) = Load[#temp1401:13] : &:r1401_2, ~m?
7749-
# 1401| r1401_8(glval<int>) = FieldAddress[y] : r1401_7
7750-
# 1401| mu1401_9(int) = Store[y] : &:r1401_1, r1401_8
7742+
# 1401| r1401_7(glval<int>) = FieldAddress[y] : r1401_2
7743+
# 1401| mu1401_8(int) = Store[y] : &:r1401_1, r1401_7
77517744
# 1402| v1402_1(void) = NoOp :
77527745
# 1391| v1391_4(void) = ReturnVoid :
77537746
# 1391| v1391_5(void) = AliasedUse : ~m?

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,7 @@ wronglyMarkedAsConflated
2222
invalidOverlap
2323
nonUniqueEnclosingIRFunction
2424
fieldAddressOnNonPointer
25-
| ir.cpp:1401:45:1401:45 | FieldAddress: y | FieldAddress instruction 'FieldAddress: y' has an object address operand that is not an address, in function '$@'. | ir.cpp:1391:6:1391:31 | void temporary_copy_constructor() | void temporary_copy_constructor() |
2625
thisArgumentIsNonPointer
27-
| ir.cpp:1373:14:1373:18 | Call: call to c_str | Call instruction 'Call: call to c_str' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1365:6:1365:21 | void temporary_string() | void temporary_string() |
28-
| ir.cpp:1374:27:1374:31 | Call: call to c_str | Call instruction 'Call: call to c_str' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1365:6:1365:21 | void temporary_string() | void temporary_string() |
29-
| ir.cpp:1385:23:1385:28 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1379:6:1379:30 | void temporary_destructor_only() | void temporary_destructor_only() |
30-
| ir.cpp:1386:36:1386:41 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1379:6:1379:30 | void temporary_destructor_only() | void temporary_destructor_only() |
31-
| ir.cpp:1397:24:1397:29 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1391:6:1391:31 | void temporary_copy_constructor() | void temporary_copy_constructor() |
32-
| ir.cpp:1398:37:1398:42 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1391:6:1391:31 | void temporary_copy_constructor() | void temporary_copy_constructor() |
3326
missingCanonicalLanguageType
3427
multipleCanonicalLanguageTypes
3528
missingIRType

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,7 @@ wronglyMarkedAsConflated
2222
invalidOverlap
2323
nonUniqueEnclosingIRFunction
2424
fieldAddressOnNonPointer
25-
| ir.cpp:1401:45:1401:45 | FieldAddress: y | FieldAddress instruction 'FieldAddress: y' has an object address operand that is not an address, in function '$@'. | ir.cpp:1391:6:1391:31 | void temporary_copy_constructor() | void temporary_copy_constructor() |
2625
thisArgumentIsNonPointer
27-
| ir.cpp:1373:14:1373:18 | Call: call to c_str | Call instruction 'Call: call to c_str' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1365:6:1365:21 | void temporary_string() | void temporary_string() |
28-
| ir.cpp:1374:27:1374:31 | Call: call to c_str | Call instruction 'Call: call to c_str' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1365:6:1365:21 | void temporary_string() | void temporary_string() |
29-
| ir.cpp:1385:23:1385:28 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1379:6:1379:30 | void temporary_destructor_only() | void temporary_destructor_only() |
30-
| ir.cpp:1386:36:1386:41 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1379:6:1379:30 | void temporary_destructor_only() | void temporary_destructor_only() |
31-
| ir.cpp:1397:24:1397:29 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1391:6:1391:31 | void temporary_copy_constructor() | void temporary_copy_constructor() |
32-
| ir.cpp:1398:37:1398:42 | Call: call to method | Call instruction 'Call: call to method' has a `this` argument operand that is not an address, in function '$@'. | ir.cpp:1391:6:1391:31 | void temporary_copy_constructor() | void temporary_copy_constructor() |
3326
missingCanonicalLanguageType
3427
multipleCanonicalLanguageTypes
3528
missingIRType

0 commit comments

Comments
 (0)