Skip to content

Commit ee3c962

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

File tree

3 files changed

+40
-4
lines changed

3 files changed

+40
-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: 27 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,17 +610,27 @@ 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+
auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg);
615+
if (DAE) {
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
}
631+
if (DAE) {
632+
Path.pop_back();
633+
}
623634
}
624635
}
625636

@@ -1060,6 +1071,9 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
10601071
if (!Path[I].Capture->capturesVariable())
10611072
continue;
10621073
return Path[I].E->getSourceRange();
1074+
1075+
case IndirectLocalPathEntry::DefaultArg:
1076+
return cast<CXXDefaultArgExpr>(Path[I].E)->getUsedLocation();
10631077
}
10641078
}
10651079
return E->getSourceRange();
@@ -1370,7 +1384,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
13701384
break;
13711385
}
13721386

1373-
case IndirectLocalPathEntry::LambdaCaptureInit:
1387+
case IndirectLocalPathEntry::LambdaCaptureInit: {
13741388
if (!Elem.Capture->capturesVariable())
13751389
break;
13761390
// FIXME: We can't easily tell apart an init-capture from a nested
@@ -1383,6 +1397,15 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
13831397
<< nextPathEntryRange(Path, I + 1, L);
13841398
break;
13851399
}
1400+
1401+
case IndirectLocalPathEntry::DefaultArg: {
1402+
const auto *DAE = cast<CXXDefaultArgExpr>(Elem.E);
1403+
SemaRef.Diag(DAE->getParam()->getDefaultArgRange().getBegin(),
1404+
diag::note_init_with_default_argument)
1405+
<< DAE->getParam() << nextPathEntryRange(Path, I + 1, L);
1406+
break;
1407+
}
1408+
}
13861409
}
13871410

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

clang/test/SemaCXX/attr-lifetimebound.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ namespace usage_invalid {
1818

1919
namespace usage_ok {
2020
struct IntRef { int *target; };
21+
using IntArray = int[];
2122

23+
const int *defaultparam(const int &def1 [[clang::lifetimebound]] = 0); // #def1
2224
int &refparam(int &param [[clang::lifetimebound]]);
2325
int &classparam(IntRef param [[clang::lifetimebound]]);
26+
const int *c = defaultparam(); // 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}}
27+
const int &defaultparam_array([[clang::lifetimebound]] const int *p = IntArray{1, 2, 3}); // #def2
2428

2529
// Do not diagnose non-void return types; they can still be lifetime-bound.
2630
long long ptrintcast(int &param [[clang::lifetimebound]]) {
@@ -34,13 +38,20 @@ namespace usage_ok {
3438
struct A {
3539
A();
3640
A(int);
41+
A(const char*, const int& def3 [[clang::lifetimebound]] = 0); // #def3
3742
int *class_member() [[clang::lifetimebound]];
3843
operator int*() [[clang::lifetimebound]];
44+
static const int &defaulted_param(const int &def4 [[clang::lifetimebound]] = 0); // #def4
45+
static const int &defaulted_param2(const int &def5 [[clang::lifetimebound]] = defaulted_param()); // #def5
3946
};
4047

4148
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}}
4249
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}}
4350
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}}
51+
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}}
52+
const int &s = A::defaulted_param(); // 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}}
53+
const int &t = A::defaulted_param2(); // 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}}
54+
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}}
4455

4556
void test_assignment() {
4657
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)