Skip to content

Commit 50d0368

Browse files
Make [[clang::lifetimebound]] work for expressions coming from default arguments
1 parent a643836 commit 50d0368

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ struct IndirectLocalPathEntry {
194194
GslReferenceInit,
195195
GslPointerInit,
196196
GslPointerAssignment,
197+
DefaultArg,
197198
} Kind;
198199
Expr *E;
199200
union {
@@ -532,6 +533,11 @@ static void visitLocalsRetainedByReferenceBinding(IndirectLocalPath &Path,
532533
}
533534
} while (Init != Old);
534535

536+
if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Init)) {
537+
Path.push_back({IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()});
538+
Init = DAE->getExpr();
539+
}
540+
535541
if (auto *MTE = dyn_cast<MaterializeTemporaryExpr>(Init)) {
536542
if (Visit(Path, Local(MTE), RK))
537543
visitLocalsRetainedByInitializer(Path, MTE->getSubExpr(), Visit, true);
@@ -917,6 +923,9 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
917923
continue;
918924
return Path[I].E->getSourceRange();
919925
}
926+
927+
case IndirectLocalPathEntry::DefaultArg:
928+
return cast<CXXDefaultArgExpr>(Path[I].E)->getUsedLocation();
920929
}
921930
return E->getSourceRange();
922931
}
@@ -1221,7 +1230,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
12211230
break;
12221231
}
12231232

1224-
case IndirectLocalPathEntry::LambdaCaptureInit:
1233+
case IndirectLocalPathEntry::LambdaCaptureInit: {
12251234
if (!Elem.Capture->capturesVariable())
12261235
break;
12271236
// FIXME: We can't easily tell apart an init-capture from a nested
@@ -1234,6 +1243,16 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
12341243
<< nextPathEntryRange(Path, I + 1, L);
12351244
break;
12361245
}
1246+
1247+
case IndirectLocalPathEntry::DefaultArg: {
1248+
const auto *DAE = cast<CXXDefaultArgExpr>(Elem.E);
1249+
SemaRef.Diag(DAE->getParam()->getDefaultArgRange().getBegin(),
1250+
diag::note_default_argument_declared_here)
1251+
<< DAE->getParam() << nextPathEntryRange(Path, I + 1, L);
1252+
break;
1253+
}
1254+
}
1255+
}
12371256
}
12381257

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

clang/test/SemaCXX/attr-lifetimebound.cpp

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

22+
const int *defaultparam(const int &def1 [[clang::lifetimebound]] = 0); // #def1
2223
int &refparam(int &param [[clang::lifetimebound]]);
2324
int &classparam(IntRef param [[clang::lifetimebound]]);
25+
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 {{default argument declared here}}
2426

2527
// Do not diagnose non-void return types; they can still be lifetime-bound.
2628
long long ptrintcast(int &param [[clang::lifetimebound]]) {
@@ -34,13 +36,17 @@ namespace usage_ok {
3436
struct A {
3537
A();
3638
A(int);
39+
A(const char*, const int& def2 [[clang::lifetimebound]] = 0); // #def2
3740
int *class_member() [[clang::lifetimebound]];
3841
operator int*() [[clang::lifetimebound]];
42+
const int *defaulted_param(const int &def3 [[clang::lifetimebound]] = 0); // #def3
3943
};
4044

4145
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}}
4246
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}}
4347
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}}
48+
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@#def2 {{default argument declared here}}
49+
const int *s = A().defaulted_param(); // expected-warning {{temporary whose address is used as value of local variable 's' will be destroyed at the end of the full-expression}} expected-note@#def3 {{default argument declared here}}
4450

4551
void test_assignment() {
4652
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)