Skip to content

Commit 310bca0

Browse files
committedApr 25, 2018
[analyzer] Fix a crash on lifetime extension through aggregate initialization.
If 'A' is a C++ aggregate with a reference field of type 'C', in code like A a = { C() }; C() is lifetime-extended by 'a'. The analyzer wasn't expecting this pattern and crashing. Additionally, destructors aren't added in the CFG for this case, so for now we shouldn't be inlining the constructor for C(). Differential Revision: https://reviews.llvm.org/D46037 llvm-svn: 330882
1 parent 0bf96f9 commit 310bca0

File tree

4 files changed

+58
-6
lines changed

4 files changed

+58
-6
lines changed
 

‎clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ class ExprEngine : public SubEngine {
110110
/// 'const int &x = C().x;'.
111111
bool IsTemporaryLifetimeExtendedViaSubobject = false;
112112

113+
/// This call is a constructor for a temporary that is lifetime-extended
114+
/// by binding it to a reference-type field within an aggregate,
115+
/// for example 'A { const C &c; }; A a = { C() };'
116+
bool IsTemporaryLifetimeExtendedViaAggregate = false;
117+
113118
EvalCallOptions() {}
114119
};
115120

‎clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -180,14 +180,25 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
180180
}
181181
case ConstructionContext::TemporaryObjectKind: {
182182
const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
183-
// See if we're lifetime-extended via our field. If so, take a note.
184-
// Because automatic destructors aren't quite working in this case.
185183
if (const auto *MTE = TOCC->getMaterializedTemporaryExpr()) {
186184
if (const ValueDecl *VD = MTE->getExtendingDecl()) {
187-
assert(VD->getType()->isReferenceType());
188-
if (VD->getType()->getPointeeType().getCanonicalType() !=
189-
MTE->GetTemporaryExpr()->getType().getCanonicalType()) {
190-
CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true;
185+
// Pattern-match various forms of lifetime extension that aren't
186+
// currently supported by the CFG.
187+
// FIXME: Is there a better way to retrieve this information from
188+
// the MaterializeTemporaryExpr?
189+
assert(MTE->getStorageDuration() != SD_FullExpression);
190+
if (VD->getType()->isReferenceType()) {
191+
assert(VD->getType()->isReferenceType());
192+
if (VD->getType()->getPointeeType().getCanonicalType() !=
193+
MTE->GetTemporaryExpr()->getType().getCanonicalType()) {
194+
// We're lifetime-extended via our field. Automatic destructors
195+
// aren't quite working in this case.
196+
CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true;
197+
}
198+
} else {
199+
// We're lifetime-extended by a surrounding aggregate.
200+
// Automatic destructors aren't quite working in this case.
201+
CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true;
191202
}
192203
}
193204
}

‎clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,11 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
699699
// within it to a reference, automatic destructors don't work properly.
700700
if (CallOpts.IsTemporaryLifetimeExtendedViaSubobject)
701701
return CIP_DisallowedOnce;
702+
703+
// If the temporary is lifetime-extended by binding it to a reference-typ
704+
// field within an aggregate, automatic destructors don't work properly.
705+
if (CallOpts.IsTemporaryLifetimeExtendedViaAggregate)
706+
return CIP_DisallowedOnce;
702707
}
703708

704709
break;

‎clang/test/Analysis/lifetime-extension.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,37 @@ void f5() {
147147
// FIXME: Should be TRUE. Should not warn about garbage value.
148148
clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
149149
}
150+
151+
struct A { // A is an aggregate.
152+
const C &c;
153+
};
154+
155+
void f6() {
156+
C *after, *before;
157+
{
158+
A a{C(true, &after, &before)};
159+
}
160+
// FIXME: Should be TRUE. Should not warn about garbage value.
161+
clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
162+
}
163+
164+
void f7() {
165+
C *after, *before;
166+
{
167+
A a = {C(true, &after, &before)};
168+
}
169+
// FIXME: Should be TRUE. Should not warn about garbage value.
170+
clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
171+
}
172+
173+
void f8() {
174+
C *after, *before;
175+
{
176+
A a[2] = {C(false, nullptr, nullptr), C(true, &after, &before)};
177+
}
178+
// FIXME: Should be TRUE. Should not warn about garbage value.
179+
clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
180+
}
150181
} // end namespace maintain_original_object_address_on_lifetime_extension
151182

152183
namespace maintain_original_object_address_on_move {

0 commit comments

Comments
 (0)
Please sign in to comment.