[clang] [clang] Fix GNU spellings of lifetimebound/lifetime_capture_by (PR #192070)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Apr 14 07:38:59 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Zeyi Xu (zeyi2)
<details>
<summary>Changes</summary>
---
Full diff: https://github.com/llvm/llvm-project/pull/192070.diff
5 Files Affected:
- (modified) clang/docs/ReleaseNotes.rst (+2)
- (modified) clang/lib/Sema/SemaType.cpp (+20-2)
- (modified) clang/test/AST/attr-lifetime-capture-by.cpp (+6)
- (modified) clang/test/SemaCXX/attr-lifetime-capture-by.cpp (+12)
- (modified) clang/test/SemaCXX/attr-lifetimebound.cpp (+26)
``````````diff
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 1db1d9fea6b98..3a8fe626b8ba4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -424,6 +424,8 @@ Bug Fixes to Attribute Support
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Fixed a behavioral discrepancy between deleted functions and private members when checking the ``enable_if`` attribute. (#GH175895)
- Fixed ``init_priority`` attribute by delaying type checks until after the type is deduced.
+- Fixed GNU spellings of ``lifetimebound`` and ``lifetime_capture_by`` on member function declarators to match the
+ corresponding ``[[clang::...]]`` spellings on implicit object parameters. (#GH121905)
Bug Fixes to C++ Support
^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 846474fe94adf..6d941a9db0ff7 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -734,6 +734,13 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state,
continue;
switch (attr.getKind()) {
+ case ParsedAttr::AT_LifetimeBound:
+ case ParsedAttr::AT_LifetimeCaptureBy:
+ if (state.getDeclarator().isDeclarationOfFunction())
+ distributeFunctionTypeAttrFromDeclarator(state, attr, declSpecType,
+ CFT);
+ break;
+
OBJC_POINTER_TYPE_ATTRS_CASELIST:
distributeObjCPointerTypeAttrFromDeclarator(state, attr, declSpecType);
break;
@@ -9162,12 +9169,23 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
attr.setUsedAsTypeAttr();
break;
case ParsedAttr::AT_LifetimeBound:
- if (TAL == TAL_DeclChunk)
+ if (TAL == TAL_DeclChunk) {
HandleLifetimeBoundAttr(state, type, attr);
+ // This GNU spelling has already been consumed as a type attribute.
+ // Remove it so the later decl pass does not re-diagnose the enclosing
+ // method as subject.
+ if (!attr.isStandardAttributeSyntax() &&
+ !attr.isRegularKeywordAttribute())
+ state.getCurrentAttributes().remove(&attr);
+ }
break;
case ParsedAttr::AT_LifetimeCaptureBy:
- if (TAL == TAL_DeclChunk)
+ if (TAL == TAL_DeclChunk) {
HandleLifetimeCaptureByAttr(state, type, attr);
+ if (!attr.isStandardAttributeSyntax() &&
+ !attr.isRegularKeywordAttribute())
+ state.getCurrentAttributes().remove(&attr);
+ }
break;
case ParsedAttr::AT_OverflowBehavior:
HandleOverflowBehaviorAttr(type, attr, state);
diff --git a/clang/test/AST/attr-lifetime-capture-by.cpp b/clang/test/AST/attr-lifetime-capture-by.cpp
index 76efd45304447..21a4e46c9a446 100644
--- a/clang/test/AST/attr-lifetime-capture-by.cpp
+++ b/clang/test/AST/attr-lifetime-capture-by.cpp
@@ -8,6 +8,12 @@ struct S {
// CHECK: CXXMethodDecl {{.*}}clang::lifetime_capture_by(a, b, global)
+struct GNUCaptureBy {
+ void capture_this() __attribute__((lifetime_capture_by(global)));
+};
+
+// CHECK: CXXMethodDecl {{.*}}capture_this 'void () {{\[\[}}clang::lifetime_capture_by(global){{\]\]}}'
+
// ****************************************************************************
// Infer annotation for STL container methods.
// ****************************************************************************
diff --git a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp
index 8606592c6b771..bad2d41722ad3 100644
--- a/clang/test/SemaCXX/attr-lifetime-capture-by.cpp
+++ b/clang/test/SemaCXX/attr-lifetime-capture-by.cpp
@@ -48,3 +48,15 @@ struct T {
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'}}
};
+
+struct GNUCaptureBy {
+ const int *x;
+ void captureParam(const int &v __attribute__((lifetime_capture_by(this)))) {
+ x = &v;
+ }
+ void captureThis() __attribute__((lifetime_capture_by(global)));
+};
+
+struct GNUCaptureByInvalid {
+ void member_bad() __attribute__((lifetime_capture_by())); // expected-error {{'lifetime_capture_by' attribute specifies no capturing entity}}
+};
diff --git a/clang/test/SemaCXX/attr-lifetimebound.cpp b/clang/test/SemaCXX/attr-lifetimebound.cpp
index 9e2aaff6559c4..22a11aaafcc11 100644
--- a/clang/test/SemaCXX/attr-lifetimebound.cpp
+++ b/clang/test/SemaCXX/attr-lifetimebound.cpp
@@ -30,6 +30,16 @@ namespace usage_invalid {
int (X::*member_func_ptr)(int) [[clang::lifetimebound]]; // expected-error {{'clang::lifetimebound' attribute only applies to parameters and implicit object parameters}}
}
+namespace usage_invalid_gnu {
+ int *not_class_member() __attribute__((lifetimebound)); // expected-error {{non-member function has no implicit object parameter}}
+ struct A {
+ A() __attribute__((lifetimebound)); // expected-error {{cannot be applied to a constructor}}
+ ~A() __attribute__((lifetimebound)); // expected-error {{cannot be applied to a destructor}}
+ static int *static_class_member() __attribute__((lifetimebound)); // expected-error {{static member function has no implicit object parameter}}
+ void void_return_member() __attribute__((lifetimebound)); // expected-error {{'lifetimebound' attribute cannot be applied to an implicit object parameter of a function that returns void; did you mean 'lifetime_capture_by(X)'}}
+ };
+}
+
namespace usage_ok {
struct IntRef { int *target; };
@@ -96,6 +106,18 @@ namespace usage_ok {
t = {C().method()}; // expected-warning {{object backing the pointer 't' will be destroyed at the end of the full-expression}}
}
+ struct D {
+ int *method() __attribute__((lifetimebound));
+ int i;
+ };
+
+ int *D::method() { return &i; }
+
+ void test_lifetimebound_on_implicit_this_gnu() {
+ int *t = D().method(); // expected-warning {{temporary whose address is used as value of local variable 't' will be destroyed at the end of the full-expression}}
+ t = {D().method()}; // expected-warning {{object backing the pointer 't' will be destroyed at the end of the full-expression}}
+ }
+
struct FieldCheck {
struct Set {
int a;
@@ -120,6 +142,10 @@ namespace usage_ok {
const int& d = FieldCheck{x}.getNoLB()->c.a;
const int* e = FieldCheck{x}.getR().d;
}
+
+ const int &gnu_crefparam(const int ¶m __attribute__((lifetimebound)));
+ const int &gnu_crefparam(const int ¶m) { return param; }
+ const int &gnu_s = gnu_crefparam(2); // expected-warning {{temporary bound to local reference 'gnu_s' will be destroyed at the end of the full-expression}}
}
namespace std {
``````````
</details>
https://github.com/llvm/llvm-project/pull/192070
More information about the cfe-commits
mailing list