Skip to content

Commit 8cd7961

Browse files
committed
[analyzer] Disable constructor inlining when lifetime extending through 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
1 parent b7f53df commit 8cd7961

File tree

4 files changed

+26
-18
lines changed

4 files changed

+26
-18
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ class ExprEngine : public SubEngine {
6565
bool IsArrayCtorOrDtor = false;
6666
/// This call is a constructor or a destructor of a temporary value.
6767
bool IsTemporaryCtorOrDtor = false;
68+
/// This call is a constructor for a temporary that is lifetime-extended
69+
/// by binding a smaller object within it to a reference, for example
70+
/// 'const int &x = C().x;'.
71+
bool IsTemporaryLifetimeExtendedViaSubobject = false;
6872

6973
EvalCallOptions() {}
7074
};

clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,18 @@ ExprEngine::getRegionForConstructedObject(const CXXConstructExpr *CE,
168168
break;
169169
}
170170
case ConstructionContext::TemporaryObjectKind: {
171+
const auto *TOCC = cast<TemporaryObjectConstructionContext>(CC);
172+
// See if we're lifetime-extended via our field. If so, take a note.
173+
// Because automatic destructors aren't quite working in this case.
174+
if (const auto *MTE = TOCC->getMaterializedTemporaryExpr()) {
175+
if (const ValueDecl *VD = MTE->getExtendingDecl()) {
176+
assert(VD->getType()->isReferenceType());
177+
if (VD->getType()->getPointeeType().getCanonicalType() !=
178+
MTE->GetTemporaryExpr()->getType().getCanonicalType()) {
179+
CallOpts.IsTemporaryLifetimeExtendedViaSubobject = true;
180+
}
181+
}
182+
}
171183
// TODO: Support temporaries lifetime-extended via static references.
172184
// They'd need a getCXXStaticTempObjectRegion().
173185
CallOpts.IsTemporaryCtorOrDtor = true;

clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,11 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
678678
// the fake temporary target.
679679
if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion)
680680
return CIP_DisallowedOnce;
681+
682+
// If the temporary is lifetime-extended by binding a smaller object
683+
// within it to a reference, automatic destructors don't work properly.
684+
if (CallOpts.IsTemporaryLifetimeExtendedViaSubobject)
685+
return CIP_DisallowedOnce;
681686
}
682687

683688
break;

clang/test/Analysis/lifetime-extension.cpp

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,18 +39,10 @@ void f() {
3939
const int &y = A().j[1]; // no-crash
4040
const int &z = (A().j[1], A().j[0]); // no-crash
4141

42-
clang_analyzer_eval(x == 1);
43-
clang_analyzer_eval(y == 3);
44-
clang_analyzer_eval(z == 2);
45-
#ifdef TEMPORARIES
46-
// expected-warning@-4{{TRUE}}
47-
// expected-warning@-4{{TRUE}}
48-
// expected-warning@-4{{TRUE}}
49-
#else
50-
// expected-warning@-8{{UNKNOWN}}
51-
// expected-warning@-8{{UNKNOWN}}
52-
// expected-warning@-8{{UNKNOWN}}
53-
#endif
42+
// FIXME: All of these should be TRUE, but constructors aren't inlined.
43+
clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}}
44+
clang_analyzer_eval(y == 3); // expected-warning{{UNKNOWN}}
45+
clang_analyzer_eval(z == 2); // expected-warning{{UNKNOWN}}
5446
}
5547
} // end namespace pr19539_crash_on_destroying_an_integer
5648

@@ -144,12 +136,7 @@ void f5() {
144136
const bool &x = C(true, &after, &before).x; // no-crash
145137
}
146138
// FIXME: Should be TRUE. Should not warn about garbage value.
147-
clang_analyzer_eval(after == before);
148-
#ifdef TEMPORARIES
149-
// expected-warning@-2{{The left operand of '==' is a garbage value}}
150-
#else
151-
// expected-warning@-4{{UNKNOWN}}
152-
#endif
139+
clang_analyzer_eval(after == before); // expected-warning{{UNKNOWN}}
153140
}
154141
} // end namespace maintain_original_object_address_on_lifetime_extension
155142

0 commit comments

Comments
 (0)