[clang] [clang][Sema] Deprecate `global`/`unknown` in `lifetime_capture_by` (PR #196635)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 11 08:03:39 PDT 2026
https://github.com/NeKon69 updated https://github.com/llvm/llvm-project/pull/196635
>From 2814f5ac8f2d74b1fd863df47b1267490c2dc621 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Fri, 8 May 2026 21:15:49 +0300
Subject: [PATCH 1/7] 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 <nobodqwe at gmail.com>
Date: Wed, 13 May 2026 11:53:27 +0300
Subject: [PATCH 2/7] 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 <nobodqwe at gmail.com>
Date: Wed, 13 May 2026 11:57:00 +0300
Subject: [PATCH 3/7] 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)
>From 78f59904114e70e0f3011d19d410f9a0d7eb740f Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Wed, 13 May 2026 16:53:29 +0300
Subject: [PATCH 4/7] add accessors
---
clang/include/clang/Basic/Attr.td | 11 ++--
.../clang/Basic/DiagnosticSemaKinds.td | 2 -
clang/lib/Sema/SemaAttr.cpp | 4 +-
clang/lib/Sema/SemaDeclAttr.cpp | 50 +++++++++++--------
.../test/SemaCXX/attr-lifetime-capture-by.cpp | 1 +
5 files changed, 40 insertions(+), 28 deletions(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index f0cde6a359601..8baeb6709d6cd 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2139,9 +2139,14 @@ def LifetimeCaptureBy : DeclOrTypeAttr {
Clang<"lifetime_capture_by_global", 0>,
Clang<"lifetime_capture_by_unknown", 0>];
let Subjects = SubjectList<[ParmVar, ImplicitObjectParameter], ErrorDiag>;
- let Args = [VariadicParamOrParamIdxArgument<"Params">,
- DefaultBoolArgument<"IsStandaloneSpecial", /*default=*/0,
- /*fake=*/1>];
+ let Args = [VariadicParamOrParamIdxArgument<"Params">];
+ let Accessors = [Accessor<"isThis", [Clang<"lifetime_capture_by_this", 0>]>,
+ Accessor<"isGlobal", [Clang<"lifetime_capture_by_global", 0>]>,
+ Accessor<"isUnknown", [Clang<"lifetime_capture_by_unknown", 0>]>,
+ Accessor<"isStandaloneSpecial",
+ [Clang<"lifetime_capture_by_this", 0>,
+ Clang<"lifetime_capture_by_global", 0>,
+ Clang<"lifetime_capture_by_unknown", 0>]>];
let Documentation = [LifetimeCaptureByDocs];
let AdditionalMembers = [{
private:
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 754019a8e7d41..cb26337a37cc1 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3552,8 +3552,6 @@ def err_callback_callee_is_variadic : Error<
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<
diff --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 8f5617cda1e5e..ffd546138008a 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, /*IsStandaloneSpecial=*/false));
+ PVD->addAttr(
+ LifetimeCaptureByAttr::CreateImplicit(Context, CaptureByThis, 1));
}
}
};
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index e95004177e762..fe75f2e8735b0 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -4402,40 +4402,48 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
S.Context, AL, EncodingIndices.data(), EncodingIndices.size()));
}
+static StringRef getLifetimeCaptureBySpecialEntity(const ParsedAttr &AL) {
+ switch (AL.getSemanticSpelling()) {
+ case LifetimeCaptureByAttr::GNU_lifetime_capture_by_this:
+ case LifetimeCaptureByAttr::CXX11_clang_lifetime_capture_by_this:
+ return "this";
+ case LifetimeCaptureByAttr::GNU_lifetime_capture_by_global:
+ case LifetimeCaptureByAttr::CXX11_clang_lifetime_capture_by_global:
+ return "global";
+ case LifetimeCaptureByAttr::GNU_lifetime_capture_by_unknown:
+ case LifetimeCaptureByAttr::CXX11_clang_lifetime_capture_by_unknown:
+ return "unknown";
+ default:
+ return "";
+ }
+}
+
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) {
+ StringRef SpecialEntity = getLifetimeCaptureBySpecialEntity(AL);
+
+ if (!SpecialEntity.empty() && AL.getNumArgs() != 0) {
Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 0;
return nullptr;
}
// Atleast one capture by is required.
- if (!IsStandaloneSpecial && AL.getNumArgs() == 0) {
+ if (SpecialEntity.empty() && AL.getNumArgs() == 0) {
Diag(AL.getLoc(), diag::err_capture_by_attribute_no_entity)
<< AL.getRange();
return nullptr;
}
- unsigned N = IsStandaloneSpecial ? 1 : AL.getNumArgs();
+ unsigned N = SpecialEntity.empty() ? AL.getNumArgs() : 1;
auto ParamIdents =
MutableArrayRef<IdentifierInfo *>(new (Context) IdentifierInfo *[N], N);
auto ParamLocs =
MutableArrayRef<SourceLocation>(new (Context) SourceLocation[N], N);
- if (IsStandaloneSpecial) {
+ if (!SpecialEntity.empty()) {
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);
+ int FakeParamIndices[] = {LifetimeCaptureByAttr::Invalid};
+ auto *CapturedBy =
+ LifetimeCaptureByAttr::Create(Context, FakeParamIndices, 1, AL);
CapturedBy->setArgs(ParamIdents, ParamLocs);
return CapturedBy;
}
@@ -4474,8 +4482,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, AL);
CapturedBy->setArgs(ParamIdents, ParamLocs);
return CapturedBy;
}
@@ -4534,7 +4542,7 @@ void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) {
auto Loc = CapturedBy->getArgLocs()[I];
if (!HasImplicitThisParam && Name == "this") {
unsigned DiagID =
- CapturedBy->getIsStandaloneSpecial()
+ CapturedBy->isStandaloneSpecial()
? diag::err_capture_by_this_attr_without_implicit_this
: diag::err_capture_by_implicit_this_not_available;
Diag(Loc, DiagID) << Loc;
@@ -4544,7 +4552,7 @@ void Sema::LazyProcessLifetimeCaptureByParams(FunctionDecl *FD) {
continue;
}
if ((Name == "unknown" || Name == "global") &&
- !CapturedBy->getIsStandaloneSpecial())
+ !CapturedBy->isStandaloneSpecial())
DisallowReservedParams(Name);
CapturedBy->setParamIdx(I, It->second);
}
diff --git a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp
index d31fa4503b644..4e699c07740be 100644
--- a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp
+++ b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp
@@ -22,6 +22,7 @@ void nonMember(
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(s)]], // expected-error {{'clang::lifetime_capture_by_global' attribute takes no arguments}}
const int &test_memory_leak[[clang::lifetime_capture_by(x1,x2, x3, x4, x5, x6, x7, x8, x9)]],
const S& u
)
>From 50f37418ad6fb9fdc21044b0874f3900346bd930 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Wed, 13 May 2026 18:09:34 +0300
Subject: [PATCH 5/7] switch to if else
---
clang/lib/Sema/SemaDeclAttr.cpp | 25 ++++++++-----------------
1 file changed, 8 insertions(+), 17 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index fe75f2e8735b0..dbbdfee2dd456 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -4402,25 +4402,16 @@ static void handleCallbackAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
S.Context, AL, EncodingIndices.data(), EncodingIndices.size()));
}
-static StringRef getLifetimeCaptureBySpecialEntity(const ParsedAttr &AL) {
- switch (AL.getSemanticSpelling()) {
- case LifetimeCaptureByAttr::GNU_lifetime_capture_by_this:
- case LifetimeCaptureByAttr::CXX11_clang_lifetime_capture_by_this:
- return "this";
- case LifetimeCaptureByAttr::GNU_lifetime_capture_by_global:
- case LifetimeCaptureByAttr::CXX11_clang_lifetime_capture_by_global:
- return "global";
- case LifetimeCaptureByAttr::GNU_lifetime_capture_by_unknown:
- case LifetimeCaptureByAttr::CXX11_clang_lifetime_capture_by_unknown:
- return "unknown";
- default:
- return "";
- }
-}
-
LifetimeCaptureByAttr *Sema::ParseLifetimeCaptureByAttr(const ParsedAttr &AL,
StringRef ParamName) {
- StringRef SpecialEntity = getLifetimeCaptureBySpecialEntity(AL);
+ 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";
if (!SpecialEntity.empty() && AL.getNumArgs() != 0) {
Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 0;
>From b565548827f42af3f6aadb43ca332150fabc46e4 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Wed, 13 May 2026 19:49:11 +0300
Subject: [PATCH 6/7] remove unrelated change
---
clang/lib/Sema/SemaChecking.cpp | 2 --
1 file changed, 2 deletions(-)
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index b7ddfc34dac55..1ed2fe121875e 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -4230,8 +4230,6 @@ 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 &&
>From 50a9011b60efeb776925f32f561c4072aa1380e3 Mon Sep 17 00:00:00 2001
From: NeKon69 <nobodqwe at gmail.com>
Date: Thu, 11 Jun 2026 18:03:19 +0300
Subject: [PATCH 7/7] switch to isStr
---
clang/lib/Sema/SemaDeclAttr.cpp | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index dbbdfee2dd456..bd92a2cf9619d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -4450,24 +4450,24 @@ LifetimeCaptureByAttr *Sema::ParseLifetimeCaptureByAttr(const ParsedAttr &AL,
}
assert(AL.isArgIdent(I));
IdentifierLoc *IdLoc = AL.getArgAsIdent(I);
- StringRef Name = IdLoc->getIdentifierInfo()->getName();
+ IdentifierInfo *Ident = IdLoc->getIdentifierInfo();
StringRef Replacement;
- if (Name == "this")
+ if (Ident->isStr("this"))
Replacement = "lifetime_capture_by_this";
- else if (Name == "global")
+ else if (Ident->isStr("global"))
Replacement = "lifetime_capture_by_global";
- else if (Name == "unknown")
+ else if (Ident->isStr("unknown"))
Replacement = "lifetime_capture_by_unknown";
if (!Replacement.empty())
Diag(IdLoc->getLoc(), diag::warn_deprecated_capture_by_special_entity)
- << Name << Replacement << IdLoc->getLoc();
- if (IdLoc->getIdentifierInfo()->getName() == ParamName) {
+ << Ident << Replacement << IdLoc->getLoc();
+ if (Ident->getName() == ParamName) {
Diag(IdLoc->getLoc(), diag::err_capture_by_references_itself)
<< IdLoc->getLoc();
IsValid = false;
continue;
}
- ParamIdents[I] = IdLoc->getIdentifierInfo();
+ ParamIdents[I] = Ident;
ParamLocs[I] = IdLoc->getLoc();
}
if (!IsValid)
More information about the cfe-commits
mailing list