[clang] [clang] Fix GNU spellings of lifetimebound/lifetime_capture_by (PR #192070)

Zeyi Xu via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 14 07:38:21 PDT 2026


https://github.com/zeyi2 created https://github.com/llvm/llvm-project/pull/192070

None

>From a7bcaa592a6acf8ee655bafd34f889bfba2cb480 Mon Sep 17 00:00:00 2001
From: mtx <mitchell.xu2 at gmail.com>
Date: Tue, 14 Apr 2026 22:38:04 +0800
Subject: [PATCH] [clang] Fix GNU spellings of
 lifetimebound/lifetime_capture_by

---
 clang/docs/ReleaseNotes.rst                   |  2 ++
 clang/lib/Sema/SemaType.cpp                   | 22 ++++++++++++++--
 clang/test/AST/attr-lifetime-capture-by.cpp   |  6 +++++
 .../test/SemaCXX/attr-lifetime-capture-by.cpp | 12 +++++++++
 clang/test/SemaCXX/attr-lifetimebound.cpp     | 26 +++++++++++++++++++
 5 files changed, 66 insertions(+), 2 deletions(-)

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 &param __attribute__((lifetimebound)));
+  const int &gnu_crefparam(const int &param) { 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 {



More information about the cfe-commits mailing list