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 {