forked from OSchip/llvm-project
[analyzer] Support temporaries conjured by conservatively evaluated functions.
Properly perform destruction and lifetime extension of such temporaries. C++ object-type return values of conservatively evaluated functions are now represented as compound values of well-defined temporary object regions. The function creates a region that represents the temporary object and will later be used for destruction or materialization, invalidates it, and returns the invalidated compound value of the object. Differential Revision: https://reviews.llvm.org/D44131 llvm-svn: 327348
This commit is contained in:
parent
98a24bf76d
commit
09a7c0c77d
|
@ -581,10 +581,25 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
|
|||
return State->BindExpr(E, LCtx, ThisV);
|
||||
}
|
||||
|
||||
// Conjure a symbol if the return value is unknown.
|
||||
SVal R;
|
||||
QualType ResultTy = Call.getResultType();
|
||||
SValBuilder &SVB = getSValBuilder();
|
||||
unsigned Count = currBldrCtx->blockCount();
|
||||
if (auto RTC = getCurrentCFGElement().getAs<CFGCXXRecordTypedCall>()) {
|
||||
// Conjure a temporary if the function returns an object by value.
|
||||
MemRegionManager &MRMgr = svalBuilder.getRegionManager();
|
||||
const CXXTempObjectRegion *TR = MRMgr.getCXXTempObjectRegion(E, LCtx);
|
||||
State = addAllNecessaryTemporaryInfo(State, RTC->getConstructionContext(),
|
||||
LCtx, TR);
|
||||
|
||||
// Invalidate the region so that it didn't look uninitialized. Don't notify
|
||||
// the checkers.
|
||||
State = State->invalidateRegions(TR, E, Count, LCtx,
|
||||
/* CausedByPointerEscape=*/false, nullptr,
|
||||
&Call, nullptr);
|
||||
|
||||
R = State->getSVal(TR, E->getType());
|
||||
} else {
|
||||
// Conjure a symbol if the return value is unknown.
|
||||
|
||||
// See if we need to conjure a heap pointer instead of
|
||||
// a regular unknown pointer.
|
||||
|
@ -595,9 +610,10 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
|
|||
IsHeapPointer = true;
|
||||
}
|
||||
|
||||
SVal R = IsHeapPointer
|
||||
? SVB.getConjuredHeapSymbolVal(E, LCtx, Count)
|
||||
: SVB.conjureSymbolVal(nullptr, E, LCtx, ResultTy, Count);
|
||||
R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count)
|
||||
: svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy,
|
||||
Count);
|
||||
}
|
||||
return State->BindExpr(E, LCtx, R);
|
||||
}
|
||||
|
||||
|
|
|
@ -94,5 +94,5 @@ public:
|
|||
|
||||
void test_6() {
|
||||
clang_analyzer_explain(conjure_S()); // expected-warning-re{{{{^lazily frozen compound value of temporary object constructed at statement 'conjure_S\(\)'$}}}}
|
||||
clang_analyzer_explain(conjure_S().z); // expected-warning-re{{{{^value derived from \(symbol of type 'struct S' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$}}}}
|
||||
clang_analyzer_explain(conjure_S().z); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$}}}}
|
||||
}
|
||||
|
|
|
@ -895,6 +895,45 @@ void test_ternary_temporary_with_copy(int coin) {
|
|||
}
|
||||
} // namespace test_match_constructors_and_destructors
|
||||
|
||||
namespace destructors_for_return_values {
|
||||
|
||||
class C {
|
||||
public:
|
||||
~C() {
|
||||
1 / 0; // expected-warning{{Division by zero}}
|
||||
}
|
||||
};
|
||||
|
||||
C make();
|
||||
|
||||
void testFloatingCall() {
|
||||
make();
|
||||
// Should have divided by zero in the destructor.
|
||||
clang_analyzer_warnIfReached();
|
||||
#ifndef TEMPORARY_DTORS
|
||||
// expected-warning@-2{{REACHABLE}}
|
||||
#endif
|
||||
}
|
||||
|
||||
void testLifetimeExtendedCall() {
|
||||
{
|
||||
const C &c = make();
|
||||
clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
|
||||
}
|
||||
// Should have divided by zero in the destructor.
|
||||
clang_analyzer_warnIfReached(); // no-warning
|
||||
}
|
||||
|
||||
void testCopiedCall() {
|
||||
C c = make();
|
||||
// Should have divided by zero in the temporary destructor.
|
||||
clang_analyzer_warnIfReached();
|
||||
#ifndef TEMPORARY_DTORS
|
||||
// expected-warning@-2{{REACHABLE}}
|
||||
#endif
|
||||
}
|
||||
} // namespace destructors_for_return_values
|
||||
|
||||
namespace dont_forget_destructor_around_logical_op {
|
||||
int glob;
|
||||
|
||||
|
@ -922,8 +961,17 @@ void test(int coin) {
|
|||
// return value of get() was initialized. However, we didn't track
|
||||
// temporaries returned from functions, so we took the wrong branch.
|
||||
coin && is(get()); // no-crash
|
||||
// FIXME: Should be true once we inline all destructors.
|
||||
if (coin) {
|
||||
clang_analyzer_eval(glob);
|
||||
#ifdef TEMPORARY_DTORS
|
||||
// expected-warning@-2{{TRUE}}
|
||||
#else
|
||||
// expected-warning@-4{{UNKNOWN}}
|
||||
#endif
|
||||
} else {
|
||||
// The destructor is not called on this branch.
|
||||
clang_analyzer_eval(glob); // expected-warning{{UNKNOWN}}
|
||||
}
|
||||
}
|
||||
} // namespace dont_forget_destructor_around_logical_op
|
||||
|
||||
|
|
Loading…
Reference in New Issue