Skip to content

Commit e9af4e3

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 2a0162c commit e9af4e3

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
@@ -611,22 +611,23 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
611611
}
612612
}
613613

614-
for (unsigned I = 0,
615-
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
616-
I != N; ++I) {
614+
const FunctionDecl *CanonCallee = Callee->getCanonicalDecl();
615+
unsigned NP = std::min(Callee->getNumParams(), CanonCallee->getNumParams());
616+
for (unsigned I = 0, N = std::min<unsigned>(NP, Args.size()); I != N; ++I) {
617617
Expr *Arg = Args[I];
618618
RevertToOldSizeRAII RAII(Path);
619619
if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg)) {
620620
Path.push_back(
621621
{IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()});
622622
Arg = DAE->getExpr();
623623
}
624-
if (CheckCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
625-
VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
624+
if (CheckCoroCall ||
625+
CanonCallee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
626+
VisitLifetimeBoundArg(CanonCallee->getParamDecl(I), Arg);
626627
else if (EnableGSLAnalysis && I == 0) {
627628
// Perform GSL analysis for the first argument
628-
if (shouldTrackFirstArgument(Callee)) {
629-
VisitGSLPointerArg(Callee, Arg);
629+
if (shouldTrackFirstArgument(CanonCallee)) {
630+
VisitGSLPointerArg(CanonCallee, Arg);
630631
} else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Call);
631632
Ctor && shouldTrackFirstArgumentForConstructor(Ctor)) {
632633
VisitGSLPointerArg(Ctor->getConstructor(), Arg);

clang/lib/Sema/SemaAttr.cpp

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

219219
void Sema::inferLifetimeBoundAttribute(FunctionDecl *FD) {
220-
if (FD->getNumParams() == 0)
220+
unsigned NumParams = FD->getNumParams();
221+
if (NumParams == 0)
221222
return;
222223

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

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)