[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1091,21 +1104,22 @@ static bool isAssignmentOperatorLifetimeBound(CXXMethodDecl *CMD) { } static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef, - const AssignedEntity &Entity) { + const CapturingEntity &Entity) { bool EnableGSLAssignmentWarnings = !SemaRef.getDiagnostics().isIgnored( diag::warn_dangling_lifetime_pointer_assignment, SourceLocation()); return (EnableGSLAssignmentWarnings && - (isRecordWithAttr(Entity.LHS->getType()) || + (isRecordWithAttr(Entity.Expression->getType()) || isAssignmentOperatorLifetimeBound(Entity.AssignmentOperator))); } static void checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity, const InitializedEntity *ExtendingEntity, LifetimeKind LK, - const AssignedEntity *AEntity, Expr *Init) { - assert((AEntity && LK == LK_Assignment) || - (InitEntity && LK != LK_Assignment)); + const CapturingEntity *CEntity, Expr *Init) { usx95 wrote: Done in https://github.com/llvm/llvm-project/pull/115921 https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3223,6 +3225,49 @@ void Sema::CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl, << ParamName << (FDecl != nullptr) << FDecl; } +void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction, + const Expr *ThisArg, + ArrayRef Args) { + auto GetArgAt = [&](int Idx) { +if (IsMemberFunction && Idx == 0) + return const_cast(ThisArg); +return const_cast(Args[Idx - int(IsMemberFunction)]); + }; + for (unsigned I = 0; I < FD->getNumParams(); ++I) { +auto *CapturedByAttr = +FD->getParamDecl(I)->getAttr(); +if (!CapturedByAttr) + continue; +for (int CapturingParamIdx : CapturedByAttr->params()) { + Expr *Capturing = GetArgAt(CapturingParamIdx); + Expr *Captured = GetArgAt(I + IsMemberFunction); + CapturingEntity CE{Capturing}; usx95 wrote: Designated initializer is available in C++20 and in earlier C++ versions only as an extension. Although LLVM code is compatible with C++20, it would not be moved to default C++20 for quite some time. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
usx95 wrote: Thanks for the revert. I think I know what caused the compile time regression. Looking into the leak now and I can reproduce with asan. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
eugenis wrote: Please note that this change also introduces new memory leaks on the waterfall: https://lab.llvm.org/buildbot/#/builders/169/builds/5193 https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
nikic wrote: I've reverted this change, because it causes a large compile-time regression, see https://llvm-compile-time-tracker.com/compare.php?from=4a68e4cbd2423dcacada8162ab7c4bb8d7f7e2cf&to=8c4331c1abeb33eabf3cdbefa7f2b6e0540e7f4f&stat=instructions:u. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 closed https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
usx95 wrote: I will proceed with landing this. Happy to address future comments in a followup. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { usx95 wrote: Added a diagnostics (error) for now. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From f32865308bf1abbb50e7c6d8dd712565d0dd79c2 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/Attr.td | 33 ++ clang/include/clang/Basic/AttrDocs.td | 69 +++ .../clang/Basic/DiagnosticSemaKinds.td| 14 +++ clang/include/clang/Sema/Sema.h | 8 ++ clang/lib/AST/TypePrinter.cpp | 15 +++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 111 ++ clang/lib/Sema/SemaType.cpp | 13 ++ clang/test/AST/attr-lifetime-capture-by.cpp | 9 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 46 11 files changed, 322 insertions(+) create mode 100644 clang/test/AST/attr-lifetime-capture-by.cpp create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c3424e0e6f34c9..93cce21156634d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,9 @@ Attribute Changes in Clang - Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or ``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + Improvements to Clang's diagnostics --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a631e81d40aa68..6a77967c32cbcb 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1889,6 +1889,39 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b64dbef6332e6a..21fcd183e8969c 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3918,6 +3918,75 @@ have their lifetimes extended. }]; } +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function +parameter or implicit object parameter indicates that that objects that are referred to +by that parameter may also be referred to by the capturing entity ``X``. + +By default, a reference is considered to refer to its referenced object, a +pointer is considered to refer to its pointee, a ``std::initializer_list`` +is considered to refer to its underlying array, and aggregates (arrays and +simple ``struct``\s) are considered to refer to all objects that their +transitive subobjects refer to. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the implicit `
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,80 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function +parameter or implicit object parameter indicates that that objects that are referred to +by that parameter may also be referred to by the capturing entity ``X``. + +The ``lifetimebound`` attribute on a function parameter or implicit object usx95 wrote: Aah. missed to push this change. Thanks. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/Xazax-hun edited https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { Xazax-hun wrote: I think this is not a blocker for this patch. We could add a diagnostic as @hokein suggested, and we can change the behavior in a follow-up patch later. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -0,0 +1,9 @@ +// RUN: %clang_cc1 %s -ast-dump | FileCheck %s + +// Verify that we print the [[clang::lifetime_capture_by(X)]] attribute. + +struct S { +void foo(int &a, int &b) [[clang::lifetime_capture_by(a, b, global)]]; +}; + +// CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) usx95 wrote: Done. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1760,6 +1760,10 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); + LifetimeCaptureByAttr *ParseLifetimeCaptureByAttr(const ParsedAttr &AL, hokein wrote: ah, I see. I missed that `HandleLifetimeCaptureByAttr` is an overload function. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From 6ae72b7267193f6ee2f47c31d8a9542466d08ea4 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/Attr.td | 33 ++ clang/include/clang/Basic/AttrDocs.td | 69 .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 8 ++ clang/lib/AST/TypePrinter.cpp | 15 +++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ clang/lib/Sema/SemaType.cpp | 13 +++ clang/test/AST/attr-lifetime-capture-by.cpp | 9 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 42 +++ 11 files changed, 308 insertions(+) create mode 100644 clang/test/AST/attr-lifetime-capture-by.cpp create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c3424e0e6f34c9c..93cce21156634d5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,9 @@ Attribute Changes in Clang - Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or ``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + Improvements to Clang's diagnostics --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a631e81d40aa686..6a77967c32cbcba 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1889,6 +1889,39 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b64dbef6332e6a8..21fcd183e8969c6 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3918,6 +3918,75 @@ have their lifetimes extended. }]; } +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function +parameter or implicit object parameter indicates that that objects that are referred to +by that parameter may also be referred to by the capturing entity ``X``. + +By default, a reference is considered to refer to its referenced object, a +pointer is considered to refer to its pointee, a ``std::initializer_list`` +is considered to refer to its underlying array, and aggregates (arrays and +simple ``struct``\s) are considered to refer to all objects that their +transitive subobjects refer to. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the impl
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,80 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { usx95 wrote: Done. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From 85df517d9e09f355691fa797e80514e738999f1b Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/Attr.td | 33 ++ clang/include/clang/Basic/AttrDocs.td | 69 .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 8 ++ clang/lib/AST/TypePrinter.cpp | 15 +++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ clang/lib/Sema/SemaType.cpp | 13 +++ clang/test/AST/attr-lifetime-capture-by.cpp | 9 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 42 +++ 11 files changed, 308 insertions(+) create mode 100644 clang/test/AST/attr-lifetime-capture-by.cpp create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c3424e0e6f34c9c..93cce21156634d5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,9 @@ Attribute Changes in Clang - Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or ``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + Improvements to Clang's diagnostics --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a631e81d40aa686..6a77967c32cbcba 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1889,6 +1889,39 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b64dbef6332e6a8..21fcd183e8969c6 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3918,6 +3918,75 @@ have their lifetimes extended. }]; } +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function +parameter or implicit object parameter indicates that that objects that are referred to +by that parameter may also be referred to by the capturing entity ``X``. + +By default, a reference is considered to refer to its referenced object, a +pointer is considered to refer to its pointee, a ``std::initializer_list`` +is considered to refer to its underlying array, and aggregates (arrays and +simple ``struct``\s) are considered to refer to all objects that their +transitive subobjects refer to. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the impl
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/hokein edited https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/hokein approved this pull request. looks good, just some nits. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,80 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { hokein wrote: nit: let's move this section immediately right after the `LifetimeboundDocs`. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -0,0 +1,9 @@ +// RUN: %clang_cc1 %s -ast-dump | FileCheck %s + +// Verify that we print the [[clang::lifetime_capture_by(X)]] attribute. + +struct S { +void foo(int &a, int &b) [[clang::lifetime_capture_by(a, b, global)]]; +}; + +// CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global) hokein wrote: add an eol https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,80 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function +parameter or implicit object parameter indicates that that objects that are referred to +by that parameter may also be referred to by the capturing entity ``X``. + +The ``lifetimebound`` attribute on a function parameter or implicit object hokein wrote: This paragraph is irrelevant. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { hokein wrote: I think it is an ambiguous case, we could emit a diagnostic. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From a0f866bd9072a7334a3209a5dc668b39154093c1 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/Attr.td | 33 ++ clang/include/clang/Basic/AttrDocs.td | 74 + .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 8 ++ clang/lib/AST/TypePrinter.cpp | 15 +++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ clang/lib/Sema/SemaType.cpp | 13 +++ clang/test/AST/attr-lifetime-capture-by.cpp | 9 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 42 +++ 11 files changed, 313 insertions(+) create mode 100644 clang/test/AST/attr-lifetime-capture-by.cpp create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c3424e0e6f34c9c..93cce21156634d5 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,9 @@ Attribute Changes in Clang - Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or ``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + Improvements to Clang's diagnostics --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a631e81d40aa686..6a77967c32cbcba 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1889,6 +1889,39 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b64dbef6332e6a8..87f6591f0b4c666 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3967,6 +3967,80 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function +parameter or implicit object parameter indicates that that objects that are referred to +by that parameter may also be referred to by the capturing entity ``X``. + +The ``lifetimebound`` attribute on a function parameter or implicit object +parameter indicates that objects that are referred to by that parameter may +also be referred to by the return value of the annotated function (or, for a +parameter of a constructor, by the value of the constructed object). + +By default, a reference is considered to refer to its referenced object, a +pointer is considered to refer to its pointee, a ``std::initializer_list`` +is considered to refer to its underlying array, and aggregates (arrays and +simple ``struct``\s) are considered to refer to all objects that their +transitive subobjects refer to. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std:
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { Xazax-hun wrote: I wonder if @erichkeane has an opinion on this. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { Xazax-hun wrote: I am ok with either using the `__` names or just documenting the behavior and a workaround (user renaming their arguments). Alternatively, we could also permit users to write indices instead of names. I don't have a really strong preference. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1760,6 +1760,10 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); + LifetimeCaptureByAttr *ParseLifetimeCaptureByAttr(const ParsedAttr &AL, usx95 wrote: Reverted. Looks like this was used by `static void HandleLifetimeCaptureByAttr(TypeProcessingState &State,` for parsing function type attribute. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1909,6 +1911,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, OS << " [[clang::lifetimebound]]"; return; } + if (T->getAttrKind() == attr::LifetimeCaptureBy) { +// FIXME: Print the attribute arguments once we have a way to retrieve these usx95 wrote: > I think it should be possible since #108631 was merged. Thanks. Added. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From 1bdaf2ec7c52e23f70c35b7dd6195eb0627eb0bd Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/Attr.td | 34 ++ clang/include/clang/Basic/AttrDocs.td | 74 + .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 6 + clang/lib/AST/TypePrinter.cpp | 8 ++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 42 +++ 9 files changed, 283 insertions(+) create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c3424e0e6f34c9..93cce21156634d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,9 @@ Attribute Changes in Clang - Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or ``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + Improvements to Clang's diagnostics --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a631e81d40aa68..163aecd30163be 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1889,6 +1889,40 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let LangOpts = [CPlusPlus]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b64dbef6332e6a..87f6591f0b4c66 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3967,6 +3967,80 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Similar to `lifetimebound`_, the ``lifetime_capture_by(X)`` attribute on a function +parameter or implicit object parameter indicates that that objects that are referred to +by that parameter may also be referred to by the capturing entity ``X``. + +The ``lifetimebound`` attribute on a function parameter or implicit object +parameter indicates that objects that are referred to by that parameter may +also be referred to by the return value of the annotated function (or, for a +parameter of a constructor, by the value of the constructed object). + +By default, a reference is considered to refer to its referenced object, a +pointer is considered to refer to its pointee, a ``std::initializer_list`` +is considered to refer to its underlying array, and aggregates (arrays and +simple ``struct``\s) are considered to refer to all objects that their +transitive subobjects refer to. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lif
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be usx95 wrote: Modified the definition and added semantic of "referenced by" from lifetimebound. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1889,6 +1889,40 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let LangOpts = [CPlusPlus]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, usx95 wrote: Seems to work. Done. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { usx95 wrote: Hmm. I didn't think about this before. Would switching to something like `__global`, `__unknown` make sense ? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1889,6 +1889,40 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let LangOpts = [CPlusPlus]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, Xazax-hun wrote: Nit: would something like `SmallVectorImpl&& Idents` work here? If it did, it would make use sites more flexible, not forcing an inline buffer size on the callee. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -std=c++23 -verify %s + +struct S { + const int *x; + void captureInt(const int&x [[clang::lifetime_capture_by(this)]]) { this->x = &x; } +}; + +/// +// Test for valid usages. +/// +[[clang::lifetime_capture_by(unknown)]] // expected-error {{'lifetime_capture_by' attribute only applies to parameters and implicit object parameters}} +void nonMember( +const int &x1 [[clang::lifetime_capture_by(s, t)]], +S &s, +S &t, +const int &x2 [[clang::lifetime_capture_by(12345 + 12)]], // expected-error {{'lifetime_capture_by' attribute argument 12345 + 12 is not a known function parameter. Must be a function parameter of one of 'this', 'global' or 'unknown'}} +const int &x3 [[clang::lifetime_capture_by(abcdefgh)]], // expected-error {{'lifetime_capture_by' attribute argument 'abcdefgh' is not a known function parameter. Must be a function parameter of one of 'this', 'global' or 'unknown'}} +const int &x4 [[clang::lifetime_capture_by("abcdefgh")]], // expected-error {{'lifetime_capture_by' attribute argument "abcdefgh" is not a known function parameter. Must be a function parameter of one of 'this', 'global' or 'unknown'}} usx95 wrote: Done. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3383,6 +3383,18 @@ def err_callback_callee_is_variadic : Error< "'callback' attribute callee may not be variadic">; def err_callback_implicit_this_not_available : Error< "'callback' argument at position %0 references unavailable implicit 'this'">; + +def err_capture_by_attribute_multiple : Error< + "multiple 'lifetime_capture' attributes specified">; +def err_capture_by_attribute_no_entity : Error< + "'lifetime_capture_by' attribute specifies no capturing entity">; +def err_capture_by_implicit_this_not_available : Error< + "'lifetime_capture_by' argument references unavailable implicit 'this'">; +def err_capture_by_attribute_argument_unknown : Error< + "'lifetime_capture_by' attribute argument %0 is not a known function parameter" + "; must be a function parameter of one of 'this', 'global' or 'unknown'">; usx95 wrote: Done. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1760,6 +1760,10 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); + LifetimeCaptureByAttr *ParseLifetimeCaptureByAttr(const ParsedAttr &AL, usx95 wrote: You are right. Removed from header. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the implicit ``this`` parameter of a member +function by writing the attribute after the function type: + +.. code-block:: c++ + + struct S { +const char *data(std::set& s) [[clang::lifetime_capture_by(s)]] { + s.insert(this); +} + }; + +The attribute supports specifying more than one capturing entities: + +.. code-block:: c++ + + void addToSets(std::string_view a [[clang::lifetime_capture_by(s1, s2)]], + std::set& s1, + std::set& s2) { +s1.insert(a); +s2.insert(a); + } + +.. _`lifetimebound`: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound usx95 wrote: Missed to refer to it in the docs. Added a reference. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1760,6 +1760,10 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); + LifetimeCaptureByAttr *ParseLifetimeCaptureByAttr(const ParsedAttr &AL, +StringRef ParamName); + void LazyProcessLifetimeCaptureByParams(FunctionDecl *FD); usx95 wrote: Added. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3867,6 +3868,105 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) { S.Context, AL, EncodingIndices.data(), EncodingIndices.size())); } +LifetimeCaptureByAttr *Sema::ParseLifetimeCaptureByAttr(const ParsedAttr &AL, +StringRef ParamName) { + // Atleast one capture by is required. + if (AL.getNumArgs() == 0) { +Diag(AL.getLoc(), diag::err_capture_by_attribute_no_entity) +<< AL.getRange(); +return nullptr; + } + SmallVector ParamIdents; + SmallVector ParamLocs; + for (unsigned I = 0; I < AL.getNumArgs(); ++I) { +if (AL.isArgExpr(I)) { + Expr *E = AL.getArgAsExpr(I); + Diag(E->getExprLoc(), diag::err_capture_by_attribute_argument_unknown) + << E << E->getExprLoc(); + continue; +} +assert(AL.isArgIdent(I)); +IdentifierLoc *IdLoc = AL.getArgAsIdent(I); +if (IdLoc->Ident->getName() == ParamName) { + Diag(IdLoc->Loc, diag::err_capture_by_references_itself) << IdLoc->Loc; + continue; +} +ParamIdents.push_back(IdLoc->Ident); +ParamLocs.push_back(IdLoc->Loc); + } + SmallVector FakeParamIndices(ParamIdents.size(), + LifetimeCaptureByAttr::INVALID); + LifetimeCaptureByAttr *CapturedBy = ::new (Context) LifetimeCaptureByAttr( + Context, AL, FakeParamIndices.data(), FakeParamIndices.size()); + CapturedBy->setArgs(std::move(ParamIdents), std::move(ParamLocs)); + return CapturedBy; +} + +static void HandleLifetimeCaptureByAttr(Sema &S, Decl *D, +const ParsedAttr &AL) { + // Do not allow multiple attributes. + if (D->hasAttr()) { +S.Diag(AL.getLoc(), diag::err_capture_by_attribute_multiple) +<< AL.getRange(); +return; + } + auto *PVD = dyn_cast(D); + assert(PVD); + auto *CaptureByAttr = S.ParseLifetimeCaptureByAttr(AL, PVD->getName()); + if (CaptureByAttr) +D->addAttr(CaptureByAttr); +} + +void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) { + bool HasImplicitThisParam = isInstanceMethod(FD); + + llvm::StringMap NameIdxMapping; + NameIdxMapping["global"] = LifetimeCaptureByAttr::GLOBAL; + NameIdxMapping["unknown"] = LifetimeCaptureByAttr::UNKNOWN; + int Idx = 0; + if (HasImplicitThisParam) { +NameIdxMapping["this"] = 0; +Idx++; + } + for (const ParmVarDecl *PVD : FD->parameters()) +NameIdxMapping[PVD->getName()] = Idx++; + auto HandleCaptureBy = [&](LifetimeCaptureByAttr *CapturedBy) { +if (!CapturedBy) + return; +const auto &Entities = CapturedBy->getArgIdents(); +for (size_t I = 0; I < Entities.size(); ++I) { + StringRef Name = Entities[I]->getName(); + auto It = NameIdxMapping.find(Name); + if (It == NameIdxMapping.end()) { +auto Loc = CapturedBy->getArgLocs()[I]; +if (!HasImplicitThisParam && Name == "this") + Diag(Loc, diag::err_capture_by_implicit_this_not_available) << Loc; +else + Diag(Loc, diag::err_capture_by_attribute_argument_unknown) + << Entities[I] << Loc; +continue; + } + CapturedBy->setParamIdx(I, It->second); +} + }; + for (ParmVarDecl *PVD : FD->parameters()) +HandleCaptureBy(PVD->getAttr()); + if (!HasImplicitThisParam) +return; + TypeSourceInfo *TSI = FD->getTypeSourceInfo(); + if (!TSI) +return; + AttributedTypeLoc ATL; + for (TypeLoc TL = TSI->getTypeLoc(); Xazax-hun wrote: Not necessarily for this PR, but I see this kind of loops many times in Sema code. I wonder if this is an indication that TSI should have a more convenient API to get a specific `AttributedTypeLoc`. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { Xazax-hun wrote: One question is, how would this behave when there is an identifies collision. E.g.: ``` void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]], bool global) ``` ? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/Xazax-hun approved this pull request. Some nits inline, but overall looks good for me. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/Xazax-hun edited https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1199,6 +1213,17 @@ static void checkExprLifetimeImpl(Sema &SemaRef, break; } +case LK_LifetimeCapture: { + if (!MTE) Xazax-hun wrote: Ah, makes sense. I guess there is only one case when we could potentially get away with this, when `addToSet` is the last operation in the function, i.e., it is a tail call. But we might not have a convenient way to tell that only based on the AST. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the implicit ``this`` parameter of a member +function by writing the attribute after the function type: + +.. code-block:: c++ + + struct S { +const char *data(std::set& s) [[clang::lifetime_capture_by(s)]] { + s.insert(this); +} + }; + +The attribute supports specifying more than one capturing entities: + +.. code-block:: c++ + + void addToSets(std::string_view a [[clang::lifetime_capture_by(s1, s2)]], + std::set& s1, + std::set& s2) { +s1.insert(a); +s2.insert(a); + } + +.. _`lifetimebound`: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound hokein wrote: is the link to lifetimebound intended here? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1909,6 +1911,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, OS << " [[clang::lifetimebound]]"; return; } + if (T->getAttrKind() == attr::LifetimeCaptureBy) { +// FIXME: Print the attribute arguments once we have a way to retrieve these +// here. +OS << " [[clang::lifetime_capture_by(...)"; hokein wrote: This doesn't seem to be resolved. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1760,6 +1760,10 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); + LifetimeCaptureByAttr *ParseLifetimeCaptureByAttr(const ParsedAttr &AL, hokein wrote: nit: per LLVM code style, function name should start with a lower case letter, `openFile` Is this function used in other place? I only see it is used in the implementation of `LazyProcessLifetimeCaptureByParams`, it is not necessary to be in the public header. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -std=c++23 -verify %s + +struct S { + const int *x; + void captureInt(const int&x [[clang::lifetime_capture_by(this)]]) { this->x = &x; } +}; + +/// +// Test for valid usages. +/// +[[clang::lifetime_capture_by(unknown)]] // expected-error {{'lifetime_capture_by' attribute only applies to parameters and implicit object parameters}} +void nonMember( +const int &x1 [[clang::lifetime_capture_by(s, t)]], +S &s, +S &t, +const int &x2 [[clang::lifetime_capture_by(12345 + 12)]], // expected-error {{'lifetime_capture_by' attribute argument 12345 + 12 is not a known function parameter. Must be a function parameter of one of 'this', 'global' or 'unknown'}} +const int &x3 [[clang::lifetime_capture_by(abcdefgh)]], // expected-error {{'lifetime_capture_by' attribute argument 'abcdefgh' is not a known function parameter. Must be a function parameter of one of 'this', 'global' or 'unknown'}} +const int &x4 [[clang::lifetime_capture_by("abcdefgh")]], // expected-error {{'lifetime_capture_by' attribute argument "abcdefgh" is not a known function parameter. Must be a function parameter of one of 'this', 'global' or 'unknown'}} hokein wrote: The diagnostic messages in the test are not updated. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3383,6 +3383,18 @@ def err_callback_callee_is_variadic : Error< "'callback' attribute callee may not be variadic">; def err_callback_implicit_this_not_available : Error< "'callback' argument at position %0 references unavailable implicit 'this'">; + +def err_capture_by_attribute_multiple : Error< + "multiple 'lifetime_capture' attributes specified">; +def err_capture_by_attribute_no_entity : Error< + "'lifetime_capture_by' attribute specifies no capturing entity">; +def err_capture_by_implicit_this_not_available : Error< + "'lifetime_capture_by' argument references unavailable implicit 'this'">; +def err_capture_by_attribute_argument_unknown : Error< + "'lifetime_capture_by' attribute argument %0 is not a known function parameter" + "; must be a function parameter of one of 'this', 'global' or 'unknown'">; hokein wrote: > a function parameter **of** one of 'this', 'global' or 'unknown'" The `of` seems like a typo, I think it is `or`. Maybe just `must be a function parameter, this, 'global', or 'unknown'` https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be hokein wrote: I think we should be clear about semantic of "references to arguments passed to such parameters" in the doc. Similar to lifetimebound, I think we have 3 major cases: - a reference parameter, we consider its referenced object; - a pointer parameter, we consider its pointee object; - a pass-by-value struct parameter, we assume this struct is pointer-like object, we consider objects that its transitive subobjects refer to; https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1760,6 +1760,10 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); + LifetimeCaptureByAttr *ParseLifetimeCaptureByAttr(const ParsedAttr &AL, +StringRef ParamName); + void LazyProcessLifetimeCaptureByParams(FunctionDecl *FD); hokein wrote: nit: add some comments (it is not clear to me why it is lazy). https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From 83f0c8ff48ca1750048688eacc02bfffc52166b7 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/Attr.td | 34 ++ clang/include/clang/Basic/AttrDocs.td | 63 +++ .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 4 + clang/lib/AST/TypePrinter.cpp | 8 ++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 40 +++ 9 files changed, 268 insertions(+) create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c3424e0e6f34c9..93cce21156634d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,9 @@ Attribute Changes in Clang - Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or ``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + Improvements to Clang's diagnostics --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a631e81d40aa68..6884ec1b2a0663 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1889,6 +1889,40 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let LangOpts = [CPlusPlus]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b64dbef6332e6a..ad6588f8380687 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3967,6 +3967,69 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the implicit ``this`` parameter of a member +function by writing the attribute after the function type: + +.. code-block:: c++ + + struct S { +const char *data(std::set& s) [[clang::lifetime_capture_by(s)]] { + s.insert(this); +} + }; + +The attribute supports specifying more than one capturing entities: + +.. code-block:: c++ + + void addToSets(std::string_view a [[clang::lifetime_capture_by(s1, s2)]], + std::set& s1, + std::set& s2) { +s1.ins
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From adaab9e5f549ae1625e8539334dfbc73a0559d8c Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/Attr.td | 34 ++ clang/include/clang/Basic/AttrDocs.td | 62 +++ .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 4 + clang/lib/AST/TypePrinter.cpp | 8 ++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 40 +++ 9 files changed, 267 insertions(+) create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c3424e0e6f34c9..93cce21156634d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,9 @@ Attribute Changes in Clang - Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or ``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + Improvements to Clang's diagnostics --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a631e81d40aa68..6884ec1b2a0663 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1889,6 +1889,40 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let LangOpts = [CPlusPlus]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b64dbef6332e6a..9ba6b4e4993de3 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3967,6 +3967,68 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the implicit ``this`` parameter of a member +function by writing the attribute after the function type: + +.. code-block:: c++ + + struct S { +const char *data(std::set& s) [[clang::lifetime_capture_by(s)]] { + s.insert(this); +} + }; + +The attribute supports specifying more than one capturing entities: + +.. code-block:: c++ + void addToSets(std::string_view a [[clang::lifetime_capture_by(s1, s2)]], + std::set& s1, + std::set& s2) { +s1.insert(
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
usx95 wrote: The PR now only includes syntax changes without lifetime analysis. I will address the unaddressed comments here related to analysis in subsequent PR. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1199,6 +1213,17 @@ static void checkExprLifetimeImpl(Sema &SemaRef, break; } +case LK_LifetimeCapture: { + if (!MTE) usx95 wrote: I don't think we can easily support this without intraprocedural data flow. For example, consider the following: ```cpp void f() { std::string local; addToSet(local); set.clear(); } ``` https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -12,6 +12,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" +#include "clang/AST/Attrs.inc" usx95 wrote: Removed. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
llvmbot wrote: @llvm/pr-subscribers-clang Author: Utkarsh Saxena (usx95) Changes This implements the RFC https://discourse.llvm.org/t/rfc-introduce-clang-lifetime-capture-by-x/81371 In this PR, we introduce `[[clang::lifetime_capture_by(X)]]` attribute as discussed in the RFC. As an implementation detail of this attribute, we store and use param indices instead of raw param expressions. The parameter indices are computed lazily at the end of function declaration since the function decl (and therefore the subsequent parameters) are not visible yet while parsing a parameter annotation. In subsequent PR, we will infer this attribute for STL containers and perform lifetime analysis to detect dangling cases. --- Full diff: https://github.com/llvm/llvm-project/pull/111499.diff 9 Files Affected: - (modified) clang/docs/ReleaseNotes.rst (+3) - (modified) clang/include/clang/Basic/Attr.td (+34) - (modified) clang/include/clang/Basic/AttrDocs.td (+62) - (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+12) - (modified) clang/include/clang/Sema/Sema.h (+4) - (modified) clang/lib/AST/TypePrinter.cpp (+8) - (modified) clang/lib/Sema/SemaDecl.cpp (+1) - (modified) clang/lib/Sema/SemaDeclAttr.cpp (+103) - (added) clang/test/SemaCXX/attr-lifetime-capture-by.cpp (+40) ``diff diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index c3424e0e6f34c9..93cce21156634d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -449,6 +449,9 @@ Attribute Changes in Clang - Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or ``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + Improvements to Clang's diagnostics --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index a631e81d40aa68..6884ec1b2a0663 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1889,6 +1889,40 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let LangOpts = [CPlusPlus]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b64dbef6332e6a..9ba6b4e4993de3 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3967,6 +3967,68 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the implicit ``this`` parameter of a member +function by writing the attribute after the f
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 ready_for_review https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From b9379b1a8516fd8c2bb444aed09335eba37c8d53 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/Basic/Attr.td | 34 ++ clang/include/clang/Basic/AttrDocs.td | 62 +++ .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 4 + clang/lib/AST/TypePrinter.cpp | 8 ++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 40 +++ 9 files changed, 267 insertions(+) create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 1372e49dfac03c..e9db685a1a161a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -386,6 +386,9 @@ Attribute Changes in Clang - Fix a bug where clang doesn't automatically apply the ``[[gsl::Owner]]`` or ``[[gsl::Pointer]]`` to STL explicit template specialization decls. (#GH109442) +- Clang now supports ``[[clang::lifetime_capture_by(X)]]``. Similar to lifetimebound, this can be + used to specify when a reference to a function parameter is captured by another capturing entity ``X``. + Improvements to Clang's diagnostics --- diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 156fbd1c4442eb..7fd93424ccddf7 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1873,6 +1873,40 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let LangOpts = [CPlusPlus]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b497cce37625c9..66da3aa0a95a52 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3794,6 +3794,68 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the implicit ``this`` parameter of a member +function by writing the attribute after the function type: + +.. code-block:: c++ + + struct S { +const char *data(std::set& s) [[clang::lifetime_capture_by(s)]] { + s.insert(this); +} + }; + +The attribute supports specifying more than one capturing entities: + +.. code-block:: c++ + void addToSets(std::string_view a [[clang::lifetime_capture_by(s1, s2)]], + std::set& s1, + std::set& s2) { +s1.insert(
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From 1778769830af741684ae6ed0278f5ecc4626edae Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/include/clang/Basic/Attr.td | 34 ++ clang/include/clang/Basic/AttrDocs.td | 62 +++ .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 4 + clang/lib/AST/TypePrinter.cpp | 8 ++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 40 +++ 8 files changed, 264 insertions(+) create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 156fbd1c4442eb..7fd93424ccddf7 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1873,6 +1873,40 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeCaptureByDocs]; + let LangOpts = [CPlusPlus]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int THIS = 0; + static constexpr int INVALID = -1; + static constexpr int UNKNOWN = -2; + static constexpr int GLOBAL = -3; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index b497cce37625c9..66da3aa0a95a52 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3794,6 +3794,68 @@ Attribute ``trivial_abi`` has no effect in the following cases: }]; } + +def LifetimeCaptureByDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``lifetime_capture_by(X)`` attribute on a function parameter or implicit object +parameter indicates that references to arguments passed to such parameters may be +captured by the capturing entity ``X``. + +The capturing entity ``X`` can be one of the following: +- Another (named) function parameter. + + .. code-block:: c++ + +void addToSet(std::string_view a [[clang::lifetime_capture_by(s)]], std::set& s) { + s.insert(a); +} + +- ``this`` (in case of member functions). + + .. code-block:: c++ + +class S { + void addToSet(std::string_view a [[clang::lifetime_capture_by(this)]]) { +s.insert(a); + } + std::set s; +}; + +- 'global', 'unknown' (without quotes). + + .. code-block:: c++ + +std::set s; +void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + s.insert(a); +} +void addSomewhere(std::string_view a [[clang::lifetime_capture_by(unknown)]]); + +The attribute can be applied to the implicit ``this`` parameter of a member +function by writing the attribute after the function type: + +.. code-block:: c++ + + struct S { +const char *data(std::set& s) [[clang::lifetime_capture_by(s)]] { + s.insert(this); +} + }; + +The attribute supports specifying more than one capturing entities: + +.. code-block:: c++ + void addToSets(std::string_view a [[clang::lifetime_capture_by(s1, s2)]], + std::set& s1, + std::set& s2) { +s1.insert(a); +s2.insert(a); + } + + .. _`lifetimebound`: https://clang.llvm.org/docs/AttributeReference.html#lifetimebound + }]; +} def MSInheritanceDocs : Documentation { let Category = DocCatDecl; let Heading = "__single_inheritance, __multiple_inheritance, __virtual_inheritance"; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d697e6d61afa9a..028c2982026352 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3383,6 +3383,18 @@ def err_callback_callee_is_variadic : Error< "'callback' attribute callee may not be variadic">; def err_callback_implicit_this_not_available : Error<
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1882,6 +1882,46 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + + // let SimpleHandler = 1; + // let LateParsed = LateAttrParseStandard; + // let HasCustomParsing = 1; + // let ParseArgumentsAsUnevaluated = 1; + + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int INVALID = -2; + static constexpr int UNKNOWN = -1; usx95 wrote: This was because GLOBAL and UNKNOWN would have the same enforcements. That is no temporary irrespective of the capturing entity. I will index differently to avoid confusion. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From 5af16e4afc15956dea0c46ffc3abb20067c047f2 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/include/clang/Basic/Attr.td | 34 ++ .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 7 ++ clang/lib/AST/TypePrinter.cpp | 8 ++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 40 +++ 7 files changed, 205 insertions(+) create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 156fbd1c4442eb..cb836621a0577a 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1873,6 +1873,40 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int INVALID = -2; + static constexpr int UNKNOWN = -1; + static constexpr int GLOBAL = -1; + static constexpr int THIS = 0; + + void setArgs(SmallVector&& Idents, + SmallVector&& Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + ArrayRef getArgIdents() const { return ArgIdents; } + ArrayRef getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d697e6d61afa9a..028c2982026352 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3383,6 +3383,18 @@ def err_callback_callee_is_variadic : Error< "'callback' attribute callee may not be variadic">; def err_callback_implicit_this_not_available : Error< "'callback' argument at position %0 references unavailable implicit 'this'">; + +def err_capture_by_attribute_multiple : Error< + "multiple 'lifetime_capture' attributes specified">; +def err_capture_by_attribute_no_entity : Error< + "'lifetime_capture_by' attribute specifies no capturing entity">; +def err_capture_by_implicit_this_not_available : Error< + "'lifetime_capture_by' argument references unavailable implicit 'this'">; +def err_capture_by_attribute_argument_unknown : Error< + "'lifetime_capture_by' attribute argument %0 is not a known function parameter" + "; must be a function parameter of one of 'this', 'global' or 'unknown'">; +def err_capture_by_references_itself : Error<"'lifetime_capture_by' argument references itself">; + def err_init_method_bad_return_type : Error< "init methods must return an object pointer type, not %0">; def err_attribute_invalid_size : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 93d98e1cbb9c81..9eb158d223bf5a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1760,6 +1760,10 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); + LifetimeCaptureByAttr *ParseLifetimeCaptureByAttr(const ParsedAttr &AL, +StringRef ParamName); + void LazyProcessLifetimeCaptureByParams(FunctionDecl *FD); + /// Add _Nullable attributes for std:: types. void inferNullableClassAttribute(CXXRecordDecl *CRD); @@ -2315,6 +2319,9 @@ class Sema final : public SemaBase { bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly = false); bool BuiltinVectorToScalarMath(CallExpr *TheCall); + void checkLifetimeCaptureBy(FunctionDecl *FDecl, bool IsMemberFunction, + const Expr *ThisArg, ArrayRef Args); + /// Handles the checks for format strings, non-POD arguments to vararg /// functions, NULL arguments passed to non-NULL parameters, diagnose_if /// attributes and AArch64 SME attributes. diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 6d8db5cf4ffd22..802b2b0fb07e6b 100644 --- a/clang/lib/AST/TypeP
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From fa191212f1d0e381503c250c3387e10a5f244576 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Sun, 10 Nov 2024 07:42:49 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/include/clang/Basic/Attr.td | 40 +++ .../clang/Basic/DiagnosticSemaKinds.td| 12 ++ clang/include/clang/Sema/Sema.h | 7 ++ clang/lib/AST/TypePrinter.cpp | 9 ++ clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 103 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 40 +++ 7 files changed, 212 insertions(+) create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 156fbd1c4442eb..cb56ace6eaff47 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1873,6 +1873,46 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + + // let SimpleHandler = 1; + // let LateParsed = LateAttrParseStandard; + // let HasCustomParsing = 1; + // let ParseArgumentsAsUnevaluated = 1; + + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int INVALID = -2; + static constexpr int UNKNOWN = -1; + static constexpr int GLOBAL = -1; + static constexpr int THIS = 0; + + void setArgs(SmallVector Idents, + SmallVector Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + const SmallVector& getArgIdents() const { return ArgIdents; } + const SmallVector& getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d697e6d61afa9a..cd36ef43113507 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3383,6 +3383,18 @@ def err_callback_callee_is_variadic : Error< "'callback' attribute callee may not be variadic">; def err_callback_implicit_this_not_available : Error< "'callback' argument at position %0 references unavailable implicit 'this'">; + +def err_capture_by_attribute_multiple : Error< + "multiple 'lifetime_capture' attributes specified">; +def err_capture_by_attribute_no_entity : Error< + "'lifetime_capture_by' attribute specifies no capturing entity">; +def err_capture_by_implicit_this_not_available : Error< + "'lifetime_capture_by' argument references unavailable implicit 'this'">; +def err_capture_by_attribute_argument_unknown : Error< + "'lifetime_capture_by' attribute argument %0 is not a known function parameter" + ". Must be a function parameter of one of 'this', 'global' or 'unknown'">; +def err_capture_by_references_itself : Error<"'lifetime_capture_by' argument references itself">; + def err_init_method_bad_return_type : Error< "init methods must return an object pointer type, not %0">; def err_attribute_invalid_size : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 93d98e1cbb9c81..9eb158d223bf5a 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1760,6 +1760,10 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD); + LifetimeCaptureByAttr *ParseLifetimeCaptureByAttr(const ParsedAttr &AL, +StringRef ParamName); + void LazyProcessLifetimeCaptureByParams(FunctionDecl *FD); + /// Add _Nullable attributes for std:: types. void inferNullableClassAttribute(CXXRecordDecl *CRD); @@ -2315,6 +2319,9 @@ class Sema final : public SemaBase { bool BuiltinVectorMath(CallExpr *TheCall, QualType &Res, bool FPOnly = false); bool BuiltinVectorToScalarMath(CallExpr *TheCall); + void checkLifetimeCaptureBy(FunctionDecl *FDecl, bool IsMemberFunction, + const Expr *ThisArg, ArrayRef Args); + /// Handles the checks for format strings, non-POD arguments to vararg /// functions, NULL arguments passed to non-NULL parameters, diagnose_if /// attributes
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
usx95 wrote: > I did not get to the end of this PR yet, but I was wondering if it would make > sense to split it up. One PR could add the attribute with the semantic > analysis (e.g., warnings for ill-formed arguments). The second PR could > introduce the lifetime analysis part. WDYT? Makes sense. I will do this now. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 edited https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1873,6 +1873,46 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + + // let SimpleHandler = 1; + // let LateParsed = LateAttrParseStandard; + // let HasCustomParsing = 1; + // let ParseArgumentsAsUnevaluated = 1; + + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int INVALID = -2; + static constexpr int UNKNOWN = -1; + static constexpr int GLOBAL = -1; + static constexpr int THIS = 0; Xazax-hun wrote: I wish we had one universal way of indexing parameters in Clang. I think probably most attributes start from 1, but there are APINotes that start from 0. Not really an actionable comment here, more like a rant. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -45,10 +48,14 @@ enum LifetimeKind { /// a default member initializer), the program is ill-formed. LK_MemInitializer, - /// The lifetime of a temporary bound to this entity probably ends too soon, + /// The lifetime of a temporary bound to this entity may end too soon, /// because the entity is a pointer and we assign the address of a temporary /// object to it. LK_Assignment, + + /// The lifetime of a temporary bound to this entity may end too soon, Xazax-hun wrote: Probably out of scope for this PR, but are these comments up to date given that we can also warn when the address of a local escapes the function (as opposed to just temporaries)? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1199,6 +1213,17 @@ static void checkExprLifetimeImpl(Sema &SemaRef, break; } +case LK_LifetimeCapture: { + if (!MTE) Xazax-hun wrote: Do we want to diagnose the following case: ``` std::set set; void addToSet(std::string_view s [[clang::lifetime_capture_by()]]) { set.insert(s); } void f() { std::string local; addToSet(local); } ``` ? I think we don't have an MTE here. It is OK to cover this in a follow-up. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -12,6 +12,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" +#include "clang/AST/Attrs.inc" Xazax-hun wrote: Is this include intended? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1873,6 +1873,46 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + + // let SimpleHandler = 1; + // let LateParsed = LateAttrParseStandard; + // let HasCustomParsing = 1; + // let ParseArgumentsAsUnevaluated = 1; + + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int INVALID = -2; + static constexpr int UNKNOWN = -1; + static constexpr int GLOBAL = -1; + static constexpr int THIS = 0; + + void setArgs(SmallVector Idents, + SmallVector Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + const SmallVector& getArgIdents() const { return ArgIdents; } Xazax-hun wrote: I think it might be better to return an `ArrayRef` here (and in the next getter). https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/Xazax-hun commented: I did not get to the end of this PR yet, but I was wondering if it would make sense to split it up. One PR could add the attribute with the semantic analysis (e.g., warnings for ill-formed arguments). The second PR could introduce the lifetime analysis part. WDYT? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1873,6 +1873,46 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + + // let SimpleHandler = 1; + // let LateParsed = LateAttrParseStandard; + // let HasCustomParsing = 1; + // let ParseArgumentsAsUnevaluated = 1; + + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int INVALID = -2; + static constexpr int UNKNOWN = -1; + static constexpr int GLOBAL = -1; + static constexpr int THIS = 0; + + void setArgs(SmallVector Idents, Xazax-hun wrote: We often take `SmallVectorImpl&` or `SmallVectorImpl&&` as arguments, so we do not need to force the caller into a specific count for the inline buffer. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/Xazax-hun edited https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1909,6 +1911,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, OS << " [[clang::lifetimebound]]"; return; } + if (T->getAttrKind() == attr::LifetimeCaptureBy) { +// FIXME: Print the attribute arguments once we have a way to retrieve these Xazax-hun wrote: I think it should be possible since https://github.com/llvm/llvm-project/pull/108631 was merged. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
Xazax-hun wrote: Hey! Thanks for working on this! Some high level comments based on the description: > 1. Class inherited from a pointer-like type. In this false positive example slicing is happening and we know that the created type (after slicing) is a pointer. Do we always get these false positives with slicing? If that is the case, could we just suppress the warning when derived-to-base conversion happens where the base is a pointer? > 2. Not able to differentiate between pointer-type and value type with > templates I think this is a really serious problem that could hinder usability. I'd prefer to not emit warnings for templates until this one is fixed. Do you think this would make sense or would this suppress all of the interesting cases that you want to catch? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From 7bfb78165fd605a928efbb39feb18b670210a66d Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Mon, 4 Nov 2024 12:06:08 + Subject: [PATCH] Introduce [[clang::lifetime_capture_by]] --- clang/include/clang/Basic/Attr.td | 40 + clang/include/clang/Basic/DiagnosticGroups.td | 1 + .../clang/Basic/DiagnosticSemaKinds.td| 15 ++ clang/include/clang/Sema/Sema.h | 10 ++ clang/lib/AST/TypePrinter.cpp | 9 + clang/lib/Sema/CheckExprLifetime.cpp | 80 ++--- clang/lib/Sema/CheckExprLifetime.h| 27 ++- clang/lib/Sema/SemaAttr.cpp | 36 clang/lib/Sema/SemaChecking.cpp | 47 ++ clang/lib/Sema/SemaDecl.cpp | 3 + clang/lib/Sema/SemaDeclAttr.cpp | 103 clang/lib/Sema/SemaExpr.cpp | 4 +- clang/lib/Sema/SemaInit.cpp | 2 +- clang/lib/Sema/SemaOverload.cpp | 4 +- .../lib/Sema/SemaTemplateInstantiateDecl.cpp | 1 - clang/lib/Sema/SemaType.cpp | 13 ++ .../Sema/warn-lifetime-analysis-nocfg.cpp | 154 ++ .../test/SemaCXX/attr-lifetime-capture-by.cpp | 40 + clang/test/SemaCXX/attr-lifetimebound.cpp | 4 +- 19 files changed, 559 insertions(+), 34 deletions(-) create mode 100644 clang/test/SemaCXX/attr-lifetime-capture-by.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 156fbd1c4442eb..cb56ace6eaff47 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1873,6 +1873,46 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + + // let SimpleHandler = 1; + // let LateParsed = LateAttrParseStandard; + // let HasCustomParsing = 1; + // let ParseArgumentsAsUnevaluated = 1; + + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int INVALID = -2; + static constexpr int UNKNOWN = -1; + static constexpr int GLOBAL = -1; + static constexpr int THIS = 0; + + void setArgs(SmallVector Idents, + SmallVector Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + const SmallVector& getArgIdents() const { return ArgIdents; } + const SmallVector& getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 72eada50a56cc9..5209cd1eb795f3 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -453,6 +453,7 @@ def ShiftOpParentheses: DiagGroup<"shift-op-parentheses">; def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">; def DanglingAssignment: DiagGroup<"dangling-assignment">; def DanglingAssignmentGsl : DiagGroup<"dangling-assignment-gsl">; +def DanglingCapture : DiagGroup<"dangling-capture">; def DanglingElse: DiagGroup<"dangling-else">; def DanglingField : DiagGroup<"dangling-field">; def DanglingInitializerList : DiagGroup<"dangling-initializer-list">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d697e6d61afa9a..52a7e816aef5fc 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3383,6 +3383,18 @@ def err_callback_callee_is_variadic : Error< "'callback' attribute callee may not be variadic">; def err_callback_implicit_this_not_available : Error< "'callback' argument at position %0 references unavailable implicit 'this'">; + +def err_capture_by_attribute_multiple : Error< + "multiple 'lifetime_capture' attributes specified">; +def err_capture_by_attribute_no_entity : Error< + "'lifetime_capture_by' attribute specifies no capturing entity">; +def err_capture_by_implicit_this_not_available : Error< + "'lifetime_capture_by' argument references unavailable implicit 'this'">; +def err_capture_by_attribute_argument_unknown : Error< + "'lifetime_capture_by' attribute argument %0 is not a known function parameter" + ". Must be a function parameter of one of 'this', 'global' or '
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -269,6 +271,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 the `[]` operator for map-like container is an important case, code like below is probably not rare: ``` std::map m; m[ReturnString(..)] = ...; // this leaves a dangling string_view key in the map. ``` https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -4961,7 +4961,6 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, bool AtEndOfTU) { if (Function->isInvalidDecl() || isa(Function)) return; - hokein wrote: nit: unintended change. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1882,6 +1882,46 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + + // let SimpleHandler = 1; + // let LateParsed = LateAttrParseStandard; + // let HasCustomParsing = 1; + // let ParseArgumentsAsUnevaluated = 1; hokein wrote: nit: these lines seem irrelevant. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3379,6 +3379,18 @@ def err_callback_callee_is_variadic : Error< "'callback' attribute callee may not be variadic">; def err_callback_implicit_this_not_available : Error< "'callback' argument at position %0 references unavailable implicit 'this'">; + +def err_capture_by_attribute_multiple : Error< + "multiple 'lifetime_capture' attributes specified">; +def err_capture_by_attribute_no_entity : Error< + "'lifetime_capture_by' attribute specifies no capturing entity">; +def err_capture_by_implicit_this_not_available : Error< + "'lifetime_capture_by' argument references unavailable implicit 'this'">; +def err_capture_by_attribute_argument_unknown : Error< + "'lifetime_capture_by' attribute argument %0 is not a known function parameter" + ". Must be a function parameter of one of 'this', 'global' or 'unknown'">; hokein wrote: We don't use sentence in diagnostic message. `. Must` => `; must` https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -269,6 +271,40 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) { } } +static bool IsPointerLikeType(QualType QT) { hokein wrote: we have a similar function in the CheckExprLifetime.cpp, we can move it to `Sema.h` to make it reusable. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -269,6 +271,40 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) { } } +static bool IsPointerLikeType(QualType QT) { + QT = QT.getNonReferenceType(); + // if (QT->isPointerType()) hokein wrote: any reason to comment it out? I think having this should fix some FIXMEs in the test. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -269,6 +271,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) { hokein wrote: Since we do the auto inference for some STL types, it would be better to have some lit-tests to verify the AST node, e.g. make sure the `lifetime_capture_by` is present. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1882,6 +1882,46 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + + // let SimpleHandler = 1; + // let LateParsed = LateAttrParseStandard; + // let HasCustomParsing = 1; + // let ParseArgumentsAsUnevaluated = 1; + + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static constexpr int INVALID = -2; + static constexpr int UNKNOWN = -1; hokein wrote: why does `UNKNOWN` share the same value as `GLOBAL`? It would be nice to have some documentation about these fields. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -18,29 +18,42 @@ namespace clang::sema { -/// Describes an entity that is being assigned. -struct AssignedEntity { - // The left-hand side expression of the assignment. - Expr *LHS = nullptr; +struct CapturingEntity { hokein wrote: +1. I'd not change the existing the `AssignedEntity`, as this structure is designed for the assignment case. It looks like for the capture_by case, we only need the `Expression`, I think we could use `Expr*` directly in the function parameter, e.g. `void checkCaptureLifetime(Sema &SemaRef, const Expr* Entity, Expr *Captured);`. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -248,9 +256,12 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path, LocalVisitor Visit); template static bool isRecordWithAttr(QualType Type) { - if (auto *RD = Type->getAsCXXRecordDecl()) -return RD->hasAttr(); - return false; + CXXRecordDecl *RD = Type.getNonReferenceType()->getAsCXXRecordDecl(); + if (!RD) +return false; + if (auto *CTSD = dyn_cast(RD)) hokein wrote: Why do we need to handle template specializations specifically? The GSL pointer/owner attribute should propagate from the primary template to its specializations. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1909,6 +1911,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, OS << " [[clang::lifetimebound]]"; return; } + if (T->getAttrKind() == attr::LifetimeCaptureBy) { +// FIXME: Print the attribute arguments once we have a way to retrieve these hokein wrote: Is it possible to get the `LifetimeCaptureByAttr` object from `T`? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1909,6 +1911,12 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, OS << " [[clang::lifetimebound]]"; return; } + if (T->getAttrKind() == attr::LifetimeCaptureBy) { +// FIXME: Print the attribute arguments once we have a way to retrieve these +// here. +OS << " [[clang::lifetime_capture_by(...)"; hokein wrote: nit: it looks like it misses the closing `]]` https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -452,6 +452,7 @@ def ShiftOpParentheses: DiagGroup<"shift-op-parentheses">; def OverloadedShiftOpParentheses: DiagGroup<"overloaded-shift-op-parentheses">; def DanglingAssignment: DiagGroup<"dangling-assignment">; def DanglingAssignmentGsl : DiagGroup<"dangling-assignment-gsl">; +def DanglingCapture : DiagGroup<"dangling-capture">; hokein wrote: we should probably add category to the `dangling` group (line 463). https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -269,6 +271,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"}; bricknerb wrote: Do we not have emplace* methods on purpose? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -18,29 +18,42 @@ namespace clang::sema { -/// Describes an entity that is being assigned. -struct AssignedEntity { - // The left-hand side expression of the assignment. - Expr *LHS = nullptr; +struct CapturingEntity { bricknerb wrote: I think that using CapturingEntity for both use cases, with one of them keeping a field null makes it much harder to follow. Is it possible to use a more specific type? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -18,29 +18,42 @@ namespace clang::sema { -/// Describes an entity that is being assigned. -struct AssignedEntity { - // The left-hand side expression of the assignment. - Expr *LHS = nullptr; +struct CapturingEntity { + // The expression of the entity which captures another entity. + // For example: + // 1. In an assignment, this would be the left-hand side expression. + //std::string_view sv; + //sv = std::string(); // Here 'sv' is the 'Entity'. + // + // 2. In an function call involving a lifetime capture, this would be the + // argument capturing the lifetime of another argument. + //void addToSet(std::string_view s [[clang::lifetime_capture_by(sv)]], bricknerb wrote: Is sv here referring to sv in (1) or a typo? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -3223,6 +3225,49 @@ void Sema::CheckArgAlignment(SourceLocation Loc, NamedDecl *FDecl, << ParamName << (FDecl != nullptr) << FDecl; } +void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction, + const Expr *ThisArg, + ArrayRef Args) { + auto GetArgAt = [&](int Idx) { +if (IsMemberFunction && Idx == 0) + return const_cast(ThisArg); +return const_cast(Args[Idx - int(IsMemberFunction)]); + }; + for (unsigned I = 0; I < FD->getNumParams(); ++I) { +auto *CapturedByAttr = +FD->getParamDecl(I)->getAttr(); +if (!CapturedByAttr) + continue; +for (int CapturingParamIdx : CapturedByAttr->params()) { + Expr *Capturing = GetArgAt(CapturingParamIdx); + Expr *Captured = GetArgAt(I + IsMemberFunction); + CapturingEntity CE{Capturing}; bricknerb wrote: Consider making this initialization clearer by writing .Expression = Capturing https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1091,21 +1104,22 @@ static bool isAssignmentOperatorLifetimeBound(CXXMethodDecl *CMD) { } static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef, - const AssignedEntity &Entity) { + const CapturingEntity &Entity) { bool EnableGSLAssignmentWarnings = !SemaRef.getDiagnostics().isIgnored( diag::warn_dangling_lifetime_pointer_assignment, SourceLocation()); return (EnableGSLAssignmentWarnings && - (isRecordWithAttr(Entity.LHS->getType()) || + (isRecordWithAttr(Entity.Expression->getType()) || isAssignmentOperatorLifetimeBound(Entity.AssignmentOperator))); } static void checkExprLifetimeImpl(Sema &SemaRef, bricknerb wrote: IIUC, this function handles different use cases and for different use cases it needs different args to be set. Since it's becoming more complex with this change, would it make sense to split it to different functions and only reuse the shared logic between the different use cases? I think this could simplify the asserts and make the calling this method much clearer and safer. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -1091,21 +1104,22 @@ static bool isAssignmentOperatorLifetimeBound(CXXMethodDecl *CMD) { } static bool shouldRunGSLAssignmentAnalysis(const Sema &SemaRef, - const AssignedEntity &Entity) { + const CapturingEntity &Entity) { bool EnableGSLAssignmentWarnings = !SemaRef.getDiagnostics().isIgnored( diag::warn_dangling_lifetime_pointer_assignment, SourceLocation()); return (EnableGSLAssignmentWarnings && - (isRecordWithAttr(Entity.LHS->getType()) || + (isRecordWithAttr(Entity.Expression->getType()) || isAssignmentOperatorLifetimeBound(Entity.AssignmentOperator))); } static void checkExprLifetimeImpl(Sema &SemaRef, const InitializedEntity *InitEntity, const InitializedEntity *ExtendingEntity, LifetimeKind LK, - const AssignedEntity *AEntity, Expr *Init) { - assert((AEntity && LK == LK_Assignment) || - (InitEntity && LK != LK_Assignment)); + const CapturingEntity *CEntity, Expr *Init) { bricknerb wrote: Can we name CEntity with something more readable? Perhaps even CaptEntity? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
@@ -45,10 +48,14 @@ enum LifetimeKind { /// a default member initializer), the program is ill-formed. LK_MemInitializer, - /// The lifetime of a temporary bound to this entity probably ends too soon, + /// The lifetime of a temporary bound to this entity may end too soon, bricknerb wrote: Move this change outside this pull request? https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/bricknerb edited https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
usx95 wrote: cc: @bricknerb @ilya-biryukov in case you are interested in taking an initial look. https://github.com/llvm/llvm-project/pull/111499 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang] Introduce [[clang::lifetime_capture_by(X)]] (PR #111499)
https://github.com/usx95 updated https://github.com/llvm/llvm-project/pull/111499 >From 4951a7b9b87f9800bc3629bd44f65141ba98c6b0 Mon Sep 17 00:00:00 2001 From: Utkarsh Saxena Date: Tue, 8 Oct 2024 08:19:56 + Subject: [PATCH 01/15] start working on lifetime capture --- clang/include/clang/Basic/Attr.td | 40 +++ .../clang/Basic/DiagnosticSemaKinds.td| 16 +++ clang/include/clang/Sema/Sema.h | 7 ++ clang/lib/AST/TypePrinter.cpp | 21 clang/lib/Sema/CheckExprLifetime.cpp | 54 +++--- clang/lib/Sema/CheckExprLifetime.h| 26 +++-- clang/lib/Sema/SemaChecking.cpp | 27 + clang/lib/Sema/SemaDecl.cpp | 1 + clang/lib/Sema/SemaDeclAttr.cpp | 102 ++ clang/lib/Sema/SemaExpr.cpp | 4 +- clang/lib/Sema/SemaInit.cpp | 2 +- clang/lib/Sema/SemaOverload.cpp | 4 +- clang/lib/Sema/SemaType.cpp | 13 +++ clang/test/SemaCXX/attr-lifetimebound.cpp | 94 14 files changed, 387 insertions(+), 24 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 35b9716e13ff21..4dcb143b91f84f 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1869,6 +1869,46 @@ def LifetimeBound : DeclOrTypeAttr { let SimpleHandler = 1; } +def LifetimeCaptureBy : DeclOrTypeAttr { + let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; + let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Documentation = [LifetimeBoundDocs]; + let LangOpts = [CPlusPlus]; + + // let SimpleHandler = 1; + // let LateParsed = LateAttrParseStandard; + // let HasCustomParsing = 1; + // let ParseArgumentsAsUnevaluated = 1; + + let AdditionalMembers = [{ +private: + SmallVector ArgIdents; + SmallVector ArgLocs; + +public: + static const int INVALID = -2; + static const int UNKNOWN = -1; + static const int GLOBAL = -1; + static const int THIS = 0; + + void setArgs(SmallVector Idents, + SmallVector Locs) { +assert(Idents.size() == Locs.size()); +assert(Idents.size() == params_Size); +ArgIdents = std::move(Idents); +ArgLocs = std::move(Locs); + } + + const SmallVector& getArgIdents() const { return ArgIdents; } + const SmallVector& getArgLocs() const { return ArgLocs; } + void setParamIdx(size_t Idx, int Val) { +assert(Idx < params_Size); +params_[Idx] = Val; + } +}]; +} + def TrivialABI : InheritableAttr { // This attribute does not have a C [[]] spelling because it requires the // CPlusPlus language option. diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index e8b64f3c5a0187..ea034af77c3dbe 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3382,6 +3382,18 @@ def err_callback_callee_is_variadic : Error< "'callback' attribute callee may not be variadic">; def err_callback_implicit_this_not_available : Error< "'callback' argument at position %0 references unavailable implicit 'this'">; + +def err_capture_by_attribute_multiple : Error< + "multiple 'lifetime_capture' attributes specified">; +def err_capture_by_attribute_no_entity : Error< + "'lifetime_capture_by' attribute specifies no capturing entity">; +def err_capture_by_implicit_this_not_available : Error< + "'lifetime_capture_by' argument references unavailable implicit 'this'">; +def err_capture_by_attribute_argument_unknown : Error< + "'lifetime_capture_by' attribute argument %0 is not a known function parameter" + ". Must be a function parameter of one of 'this', 'global' or 'unknown'">; +def err_capture_by_references_itself : Error<"'lifetime_capture_by' argument references itself">; + def err_init_method_bad_return_type : Error< "init methods must return an object pointer type, not %0">; def err_attribute_invalid_size : Error< @@ -10185,6 +10197,10 @@ def warn_dangling_pointer_assignment : Warning< "object backing the pointer %0 " "will be destroyed at the end of the full-expression">, InGroup; +def warn_dangling_reference_captured : Warning< + "object captured by the '%0' " + "will be destroyed at the end of the full-expression">, + InGroup; // For non-floating point, expressions of the form x == x or x != x // should result in a warning, since these always evaluate to a constant. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0809ac1b144ef6..a26b3fa8755161 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1830,6 +1830,10 @@ class Sema final : public SemaBase { /// Add [[gsl::Pointer]] attributes for std:: types. void inferGslPointerAttribute(TypedefNameDecl *TD);