Skip to content

Commit 581264c

Browse files
Propagate lifetimebound from formal parameters to those in the canonical declaration, then use the canonical declaration for analysis
Note that this doesn't handle the implicit 'this' parameter; that can be addressed in a separate commit.
1 parent b7d635e commit 581264c

File tree

3 files changed

+35
-19
lines changed

3 files changed

+35
-19
lines changed

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -607,22 +607,23 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
607607
}
608608
}
609609

610-
for (unsigned I = 0,
611-
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
612-
I != N; ++I) {
610+
const FunctionDecl *CanonCallee = Callee->getCanonicalDecl();
611+
unsigned NP = std::min(Callee->getNumParams(), CanonCallee->getNumParams());
612+
for (unsigned I = 0, N = std::min<unsigned>(NP, Args.size()); I != N; ++I) {
613613
Expr *Arg = Args[I];
614614
RevertToOldSizeRAII RAII(Path);
615615
if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg)) {
616616
Path.push_back(
617617
{IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()});
618618
Arg = DAE->getExpr();
619619
}
620-
if (CheckCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
621-
VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
620+
if (CheckCoroCall ||
621+
CanonCallee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
622+
VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg);
622623
else if (EnableGSLAnalysis && I == 0) {
623624
// Perform GSL analysis for the first argument
624-
if (shouldTrackFirstArgument(Callee)) {
625-
VisitGSLPointerArg(Callee, Arg);
625+
if (shouldTrackFirstArgument(CanonCallee)) {
626+
VisitGSLPointerArg(CanonCallee, Arg);
626627
} else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Call);
627628
Ctor && shouldTrackFirstArgumentForConstructor(Ctor)) {
628629
VisitGSLPointerArg(Ctor->getConstructor(), Arg);

clang/lib/Sema/SemaAttr.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,8 @@ void Sema::inferGslOwnerPointerAttribute(CXXRecordDecl *Record) {
216216
}
217217

218218
void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
219-
if (FD->getNumParams() == 0)
219+
unsigned NumParams = FD->getNumParams();
220+
if (NumParams == 0)
220221
return;
221222

222223
if (unsigned BuiltinID = FD->getBuiltinID()) {
@@ -238,18 +239,13 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
238239
default:
239240
break;
240241
}
241-
return;
242-
}
243-
if (auto *CMD = dyn_cast<CXXMethodDecl>(FD)) {
244-
const auto *CRD = CMD->getParent();
245-
if (!CRD->isInStdNamespace() || !CRD->getIdentifier())
246-
return;
247-
248-
if (isa<CXXConstructorDecl>(CMD)) {
242+
} else if (auto *CMD = dyn_cast<CXXMethodDecl>(FD)) {
243+
const CXXRecordDecl *CRD = CMD->getParent();
244+
if (CRD->isInStdNamespace() && CRD->getIdentifier() &&
245+
isa<CXXConstructorDecl>(CMD)) {
249246
auto *Param = CMD->getParamDecl(0);
250-
if (Param->hasAttr<LifetimeBoundAttr>())
251-
return;
252-
if (CRD->getName() == "basic_string_view" &&
247+
if (!Param->hasAttr<LifetimeBoundAttr>() &&
248+
CRD->getName() == "basic_string_view" &&
253249
Param->getType()->isPointerType()) {
254250
// construct from a char array pointed by a pointer.
255251
// basic_string_view(const CharT* s);
@@ -265,6 +261,20 @@ void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
265261
LifetimeBoundAttr::CreateImplicit(Context, FD->getLocation()));
266262
}
267263
}
264+
} else if (auto *CanonDecl = FD->getCanonicalDecl(); FD != CanonDecl) {
265+
// Propagate the lifetimebound attribute from parameters to the canonical
266+
// declaration.
267+
// Note that this doesn't include the implicit 'this' parameter, as the
268+
// attribute is applied to the function type in that case.
269+
unsigned NP = std::min(NumParams, CanonDecl->getNumParams());
270+
for (unsigned I = 0; I < NP; ++I) {
271+
auto *CanonParam = CanonDecl->getParamDecl(I);
272+
if (!CanonParam->hasAttr<LifetimeBoundAttr>() &&
273+
FD->getParamDecl(I)->hasAttr<LifetimeBoundAttr>()) {
274+
CanonParam->addAttr(LifetimeBoundAttr::CreateImplicit(
275+
Context, CanonParam->getLocation()));
276+
}
277+
}
268278
}
269279
}
270280

clang/test/SemaCXX/attr-lifetimebound.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ namespace usage_invalid {
1919
namespace usage_ok {
2020
struct IntRef { int *target; };
2121

22+
const int &crefparam(const int &param); // Omitted in first decl
23+
const int &crefparam(const int &param); // Omitted in second decl
24+
const int &crefparam(const int &param [[clang::lifetimebound]]); // Add LB
25+
const int &crefparam(const int &param) { return param; } // Omit in impl
2226
int &refparam(int &param [[clang::lifetimebound]]);
2327
int &classparam(IntRef param [[clang::lifetimebound]]);
2428

@@ -48,6 +52,7 @@ namespace usage_ok {
4852
int *p = A().class_member(); // expected-warning {{temporary whose address is used as value of local variable 'p' will be destroyed at the end of the full-expression}}
4953
int *q = A(); // expected-warning {{temporary whose address is used as value of local variable 'q' will be destroyed at the end of the full-expression}}
5054
int *r = A(1); // expected-warning {{temporary whose address is used as value of local variable 'r' will be destroyed at the end of the full-expression}}
55+
const int& s = crefparam(2); // expected-warning {{temporary bound to local reference 's' will be destroyed at the end of the full-expression}}
5156

5257
void test_assignment() {
5358
p = A().class_member(); // expected-warning {{object backing the pointer p will be destroyed at the end of the full-expression}}

0 commit comments

Comments
 (0)