Skip to content

Commit

Permalink
[analyzer] Disable constructor inlining when lifetime extending throu…
Browse files Browse the repository at this point in the history
…gh a field.

Automatic destructors are missing in the CFG in situations like

  const int &x = C().x;

For now it's better to disable construction inlining, because inlining
constructors while doing nothing on destructors is very bad.

Differential Revision: https://reviews.llvm.org/D43689

llvm-svn: 326240
  • Loading branch information
haoNoQ committed Feb 27, 2018
1 parent b7f53df commit 8cd7961
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 18 deletions.
Expand Up @@ -65,6 +65,10 @@ class ExprEngine : public SubEngine {
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() {}
};
Expand Down
12 changes: 12 additions & 0 deletions clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
Expand Up @@ -168,6 +168,18 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
break;
}
case ConstructionContext::TemporaryObjectKind: {
const auto *TOCC = cast<TemporaryObjectConstructionContext>(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;
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
Expand Up @@ -678,6 +678,11 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
// 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;
Expand Down
23 changes: 5 additions & 18 deletions clang/test/Analysis/lifetime-extension.cpp
Expand Up @@ -39,18 +39,10 @@ void f() {
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

Expand Down Expand Up @@ -144,12 +136,7 @@ void f5() {
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

Expand Down

0 comments on commit 8cd7961

Please sign in to comment.