Skip to content

Commit bfe0c37

Browse files
committed
[LifetimeAnalysis] Fix false negatives of statement local lifetime analysis for some STL implementation
Differential Revision: https://reviews.llvm.org/D66152 llvm-svn: 368871
1 parent dc8dcb6 commit bfe0c37

File tree

2 files changed

+53
-28
lines changed

2 files changed

+53
-28
lines changed

clang/lib/Sema/SemaInit.cpp

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "clang/AST/ExprObjC.h"
1717
#include "clang/AST/ExprOpenMP.h"
1818
#include "clang/AST/TypeLoc.h"
19+
#include "clang/Basic/CharInfo.h"
1920
#include "clang/Basic/TargetInfo.h"
2021
#include "clang/Sema/Designator.h"
2122
#include "clang/Sema/Initialization.h"
@@ -6564,11 +6565,29 @@ template <typename T> static bool isRecordWithAttr(QualType Type) {
65646565
return false;
65656566
}
65666567

6568+
// Decl::isInStdNamespace will return false for iterators in some STL
6569+
// implementations due to them being defined in a namespace outside of the std
6570+
// namespace.
6571+
static bool isInStlNamespace(const Decl *D) {
6572+
const DeclContext *DC = D->getDeclContext();
6573+
if (!DC)
6574+
return false;
6575+
if (const auto *ND = dyn_cast<NamespaceDecl>(DC))
6576+
if (const IdentifierInfo *II = ND->getIdentifier()) {
6577+
StringRef Name = II->getName();
6578+
if (Name.size() >= 2 && Name.front() == '_' &&
6579+
(Name[1] == '_' || isUppercase(Name[1])))
6580+
return true;
6581+
}
6582+
6583+
return DC->isStdNamespace();
6584+
}
6585+
65676586
static bool shouldTrackImplicitObjectArg(const CXXMethodDecl *Callee) {
65686587
if (auto *Conv = dyn_cast_or_null<CXXConversionDecl>(Callee))
65696588
if (isRecordWithAttr<PointerAttr>(Conv->getConversionType()))
65706589
return true;
6571-
if (!Callee->getParent()->isInStdNamespace())
6590+
if (!isInStlNamespace(Callee->getParent()))
65726591
return false;
65736592
if (!isRecordWithAttr<PointerAttr>(Callee->getThisObjectType()) &&
65746593
!isRecordWithAttr<OwnerAttr>(Callee->getThisObjectType()))
@@ -7107,25 +7126,29 @@ void Sema::checkInitializerLifetime(const InitializedEntity &Entity,
71077126
SourceLocation DiagLoc = DiagRange.getBegin();
71087127

71097128
auto *MTE = dyn_cast<MaterializeTemporaryExpr>(L);
7110-
bool IsTempGslOwner = MTE && !MTE->getExtendingDecl() &&
7111-
isRecordWithAttr<OwnerAttr>(MTE->getType());
7112-
bool IsLocalGslOwner =
7113-
isa<DeclRefExpr>(L) && isRecordWithAttr<OwnerAttr>(L->getType());
7114-
7115-
// Skipping a chain of initializing gsl::Pointer annotated objects.
7116-
// We are looking only for the final source to find out if it was
7117-
// a local or temporary owner or the address of a local variable/param. We
7118-
// do not want to follow the references when returning a pointer originating
7119-
// from a local owner to avoid the following false positive:
7120-
// int &p = *localUniquePtr;
7121-
// someContainer.add(std::move(localUniquePtr));
7122-
// return p;
7123-
if (!IsTempGslOwner && pathOnlyInitializesGslPointer(Path) &&
7124-
!(IsLocalGslOwner && !pathContainsInit(Path)))
7125-
return true;
71267129

7127-
bool IsGslPtrInitWithGslTempOwner =
7128-
IsTempGslOwner && pathOnlyInitializesGslPointer(Path);
7130+
bool IsGslPtrInitWithGslTempOwner = false;
7131+
bool IsLocalGslOwner = false;
7132+
if (pathOnlyInitializesGslPointer(Path)) {
7133+
if (isa<DeclRefExpr>(L)) {
7134+
// We do not want to follow the references when returning a pointer originating
7135+
// from a local owner to avoid the following false positive:
7136+
// int &p = *localUniquePtr;
7137+
// someContainer.add(std::move(localUniquePtr));
7138+
// return p;
7139+
IsLocalGslOwner = isRecordWithAttr<OwnerAttr>(L->getType());
7140+
if (pathContainsInit(Path) || !IsLocalGslOwner)
7141+
return false;
7142+
} else {
7143+
IsGslPtrInitWithGslTempOwner = MTE && !MTE->getExtendingDecl() &&
7144+
isRecordWithAttr<OwnerAttr>(MTE->getType());
7145+
// Skipping a chain of initializing gsl::Pointer annotated objects.
7146+
// We are looking only for the final source to find out if it was
7147+
// a local or temporary owner or the address of a local variable/param.
7148+
if (!IsGslPtrInitWithGslTempOwner)
7149+
return true;
7150+
}
7151+
}
71297152

71307153
switch (LK) {
71317154
case LK_FullExpression:

clang/test/Sema/warn-lifetime-analysis-nocfg.cpp

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,7 @@ void initLocalGslPtrWithTempOwner() {
119119
global2 = MyLongOwnerWithConversion{}; // TODO ?
120120
}
121121

122-
namespace std {
123-
template<class T> struct remove_reference { typedef T type; };
124-
template<class T> struct remove_reference<T &> { typedef T type; };
125-
template<class T> struct remove_reference<T &&> { typedef T type; };
126-
127-
template<class T>
128-
typename remove_reference<T>::type &&move(T &&t) noexcept;
129-
122+
namespace __gnu_cxx {
130123
template <typename T>
131124
struct basic_iterator {
132125
basic_iterator operator++();
@@ -135,10 +128,19 @@ struct basic_iterator {
135128

136129
template<typename T>
137130
bool operator!=(basic_iterator<T>, basic_iterator<T>);
131+
}
132+
133+
namespace std {
134+
template<class T> struct remove_reference { typedef T type; };
135+
template<class T> struct remove_reference<T &> { typedef T type; };
136+
template<class T> struct remove_reference<T &&> { typedef T type; };
137+
138+
template<class T>
139+
typename remove_reference<T>::type &&move(T &&t) noexcept;
138140

139141
template <typename T>
140142
struct vector {
141-
typedef basic_iterator<T> iterator;
143+
typedef __gnu_cxx::basic_iterator<T> iterator;
142144
iterator begin();
143145
iterator end();
144146
const T *data() const;

0 commit comments

Comments
 (0)