https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/196635
>From 2814f5ac8f2d74b1fd863df47b1267490c2dc621 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Fri, 8 May 2026 21:15:49 +0300 Subject: [PATCH 1/3] add support for __global__/__unknown__ --- clang/include/clang/Basic/AttrDocs.td | 9 ++++--- .../clang/Basic/DiagnosticSemaKinds.td | 7 +++-- clang/lib/Sema/SemaDeclAttr.cpp | 18 ++++++++++--- .../warn-lifetime-analysis-capture-by.cpp | 4 +-- .../test/SemaCXX/attr-lifetime-capture-by.cpp | 26 ++++++++++++------- 5 files changed, 44 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 4b45f413054f3..2884a7a9359f4 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4643,15 +4643,18 @@ The capturing entity ``X`` can be one of the following: Note: When applied to a constructor parameter, `[[clang::lifetime_capture_by(this)]]` is just an alias of `[[clang::lifetimebound]]`. -- `global`, `unknown`. +- ``__global__``, ``__unknown__``. .. code-block:: c++ std::set<std::string_view> s; - void addToSet(std::string_view a [[clang::lifetime_capture_by(global)]]) { + 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)]]); + void addSomewhere(std::string_view a [[clang::lifetime_capture_by(__unknown__)]]); + + The older spellings ``global`` and ``unknown`` are deprecated, but remain + accepted for compatibility. The attribute can be applied to the implicit ``this`` parameter of a member function by writing the attribute after the function type: diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c15a9ec1ff0f6..7964006f5ce0b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3560,10 +3560,13 @@ 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, 'this', 'global' or 'unknown'">; + "; must be a function parameter, 'this', '__global__' or '__unknown__'">; def err_capture_by_references_itself : Error<"'lifetime_capture_by' argument references itself">; def err_capture_by_param_uses_reserved_name : Error< - "parameter cannot be named '%select{global|unknown}0' while using 'lifetime_capture_by(%select{global|unknown}0)'">; + "parameter cannot be named '%0' while using 'lifetime_capture_by(%0)'">; +def warn_deprecated_capture_by_special_entity : Warning< + "'lifetime_capture_by(%0)' is deprecated; use 'lifetime_capture_by(%1)' instead">, + InGroup<DeprecatedAttributes>; def err_init_method_bad_return_type : Error< "init methods must return an object pointer type, not %0">; diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 55b6cbcbba57d..9785c381c7ff6 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4426,6 +4426,15 @@ LifetimeCaptureByAttr *Sema::ParseLifetimeCaptureByAttr(const ParsedAttr &AL, } assert(AL.isArgIdent(I)); IdentifierLoc *IdLoc = AL.getArgAsIdent(I); + StringRef Name = IdLoc->getIdentifierInfo()->getName(); + StringRef Replacement; + if (Name == "global") + Replacement = "__global__"; + else if (Name == "unknown") + Replacement = "__unknown__"; + if (!Replacement.empty()) + Diag(IdLoc->getLoc(), diag::warn_deprecated_capture_by_special_entity) + << Name << Replacement; if (IdLoc->getIdentifierInfo()->getName() == ParamName) { Diag(IdLoc->getLoc(), diag::err_capture_by_references_itself) << IdLoc->getLoc(); @@ -4481,7 +4490,9 @@ void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) { return; llvm::StringMap<int> NameIdxMapping = { {"global", LifetimeCaptureByAttr::Global}, - {"unknown", LifetimeCaptureByAttr::Unknown}}; + {"unknown", LifetimeCaptureByAttr::Unknown}, + {"__global__", LifetimeCaptureByAttr::Global}, + {"__unknown__", LifetimeCaptureByAttr::Unknown}}; int Idx = 0; if (HasImplicitThisParam) { NameIdxMapping["this"] = 0; @@ -4493,7 +4504,7 @@ void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) { for (const ParmVarDecl *PVD : FD->parameters()) if (PVD->getName() == Reserved) Diag(PVD->getLocation(), diag::err_capture_by_param_uses_reserved_name) - << (PVD->getName() == "unknown"); + << PVD->getName(); }; for (auto *CapturedBy : Attrs) { const auto &Entities = CapturedBy->getArgIdents(); @@ -4509,7 +4520,8 @@ void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) { << Entities[I] << Loc; continue; } - if (Name == "unknown" || Name == "global") + if (Name == "unknown" || Name == "global" || Name == "__unknown__" || + Name == "__global__") DisallowReservedParams(Name); CapturedBy->setParamIdx(I, It->second); } diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp index 2877d8d6cd5f9..bfd87490cf7b7 100644 --- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp @@ -161,8 +161,8 @@ void test() { // Capture by Global and Unknown. // **************************************************************************** namespace capture_by_global_unknown { -void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(global)]]); -void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(unknown)]]); +void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(__global__)]]); +void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(__unknown__)]]); std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); diff --git a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp index 8606592c6b771..68299192bf8aa 100644 --- a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp +++ b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp @@ -13,16 +13,18 @@ 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, '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, '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, 'this', 'global' or 'unknown'}} + 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, '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, '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, 'this', '__global__' or '__unknown__'}} const int &x5 [[clang::lifetime_capture_by(this)]], // expected-error {{'lifetime_capture_by' argument references unavailable implicit 'this'}} const int &x6 [[clang::lifetime_capture_by()]], // expected-error {{'lifetime_capture_by' attribute specifies no capturing entity}} const int& x7 [[clang::lifetime_capture_by(u, - x7)]], // expected-error {{'lifetime_capture_by' argument references itself}} - const int &x8 [[clang::lifetime_capture_by(global)]], - const int &x9 [[clang::lifetime_capture_by(unknown)]], - const int &test_memory_leak[[clang::lifetime_capture_by(x1,x2, x3, x4, x5, x6, x7, x8, x9)]], + x7)]], // expected-error {{'lifetime_capture_by' argument references itself}} + const int &x8 [[clang::lifetime_capture_by(global)]], // expected-warning {{'lifetime_capture_by(global)' is deprecated; use 'lifetime_capture_by(__global__)' instead}} + const int &x9 [[clang::lifetime_capture_by(unknown)]], // expected-warning {{'lifetime_capture_by(unknown)' is deprecated; use 'lifetime_capture_by(__unknown__)' instead}} + const int &x10 [[clang::lifetime_capture_by(__global__)]], + const int &x11 [[clang::lifetime_capture_by(__unknown__)]], + const int &test_memory_leak[[clang::lifetime_capture_by(x1,x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)]], const S& u ) { @@ -30,9 +32,13 @@ void nonMember( } void unknown_param_name(const int& unknown, // expected-error {{parameter cannot be named 'unknown' while using 'lifetime_capture_by(unknown)'}} - const int& s [[clang::lifetime_capture_by(unknown)]]); + const int& s [[clang::lifetime_capture_by(unknown)]]); // expected-warning {{'lifetime_capture_by(unknown)' is deprecated; use 'lifetime_capture_by(__unknown__)' instead}} void global_param_name(const int& global, // expected-error {{parameter cannot be named 'global' while using 'lifetime_capture_by(global)'}} - const int& s [[clang::lifetime_capture_by(global)]]); + const int& s [[clang::lifetime_capture_by(global)]]); // expected-warning {{'lifetime_capture_by(global)' is deprecated; use 'lifetime_capture_by(__global__)' instead}} +void unknown_param_name_new(const int& unknown, + const int& s [[clang::lifetime_capture_by(__unknown__)]]); +void global_param_name_new(const int& global, + const int& s [[clang::lifetime_capture_by(__global__)]]); struct T { void member( const int &x [[clang::lifetime_capture_by(s)]], @@ -40,7 +46,7 @@ struct T { S &t, const int &y [[clang::lifetime_capture_by(s)]], const int &z [[clang::lifetime_capture_by(this, x, y)]], - const int &u [[clang::lifetime_capture_by(global, unknown, x, s)]]) + const int &u [[clang::lifetime_capture_by(global, unknown, __global__, __unknown__, x, s)]]) // expected-warning 2 {{deprecated}} { s.captureInt(x); } >From aeded69e5ecf76c4527a202d016ad9eadffb9cc8 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Wed, 13 May 2026 11:53:27 +0300 Subject: [PATCH 2/3] implement a draft with different spellings for the same attribute --- clang/include/clang/Basic/Attr.td | 9 ++- clang/include/clang/Basic/AttrDocs.td | 9 +-- .../clang/Basic/DiagnosticSemaKinds.td | 6 +- clang/lib/Sema/CheckExprLifetime.cpp | 14 ++-- clang/lib/Sema/SemaAttr.cpp | 4 +- clang/lib/Sema/SemaChecking.cpp | 7 +- clang/lib/Sema/SemaDeclAttr.cpp | 68 +++++++++++++------ .../warn-lifetime-analysis-capture-by.cpp | 22 +++--- .../test/SemaCXX/attr-lifetime-capture-by.cpp | 32 ++++----- 9 files changed, 99 insertions(+), 72 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 70b5773f95b08..f0cde6a359601 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2134,9 +2134,14 @@ def LifetimeBound : DeclOrTypeAttr { } def LifetimeCaptureBy : DeclOrTypeAttr { - let Spellings = [Clang<"lifetime_capture_by", 0>]; + let Spellings = [Clang<"lifetime_capture_by", 0>, + Clang<"lifetime_capture_by_this", 0>, + Clang<"lifetime_capture_by_global", 0>, + Clang<"lifetime_capture_by_unknown", 0>]; let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>; - let Args = [VariadicParamOrParamIdxArgument<"Params">]; + let Args = [VariadicParamOrParamIdxArgument<"Params">, + DefaultBoolArgument<"IsStandaloneSpecial", /*default=*/0, + /*fake=*/1>]; let Documentation = [LifetimeCaptureByDocs]; let AdditionalMembers = [{ private: diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 2884a7a9359f4..4b45f413054f3 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4643,18 +4643,15 @@ The capturing entity ``X`` can be one of the following: Note: When applied to a constructor parameter, `[[clang::lifetime_capture_by(this)]]` is just an alias of `[[clang::lifetimebound]]`. -- ``__global__``, ``__unknown__``. +- `global`, `unknown`. .. code-block:: c++ std::set<std::string_view> s; - void addToSet(std::string_view a [[clang::lifetime_capture_by(__global__)]]) { + 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 older spellings ``global`` and ``unknown`` are deprecated, but remain - accepted for compatibility. + 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: diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7964006f5ce0b..754019a8e7d41 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3558,14 +3558,16 @@ 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_this_attr_without_implicit_this : Error< + "'lifetime_capture_by_this' attribute requires an implicit object parameter">; 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, 'this', '__global__' or '__unknown__'">; + "; must be a function parameter, 'this', 'global' or 'unknown'">; def err_capture_by_references_itself : Error<"'lifetime_capture_by' argument references itself">; def err_capture_by_param_uses_reserved_name : Error< "parameter cannot be named '%0' while using 'lifetime_capture_by(%0)'">; def warn_deprecated_capture_by_special_entity : Warning< - "'lifetime_capture_by(%0)' is deprecated; use 'lifetime_capture_by(%1)' instead">, + "'lifetime_capture_by(%0)' is deprecated; use '%1' instead">, InGroup<DeprecatedAttributes>; def err_init_method_bad_return_type : Error< diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp index 4e050e9bf6045..7a256e33b88f5 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -502,12 +502,14 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call, if (CheckCoroCall || CanonCallee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>()) VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg); - else if (const auto *CaptureAttr = - CanonCallee->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>(); - CaptureAttr && isa<CXXConstructorDecl>(CanonCallee) && - llvm::any_of(CaptureAttr->params(), [](int ArgIdx) { - return ArgIdx == LifetimeCaptureByAttr::This; - })) + else if (isa<CXXConstructorDecl>(CanonCallee) && + llvm::any_of(CanonCallee->getParamDecl(I) + ->specific_attrs<LifetimeCaptureByAttr>(), + [](const LifetimeCaptureByAttr *CaptureAttr) { + return llvm::is_contained( + CaptureAttr->params(), + LifetimeCaptureByAttr::This); + })) // `lifetime_capture_by(this)` in a class constructor has the same // semantics as `lifetimebound`: // diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp index ffd546138008a..8f5617cda1e5e 100644 --- a/clang/lib/Sema/SemaAttr.cpp +++ b/clang/lib/Sema/SemaAttr.cpp @@ -332,8 +332,8 @@ void Sema::inferLifetimeCaptureByAttribute(FunctionDecl *FD) { if (PVD->getType()->isReferenceType() && lifetimes::isGslPointerType(PVD->getType().getNonReferenceType())) { int CaptureByThis[] = {LifetimeCaptureByAttr::This}; - PVD->addAttr( - LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1)); + PVD->addAttr(LifetimeCaptureByAttr::CreateImplicit( + Context, CaptureByThis, 1, /*IsStandaloneSpecial=*/false)); } } }; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 4706fa5d3cde0..b7ddfc34dac55 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4230,6 +4230,8 @@ void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction, Expr *Captured = const_cast<Expr *>(GetArgAt(ArgIdx)); for (int CapturingParamIdx : Attr->params()) { + if (CapturingParamIdx == LifetimeCaptureByAttr::Invalid) + continue; // lifetime_capture_by(this) case is handled in the lifetimebound expr // initialization codepath. if (CapturingParamIdx == LifetimeCaptureByAttr::This && @@ -4242,8 +4244,9 @@ void Sema::checkLifetimeCaptureBy(FunctionDecl *FD, bool IsMemberFunction, } }; for (unsigned I = 0; I < FD->getNumParams(); ++I) - HandleCaptureByAttr(FD->getParamDecl(I)->getAttr<LifetimeCaptureByAttr>(), - I + IsMemberFunction); + for (const auto *A : + FD->getParamDecl(I)->specific_attrs<LifetimeCaptureByAttr>()) + HandleCaptureByAttr(A, I + IsMemberFunction); // Check when the implicit object param is captured. if (IsMemberFunction) { TypeSourceInfo *TSI = FD->getTypeSourceInfo(); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 9785c381c7ff6..44148f39fdbec 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4404,17 +4404,43 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) { LifetimeCaptureByAttr *Sema::ParseLifetimeCaptureByAttr(const ParsedAttr &AL, StringRef ParamName) { + StringRef AttrName = AL.getAttrName()->getName(); + StringRef SpecialEntity; + if (AttrName == "lifetime_capture_by_this") + SpecialEntity = "this"; + else if (AttrName == "lifetime_capture_by_global") + SpecialEntity = "global"; + else if (AttrName == "lifetime_capture_by_unknown") + SpecialEntity = "unknown"; + bool IsStandaloneSpecial = !SpecialEntity.empty(); + + if (IsStandaloneSpecial && AL.getNumArgs() != 0) { + Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 0; + return nullptr; + } + // Atleast one capture by is required. - if (AL.getNumArgs() == 0) { + if (!IsStandaloneSpecial && AL.getNumArgs() == 0) { Diag(AL.getLoc(), diag::err_capture_by_attribute_no_entity) << AL.getRange(); return nullptr; } - unsigned N = AL.getNumArgs(); + unsigned N = IsStandaloneSpecial ? 1 : AL.getNumArgs(); auto ParamIdents = MutableArrayRef<IdentifierInfo *>(new (Context) IdentifierInfo *[N], N); auto ParamLocs = MutableArrayRef<SourceLocation>(new (Context) SourceLocation[N], N); + if (IsStandaloneSpecial) { + ParamIdents[0] = &Context.Idents.get(SpecialEntity); + ParamLocs[0] = AL.getRange().getEnd(); + SmallVector<int> FakeParamIndices(N, LifetimeCaptureByAttr::Invalid); + auto *CapturedBy = + LifetimeCaptureByAttr::Create(Context, FakeParamIndices.data(), N, + IsStandaloneSpecial, AL); + CapturedBy->setArgs(ParamIdents, ParamLocs); + return CapturedBy; + } + bool IsValid = true; for (unsigned I = 0; I < N; ++I) { if (AL.isArgExpr(I)) { @@ -4428,13 +4454,15 @@ LifetimeCaptureByAttr *Sema::ParseLifetimeCaptureByAttr(const ParsedAttr &AL, IdentifierLoc *IdLoc = AL.getArgAsIdent(I); StringRef Name = IdLoc->getIdentifierInfo()->getName(); StringRef Replacement; - if (Name == "global") - Replacement = "__global__"; + if (Name == "this") + Replacement = "lifetime_capture_by_this"; + else if (Name == "global") + Replacement = "lifetime_capture_by_global"; else if (Name == "unknown") - Replacement = "__unknown__"; + Replacement = "lifetime_capture_by_unknown"; if (!Replacement.empty()) Diag(IdLoc->getLoc(), diag::warn_deprecated_capture_by_special_entity) - << Name << Replacement; + << Name << Replacement << IdLoc->getLoc(); if (IdLoc->getIdentifierInfo()->getName() == ParamName) { Diag(IdLoc->getLoc(), diag::err_capture_by_references_itself) << IdLoc->getLoc(); @@ -4448,19 +4476,14 @@ LifetimeCaptureByAttr *Sema::ParseLifetimeCaptureByAttr(const ParsedAttr &AL, return nullptr; SmallVector<int> FakeParamIndices(N, LifetimeCaptureByAttr::Invalid); auto *CapturedBy = - LifetimeCaptureByAttr::Create(Context, FakeParamIndices.data(), N, AL); + LifetimeCaptureByAttr::Create(Context, FakeParamIndices.data(), N, + IsStandaloneSpecial, AL); CapturedBy->setArgs(ParamIdents, ParamLocs); return CapturedBy; } static void handleLifetimeCaptureByAttr(Sema &S, Decl *D, const ParsedAttr &AL) { - // Do not allow multiple attributes. - if (D->hasAttr<LifetimeCaptureByAttr>()) { - S.Diag(AL.getLoc(), diag::err_capture_by_attribute_multiple) - << AL.getRange(); - return; - } auto *PVD = dyn_cast<ParmVarDecl>(D); assert(PVD); auto *CaptureByAttr = S.ParseLifetimeCaptureByAttr(AL, PVD->getName()); @@ -4472,7 +4495,7 @@ void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) { bool HasImplicitThisParam = hasImplicitObjectParameter(FD); SmallVector<LifetimeCaptureByAttr *, 1> Attrs; for (ParmVarDecl *PVD : FD->parameters()) - if (auto *A = PVD->getAttr<LifetimeCaptureByAttr>()) + for (auto *A : PVD->specific_attrs<LifetimeCaptureByAttr>()) Attrs.push_back(A); if (HasImplicitThisParam) { TypeSourceInfo *TSI = FD->getTypeSourceInfo(); @@ -4490,9 +4513,7 @@ void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) { return; llvm::StringMap<int> NameIdxMapping = { {"global", LifetimeCaptureByAttr::Global}, - {"unknown", LifetimeCaptureByAttr::Unknown}, - {"__global__", LifetimeCaptureByAttr::Global}, - {"__unknown__", LifetimeCaptureByAttr::Unknown}}; + {"unknown", LifetimeCaptureByAttr::Unknown}}; int Idx = 0; if (HasImplicitThisParam) { NameIdxMapping["this"] = 0; @@ -4513,15 +4534,18 @@ void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) { 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 + if (!HasImplicitThisParam && Name == "this") { + unsigned DiagID = CapturedBy->getIsStandaloneSpecial() + ? diag::err_capture_by_this_attr_without_implicit_this + : diag::err_capture_by_implicit_this_not_available; + Diag(Loc, DiagID) << Loc; + } else Diag(Loc, diag::err_capture_by_attribute_argument_unknown) << Entities[I] << Loc; continue; } - if (Name == "unknown" || Name == "global" || Name == "__unknown__" || - Name == "__global__") + if ((Name == "unknown" || Name == "global") && + !CapturedBy->getIsStandaloneSpecial()) DisallowReservedParams(Name); CapturedBy->setParamIdx(I, It->second); } diff --git a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp index bfd87490cf7b7..285f50d61fa1d 100644 --- a/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-capture-by.cpp @@ -145,7 +145,7 @@ void use() { namespace temporary_capturing_object { struct S { - void add(const int& x [[clang::lifetime_capture_by(this)]]); + void add(const int& x [[clang::lifetime_capture_by_this]]); }; void test() { @@ -161,8 +161,8 @@ void test() { // Capture by Global and Unknown. // **************************************************************************** namespace capture_by_global_unknown { -void captureByGlobal(std::string_view s [[clang::lifetime_capture_by(__global__)]]); -void captureByUnknown(std::string_view s [[clang::lifetime_capture_by(__unknown__)]]); +void captureByGlobal(std::string_view s [[clang::lifetime_capture_by_global]]); +void captureByUnknown(std::string_view s [[clang::lifetime_capture_by_unknown]]); std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); @@ -188,8 +188,8 @@ void use() { // **************************************************************************** namespace capture_by_this { struct S { - void captureInt(const int& x [[clang::lifetime_capture_by(this)]]); - void captureView(std::string_view sv [[clang::lifetime_capture_by(this)]]); + void captureInt(const int& x [[clang::lifetime_capture_by_this]]); + void captureView(std::string_view sv [[clang::lifetime_capture_by_this]]); }; std::string_view getLifetimeBoundView(const std::string& s [[clang::lifetimebound]]); std::string_view getNotLifetimeBoundView(const std::string& s); @@ -248,8 +248,8 @@ void useCaptureDefaultArg() { namespace containers_no_distinction { template<class T> struct MySet { - void insert(T&& t [[clang::lifetime_capture_by(this)]]); - void insert(const T& t [[clang::lifetime_capture_by(this)]]); + void insert(T&& t [[clang::lifetime_capture_by_this]]); + void insert(const T& t [[clang::lifetime_capture_by_this]]); }; void user_defined_containers() { MySet<int> set_of_int; @@ -269,8 +269,8 @@ template<> struct IsPointerLikeTypeImpl<std::string_view> : std::true_type {}; template<typename T> concept IsPointerLikeType = std::is_pointer<T>::value || IsPointerLikeTypeImpl<T>::value; template<class T> struct MyVector { - void push_back(T&& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>; - void push_back(const T& t [[clang::lifetime_capture_by(this)]]) requires IsPointerLikeType<T>; + void push_back(T&& t [[clang::lifetime_capture_by_this]]) requires IsPointerLikeType<T>; + void push_back(const T& t [[clang::lifetime_capture_by_this]]) requires IsPointerLikeType<T>; void push_back(T&& t) requires (!IsPointerLikeType<T>); void push_back(const T& t) requires (!IsPointerLikeType<T>); @@ -428,13 +428,13 @@ void use() { namespace on_constructor { struct T { - T(const int& t [[clang::lifetime_capture_by(this)]]); + T(const int& t [[clang::lifetime_capture_by_this]]); }; struct T2 { T2(const int& t [[clang::lifetime_capture_by(x)]], int& x); }; struct T3 { - T3(const T& t [[clang::lifetime_capture_by(this)]]); + T3(const T& t [[clang::lifetime_capture_by_this]]); }; int foo(const T& t); diff --git a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp index 68299192bf8aa..d31fa4503b644 100644 --- a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp +++ b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp @@ -2,7 +2,7 @@ struct S { const int *x; - void captureInt(const int&x [[clang::lifetime_capture_by(this)]]) { this->x = &x; } + void captureInt(const int&x [[clang::lifetime_capture_by_this]]) { this->x = &x; } }; /////////////////////////// @@ -13,18 +13,16 @@ 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, '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, '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, 'this', '__global__' or '__unknown__'}} - const int &x5 [[clang::lifetime_capture_by(this)]], // expected-error {{'lifetime_capture_by' argument references unavailable implicit 'this'}} + 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, '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, '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, 'this', 'global' or 'unknown'}} + const int &x5 [[clang::lifetime_capture_by_this]], // expected-error {{'lifetime_capture_by_this' attribute requires an implicit object parameter}} const int &x6 [[clang::lifetime_capture_by()]], // expected-error {{'lifetime_capture_by' attribute specifies no capturing entity}} const int& x7 [[clang::lifetime_capture_by(u, x7)]], // expected-error {{'lifetime_capture_by' argument references itself}} - const int &x8 [[clang::lifetime_capture_by(global)]], // expected-warning {{'lifetime_capture_by(global)' is deprecated; use 'lifetime_capture_by(__global__)' instead}} - const int &x9 [[clang::lifetime_capture_by(unknown)]], // expected-warning {{'lifetime_capture_by(unknown)' is deprecated; use 'lifetime_capture_by(__unknown__)' instead}} - const int &x10 [[clang::lifetime_capture_by(__global__)]], - const int &x11 [[clang::lifetime_capture_by(__unknown__)]], - const int &test_memory_leak[[clang::lifetime_capture_by(x1,x2, x3, x4, x5, x6, x7, x8, x9, x10, x11)]], + const int &x8 [[clang::lifetime_capture_by(global)]], // expected-warning {{'lifetime_capture_by(global)' is deprecated; use 'lifetime_capture_by_global' instead}} + const int &x9 [[clang::lifetime_capture_by(unknown)]], // expected-warning {{'lifetime_capture_by(unknown)' is deprecated; use 'lifetime_capture_by_unknown' instead}} + const int &test_memory_leak[[clang::lifetime_capture_by(x1,x2, x3, x4, x5, x6, x7, x8, x9)]], const S& u ) { @@ -32,25 +30,21 @@ void nonMember( } void unknown_param_name(const int& unknown, // expected-error {{parameter cannot be named 'unknown' while using 'lifetime_capture_by(unknown)'}} - const int& s [[clang::lifetime_capture_by(unknown)]]); // expected-warning {{'lifetime_capture_by(unknown)' is deprecated; use 'lifetime_capture_by(__unknown__)' instead}} + const int& s [[clang::lifetime_capture_by(unknown)]]); // expected-warning {{'lifetime_capture_by(unknown)' is deprecated; use 'lifetime_capture_by_unknown' instead}} void global_param_name(const int& global, // expected-error {{parameter cannot be named 'global' while using 'lifetime_capture_by(global)'}} - const int& s [[clang::lifetime_capture_by(global)]]); // expected-warning {{'lifetime_capture_by(global)' is deprecated; use 'lifetime_capture_by(__global__)' instead}} -void unknown_param_name_new(const int& unknown, - const int& s [[clang::lifetime_capture_by(__unknown__)]]); -void global_param_name_new(const int& global, - const int& s [[clang::lifetime_capture_by(__global__)]]); + const int& s [[clang::lifetime_capture_by(global)]]); // expected-warning {{'lifetime_capture_by(global)' is deprecated; use 'lifetime_capture_by_global' instead}} struct T { void member( const int &x [[clang::lifetime_capture_by(s)]], S &s, S &t, const int &y [[clang::lifetime_capture_by(s)]], - const int &z [[clang::lifetime_capture_by(this, x, y)]], - const int &u [[clang::lifetime_capture_by(global, unknown, __global__, __unknown__, x, s)]]) // expected-warning 2 {{deprecated}} + const int &z [[clang::lifetime_capture_by(x, y), clang::lifetime_capture_by_this]], + const int &u [[clang::lifetime_capture_by(global, unknown, x, s)]]) // expected-warning 2 {{deprecated}} { s.captureInt(x); } void explicit_this1(this T& self, const int &x [[clang::lifetime_capture_by(self)]]); - void explicit_this2(this T& self, const int &x [[clang::lifetime_capture_by(this)]]); // expected-error {{argument references unavailable implicit 'this'}} + void explicit_this2(this T& self, const int &x [[clang::lifetime_capture_by(this)]]); // expected-warning {{'lifetime_capture_by(this)' is deprecated; use 'lifetime_capture_by_this' instead}} expected-error {{argument references unavailable implicit 'this'}} }; >From 26ae9d538924d24e9b2c6c2be3ebba92afe4d991 Mon Sep 17 00:00:00 2001 From: NeKon69 <[email protected]> Date: Wed, 13 May 2026 11:57:00 +0300 Subject: [PATCH 3/3] clang-format --- clang/lib/Sema/SemaDeclAttr.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 44148f39fdbec..e95004177e762 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -4434,9 +4434,8 @@ LifetimeCaptureByAttr *Sema::ParseLifetimeCaptureByAttr(const ParsedAttr &AL, ParamIdents[0] = &Context.Idents.get(SpecialEntity); ParamLocs[0] = AL.getRange().getEnd(); SmallVector<int> FakeParamIndices(N, LifetimeCaptureByAttr::Invalid); - auto *CapturedBy = - LifetimeCaptureByAttr::Create(Context, FakeParamIndices.data(), N, - IsStandaloneSpecial, AL); + auto *CapturedBy = LifetimeCaptureByAttr::Create( + Context, FakeParamIndices.data(), N, IsStandaloneSpecial, AL); CapturedBy->setArgs(ParamIdents, ParamLocs); return CapturedBy; } @@ -4475,9 +4474,8 @@ LifetimeCaptureByAttr *Sema::ParseLifetimeCaptureByAttr(const ParsedAttr &AL, if (!IsValid) return nullptr; SmallVector<int> FakeParamIndices(N, LifetimeCaptureByAttr::Invalid); - auto *CapturedBy = - LifetimeCaptureByAttr::Create(Context, FakeParamIndices.data(), N, - IsStandaloneSpecial, AL); + auto *CapturedBy = LifetimeCaptureByAttr::Create( + Context, FakeParamIndices.data(), N, IsStandaloneSpecial, AL); CapturedBy->setArgs(ParamIdents, ParamLocs); return CapturedBy; } @@ -4535,9 +4533,10 @@ void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) { if (It == NameIdxMapping.end()) { auto Loc = CapturedBy->getArgLocs()[I]; if (!HasImplicitThisParam && Name == "this") { - unsigned DiagID = CapturedBy->getIsStandaloneSpecial() - ? diag::err_capture_by_this_attr_without_implicit_this - : diag::err_capture_by_implicit_this_not_available; + unsigned DiagID = + CapturedBy->getIsStandaloneSpecial() + ? diag::err_capture_by_this_attr_without_implicit_this + : diag::err_capture_by_implicit_this_not_available; Diag(Loc, DiagID) << Loc; } else Diag(Loc, diag::err_capture_by_attribute_argument_unknown) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
