NoQ created this revision. NoQ added reviewers: rsmith, doug.gregor, dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet. Herald added subscribers: cfe-commits, rnkovacs.
In code like const int &x = A().x; the destructor for `A()` was not present in the CFG due to two problems in the pattern-matching in `getReferenceInitTemporaryType()`: 1. The no-op cast from `T` to `const T` is not necessarily performed on a record type. It may be performed on essentially any type. In the current example, it is performed on an xvalue of type `int` in order to turn it into a `const int`. 2. Since https://reviews.llvm.org/rC288563 member access is no longer performed over rvalues, but goes after the `MaterializeTemporaryExpr` instead. **This is not an analyzer-specific change.** It may add/remove compiler warnings, but i don't think it makes any sense to hide this behind an analyzer-specific flag. Repository: rC Clang https://reviews.llvm.org/D44238 Files: lib/Analysis/CFG.cpp test/Analysis/auto-obj-dtors-cfg-output.cpp Index: test/Analysis/auto-obj-dtors-cfg-output.cpp =================================================================== --- test/Analysis/auto-obj-dtors-cfg-output.cpp +++ test/Analysis/auto-obj-dtors-cfg-output.cpp @@ -14,6 +14,8 @@ class A { public: + int x; + // CHECK: [B1 (ENTRY)] // CHECK-NEXT: Succs (1): B0 // CHECK: [B0 (EXIT)] @@ -67,6 +69,25 @@ const A& c = A(); } +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// WARNINGS-NEXT: 1: A() (CXXConstructExpr, class A) +// ANALYZER-NEXT: 1: A() (CXXConstructExpr, [B1.2], [B1.3], class A) +// CHECK-NEXT: 2: [B1.1] (BindTemporary) +// CHECK-NEXT: 3: [B1.2] +// CHECK-NEXT: 4: [B1.3].x +// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const int) +// CHECK-NEXT: 6: const int &x = A().x; +// CHECK-NEXT: 7: [B1.6].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_const_ref_to_field() { + const int &x = A().x; +} + // CHECK: [B2 (ENTRY)] // CHECK-NEXT: Succs (1): B1 // CHECK: [B1] Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -1458,16 +1458,15 @@ if (const CastExpr *CE = dyn_cast<CastExpr>(Init)) { if ((CE->getCastKind() == CK_DerivedToBase || CE->getCastKind() == CK_UncheckedDerivedToBase || - CE->getCastKind() == CK_NoOp) && - Init->getType()->isRecordType()) { + CE->getCastKind() == CK_NoOp)) { Init = CE->getSubExpr(); continue; } } - // Skip member accesses into rvalues. + // Skip member accesses. if (const MemberExpr *ME = dyn_cast<MemberExpr>(Init)) { - if (!ME->isArrow() && ME->getBase()->isRValue()) { + if (!ME->isArrow()) { Init = ME->getBase(); continue; } @@ -4873,10 +4872,12 @@ const VarDecl *VD = DE->getVarDecl(); Helper.handleDecl(VD, OS); - const Type* T = VD->getType().getTypePtr(); - if (const ReferenceType* RT = T->getAs<ReferenceType>()) - T = RT->getPointeeType().getTypePtr(); - T = T->getBaseElementTypeUnsafe(); + ASTContext &ACtx = VD->getASTContext(); + QualType T = VD->getType(); + if (T->isReferenceType()) + T = getReferenceInitTemporaryType(ACtx, VD->getInit(), nullptr); + if (const ArrayType *AT = ACtx.getAsArrayType(T)) + T = ACtx.getBaseElementType(AT); OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; OS << " (Implicit destructor)\n";
Index: test/Analysis/auto-obj-dtors-cfg-output.cpp =================================================================== --- test/Analysis/auto-obj-dtors-cfg-output.cpp +++ test/Analysis/auto-obj-dtors-cfg-output.cpp @@ -14,6 +14,8 @@ class A { public: + int x; + // CHECK: [B1 (ENTRY)] // CHECK-NEXT: Succs (1): B0 // CHECK: [B0 (EXIT)] @@ -67,6 +69,25 @@ const A& c = A(); } +// CHECK: [B2 (ENTRY)] +// CHECK-NEXT: Succs (1): B1 +// CHECK: [B1] +// WARNINGS-NEXT: 1: A() (CXXConstructExpr, class A) +// ANALYZER-NEXT: 1: A() (CXXConstructExpr, [B1.2], [B1.3], class A) +// CHECK-NEXT: 2: [B1.1] (BindTemporary) +// CHECK-NEXT: 3: [B1.2] +// CHECK-NEXT: 4: [B1.3].x +// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, NoOp, const int) +// CHECK-NEXT: 6: const int &x = A().x; +// CHECK-NEXT: 7: [B1.6].~A() (Implicit destructor) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK-NEXT: Preds (1): B1 +void test_const_ref_to_field() { + const int &x = A().x; +} + // CHECK: [B2 (ENTRY)] // CHECK-NEXT: Succs (1): B1 // CHECK: [B1] Index: lib/Analysis/CFG.cpp =================================================================== --- lib/Analysis/CFG.cpp +++ lib/Analysis/CFG.cpp @@ -1458,16 +1458,15 @@ if (const CastExpr *CE = dyn_cast<CastExpr>(Init)) { if ((CE->getCastKind() == CK_DerivedToBase || CE->getCastKind() == CK_UncheckedDerivedToBase || - CE->getCastKind() == CK_NoOp) && - Init->getType()->isRecordType()) { + CE->getCastKind() == CK_NoOp)) { Init = CE->getSubExpr(); continue; } } - // Skip member accesses into rvalues. + // Skip member accesses. if (const MemberExpr *ME = dyn_cast<MemberExpr>(Init)) { - if (!ME->isArrow() && ME->getBase()->isRValue()) { + if (!ME->isArrow()) { Init = ME->getBase(); continue; } @@ -4873,10 +4872,12 @@ const VarDecl *VD = DE->getVarDecl(); Helper.handleDecl(VD, OS); - const Type* T = VD->getType().getTypePtr(); - if (const ReferenceType* RT = T->getAs<ReferenceType>()) - T = RT->getPointeeType().getTypePtr(); - T = T->getBaseElementTypeUnsafe(); + ASTContext &ACtx = VD->getASTContext(); + QualType T = VD->getType(); + if (T->isReferenceType()) + T = getReferenceInitTemporaryType(ACtx, VD->getInit(), nullptr); + if (const ArrayType *AT = ACtx.getAsArrayType(T)) + T = ACtx.getBaseElementType(AT); OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()"; OS << " (Implicit destructor)\n";
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits