Skip to content

Commit d643b4f

Browse files
Make [[clang::lifetimebound]] work for expressions coming from default arguments
1 parent c79e5ac commit d643b4f

File tree

3 files changed

+50
-4
lines changed

3 files changed

+50
-4
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10124,6 +10124,8 @@ def note_lambda_capture_initializer : Note<
1012410124
" via initialization of lambda capture %0}1">;
1012510125
def note_init_with_default_member_initializer : Note<
1012610126
"initializing field %0 with default member initializer">;
10127+
def note_init_with_default_argument : Note<
10128+
"initializing parameter %0 with default argument">;
1012710129

1012810130
// Check for initializing a member variable with the address or a reference to
1012910131
// a constructor parameter.

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ struct IndirectLocalPathEntry {
198198
GslReferenceInit,
199199
GslPointerInit,
200200
GslPointerAssignment,
201+
DefaultArg,
201202
} Kind;
202203
Expr *E;
203204
union {
@@ -609,15 +610,22 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
609610
for (unsigned I = 0,
610611
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
611612
I != N; ++I) {
613+
Expr *Arg = Args[I];
614+
RevertToOldSizeRAII RAII(Path);
615+
if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg)) {
616+
Path.push_back(
617+
{IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()});
618+
Arg = DAE->getExpr();
619+
}
612620
if (CheckCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
613-
VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]);
621+
VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
614622
else if (EnableGSLAnalysis && I == 0) {
615623
// Perform GSL analysis for the first argument
616624
if (shouldTrackFirstArgument(Callee)) {
617-
VisitGSLPointerArg(Callee, Args[0]);
625+
VisitGSLPointerArg(Callee, Arg);
618626
} else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Call);
619627
Ctor && shouldTrackFirstArgumentForConstructor(Ctor)) {
620-
VisitGSLPointerArg(Ctor->getConstructor(), Args[0]);
628+
VisitGSLPointerArg(Ctor->getConstructor(), Arg);
621629
}
622630
}
623631
}
@@ -1060,6 +1068,9 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
10601068
if (!Path[I].Capture->capturesVariable())
10611069
continue;
10621070
return Path[I].E->getSourceRange();
1071+
1072+
case IndirectLocalPathEntry::DefaultArg:
1073+
return cast<CXXDefaultArgExpr>(Path[I].E)->getUsedLocation();
10631074
}
10641075
}
10651076
return E->getSourceRange();
@@ -1370,7 +1381,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
13701381
break;
13711382
}
13721383

1373-
case IndirectLocalPathEntry::LambdaCaptureInit:
1384+
case IndirectLocalPathEntry::LambdaCaptureInit: {
13741385
if (!Elem.Capture->capturesVariable())
13751386
break;
13761387
// FIXME: We can't easily tell apart an init-capture from a nested
@@ -1383,6 +1394,15 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
13831394
<< nextPathEntryRange(Path, I + 1, L);
13841395
break;
13851396
}
1397+
1398+
case IndirectLocalPathEntry::DefaultArg: {
1399+
const auto *DAE = cast<CXXDefaultArgExpr>(Elem.E);
1400+
SemaRef.Diag(DAE->getParam()->getDefaultArgRange().getBegin(),
1401+
diag::note_init_with_default_argument)
1402+
<< DAE->getParam() << nextPathEntryRange(Path, I + 1, L);
1403+
break;
1404+
}
1405+
}
13861406
}
13871407

13881408
// We didn't lifetime-extend, so don't go any further; we don't need more

clang/test/SemaCXX/attr-lifetimebound.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,30 @@ namespace usage_ok {
7575
}
7676
}
7777

78+
namespace default_args {
79+
using IntArray = int[];
80+
const int *defaultparam1(const int &def1 [[clang::lifetimebound]] = 0); // #def1
81+
const int &defaultparam_array([[clang::lifetimebound]] const int *p = IntArray{1, 2, 3}); // #def2
82+
struct A {
83+
A(const char*, const int& def3 [[clang::lifetimebound]] = 0); // #def3
84+
};
85+
const int &defaultparam2(const int &def4 [[clang::lifetimebound]] = 0); // #def4
86+
const int &defaultparam3(const int &def5 [[clang::lifetimebound]] = defaultparam2()); // #def5
87+
88+
void test_default_args() {
89+
const int *c = defaultparam1(); // expected-warning {{temporary whose address is used as value of local variable 'c' will be destroyed at the end of the full-expression}} expected-note@#def1 {{initializing parameter 'def1' with default argument}}
90+
A a = A(""); // expected-warning {{temporary whose address is used as value of local variable 'a' will be destroyed at the end of the full-expression}} expected-note@#def3 {{initializing parameter 'def3' with default argument}}
91+
const int &s = defaultparam2(); // expected-warning {{temporary bound to local reference 's' will be destroyed at the end of the full-expression}} expected-note@#def4 {{initializing parameter 'def4' with default argument}}
92+
const int &t = defaultparam3(); // expected-warning {{temporary bound to local reference 't' will be destroyed at the end of the full-expression}} expected-note@#def4 {{initializing parameter 'def4' with default argument}} expected-note@#def5 {{initializing parameter 'def5' with default argument}}
93+
const int &u = defaultparam_array(); // expected-warning {{temporary bound to local reference 'u' will be destroyed at the end of the full-expression}} expected-note@#def2 {{initializing parameter 'p' with default argument}}
94+
int local;
95+
const int &v = defaultparam2(local); // no warning
96+
const int &w = defaultparam2(1); // expected-warning {{temporary bound to local reference 'w' will be destroyed at the end of the full-expression}}
97+
int x = defaultparam2(1); // FIXME: This should warn
98+
x = defaultparam2(1); // FIXME: This should warn
99+
}
100+
}
101+
78102
namespace std {
79103
using size_t = __SIZE_TYPE__;
80104
template<typename T>

0 commit comments

Comments
 (0)