public:
/// Returns true when call expression \p CE needs to be represented
/// by CFGCXXRecordTypedCall, as opposed to a regular CFGStmt.
- static bool isCXXRecordTypedCall(CallExpr *CE) {
- return CE->getType().getCanonicalType()->getAsCXXRecordDecl();
+ static bool isCXXRecordTypedCall(CallExpr *CE, const ASTContext &ACtx) {
+ return CE->getCallReturnType(ACtx).getCanonicalType()->getAsCXXRecordDecl();
}
explicit CFGCXXRecordTypedCall(CallExpr *CE,
- const TemporaryObjectConstructionContext *C)
+ const TemporaryObjectConstructionContext *C)
: CFGStmt(CE, CXXRecordTypedCall) {
- assert(isCXXRecordTypedCall(CE));
+ // FIXME: This is not protected against squeezing a non-record-typed-call
+ // into the constructor. An assertion would require passing an ASTContext
+ // which would mean paying for something we don't use.
assert(C);
Data2.setPointer(const_cast<TemporaryObjectConstructionContext *>(C));
}
void appendCall(CFGBlock *B, CallExpr *CE) {
if (BuildOpts.AddRichCXXConstructors) {
- if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE)) {
+ if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE, *Context)) {
if (const ConstructionContextLayer *Layer =
ConstructionContextMap.lookup(CE)) {
const ConstructionContext *CC =
case Stmt::CXXOperatorCallExprClass:
case Stmt::UserDefinedLiteralClass: {
auto *CE = cast<CallExpr>(Child);
- if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE))
+ if (CFGCXXRecordTypedCall::isCXXRecordTypedCall(CE, *Context))
consumeConstructionContext(Layer, CE);
break;
}
return 0;
}
+namespace pass_references_through {
+class C {
+public:
+ ~C() {}
+};
+
+const C &foo1();
+C &&foo2();
+
+// In these examples the foo() expression has record type, not reference type.
+// Don't try to figure out how to perform construction of the record here.
+const C &bar1() { return foo1(); } // no-crash
+C &&bar2() { return foo2(); } // no-crash
+} // end namespace pass_references_through
+
// CHECK: [B1 (ENTRY)]
// CHECK: Succs (1): B0
// CHECK: [B0 (EXIT)]
// CHECK: Succs (2): B8 B1
// CHECK: [B0 (EXIT)]
// CHECK: Preds (3): B1 B2 B4
+// CHECK: [B1 (ENTRY)]
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B2 (ENTRY)]
+// CHECK: Succs (1): B1
+// CHECK: [B1]
+// CHECK: 1: foo1
+// CHECK: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, const class pass_references_through::C &(*)(void))
+// CHECK: 3: [B1.2]()
+// CHECK: 4: return [B1.3];
+// CHECK: Preds (1): B2
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
+// CHECK: [B2 (ENTRY)]
+// CHECK: Succs (1): B1
+// CHECK: [B1]
+// CHECK: 1: foo2
+// CHECK: 2: [B1.1] (ImplicitCastExpr, FunctionToPointerDecay, class pass_references_through::C &&(*)(void))
+// CHECK: 3: [B1.2]()
+// CHECK: 4: return [B1.3];
+// CHECK: Preds (1): B2
+// CHECK: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK: Preds (1): B1
}
} // end namespace implicit_constructor_conversion
+namespace pass_references_through {
+class C {
+public:
+ ~C() {}
+};
+
+const C &foo1();
+C &&foo2();
+// In these examples the foo() expression has record type, not reference type.
+// Don't try to figure out how to perform construction of the record here.
+const C &bar1() { return foo1(); } // no-crash
+C &&bar2() { return foo2(); } // no-crash
+} // end namespace pass_references_through