[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
llvm-ci wrote: LLVM Buildbot has detected a new failure on builder `openmp-offload-amdgpu-runtime` running on `omp-vega20-0` while building `clang` at step 6 "test-openmp". Full details are available at: https://lab.llvm.org/buildbot/#/builders/30/builds/10839 Here is the relevant piece of the build log for the reference ``` Step 6 (test-openmp) failure: test (failure) TEST 'libomp :: tasking/issue-94260-2.c' FAILED Exit Code: -11 Command Output (stdout): -- # RUN: at line 1 /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/openmp/runtime/test -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -fno-omit-frame-pointer -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/openmp/runtime/test/ompt /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic && /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp # executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/./bin/clang -fopenmp -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/openmp/runtime/test -L /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/src -fno-omit-frame-pointer -I /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/openmp/runtime/test/ompt /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.src/openmp/runtime/test/tasking/issue-94260-2.c -o /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp -lm -latomic # note: command had no output on stdout or stderr # executed command: /home/ompworker/bbot/openmp-offload-amdgpu-runtime/llvm.build/runtimes/runtimes-bins/openmp/runtime/test/tasking/Output/issue-94260-2.c.tmp # note: command had no output on stdout or stderr # error: command failed with exit status: -11 -- ``` https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -7,3 +7,106 @@ struct S { }; // CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) + +// +// Infer annotation for STL container methods. +// +namespace __gnu_cxx { +template +struct basic_iterator {}; +} + +namespace std { +template class allocator {}; +template > +struct vector { + typedef __gnu_cxx::basic_iterator iterator; + iterator begin(); + + vector(); + + void push_back(const T&); + void push_back(T&&); + + void insert(iterator, T&&); +}; +} // namespace std +struct [[gsl::Pointer()]] View {}; +std::vector views; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'View' +// CHECK-NOT: LifetimeCaptureByAttr hokein wrote: Ah, I see. Thanks. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 closed https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/hokein approved this pull request. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -7,3 +7,106 @@ struct S { }; // CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) + +// +// Infer annotation for STL container methods. +// +namespace __gnu_cxx { +template +struct basic_iterator {}; +} + +namespace std { +template class allocator {}; +template > +struct vector { + typedef __gnu_cxx::basic_iterator iterator; + iterator begin(); + + vector(); + + void push_back(const T&); + void push_back(T&&); + + void insert(iterator, T&&); +}; +} // namespace std +struct [[gsl::Pointer()]] View {}; +std::vector views; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'View' +// CHECK-NOT: LifetimeCaptureByAttr usx95 wrote: Well this is along the lines of not having capture by on values and only for view types. For example: ``` std::vector strings; strings.push_back(std::string()); strings.insert(strings.begin(), std::string()); ``` https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -7,3 +7,106 @@ struct S { }; // CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) + +// +// Infer annotation for STL container methods. +// +namespace __gnu_cxx { +template +struct basic_iterator {}; +} + +namespace std { +template class allocator {}; +template > +struct vector { + typedef __gnu_cxx::basic_iterator iterator; + iterator begin(); + + vector(); + + void push_back(const T&); + void push_back(T&&); + + void insert(iterator, T&&); +}; +} // namespace std +struct [[gsl::Pointer()]] View {}; +std::vector views; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'View' +// CHECK-NOT: LifetimeCaptureByAttr hokein wrote: > That would add the annotation to all of its specializations which is not > something we want. Can you give a counter example? https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); usx95 wrote: Hmm. It can reproduce with `` include but not with handcrafted `vector` in lit tests. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); hokein wrote: hmm, this is strange. I tested it locally, it works for me. ``` $ cat /t/t6.cpp #include template struct [[gsl::Pointer]] Span { Span(const std::vector &V); }; void use() { Span pp(std::vector{}); std::vector> spans; spans.push_back(std::vector()); // warning. } $ ./bin/clang -Xclang -fsyntax-only -Wdangling-capture /t/t6.cpp <<< /t/t6.cpp:9:16: warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl] 9 | Span pp(std::vector{}); |^~ /t/t6.cpp:11:19: warning: object whose reference is captured by 'spans' will be destroyed at the end of the full-expression [-Wdangling-capture] 11 | spans.push_back(std::vector()); // warning. | ^~ 2 warnings generated. /usr/bin/ld: /lib/x86_64-linux-gnu/Scrt1.o: in function `_start': (.text+0x17): undefined reference to `main' clang: error: linker command failed with exit code 1 (use -v to see invocation) ``` https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -7,3 +7,106 @@ struct S { }; // CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) + +// +// Infer annotation for STL container methods. +// +namespace __gnu_cxx { +template +struct basic_iterator {}; +} + +namespace std { +template class allocator {}; +template > +struct vector { + typedef __gnu_cxx::basic_iterator iterator; + iterator begin(); + + vector(); + + void push_back(const T&); + void push_back(T&&); + + void insert(iterator, T&&); +}; +} // namespace std +struct [[gsl::Pointer()]] View {}; +std::vector views; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'View' +// CHECK-NOT: LifetimeCaptureByAttr usx95 wrote: No. That would add the annotation to all of its specializations which is not something we want. Tested: Added an extra `// CHECK-NOT: LifetimeCaptureByAttr` before the beginning of the tests as well. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/117122 >From 9a57223b06a8331a0ef123739a430863dee19d98 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Thu, 21 Nov 2024 07:00:56 + Subject: [PATCH 1/4] [clang] Infer lifetime_capture_by for STL containers --- clang/include/clang/Sema/Sema.h | 3 + clang/lib/Sema/SemaAttr.cpp | 34 clang/lib/Sema/SemaDecl.cpp | 2 + clang/test/Sema/Inputs/lifetime-analysis.h| 5 ++ .../warn-lifetime-analysis-capture-by.cpp | 79 +++ 5 files changed, 123 insertions(+) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6ea6c67447b6f0..9bafcfec5d4786 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(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..507f7c40d58782 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) +return; + auto *MD = dyn_cast(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) +return; + static const llvm::StringSet<> CapturingMethods{"insert", "push", + "push_front", "push_back"}; + if (!CapturingMethods.contains(MD->getName())) +return; + for (ParmVarDecl *PVD : MD->parameters()) { +if (PVD->hasAttr()) + return; +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 be570f3a1829d0..5b30d0f2c22d16 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11913,6 +11913,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()) { @@ -16716,6 +16717,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/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 diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp index b3fde386b8616c..462cb2d3f3fd6e 100644 --- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp @@ -366,3 +366,82 @@ 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]
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -7,3 +7,106 @@ struct S { }; // CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) + +// +// Infer annotation for STL container methods. +// +namespace __gnu_cxx { +template +struct basic_iterator {}; +} + +namespace std { +template class allocator {}; +template > +struct vector { + typedef __gnu_cxx::basic_iterator iterator; + iterator begin(); + + vector(); + + void push_back(const T&); + void push_back(T&&); + + void insert(iterator, T&&); +}; +} // namespace std +struct [[gsl::Pointer()]] View {}; +std::vector 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 struct [[gsl::Pointer()]] ViewTemplate {}; +std::vector> templated_views; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'ViewTemplate' +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (const ViewTemplate &)' +// CHECK: ParmVarDecl {{.*}} 'const ViewTemplate &' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (ViewTemplate &&)' +// CHECK: ParmVarDecl {{.*}} 'ViewTemplate &&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit + +// CHECK: CXXMethodDecl {{.*}} insert 'void (iterator, ViewTemplate &&)' +// CHECK: ParmVarDecl {{.*}} 'iterator' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK: ParmVarDecl {{.*}} 'ViewTemplate &&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +std::vector 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 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 usx95 wrote: Done. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/117122 >From 9a57223b06a8331a0ef123739a430863dee19d98 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Thu, 21 Nov 2024 07:00:56 + Subject: [PATCH 1/3] [clang] Infer lifetime_capture_by for STL containers --- clang/include/clang/Sema/Sema.h | 3 + clang/lib/Sema/SemaAttr.cpp | 34 clang/lib/Sema/SemaDecl.cpp | 2 + clang/test/Sema/Inputs/lifetime-analysis.h| 5 ++ .../warn-lifetime-analysis-capture-by.cpp | 79 +++ 5 files changed, 123 insertions(+) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6ea6c67447b6f0..9bafcfec5d4786 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(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..507f7c40d58782 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) +return; + auto *MD = dyn_cast(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) +return; + static const llvm::StringSet<> CapturingMethods{"insert", "push", + "push_front", "push_back"}; + if (!CapturingMethods.contains(MD->getName())) +return; + for (ParmVarDecl *PVD : MD->parameters()) { +if (PVD->hasAttr()) + return; +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 be570f3a1829d0..5b30d0f2c22d16 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11913,6 +11913,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()) { @@ -16716,6 +16717,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/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 diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp index b3fde386b8616c..462cb2d3f3fd6e 100644 --- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp @@ -366,3 +366,82 @@ 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]
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -7,3 +7,106 @@ struct S { }; // CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) + +// +// Infer annotation for STL container methods. +// +namespace __gnu_cxx { +template +struct basic_iterator {}; +} + +namespace std { +template class allocator {}; +template > +struct vector { + typedef __gnu_cxx::basic_iterator iterator; + iterator begin(); + + vector(); + + void push_back(const T&); + void push_back(T&&); + + void insert(iterator, T&&); +}; +} // namespace std +struct [[gsl::Pointer()]] View {}; +std::vector 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 struct [[gsl::Pointer()]] ViewTemplate {}; +std::vector> templated_views; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'ViewTemplate' +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (const ViewTemplate &)' +// CHECK: ParmVarDecl {{.*}} 'const ViewTemplate &' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +// CHECK: CXXMethodDecl {{.*}} push_back 'void (ViewTemplate &&)' +// CHECK: ParmVarDecl {{.*}} 'ViewTemplate &&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit + +// CHECK: CXXMethodDecl {{.*}} insert 'void (iterator, ViewTemplate &&)' +// CHECK: ParmVarDecl {{.*}} 'iterator' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK: ParmVarDecl {{.*}} 'ViewTemplate &&' +// CHECK: LifetimeCaptureByAttr {{.*}} Implicit +// CHECK-NOT: LifetimeCaptureByAttr + +std::vector 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 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 Xazax-hun wrote: Nit: missing new line https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/Xazax-hun edited https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/Xazax-hun approved this pull request. Once @hokein's comments are addressed, it looks good to me. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); usx95 wrote: > Should we do the same thing for the one in CheckExprLifetime.cpp? Makes a lot of sense. Let me do this separately in parallel though. That said, your example still doesn't work show the warning with the lines removed: ```cpp if (auto *CTSD = dyn_cast(RD)) RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); ``` https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); hokein wrote: Thanks for the example. I think I understand what’s happening here. The primary template Span is annotated with the gsl::Pointer attribute, so all its specializations should inherit this attribute. However, in this example, at the point where we infer the `lifetime_capture_by` attribute for `vector`'s method, we don’t yet have a fully completed `ClassTemplateSpecializationDecl` for `Span` (and therefore, no `PointerAttr`). This is likely because the instantiation of `Span` isn’t required for the statement `std::vector> spans;`. I think the following case would work without this special handling. ``` void use() { Span abc({}); // trigger an instantiation of `Span`. std::vector> spans; spans.push_back(std::vector{1, 2, 3}); // warning. } ``` I don’t have a better suggestion for addressing this issue directly. However, I think we should have a comment explaining it. (Should we do the same thing for the one in `CheckExprLifetime.cpp`?) https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -7,3 +7,106 @@ struct S { }; // CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) + +// +// Infer annotation for STL container methods. +// +namespace __gnu_cxx { +template +struct basic_iterator {}; +} + +namespace std { +template class allocator {}; +template > +struct vector { + typedef __gnu_cxx::basic_iterator iterator; + iterator begin(); + + vector(); + + void push_back(const T&); + void push_back(T&&); + + void insert(iterator, T&&); +}; +} // namespace std +struct [[gsl::Pointer()]] View {}; +std::vector views; +// CHECK: ClassTemplateSpecializationDecl {{.*}} struct vector definition implicit_instantiation +// CHECK: TemplateArgument type 'View' +// CHECK-NOT: LifetimeCaptureByAttr hokein wrote: Is the `LifetimeCaptureByAttr` attached to the primary template `push_back(const T&)`? https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); usx95 wrote: Maybe a better fix would be to propagate this change to the template specialization in clang. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); usx95 wrote: We fail to detect pointer types which are templates: ```cpp template struct [[gsl::Pointer()]] ViewTemplate {}; std::vector> templated_views; ``` ```cpp template struct [[gsl::Pointer]] Span { Span(const std::vector &V); }; void use() { std::vector> spans; spans.push_back(std::vector{1, 2, 3}); // warning. } ``` https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/117122 >From 9a57223b06a8331a0ef123739a430863dee19d98 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Thu, 21 Nov 2024 07:00:56 + Subject: [PATCH 1/2] [clang] Infer lifetime_capture_by for STL containers --- clang/include/clang/Sema/Sema.h | 3 + clang/lib/Sema/SemaAttr.cpp | 34 clang/lib/Sema/SemaDecl.cpp | 2 + clang/test/Sema/Inputs/lifetime-analysis.h| 5 ++ .../warn-lifetime-analysis-capture-by.cpp | 79 +++ 5 files changed, 123 insertions(+) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6ea6c67447b6f0..9bafcfec5d4786 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(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..507f7c40d58782 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) +return; + auto *MD = dyn_cast(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) +return; + static const llvm::StringSet<> CapturingMethods{"insert", "push", + "push_front", "push_back"}; + if (!CapturingMethods.contains(MD->getName())) +return; + for (ParmVarDecl *PVD : MD->parameters()) { +if (PVD->hasAttr()) + return; +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 be570f3a1829d0..5b30d0f2c22d16 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11913,6 +11913,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()) { @@ -16716,6 +16717,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/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 diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp index b3fde386b8616c..462cb2d3f3fd6e 100644 --- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp @@ -366,3 +366,82 @@ 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]
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -366,3 +366,82 @@ 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 +// usx95 wrote: Done. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) { } } +static bool IsPointerLikeType(QualType QT) { usx95 wrote: Done. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -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(this)]] to STL container methods. usx95 wrote: Done. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) +return; + auto *MD = dyn_cast(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) +return; + static const llvm::StringSet<> CapturingMethods{"insert", "push", + "push_front", "push_back"}; + if (!CapturingMethods.contains(MD->getName())) +return; + for (ParmVarDecl *PVD : MD->parameters()) { +if (PVD->hasAttr()) + return; usx95 wrote: Yes, this was intentional. Good catch. Disabled if any of the parameters is explicitly annotated. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) +return; + auto *MD = dyn_cast(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) +return; + static const llvm::StringSet<> CapturingMethods{"insert", "push", usx95 wrote: SG. Done. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) +return; + auto *MD = dyn_cast(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) +return; + static const llvm::StringSet<> CapturingMethods{"insert", "push", hokein wrote: I think it is fine to not support it in this patch, worth adding a `FIXME`. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); hokein wrote: It’s unclear to me why this case needs to be handled separately, and we already have one in `CheckExprLifetime.cpp` https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -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(this)]] to STL container methods. hokein wrote: nit: lifetime_capture_by https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -366,3 +366,82 @@ 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 +// hokein wrote: Instead of thoroughly testing diagnostics triggered by the implicitly-added `lifetime_capture_by` attribute, I think we can verify the attribute directly by inspecting the AST node( see `attr-gsl-owner-pointer.cpp`), and include a few small smoke tests here. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) { } } +static bool IsPointerLikeType(QualType QT) { hokein wrote: nit: `isPointerLikeType` https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
@@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) +return; + auto *MD = dyn_cast(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) +return; + static const llvm::StringSet<> CapturingMethods{"insert", "push", + "push_front", "push_back"}; + if (!CapturingMethods.contains(MD->getName())) +return; + for (ParmVarDecl *PVD : MD->parameters()) { +if (PVD->hasAttr()) + return; Xazax-hun wrote: Is return intentional here? Do we want an explicit attr to turn off the inference for the rest of the params? If that is the case, I think we want to turn off the inference for the params before the explicit attr as well, not just after. https://github.com/llvm/llvm-project/pull/117122 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
llvmbot wrote: @llvm/pr-subscribers-clang Author: Utkarsh Saxena (usx95) Changes This is behind `-Wdangling-capture` warning which is disabled by default. --- Full diff: https://github.com/llvm/llvm-project/pull/117122.diff 5 Files Affected: - (modified) clang/include/clang/Sema/Sema.h (+3) - (modified) clang/lib/Sema/SemaAttr.cpp (+34) - (modified) clang/lib/Sema/SemaDecl.cpp (+2) - (modified) clang/test/Sema/Inputs/lifetime-analysis.h (+5) - (modified) clang/test/Sema/warn-lifetime-analysis-capture-by.cpp (+79) ``diff diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6ea6c67447b6f0..9bafcfec5d4786 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(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..507f7c40d58782 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) +return; + auto *MD = dyn_cast(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) +return; + static const llvm::StringSet<> CapturingMethods{"insert", "push", + "push_front", "push_back"}; + if (!CapturingMethods.contains(MD->getName())) +return; + for (ParmVarDecl *PVD : MD->parameters()) { +if (PVD->hasAttr()) + return; +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 be570f3a1829d0..5b30d0f2c22d16 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11913,6 +11913,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()) { @@ -16716,6 +16717,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/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 diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp index b3fde386b8616c..462cb2d3f3fd6e 100644 --- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp @@ -366,3 +366,82 @@ 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); +
[clang] [clang] Infer lifetime_capture_by for STL containers (PR #117122)
https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/117122 This is behind `-Wdangling-capture` warning which is disabled by default. >From 9a57223b06a8331a0ef123739a430863dee19d98 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Thu, 21 Nov 2024 07:00:56 + Subject: [PATCH] [clang] Infer lifetime_capture_by for STL containers --- clang/include/clang/Sema/Sema.h | 3 + clang/lib/Sema/SemaAttr.cpp | 34 clang/lib/Sema/SemaDecl.cpp | 2 + clang/test/Sema/Inputs/lifetime-analysis.h| 5 ++ .../warn-lifetime-analysis-capture-by.cpp | 79 +++ 5 files changed, 123 insertions(+) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6ea6c67447b6f0..9bafcfec5d4786 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(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..507f7c40d58782 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -268,6 +268,40 @@ 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; + RD = RD->getCanonicalDecl(); + if (auto *CTSD = dyn_cast(RD)) +RD = CTSD->getSpecializedTemplate()->getTemplatedDecl(); + return RD->hasAttr(); +} + +void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { + if (!FD) +return; + auto *MD = dyn_cast(FD); + if (!MD || !MD->getIdentifier() || !MD->getParent()->isInStdNamespace()) +return; + static const llvm::StringSet<> CapturingMethods{"insert", "push", + "push_front", "push_back"}; + if (!CapturingMethods.contains(MD->getName())) +return; + for (ParmVarDecl *PVD : MD->parameters()) { +if (PVD->hasAttr()) + return; +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 be570f3a1829d0..5b30d0f2c22d16 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11913,6 +11913,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()) { @@ -16716,6 +16717,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/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 diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp index b3fde386b8616c..462cb2d3f3fd6e 100644 --- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp @@ -366,3 +366,82 @@ 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