Author: Utkarsh Saxena Date: 2024-11-22T16:01:16+01:00 New Revision: 912c502a9e4bab8e07de4419f8cbae35c98b112f
URL: https://github.com/llvm/llvm-project/commit/912c502a9e4bab8e07de4419f8cbae35c98b112f DIFF: https://github.com/llvm/llvm-project/commit/912c502a9e4bab8e07de4419f8cbae35c98b112f.diff LOG: [clang] Infer lifetime_capture_by for STL containers (#117122) This is behind `-Wdangling-capture` warning which is disabled by default. Added: Modified: clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaAttr.cpp clang/lib/Sema/SemaDecl.cpp clang/test/AST/attr-lifetime-capture-by.cpp clang/test/Sema/Inputs/lifetime-analysis.h clang/test/Sema/warn-lifetime-analysis-capture-by.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6ea6c67447b6f0..5fe23e0d0efd3b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1757,6 +1757,9 @@ class Sema final : public SemaBase { /// Add [[clang:::lifetimebound]] attr for std:: functions and methods. void inferLifetimeBoundAttribute(FunctionDecl *FD); + /// Add [[clang:::lifetime_capture_by(this)]] to STL container methods. + void inferLifetimeCaptureByAttribute(FunctionDecl *FD); + /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index 9fbad7ed67ccbe..716d8ed1fae4f8 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "CheckExprLifetime.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/Attr.h" #include "clang/AST/Expr.h" @@ -268,6 +269,44 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) { } } +static bool isPointerLikeType(QualType QT) { + QT = QT.getNonReferenceType(); + if (QT->isPointerType()) + return true; + auto *RD = QT->getAsCXXRecordDecl(); + if (!RD) + return false; + if (auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) + RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr<PointerAttr>(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) + return; + auto *MD = dyn_cast<CXXMethodDecl>(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) + return; + // FIXME: Infer for operator[] for map-like containers. For example: + // std::map<string_view, ...> m; + // m[ReturnString(..)] = ...; + static const llvm::StringSet<> CapturingMethods{"insert", "push", + "push_front", "push_back"}; + if (!CapturingMethods.contains(MD->getName())) + return; + // Do not infer if any parameter is explicitly annotated. + for (ParmVarDecl *PVD : MD->parameters()) + if (PVD->hasAttr<LifetimeCaptureByAttr>()) + return; + for (ParmVarDecl *PVD : MD->parameters()) { + if (isPointerLikeType(PVD->getType())) { + int CaptureByThis[] = {LifetimeCaptureByAttr::THIS}; + PVD->addAttr( + LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1)); + } + } +} + void Sema::inferNullableClassAttribute(CXXRecordDecl *CRD) { static const llvm::StringSet<> Nullable{ "auto_ptr", "shared_ptr", "unique_ptr", "exception_ptr", diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 223a532df41d9b..74b0e5ad23bd48 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11916,6 +11916,7 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD, NamedDecl *OldDecl = nullptr; bool MayNeedOverloadableChecks = false; + inferLifetimeCaptureByAttribute(NewFD); // Merge or overload the declaration with an existing declaration of // the same name, if appropriate. if (!Previous.empty()) { @@ -16719,6 +16720,7 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) { LazyProcessLifetimeCaptureByParams(FD); inferLifetimeBoundAttribute(FD); + inferLifetimeCaptureByAttribute(FD); AddKnownFunctionAttributesForReplaceableGlobalAllocationFunction(FD); // If C++ exceptions are enabled but we are told extern "C" functions cannot diff --git a/clang/test/AST/attr-lifetime-capture-by.cpp b/clang/test/AST/attr-lifetime-capture-by.cpp index da2eb0cf3d592e..c3afe267301ad7 100644 --- a/clang/test/AST/attr-lifetime-capture-by.cpp +++ b/clang/test/AST/attr-lifetime-capture-by.cpp @@ -7,3 +7,109 @@ struct S { }; // CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) + +// **************************************************************************** +// Infer annotation for STL container methods. +// **************************************************************************** +namespace __gnu_cxx { +template <typename T> +struct basic_iterator {}; +} + +namespace std { +template<typename T> class allocator {}; +template <typename T, typename Alloc = allocator<T>> +struct vector { + typedef __gnu_cxx::basic_iterator<T> iterator; + iterator begin(); + + vector(); + + void push_back(const T&); + void push_back(T&&); + + void insert(iterator, T&&); +}; +} // namespace std + +// CHECK-NOT: LifetimeCaptureByAttr + +struct [[gsl::Pointer()]] View {}; +std::vector<View> views; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'View' +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (const View &)' +// CHECK: ParmVarDecl {{.*}} 'const View &' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (View &&)' +// CHECK: ParmVarDecl {{.*}} 'View &&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit + +// CHECK: CXXMethodDecl {{.*}} insert 'void (iterator, View &&)' +// CHECK: ParmVarDecl {{.*}} 'iterator' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK: ParmVarDecl {{.*}} 'View &&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +template <class T> struct [[gsl::Pointer()]] ViewTemplate {}; +std::vector<ViewTemplate<int>> templated_views; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'ViewTemplate<int>' +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (const ViewTemplate<int> &)' +// CHECK: ParmVarDecl {{.*}} 'const ViewTemplate<int> &' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (ViewTemplate<int> &&)' +// CHECK: ParmVarDecl {{.*}} 'ViewTemplate<int> &&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit + +// CHECK: CXXMethodDecl {{.*}} insert 'void (iterator, ViewTemplate<int> &&)' +// CHECK: ParmVarDecl {{.*}} 'iterator' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK: ParmVarDecl {{.*}} 'ViewTemplate<int> &&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +std::vector<int*> pointers; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'int *' +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (int *const &)' +// CHECK: ParmVarDecl {{.*}} 'int *const &' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (int *&&)' +// CHECK: ParmVarDecl {{.*}} 'int *&&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit + +// CHECK: CXXMethodDecl {{.*}} insert 'void (iterator, int *&&)' +// CHECK: ParmVarDecl {{.*}} 'iterator' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK: ParmVarDecl {{.*}} 'int *&&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +std::vector<int> ints; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'int' + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (const int &)' +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (int &&)' +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} insert 'void (iterator, int &&)' +// CHECK: ParmVarDecl {{.*}} 'iterator' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr diff --git a/clang/test/Sema/Inputs/lifetime-analysis.h b/clang/test/Sema/Inputs/lifetime-analysis.h index 41d1e2f074cc83..5c151385b1fe5a 100644 --- a/clang/test/Sema/Inputs/lifetime-analysis.h +++ b/clang/test/Sema/Inputs/lifetime-analysis.h @@ -49,6 +49,11 @@ struct vector { vector(InputIterator first, InputIterator __last); T &at(int n); + + void push_back(const T&); + void push_back(T&&); + + void insert(iterator, T&&); }; template<typename T> diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp index b3fde386b8616c..4d562bac1e305b 100644 --- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp @@ -366,3 +366,48 @@ void use() { capture3(std::string(), x3); // expected-warning {{object whose reference is captured by 'x3' will be destroyed at the end of the full-expression}} } } // namespace temporary_views + +// **************************************************************************** +// Inferring annotation for STL containers +// **************************************************************************** +namespace inferred_capture_by { +const std::string* getLifetimeBoundPointer(const std::string &s [[clang::lifetimebound]]); +const std::string* getNotLifetimeBoundPointer(const std::string &s); + +std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); +std::string_view getNotLifetimeBoundView(const std::string& s); +void use() { + std::string local; + std::vector<std::string_view> views; + views.push_back(std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}} + views.insert(views.begin(), + std::string()); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}} + views.push_back(getLifetimeBoundView(std::string())); // expected-warning {{object whose reference is captured by 'views' will be destroyed at the end of the full-expression}} + views.push_back(getNotLifetimeBoundView(std::string())); + views.push_back(local); + views.insert(views.end(), local); + + std::vector<std::string> strings; + strings.push_back(std::string()); + strings.insert(strings.begin(), std::string()); + + std::vector<const std::string*> pointers; + pointers.push_back(getLifetimeBoundPointer(std::string())); // expected-warning {{object whose reference is captured by 'pointers' will be destroyed at the end of the full-expression}} + pointers.push_back(&local); +} + +namespace with_span { +// Templated view types. +template<typename T> +struct [[gsl::Pointer]] Span { + Span(const std::vector<T> &V); +}; + +void use() { + std::vector<Span<int>> spans; + spans.push_back(std::vector<int>{1, 2, 3}); // expected-warning {{object whose reference is captured by 'spans' will be destroyed at the end of the full-expression}} + std::vector<int> local; + spans.push_back(local); +} +} // namespace with_span +} // namespace inferred_capture_by _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits