Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 41e231c

Browse files
authored
[LifetimeSafety] Read lifetimebound attribute on implicit 'this' from all redeclarations (llvm#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 llvm#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 llvm#4 0x000055971cf38116 CrashRecoverySignalHandler(int) /REDACTED//llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp:390:51 llvm#5 0x00007fe9ebe49df0 (/lib/x86_64-linux-gnu/libc.so.6+0x3fdf0) llvm#6 0x00007fe9ebe9e95c __pthread_kill_implementation ./nptl/pthread_kill.c:44:76 llvm#7 0x00007fe9ebe49cc2 raise ./signal/../sysdeps/posix/raise.c:27:6 llvm#8 0x00007fe9ebe324ac abort ./stdlib/abort.c:81:3 llvm#9 0x00007fe9ebe32420 __assert_perror_fail ./assert/assert-perr.c:31:1 llvm#10 0x000055971f969ade clang::TypeLocBuilder::pushImpl(clang::QualType, unsigned long, unsigned int) /REDACTED//llvm-project/clang/lib/Sema/TypeLocBuilder.cpp:93:3 llvm#11 0x000055971f237255 clang::QualType::hasLocalQualifiers() const /REDACTED//llvm-project/clang/include/clang/AST/TypeBase.h:1065:37 llvm#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 llvm#13 0x000055971f237255 clang::AttributedTypeLoc clang::TypeLoc::castAs<clang::AttributedTypeLoc>() const /REDACTED//llvm-project/clang/include/clang/AST/TypeLoc.h:79:5 llvm#14 0x000055971f237255 clang::AttributedTypeLoc clang::TypeLocBuilder::push<clang::AttributedTypeLoc>(clang::QualType) /REDACTED//llvm-project/clang/lib/Sema/TypeLocBuilder.h:106:47 llvm#15 0x000055971f280cc8 clang::AttributedTypeLoc::setAttr(clang::Attr const*) /REDACTED//llvm-project/clang/include/clang/AST/TypeLoc.h:1035:30 llvm#16 0x000055971f280cc8 mergeLifetimeBoundAttrOnMethod(clang::Sema&, clang::CXXMethodDecl*, clang::CXXMethodDecl const*) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:4497:11 llvm#17 0x000055971f280cc8 clang::Sema::MergeCompatibleFunctionDecls(clang::FunctionDecl*, clang::FunctionDecl*, clang::Scope*, bool) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:4528:5 llvm#18 0x000055971f27eb1f clang::Sema::MergeFunctionDecl(clang::FunctionDecl*, clang::NamedDecl*&, clang::Scope*, bool, bool) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:0:0 llvm#19 0x000055971f29c256 clang::Sema::CheckFunctionDeclaration(clang::Scope*, clang::FunctionDecl*, clang::LookupResult&, bool, bool) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:12371:9 llvm#20 0x000055971f28dab0 clang::Declarator::setRedeclaration(bool) /REDACTED//llvm-project/clang/include/clang/Sema/DeclSpec.h:2738:51 llvm#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 llvm#22 0x000055971f2890fc clang::Sema::HandleDeclarator(clang::Scope*, clang::Declarator&, llvm::MutableArrayRef<clang::TemplateParameterList*>) /REDACTED//llvm-project/clang/lib/Sema/SemaDecl.cpp:0:11 llvm#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 llvm#24 0x000055971efab286 clang::Parser::ParseFunctionDefinition(clang::ParsingDeclarator&, clang::Parser::ParsedTemplateInfo const&, clang::Parser::LateParsedAttrList*) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:1364:23 llvm#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 llvm#26 0x000055971efaa54f clang::Parser::ParseDeclOrFunctionDefInternal(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec&, clang::AccessSpecifier) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:0:10 llvm#27 0x000055971efa9e36 clang::Parser::ParseDeclarationOrFunctionDefinition(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*, clang::AccessSpecifier) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:1202:12 llvm#28 0x000055971efa8df8 clang::Parser::ParseExternalDeclaration(clang::ParsedAttributes&, clang::ParsedAttributes&, clang::ParsingDeclSpec*) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:0:14 llvm#29 0x000055971efa7574 clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&, clang::Sema::ModuleImportState&) /REDACTED//llvm-project/clang/lib/Parse/Parser.cpp:743:10 llvm#30 0x000055971ef9c0ee clang::ParseAST(clang::Sema&, bool, bool) /REDACTED//llvm-project/clang/lib/Parse/ParseAST.cpp:169:5 llvm#31 0x000055971dbcdad6 clang::FrontendAction::Execute() /REDACTED//llvm-project/clang/lib/Frontend/FrontendAction.cpp:1317:10 llvm#32 0x000055971db3c5fd llvm::Error::getPtr() const /REDACTED//llvm-project/llvm/include/llvm/Support/Error.h:278:42 llvm#33 0x000055971db3c5fd llvm::Error::operator bool() /REDACTED//llvm-project/llvm/include/llvm/Support/Error.h:241:16 llvm#34 0x000055971db3c5fd clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) /REDACTED//llvm-project/clang/lib/Frontend/CompilerInstance.cpp:1006:23 llvm#35 0x000055971dcb4f9c clang::ExecuteCompilerInvocation(clang::CompilerInstance*) /REDACTED//llvm-project/clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp:310:25 llvm#36 0x000055971a5e655e cc1_main(llvm::ArrayRef<char const*>, char const*, void*) /REDACTED//llvm-project/clang/tools/driver/cc1_main.cpp:304:15 llvm#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 llvm#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 llvm#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 llvm#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 llvm#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 llvm#42 0x000055971cf37dbe llvm::function_ref<void ()>::operator()() const /REDACTED//llvm-project/llvm/include/llvm/ADT/STLFunctionalExtras.h:0:12 llvm#43 0x000055971cf37dbe llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) /REDACTED//llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp:426:3 llvm#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 llvm#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 llvm#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 llvm#47 0x000055971d99ee08 llvm::SmallVectorBase<unsigned int>::empty() const /REDACTED//llvm-project/llvm/include/llvm/ADT/SmallVector.h:83:46 llvm#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 llvm#49 0x000055971a5e2303 clang_main(int, char**, llvm::ToolContext const&) /REDACTED//llvm-project/clang/tools/driver/driver.cpp:414:21 llvm#50 0x000055971a5f2527 main /usr/local/google/home/usx/build/tools/clang/tools/driver/clang-driver.cpp:17:10 llvm#51 0x00007fe9ebe33ca8 __libc_start_call_main ./csu/../sysdeps/nptl/libc_start_call_main.h:74:3 llvm#52 0x00007fe9ebe33d65 call_init ./csu/../csu/libc-start.c:128:20 llvm#53 0x00007fe9ebe33d65 __libc_start_main ./csu/../csu/libc-start.c:347:5 llvm#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 282a065) Target: i686-w64-windows-gnu Thread model: posix InstalledDir: /usr/local/google/home/usx/build/bin Build config: +assertions clang++: note: diagnostic msg: ******************** ``` </details>
1 parent cf4cf85 commit 41e231c

5 files changed

Lines changed: 205 additions & 12 deletions

File tree

clang/include/clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H
1111
#define LLVM_CLANG_ANALYSIS_ANALYSES_LIFETIMEANNOTATIONS_H
1212

13+
#include "clang/AST/Attr.h"
1314
#include "clang/AST/DeclCXX.h"
1415

1516
namespace clang ::lifetimes {

clang/lib/Analysis/LifetimeSafety/LifetimeAnnotations.cpp

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,33 @@ bool isAssignmentOperatorLifetimeBound(const CXXMethodDecl *CMD) {
5252
CMD->getParamDecl(0)->hasAttr<clang::LifetimeBoundAttr>();
5353
}
5454

55+
/// Check if a function has a lifetimebound attribute on its function type
56+
/// (which represents the implicit 'this' parameter for methods).
57+
/// Returns the attribute if found, nullptr otherwise.
58+
static const LifetimeBoundAttr *
59+
getLifetimeBoundAttrFromFunctionType(const TypeSourceInfo &TSI) {
60+
// Walk through the type layers looking for a lifetimebound attribute.
61+
TypeLoc TL = TSI.getTypeLoc();
62+
while (true) {
63+
auto ATL = TL.getAsAdjusted<AttributedTypeLoc>();
64+
if (!ATL)
65+
break;
66+
if (auto *LBAttr = ATL.getAttrAs<LifetimeBoundAttr>())
67+
return LBAttr;
68+
TL = ATL.getModifiedLoc();
69+
}
70+
return nullptr;
71+
}
72+
5573
bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
5674
FD = getDeclWithMergedLifetimeBoundAttrs(FD);
57-
const TypeSourceInfo *TSI = FD->getTypeSourceInfo();
58-
if (!TSI)
59-
return false;
60-
// Don't declare this variable in the second operand of the for-statement;
61-
// GCC miscompiles that by ending its lifetime before evaluating the
62-
// third operand. See gcc.gnu.org/PR86769.
63-
AttributedTypeLoc ATL;
64-
for (TypeLoc TL = TSI->getTypeLoc();
65-
(ATL = TL.getAsAdjusted<AttributedTypeLoc>());
66-
TL = ATL.getModifiedLoc()) {
67-
if (ATL.getAttrAs<clang::LifetimeBoundAttr>())
75+
// Attribute merging doesn't work well with attributes on function types (like
76+
// 'this' param). We need to check all redeclarations.
77+
for (const FunctionDecl *Redecl : FD->redecls()) {
78+
const TypeSourceInfo *TSI = Redecl->getTypeSourceInfo();
79+
if (TSI && getLifetimeBoundAttrFromFunctionType(*TSI))
6880
return true;
6981
}
70-
7182
return isNormalAssignmentOperator(FD);
7283
}
7384

clang/test/Sema/warn-lifetime-analysis-nocfg.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,3 +1039,141 @@ const char* foo() {
10391039
}
10401040

10411041
} // namespace GH127195
1042+
1043+
// Lifetimebound on definition vs declaration on implicit this param.
1044+
namespace GH175391 {
1045+
// Version A: Attribute on declaration only
1046+
class StringA {
1047+
public:
1048+
const char* data() const [[clang::lifetimebound]]; // Declaration with attribute
1049+
private:
1050+
char buffer[32] = "hello";
1051+
};
1052+
inline const char* StringA::data() const { // Definition WITHOUT attribute
1053+
return buffer;
1054+
}
1055+
1056+
// Version B: Attribute on definition only
1057+
class StringB {
1058+
public:
1059+
const char* data() const; // No attribute
1060+
private:
1061+
char buffer[32] = "hello";
1062+
};
1063+
inline const char* StringB::data() const [[clang::lifetimebound]] {
1064+
return buffer;
1065+
}
1066+
1067+
// Version C: Attribute on BOTH declaration and definition
1068+
class StringC {
1069+
public:
1070+
const char* data() const [[clang::lifetimebound]];
1071+
private:
1072+
char buffer[32] = "hello";
1073+
};
1074+
inline const char* StringC::data() const [[clang::lifetimebound]] {
1075+
return buffer;
1076+
}
1077+
1078+
// TEMPLATED VERSIONS
1079+
1080+
// Template Version A: Attribute on declaration only
1081+
template<typename T>
1082+
class StringTemplateA {
1083+
public:
1084+
const T* data() const [[clang::lifetimebound]]; // Declaration with attribute
1085+
private:
1086+
T buffer[32];
1087+
};
1088+
template<typename T>
1089+
inline const T* StringTemplateA<T>::data() const { // Definition WITHOUT attribute
1090+
return buffer;
1091+
}
1092+
1093+
// Template Version B: Attribute on definition only
1094+
template<typename T>
1095+
class StringTemplateB {
1096+
public:
1097+
const T* data() const; // No attribute
1098+
private:
1099+
T buffer[32];
1100+
};
1101+
template<typename T>
1102+
inline const T* StringTemplateB<T>::data() const [[clang::lifetimebound]] {
1103+
return buffer;
1104+
}
1105+
1106+
// Template Version C: Attribute on BOTH declaration and definition
1107+
template<typename T>
1108+
class StringTemplateC {
1109+
public:
1110+
const T* data() const [[clang::lifetimebound]];
1111+
private:
1112+
T buffer[32];
1113+
};
1114+
template<typename T>
1115+
inline const T* StringTemplateC<T>::data() const [[clang::lifetimebound]] {
1116+
return buffer;
1117+
}
1118+
1119+
// TEMPLATE SPECIALIZATION VERSIONS
1120+
1121+
// Template predeclarations for specializations
1122+
template<typename T> class StringTemplateSpecA;
1123+
template<typename T> class StringTemplateSpecB;
1124+
template<typename T> class StringTemplateSpecC;
1125+
1126+
// Template Specialization Version A: Attribute on declaration only - <char> specialization
1127+
template<>
1128+
class StringTemplateSpecA<char> {
1129+
public:
1130+
const char* data() const [[clang::lifetimebound]]; // Declaration with attribute
1131+
private:
1132+
char buffer[32] = "hello";
1133+
};
1134+
inline const char* StringTemplateSpecA<char>::data() const { // Definition WITHOUT attribute
1135+
return buffer;
1136+
}
1137+
1138+
// Template Specialization Version B: Attribute on definition only - <char> specialization
1139+
template<>
1140+
class StringTemplateSpecB<char> {
1141+
public:
1142+
const char* data() const; // No attribute
1143+
private:
1144+
char buffer[32] = "hello";
1145+
};
1146+
inline const char* StringTemplateSpecB<char>::data() const [[clang::lifetimebound]] {
1147+
return buffer;
1148+
}
1149+
1150+
// Template Specialization Version C: Attribute on BOTH declaration and definition - <char> specialization
1151+
template<>
1152+
class StringTemplateSpecC<char> {
1153+
public:
1154+
const char* data() const [[clang::lifetimebound]];
1155+
private:
1156+
char buffer[32] = "hello";
1157+
};
1158+
inline const char* StringTemplateSpecC<char>::data() const [[clang::lifetimebound]] {
1159+
return buffer;
1160+
}
1161+
1162+
void test() {
1163+
// Non-templated tests
1164+
const auto ptrA = StringA().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}}
1165+
const auto ptrB = StringB().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}}
1166+
const auto ptrC = StringC().data(); // Both have attribute // expected-warning {{temporary whose address is used}}
1167+
1168+
// Templated tests (generic templates)
1169+
const auto ptrTA = StringTemplateA<char>().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}}
1170+
// FIXME: Definition is not instantiated until the end of TU. The attribute is not merged when this call is processed.
1171+
const auto ptrTB = StringTemplateB<char>().data(); // Definition-only attribute
1172+
const auto ptrTC = StringTemplateC<char>().data(); // Both have attribute // expected-warning {{temporary whose address is used}}
1173+
1174+
// Template specialization tests
1175+
const auto ptrTSA = StringTemplateSpecA<char>().data(); // Declaration-only attribute // expected-warning {{temporary whose address is used}}
1176+
const auto ptrTSB = StringTemplateSpecB<char>().data(); // Definition-only attribute // expected-warning {{temporary whose address is used}}
1177+
const auto ptrTSC = StringTemplateSpecC<char>().data(); // Both have attribute // expected-warning {{temporary whose address is used}}
1178+
}
1179+
} // namespace GH175391

clang/test/Sema/warn-lifetime-safety.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,3 +1431,25 @@ void not_silenced_via_conditional(bool cond) {
14311431
(void)v; // expected-note 2 {{later used here}}
14321432
}
14331433
} // namespace do_not_warn_on_std_move
1434+
1435+
// Implicit this annotations with redecls.
1436+
namespace GH172013 {
1437+
// https://github.com/llvm/llvm-project/issues/62072
1438+
// https://github.com/llvm/llvm-project/issues/172013
1439+
struct S {
1440+
View x() const [[clang::lifetimebound]];
1441+
MyObj i;
1442+
};
1443+
1444+
View S::x() const { return i; }
1445+
1446+
void bar() {
1447+
View x;
1448+
{
1449+
S s;
1450+
x = s.x(); // expected-warning {{object whose reference is captured does not live long enough}}
1451+
View y = S().x(); // FIXME: Handle temporaries.
1452+
} // expected-note {{destroyed here}}
1453+
(void)x; // expected-note {{used here}}
1454+
}
1455+
}

clang/test/SemaCXX/attr-lifetimebound.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,27 @@ namespace usage_ok {
7575
r = A(1); // expected-warning {{object backing the pointer 'r' will be destroyed at the end of the full-expression}}
7676
}
7777

78+
// Test that lifetimebound on implicit 'this' is propagated across redeclarations
79+
struct B {
80+
int *method() [[clang::lifetimebound]];
81+
int i;
82+
};
83+
int *B::method() { return &i; }
84+
85+
// Test that lifetimebound on implicit 'this' is propagated across redeclarations
86+
struct C {
87+
int *method();
88+
int i;
89+
};
90+
int *C::method() [[clang::lifetimebound]] { return &i; }
91+
92+
void test_lifetimebound_on_implicit_this() {
93+
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}}
94+
t = {B().method()}; // expected-warning {{object backing the pointer 't' will be destroyed at the end of the full-expression}}
95+
t = C().method(); // expected-warning {{object backing the pointer 't' will be destroyed at the end of the full-expression}}
96+
t = {C().method()}; // expected-warning {{object backing the pointer 't' will be destroyed at the end of the full-expression}}
97+
}
98+
7899
struct FieldCheck {
79100
struct Set {
80101
int a;

0 commit comments

Comments
 (0)