[PATCH] D43689: [analyzer] Disable constructor inlining when lifetime extension through fields occurs.
chh added a comment. This change caused an assertion failure in ExprEngineCXX.cpp: https://bugs.llvm.org/show_bug.cgi?id=37166 Repository: rC Clang https://reviews.llvm.org/D43689 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43689: [analyzer] Disable constructor inlining when lifetime extension through fields occurs.
This revision was automatically updated to reflect the committed changes. Closed by commit rC326240: [analyzer] Disable constructor inlining when lifetime extending through a field. (authored by dergachev, committed by ). Changed prior to commit: https://reviews.llvm.org/D43689?vs=135669&id=136133#toc Repository: rC Clang https://reviews.llvm.org/D43689 Files: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp test/Analysis/lifetime-extension.cpp Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp === --- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -678,6 +678,11 @@ // the fake temporary target. if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) return CIP_DisallowedOnce; + + // If the temporary is lifetime-extended by binding a smaller object + // within it to a reference, automatic destructors don't work properly. + if (CallOpts.IsTemporaryLifetimeExtendedViaSubobject) +return CIP_DisallowedOnce; } break; Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp === --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -168,6 +168,18 @@ break; } case ConstructionContext::TemporaryObjectKind: { + const auto *TOCC = cast(CC); + // See if we're lifetime-extended via our field. If so, take a note. + // Because automatic destructors aren't quite working in this case. + if (const auto *MTE = TOCC->getMaterializedTemporaryExpr()) { +if (const ValueDecl *VD = MTE->getExtendingDecl()) { + assert(VD->getType()->isReferenceType()); + if (VD->getType()->getPointeeType().getCanonicalType() != + MTE->GetTemporaryExpr()->getType().getCanonicalType()) { +CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true; + } +} + } // TODO: Support temporaries lifetime-extended via static references. // They'd need a getCXXStaticTempObjectRegion(). CallOpts.IsTemporaryCtorOrDtor = true; Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h === --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -65,6 +65,10 @@ bool IsArrayCtorOrDtor = false; /// This call is a constructor or a destructor of a temporary value. bool IsTemporaryCtorOrDtor = false; +/// This call is a constructor for a temporary that is lifetime-extended +/// by binding a smaller object within it to a reference, for example +/// 'const int &x = C().x;'. +bool IsTemporaryLifetimeExtendedViaSubobject = false; EvalCallOptions() {} }; Index: test/Analysis/lifetime-extension.cpp === --- test/Analysis/lifetime-extension.cpp +++ test/Analysis/lifetime-extension.cpp @@ -39,18 +39,10 @@ const int &y = A().j[1]; // no-crash const int &z = (A().j[1], A().j[0]); // no-crash - clang_analyzer_eval(x == 1); - clang_analyzer_eval(y == 3); - clang_analyzer_eval(z == 2); -#ifdef TEMPORARIES - // expected-warning@-4{{TRUE}} - // expected-warning@-4{{TRUE}} - // expected-warning@-4{{TRUE}} -#else - // expected-warning@-8{{UNKNOWN}} - // expected-warning@-8{{UNKNOWN}} - // expected-warning@-8{{UNKNOWN}} -#endif + // FIXME: All of these should be TRUE, but constructors aren't inlined. + clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(y == 3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(z == 2); // expected-warning{{UNKNOWN}} } } // end namespace pr19539_crash_on_destroying_an_integer @@ -144,12 +136,7 @@ const bool &x = C(true, &after, &before).x; // no-crash } // FIXME: Should be TRUE. Should not warn about garbage value. - clang_analyzer_eval(after == before); -#ifdef TEMPORARIES - // expected-warning@-2{{The left operand of '==' is a garbage value}} -#else - // expected-warning@-4{{UNKNOWN}} -#endif + clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}} } } // end namespace maintain_original_object_address_on_lifetime_extension Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp === --- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -678,6 +678,11 @@ // the fake temporary target. if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) return CIP_DisallowedOnce; + + // If the temporary is lifetime-extended by binding a smal
[PATCH] D43689: [analyzer] Disable constructor inlining when lifetime extension through fields occurs.
NoQ added inline comments. Comment at: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h:70 +/// by binding a smaller object within it to a reference. +bool IsTemporaryLifetimeExtendedViaSubobject = false; dcoughlin wrote: > Would you be willing to add a tiny code example to the comment? I.e., `const > int &x = C().x` Fixed in the commit. Repository: rC Clang https://reviews.llvm.org/D43689 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43689: [analyzer] Disable constructor inlining when lifetime extension through fields occurs.
NoQ added inline comments. Comment at: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp:177 + assert(VD->getType()->isReferenceType()); + if (VD->getType()->getPointeeType().getCanonicalType() != + MTE->GetTemporaryExpr()->getType().getCanonicalType()) { dcoughlin wrote: > I *think* this is safe. But it seems like it would be more direct to use > skipRValueSubobjectAdjustments() and check for sub-object adjustments since > ultimately that is what you care about, right? > skipRValueSubobjectAdjustments() This function doesn't do anything anymore (since rC288563) (in most cases, at least). We now have lvalue sub-objects adjustments that are outside of `MaterializeTemporaryExpr`, and i'm trying to see if there are any by comparing the variable type with the temporary type. If there are still any rvalue sub-object adjustments present in the AST, then i'd have to skip them, as an additional step. Repository: rC Clang https://reviews.llvm.org/D43689 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43689: [analyzer] Disable constructor inlining when lifetime extension through fields occurs.
dcoughlin accepted this revision. dcoughlin added inline comments. This revision is now accepted and ready to land. Comment at: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h:70 +/// by binding a smaller object within it to a reference. +bool IsTemporaryLifetimeExtendedViaSubobject = false; Would you be willing to add a tiny code example to the comment? I.e., `const int &x = C().x` Comment at: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp:177 + assert(VD->getType()->isReferenceType()); + if (VD->getType()->getPointeeType().getCanonicalType() != + MTE->GetTemporaryExpr()->getType().getCanonicalType()) { I *think* this is safe. But it seems like it would be more direct to use skipRValueSubobjectAdjustments() and check for sub-object adjustments since ultimately that is what you care about, right? Repository: rC Clang https://reviews.llvm.org/D43689 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D43689: [analyzer] Disable constructor inlining when lifetime extension through fields occurs.
NoQ created this revision. NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet. Herald added subscribers: cfe-commits, rnkovacs. As i mentioned in https://reviews.llvm.org/D43497, automatic destructors are missing in the CFG in situations like const int &x = C().x; For now i'd rather disable construction inlining, because inlining constructors while doing nothing on destructors is very bad. Repository: rC Clang https://reviews.llvm.org/D43689 Files: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp test/Analysis/lifetime-extension.cpp Index: test/Analysis/lifetime-extension.cpp === --- test/Analysis/lifetime-extension.cpp +++ test/Analysis/lifetime-extension.cpp @@ -39,18 +39,10 @@ const int &y = A().j[1]; // no-crash const int &z = (A().j[1], A().j[0]); // no-crash - clang_analyzer_eval(x == 1); - clang_analyzer_eval(y == 3); - clang_analyzer_eval(z == 2); -#ifdef TEMPORARIES - // expected-warning@-4{{TRUE}} - // expected-warning@-4{{TRUE}} - // expected-warning@-4{{TRUE}} -#else - // expected-warning@-8{{UNKNOWN}} - // expected-warning@-8{{UNKNOWN}} - // expected-warning@-8{{UNKNOWN}} -#endif + // FIXME: All of these should be TRUE, but constructors aren't inlined. + clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(y == 3); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(z == 2); // expected-warning{{UNKNOWN}} } } // end namespace pr19539_crash_on_destroying_an_integer @@ -144,12 +136,7 @@ const bool &x = C(true, &after, &before).x; // no-crash } // FIXME: Should be TRUE. Should not warn about garbage value. - clang_analyzer_eval(after == before); -#ifdef TEMPORARIES - // expected-warning@-2{{The left operand of '==' is a garbage value}} -#else - // expected-warning@-4{{UNKNOWN}} -#endif + clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}} } } // end namespace maintain_original_object_address_on_lifetime_extension Index: lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp === --- lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -678,6 +678,11 @@ // the fake temporary target. if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) return CIP_DisallowedOnce; + + // If the temporary is lifetime-extended by binding a smaller object + // within it to a reference, automatic destructors don't work properly. + if (CallOpts.IsTemporaryLifetimeExtendedViaSubobject) +return CIP_DisallowedOnce; } break; Index: lib/StaticAnalyzer/Core/ExprEngineCXX.cpp === --- lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -168,6 +168,18 @@ break; } case ConstructionContext::TemporaryObjectKind: { + const auto *TOCC = cast(CC); + // See if we're lifetime-extended via our field. If so, take a note. + // Because automatic destructors aren't quite working in this case. + if (const auto *MTE = TOCC->getMaterializedTemporaryExpr()) { +if (const ValueDecl *VD = MTE->getExtendingDecl()) { + assert(VD->getType()->isReferenceType()); + if (VD->getType()->getPointeeType().getCanonicalType() != + MTE->GetTemporaryExpr()->getType().getCanonicalType()) { +CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true; + } +} + } // TODO: Support temporaries lifetime-extended via static references. // They'd need a getCXXStaticTempObjectRegion(). CallOpts.IsTemporaryCtorOrDtor = true; Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h === --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -65,6 +65,9 @@ bool IsArrayCtorOrDtor = false; /// This call is a constructor or a destructor of a temporary value. bool IsTemporaryCtorOrDtor = false; +/// This call is a constructor for a temporary that is lifetime-extended +/// by binding a smaller object within it to a reference. +bool IsTemporaryLifetimeExtendedViaSubobject = false; EvalCallOptions() {} }; Index: test/Analysis/lifetime-extension.cpp === --- test/Analysis/lifetime-extension.cpp +++ test/Analysis/lifetime-extension.cpp @@ -39,18 +39,10 @@ const int &y = A().j[1]; // no-crash const int &z = (A().j[1], A().j[0]); // no-crash - clang_analyzer_eval(x == 1); - clang_analyzer_eval(y == 3); - clang_analyzer_ev