Skip to content

Commit 5a7cd2a

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

File tree

3 files changed

+39
-5
lines changed

3 files changed

+39
-5
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10107,6 +10107,8 @@ def note_lambda_capture_initializer : Note<
1010710107
" via initialization of lambda capture %0}1">;
1010810108
def note_init_with_default_member_initializer : Note<
1010910109
"initializing field %0 with default member initializer">;
10110+
def note_init_with_default_argument : Note<
10111+
"initializing parameter %0 with default argument">;
1011010112

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

clang/lib/Sema/CheckExprLifetime.cpp

Lines changed: 26 additions & 5 deletions
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 {
@@ -371,25 +372,33 @@ static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) {
371372
static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
372373
LocalVisitor Visit) {
373374
const FunctionDecl *Callee;
374-
ArrayRef<Expr *> Args;
375+
llvm::SmallVector<Expr *, 8> Args;
375376

376377
if (auto *CE = dyn_cast<CallExpr>(Call)) {
377378
Callee = CE->getDirectCallee();
378-
Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs());
379+
Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs());
379380
} else {
380381
auto *CCE = cast<CXXConstructExpr>(Call);
381382
Callee = CCE->getConstructor();
382-
Args = llvm::ArrayRef(CCE->getArgs(), CCE->getNumArgs());
383+
Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs());
383384
}
384385
if (!Callee)
385386
return;
386387

388+
for (Expr *&Arg : Args) {
389+
if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg)) {
390+
Path.push_back(
391+
{IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()});
392+
Arg = DAE->getExpr();
393+
}
394+
}
395+
387396
bool EnableGSLAnalysis = !Callee->getASTContext().getDiagnostics().isIgnored(
388397
diag::warn_dangling_lifetime_pointer, SourceLocation());
389398
Expr *ObjectArg = nullptr;
390399
if (isa<CXXOperatorCallExpr>(Call) && Callee->isCXXInstanceMember()) {
391400
ObjectArg = Args[0];
392-
Args = Args.slice(1);
401+
Args.erase(Args.begin() + 1);
393402
} else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) {
394403
ObjectArg = MCE->getImplicitObjectArgument();
395404
}
@@ -916,6 +925,9 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
916925
if (!Path[I].Capture->capturesVariable())
917926
continue;
918927
return Path[I].E->getSourceRange();
928+
929+
case IndirectLocalPathEntry::DefaultArg:
930+
return cast<CXXDefaultArgExpr>(Path[I].E)->getUsedLocation();
919931
}
920932
}
921933
return E->getSourceRange();
@@ -1221,7 +1233,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
12211233
break;
12221234
}
12231235

1224-
case IndirectLocalPathEntry::LambdaCaptureInit:
1236+
case IndirectLocalPathEntry::LambdaCaptureInit: {
12251237
if (!Elem.Capture->capturesVariable())
12261238
break;
12271239
// FIXME: We can't easily tell apart an init-capture from a nested
@@ -1234,6 +1246,15 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
12341246
<< nextPathEntryRange(Path, I + 1, L);
12351247
break;
12361248
}
1249+
1250+
case IndirectLocalPathEntry::DefaultArg: {
1251+
const auto *DAE = cast<CXXDefaultArgExpr>(Elem.E);
1252+
SemaRef.Diag(DAE->getParam()->getDefaultArgRange().getBegin(),
1253+
diag::note_init_with_default_argument)
1254+
<< DAE->getParam() << nextPathEntryRange(Path, I + 1, L);
1255+
break;
1256+
}
1257+
}
12371258
}
12381259

12391260
// 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)