Author: Utkarsh Saxena Date: 2026-01-19T13:02:59+01:00 New Revision: 41e231cae38028473dd327e8de5f65792b521ffe
URL: https://github.com/llvm/llvm-project/commit/41e231cae38028473dd327e8de5f65792b521ffe DIFF: https://github.com/llvm/llvm-project/commit/41e231cae38028473dd327e8de5f65792b521ffe.diff LOG: [LifetimeSafety] Read lifetimebound attribute on implicit 'this' from all redeclarations (#176188) Fix handling of `lifetimebound` attributes on implicit `this` parameters across function redeclarations. Previously, the lifetime analysis would miss `lifetimebound` attributes on implicit `this` parameters if they were only present on certain redeclarations of a method. This could lead to false negatives in the lifetime safety analysis. This change ensures that if any redeclaration of a method has the attribute, it will be properly detected and used in the analysis. I can't seem to work around the crash in the earlier attempt https://github.com/llvm/llvm-project/pull/172146. Reproducer of the original crash: ```cpp struct a { a &b() [[_Clang::__lifetimebound__]]; }; a &a::b() {} ``` This only crashes with `-target i686-w64-mingw32`. `bin/clang++ -c a.cpp` works fine. Problematic merging logic: ```cpp // If Old has lifetimebound but New doesn't, add it to New. if (OldLBAttr && !NewLBAttr) { QualType NewMethodType = New->getType(); QualType AttributedType = S.Context.getAttributedType(OldLBAttr, NewMethodType, NewMethodType); TypeLocBuilder TLB; TLB.pushFullCopy(NewTSI->getTypeLoc()); AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType); // Crashes. TyLoc.setAttr(OldLBAttr); New->setType(AttributedType); New->setTypeSourceInfo(TLB.getTypeSourceInfo(S.Context, AttributedType)); } ``` <details> <summary>Crash</summary> ``` clang++: /REDACTED//llvm-project/clang/lib/Sema/TypeLocBuilder.cpp:89: TypeLoc clang::TypeLocBuilder::pushImpl(QualType, size_t, unsigned int): Assertion `TLast == LastTy && "mismatch between last type and new type's inner type"' failed. PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script. Stack dump: 0. Program arguments: bin/clang++ -target i686-w64-mingw32 -c /REDACTED//a.cpp 1. /REDACTED//a.cpp:4:11: current parser token '{' #0 0x000055971cfcb838 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) /REDACTED//llvm-project/llvm/lib/Support/Unix/Signals.inc:842:13 #1 0x000055971cfc9374 llvm::sys::RunSignalHandlers() /REDACTED//llvm-project/llvm/lib/Support/Signals.cpp:109:18 #2 0x000055971cfcaf0c llvm::sys::CleanupOnSignal(unsigned long) /REDACTED//llvm-project/llvm/lib/Support/Unix/Signals.inc:0:3 #3 0x000055971cf38116 (anonymous namespace)::CrashRecoveryContextImpl::HandleCrash(int, unsigned long) /REDACTED//llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp:73:5 #4 0x000055971cf38116 CrashRecoverySignalHandler(int) /REDACTED//llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp:390:51 #5 0x00007fe9ebe49df0 (/lib/x86_64-linux-gnu/libc.so.6+0x3fdf0) #6 0x00007fe9ebe9e95c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76 #7 0x00007fe9ebe49cc2 raise ./signal/../sysdeps/posix/raise.c:27:6 #8 0x00007fe9ebe324ac abort ./stdlib/abort.c:81:3 #9 0x00007fe9ebe32420 __assert_perror_fail ./assert/assert-perr.c:31:1 #10 0x000055971f969ade clang::TypeLocBuilder::pushImpl(clang::QualType, unsigned long, unsigned int) /REDACTED//llvm-project/clang/lib/Sema/TypeLocBuilder.cpp:93:3 #11 0x000055971f237255 clang::QualType::hasLocalQualifiers() const /REDACTED//llvm-project/clang/include/clang/AST/TypeBase.h:1065:37 #12 0x000055971f237255 clang::ConcreteTypeLoc<clang::UnqualTypeLoc, clang::AttributedTypeLoc, clang::AttributedType, clang::AttributedLocInfo>::isKind(clang::TypeLoc const&) /REDACTED//llvm-project/clang/include/clang/AST/TypeLoc.h:392:26 #13 0x000055971f237255 clang::AttributedTypeLoc clang::TypeLoc::castAs<clang::AttributedTypeLoc>() const /REDACTED//llvm-project/clang/include/clang/AST/TypeLoc.h:79:5 #14 0x000055971f237255 clang::AttributedTypeLoc clang::TypeLocBuilder::push<clang::AttributedTypeLoc>(clang::QualType) /REDACTED//llvm-project/clang/lib/Sema/TypeLocBuilder.h:106:47 #15 0x000055971f280cc8 clang::AttributedTypeLoc::setAttr(clang::Attr const*) /REDACTED//llvm-project/clang/include/clang/AST/TypeLoc.h:1035:30 #16 0x000055971f280cc8 mergeLifetimeBoundAttrOnMethod(clang::Sema&, clang::CXXMethodDecl*, clang::CXXMethodDecl const*) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:4497:11 #17 0x000055971f280cc8 clang::Sema::MergeCompatibleFunctionDecls(clang::FunctionDecl*, clang::FunctionDecl*, clang::Scope*, bool) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:4528:5 #18 0x000055971f27eb1f clang::Sema::MergeFunctionDecl(clang::FunctionDecl*, clang::NamedDecl*&, clang::Scope*, bool, bool) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:0:0 #19 0x000055971f29c256 clang::Sema::CheckFunctionDeclaration(clang::Scope*, clang::FunctionDecl*, clang::LookupResult&, bool, bool) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:12371:9 #20 0x000055971f28dab0 clang::Declarator::setRedeclaration(bool) /REDACTED//llvm-project/clang/include/clang/Sema/DeclSpec.h:2738:51 #21 0x000055971f28dab0 clang::Sema::ActOnFunctionDeclarator(clang::Scope*, clang::Declarator&, clang::DeclContext*, clang::TypeSourceInfo*, clang::LookupResult&, llvm::MutableArrayRef<clang::TemplateParameterList*>, bool&) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:10877:9 #22 0x000055971f2890fc clang::Sema::HandleDeclarator(clang::Scope*, clang::Declarator&, llvm::MutableArrayRef<clang::TemplateParameterList*>) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:0:11 #23 0x000055971f2aab99 clang::Sema::ActOnStartOfFunctionDef(clang::Scope*, clang::Declarator&, llvm::MutableArrayRef<clang::TemplateParameterList*>, clang::SkipBodyInfo*, clang::Sema::FnBodyKind) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:15904:15 #24 0x000055971efab286 clang::Parser::ParseFunctionDefinition(clang::ParsingDeclarator&, clang::Parser::ParsedTemplateInfo const&, clang::Parser::LateParsedAttrList*) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:1364:23 #25 0x000055971f013b40 clang::Parser::ParseDeclGroup(clang::ParsingDeclSpec&, clang::DeclaratorContext, clang::ParsedAttributes&, clang::Parser::ParsedTemplateInfo&, clang::SourceLocation*, clang::Parser::ForRangeInit*) /REDACTED//llvm-project/clang/lib/Parse/ParseDecl.cpp:2268:18 #26 0x000055971efaa54f clang::Parser::ParseDeclOrFunctionDefInternal(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec&, clang::AccessSpecifier) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:0:10 #27 0x000055971efa9e36 clang::Parser::ParseDeclarationOrFunctionDefinition(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*, clang::AccessSpecifier) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:1202:12 #28 0x000055971efa8df8 clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:0:14 #29 0x000055971efa7574 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:743:10 #30 0x000055971ef9c0ee clang::ParseAST(clang::Sema&, bool, bool) /REDACTED//llvm-project/clang/lib/Parse/ParseAST.cpp:169:5 #31 0x000055971dbcdad6 clang::FrontendAction::Execute() /REDACTED//llvm-project/clang/lib/Frontend/FrontendAction.cpp:1317:10 #32 0x000055971db3c5fd llvm::Error::getPtr() const /REDACTED//llvm-project/llvm/include/llvm/Support/Error.h:278:42 #33 0x000055971db3c5fd llvm::Error::operator bool() /REDACTED//llvm-project/llvm/include/llvm/Support/Error.h:241:16 #34 0x000055971db3c5fd clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) /REDACTED//llvm-project/clang/lib/Frontend/CompilerInstance.cpp:1006:23 #35 0x000055971dcb4f9c clang::ExecuteCompilerInvocation(clang::CompilerInstance*) /REDACTED//llvm-project/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp:310:25 #36 0x000055971a5e655e cc1_main(llvm::ArrayRef<char const*>, char const*, void*) /REDACTED//llvm-project/clang/tools/driver/cc1_main.cpp:304:15 #37 0x000055971a5e29cb ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) /REDACTED//llvm-project/clang/tools/driver/driver.cpp:226:12 #38 0x000055971a5e4c1d clang_main(int, char**, llvm::ToolContext const&)::$_0::operator()(llvm::SmallVectorImpl<char const*>&) const /REDACTED//llvm-project/clang/tools/driver/driver.cpp:0:12 #39 0x000055971a5e4c1d int llvm::function_ref<int (llvm::SmallVectorImpl<char const*>&)>::callback_fn<clang_main(int, char**, llvm::ToolContext const&)::$_0>(long, llvm::SmallVectorImpl<char const*>&) /REDACTED//llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:46:12 #40 0x000055971d9bfe79 clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const::$_0::operator()() const /REDACTED//llvm-project/clang/lib/Driver/Job.cpp:442:30 #41 0x000055971d9bfe79 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const::$_0>(long) /REDACTED//llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:46:12 #42 0x000055971cf37dbe llvm::function_ref<void ()>::operator()() const /REDACTED//llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:0:12 #43 0x000055971cf37dbe llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) /REDACTED//llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp:426:3 #44 0x000055971d9bf5ac clang::driver::CC1Command::Execute(llvm::ArrayRef<std::optional<llvm::StringRef>>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>*, bool*) const /REDACTED//llvm-project/clang/lib/Driver/Job.cpp:442:7 #45 0x000055971d98422c clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const /REDACTED//llvm-project/clang/lib/Driver/Compilation.cpp:196:15 #46 0x000055971d984447 clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&, bool) const /REDACTED//llvm-project/clang/lib/Driver/Compilation.cpp:246:13 #47 0x000055971d99ee08 llvm::SmallVectorBase<unsigned int>::empty() const /REDACTED//llvm-project/llvm/include/llvm/ADT/SmallVector.h:83:46 #48 0x000055971d99ee08 clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*>>&) /REDACTED//llvm-project/clang/lib/Driver/Driver.cpp:2265:23 #49 0x000055971a5e2303 clang_main(int, char**, llvm::ToolContext const&) /REDACTED//llvm-project/clang/tools/driver/driver.cpp:414:21 #50 0x000055971a5f2527 main /usr/local/google/home/usx/build/tools/clang/tools/driver/clang-driver.cpp:17:10 #51 0x00007fe9ebe33ca8 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3 #52 0x00007fe9ebe33d65 call_init ./csu/../csu/libc-start.c:128:20 #53 0x00007fe9ebe33d65 __libc_start_main ./csu/../csu/libc-start.c:347:5 #54 0x000055971a5e0361 _start (bin/clang+++0x6636361) clang++: error: clang frontend command failed with exit code 134 (use -v to see invocation) clang version 23.0.0git (https://github.com/llvm/llvm-project.git 282a065c5bb36186c05b903980d1d3a37fce2175) Target: i686-w64-windows-gnu Thread model: posix InstalledDir: /usr/local/google/home/usx/build/bin Build config: +assertions clang++: note: diagnostic msg: ******************** ``` </details> Added: Modified: clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp clang/test/Sema/warn-lifetime-analysis-nocfg.cpp clang/test/Sema/warn-lifetime-safety.cpp clang/test/SemaCXX/attr-lifetimebound.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h index f96d412aa63d2..760d34d33b15b 100644 --- a/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h +++ b/clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H #define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H +#include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" namespace clang ::lifetimes { diff --git a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp index 2772fe20de19b..ced0ad537604a 100644 --- a/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp +++ b/clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp @@ -52,22 +52,33 @@ bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) { CMD->getParamDecl(0)->hasAttr<clang::LifetimeBoundAttr>(); } +/// Check if a function has a lifetimebound attribute on its function type +/// (which represents the implicit 'this' parameter for methods). +/// Returns the attribute if found, nullptr otherwise. +static const LifetimeBoundAttr * +getLifetimeBoundAttrFromFunctionType(const TypeSourceInfo &TSI) { + // Walk through the type layers looking for a lifetimebound attribute. + TypeLoc TL = TSI.getTypeLoc(); + while (true) { + auto ATL = TL.getAsAdjusted<AttributedTypeLoc>(); + if (!ATL) + break; + if (auto *LBAttr = ATL.getAttrAs<LifetimeBoundAttr>()) + return LBAttr; + TL = ATL.getModifiedLoc(); + } + return nullptr; +} + bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { FD = getDeclWithMergedLifetimeBoundAttrs(FD); - const TypeSourceInfo *TSI = FD->getTypeSourceInfo(); - if (!TSI) - return false; - // Don't declare this variable in the second operand of the for-statement; - // GCC miscompiles that by ending its lifetime before evaluating the - // third operand. See gcc.gnu.org/PR86769. - AttributedTypeLoc ATL; - for (TypeLoc TL = TSI->getTypeLoc(); - (ATL = TL.getAsAdjusted<AttributedTypeLoc>()); - TL = ATL.getModifiedLoc()) { - if (ATL.getAttrAs<clang::LifetimeBoundAttr>()) + // Attribute merging doesn't work well with attributes on function types (like + // 'this' param). We need to check all redeclarations. + for (const FunctionDecl *Redecl : FD->redecls()) { + const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo(); + if (TSI && getLifetimeBoundAttrFromFunctionType(*TSI)) return true; } - return isNormalAssignmentOperator(FD); } diff --git a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp index 7fdc493dbd17a..441f9fc602916 100644 --- a/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp +++ b/clang/test/Sema/warn-lifetime-analysis-nocfg.cpp @@ -1039,3 +1039,141 @@ const char* foo() { } } // namespace GH127195 + +// Lifetimebound on definition vs declaration on implicit this param. +namespace GH175391 { +// Version A: Attribute on declaration only +class StringA { +public: + const char* data() const [[clang::lifetimebound]]; // Declaration with attribute +private: + char buffer[32] = "hello"; +}; +inline const char* StringA::data() const { // Definition WITHOUT attribute + return buffer; +} + +// Version B: Attribute on definition only +class StringB { +public: + const char* data() const; // No attribute +private: + char buffer[32] = "hello"; +}; +inline const char* StringB::data() const [[clang::lifetimebound]] { + return buffer; +} + +// Version C: Attribute on BOTH declaration and definition +class StringC { +public: + const char* data() const [[clang::lifetimebound]]; +private: + char buffer[32] = "hello"; +}; +inline const char* StringC::data() const [[clang::lifetimebound]] { + return buffer; +} + +// TEMPLATED VERSIONS + +// Template Version A: Attribute on declaration only +template<typename T> +class StringTemplateA { +public: + const T* data() const [[clang::lifetimebound]]; // Declaration with attribute +private: + T buffer[32]; +}; +template<typename T> +inline const T* StringTemplateA<T>::data() const { // Definition WITHOUT attribute + return buffer; +} + +// Template Version B: Attribute on definition only +template<typename T> +class StringTemplateB { +public: + const T* data() const; // No attribute +private: + T buffer[32]; +}; +template<typename T> +inline const T* StringTemplateB<T>::data() const [[clang::lifetimebound]] { + return buffer; +} + +// Template Version C: Attribute on BOTH declaration and definition +template<typename T> +class StringTemplateC { +public: + const T* data() const [[clang::lifetimebound]]; +private: + T buffer[32]; +}; +template<typename T> +inline const T* StringTemplateC<T>::data() const [[clang::lifetimebound]] { + return buffer; +} + +// TEMPLATE SPECIALIZATION VERSIONS + +// Template predeclarations for specializations +template<typename T> class StringTemplateSpecA; +template<typename T> class StringTemplateSpecB; +template<typename T> class StringTemplateSpecC; + +// Template Specialization Version A: Attribute on declaration only - <char> specialization +template<> +class StringTemplateSpecA<char> { +public: + const char* data() const [[clang::lifetimebound]]; // Declaration with attribute +private: + char buffer[32] = "hello"; +}; +inline const char* StringTemplateSpecA<char>::data() const { // Definition WITHOUT attribute + return buffer; +} + +// Template Specialization Version B: Attribute on definition only - <char> specialization +template<> +class StringTemplateSpecB<char> { +public: + const char* data() const; // No attribute +private: + char buffer[32] = "hello"; +}; +inline const char* StringTemplateSpecB<char>::data() const [[clang::lifetimebound]] { + return buffer; +} + +// Template Specialization Version C: Attribute on BOTH declaration and definition - <char> specialization +template<> +class StringTemplateSpecC<char> { +public: + const char* data() const [[clang::lifetimebound]]; +private: + char buffer[32] = "hello"; +}; +inline const char* StringTemplateSpecC<char>::data() const [[clang::lifetimebound]] { + return buffer; +} + +void test() { + // Non-templated tests + const auto ptrA = StringA().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} + const auto ptrB = StringB().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}} + const auto ptrC = StringC().data(); // Both have attribute // expected-warning {{temporary whose address is used}} + + // Templated tests (generic templates) + const auto ptrTA = StringTemplateA<char>().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} + // FIXME: Definition is not instantiated until the end of TU. The attribute is not merged when this call is processed. + const auto ptrTB = StringTemplateB<char>().data(); // Definition-only attribute + const auto ptrTC = StringTemplateC<char>().data(); // Both have attribute // expected-warning {{temporary whose address is used}} + + // Template specialization tests + const auto ptrTSA = StringTemplateSpecA<char>().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}} + const auto ptrTSB = StringTemplateSpecB<char>().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}} + const auto ptrTSC = StringTemplateSpecC<char>().data(); // Both have attribute // expected-warning {{temporary whose address is used}} +} +} // namespace GH175391 diff --git a/clang/test/Sema/warn-lifetime-safety.cpp b/clang/test/Sema/warn-lifetime-safety.cpp index 0b1962b7cb651..24ac72e7aad4d 100644 --- a/clang/test/Sema/warn-lifetime-safety.cpp +++ b/clang/test/Sema/warn-lifetime-safety.cpp @@ -1431,3 +1431,25 @@ void not_silenced_via_conditional(bool cond) { (void)v; // expected-note 2 {{later used here}} } } // namespace do_not_warn_on_std_move + +// Implicit this annotations with redecls. +namespace GH172013 { +// https://github.com/llvm/llvm-project/issues/62072 +// https://github.com/llvm/llvm-project/issues/172013 +struct S { + View x() const [[clang::lifetimebound]]; + MyObj i; +}; + +View S::x() const { return i; } + +void bar() { + View x; + { + S s; + x = s.x(); // expected-warning {{object whose reference is captured does not live long enough}} + View y = S().x(); // FIXME: Handle temporaries. + } // expected-note {{destroyed here}} + (void)x; // expected-note {{used here}} +} +} diff --git a/clang/test/SemaCXX/attr-lifetimebound.cpp b/clang/test/SemaCXX/attr-lifetimebound.cpp index 111bad65f7e1b..9e2aaff6559c4 100644 --- a/clang/test/SemaCXX/attr-lifetimebound.cpp +++ b/clang/test/SemaCXX/attr-lifetimebound.cpp @@ -75,6 +75,27 @@ namespace usage_ok { r = A(1); // expected-warning {{object backing the pointer 'r' will be destroyed at the end of the full-expression}} } + // Test that lifetimebound on implicit 'this' is propagated across redeclarations + struct B { + int *method() [[clang::lifetimebound]]; + int i; + }; + int *B::method() { return &i; } + + // Test that lifetimebound on implicit 'this' is propagated across redeclarations + struct C { + int *method(); + int i; + }; + int *C::method() [[clang::lifetimebound]] { return &i; } + + void test_lifetimebound_on_implicit_this() { + int *t = B().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 = {B().method()}; // expected-warning {{object backing the pointer 't' will be destroyed at the end of the full-expression}} + t = C().method(); // expected-warning {{object backing the pointer 't' will be destroyed at the end of the full-expression}} + t = {C().method()}; // expected-warning {{object backing the pointer 't' will be destroyed at the end of the full-expression}} + } + struct FieldCheck { struct Set { int a; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
