Skip to content

Commit 066e874

Browse files
committed
merge-attr-implicit-this
1 parent 39fcd2c commit 066e874

File tree

3 files changed

+92
-6
lines changed

3 files changed

+92
-6
lines changed

clang/lib/Sema/SemaDecl.cpp

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4469,6 +4469,53 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S,
44694469
return true;
44704470
}
44714471

4472+
/// Check if a function has a lifetimebound attribute on its function type
4473+
/// (which represents the implicit 'this' parameter for methods).
4474+
/// Returns the attribute if found, nullptr otherwise.
4475+
static const LifetimeBoundAttr *
4476+
getLifetimeBoundAttrFromType(const TypeSourceInfo *TSI) {
4477+
if (!TSI)
4478+
return nullptr;
4479+
// Walk through the type layers looking for a lifetimebound attribute.
4480+
TypeLoc TL = TSI->getTypeLoc();
4481+
while (true) {
4482+
auto ATL = TL.getAsAdjusted<AttributedTypeLoc>();
4483+
if (!ATL)
4484+
break;
4485+
if (auto *LBAttr = ATL.getAttrAs<LifetimeBoundAttr>())
4486+
return LBAttr;
4487+
TL = ATL.getModifiedLoc();
4488+
}
4489+
return nullptr;
4490+
}
4491+
4492+
/// Merge lifetimebound attribute on function type (implicit 'this')
4493+
/// from Old to New method declaration.
4494+
static void mergeLifetimeBoundAttrOnMethod(Sema &S, CXXMethodDecl *New,
4495+
const CXXMethodDecl *Old) {
4496+
const TypeSourceInfo *OldTSI = Old->getTypeSourceInfo();
4497+
const TypeSourceInfo *NewTSI = New->getTypeSourceInfo();
4498+
4499+
if (!OldTSI || !NewTSI)
4500+
return;
4501+
4502+
const LifetimeBoundAttr *OldLBAttr = getLifetimeBoundAttrFromType(OldTSI);
4503+
const LifetimeBoundAttr *NewLBAttr = getLifetimeBoundAttrFromType(NewTSI);
4504+
4505+
// If Old has lifetimebound but New doesn't, add it to New
4506+
if (OldLBAttr && !NewLBAttr) {
4507+
QualType NewMethodType = New->getType();
4508+
QualType AttributedType =
4509+
S.Context.getAttributedType(OldLBAttr, NewMethodType, NewMethodType);
4510+
TypeLocBuilder TLB;
4511+
TLB.pushFullCopy(NewTSI->getTypeLoc());
4512+
AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType);
4513+
TyLoc.setAttr(OldLBAttr);
4514+
New->setType(AttributedType);
4515+
New->setTypeSourceInfo(TLB.getTypeSourceInfo(S.Context, AttributedType));
4516+
}
4517+
}
4518+
44724519
bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old,
44734520
Scope *S, bool MergeTypeWithOld) {
44744521
// Merge the attributes
@@ -4485,12 +4532,17 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old,
44854532
// Merge attributes from the parameters. These can mismatch with K&R
44864533
// declarations.
44874534
if (New->getNumParams() == Old->getNumParams())
4488-
for (unsigned i = 0, e = New->getNumParams(); i != e; ++i) {
4489-
ParmVarDecl *NewParam = New->getParamDecl(i);
4490-
ParmVarDecl *OldParam = Old->getParamDecl(i);
4491-
mergeParamDeclAttributes(NewParam, OldParam, *this);
4492-
mergeParamDeclTypes(NewParam, OldParam, *this);
4493-
}
4535+
for (unsigned i = 0, e = New->getNumParams(); i != e; ++i) {
4536+
ParmVarDecl *NewParam = New->getParamDecl(i);
4537+
ParmVarDecl *OldParam = Old->getParamDecl(i);
4538+
mergeParamDeclAttributes(NewParam, OldParam, *this);
4539+
mergeParamDeclTypes(NewParam, OldParam, *this);
4540+
}
4541+
4542+
// Merge function type attributes (e.g., lifetimebound on implicit 'this').
4543+
if (auto *NewMethod = dyn_cast<CXXMethodDecl>(New))
4544+
if (auto *OldMethod = dyn_cast<CXXMethodDecl>(Old))
4545+
mergeLifetimeBoundAttrOnMethod(*this, NewMethod, OldMethod);
44944546

44954547
if (getLangOpts().CPlusPlus)
44964548
return MergeCXXFunctionDecl(New, Old, S);

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -943,3 +943,25 @@ void parentheses(bool cond) {
943943
} // expected-note 4 {{destroyed here}}
944944
(void)*p; // expected-note 4 {{later used here}}
945945
}
946+
947+
// Implicit this annotations with redecls.
948+
namespace GH172013 {
949+
// https://github.com/llvm/llvm-project/issues/62072
950+
// https://github.com/llvm/llvm-project/issues/172013
951+
struct S {
952+
View x() const [[clang::lifetimebound]];
953+
MyObj i;
954+
};
955+
956+
View S::x() const { return i; }
957+
958+
void bar() {
959+
View x;
960+
{
961+
S s;
962+
x = s.x(); // expected-warning {{object whose reference is captured does not live long enough}}
963+
View y = S().x(); // FIXME: Handle temporaries.
964+
} // expected-note {{destroyed here}}
965+
(void)x; // expected-note {{used here}}
966+
}
967+
}

clang/test/SemaCXX/attr-lifetimebound.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ 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+
void test_lifetimebound_on_implicit_this() {
86+
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}}
87+
t = {B().method()}; // expected-warning {{object backing the pointer 't' will be destroyed at the end of the full-expression}}
88+
}
89+
7890
struct FieldCheck {
7991
struct Set {
8092
int a;

0 commit comments

Comments
 (0)