[clang] Make [[clang::lifetimebound]] work for expressions coming from default arguments (PR #112047)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 15 13:12:03 PDT 2024
https://github.com/higher-performance updated https://github.com/llvm/llvm-project/pull/112047
>From 604835395cd4006cb0344e94553191035fe4cbfb Mon Sep 17 00:00:00 2001
From: higher-performance <higher.performance.github at gmail.com>
Date: Fri, 11 Oct 2024 17:09:13 -0400
Subject: [PATCH] Make [[clang::lifetimebound]] work for expressions coming
from default arguments
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 ++
clang/lib/Sema/CheckExprLifetime.cpp | 29 ++++++++++++++---
clang/test/SemaCXX/attr-lifetimebound.cpp | 31 +++++++++++++++++++
3 files changed, 58 insertions(+), 4 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c709795e7b21d8..4c54341868fe67 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10124,6 +10124,8 @@ def note_lambda_capture_initializer : Note<
" via initialization of lambda capture %0}1">;
def note_init_with_default_member_initializer : Note<
"initializing field %0 with default member initializer">;
+def note_init_with_default_argument : Note<
+ "initializing parameter %0 with default argument">;
// Check for initializing a member variable with the address or a reference to
// a constructor parameter.
diff --git a/clang/lib/Sema/CheckExprLifetime.cpp b/clang/lib/Sema/CheckExprLifetime.cpp
index 009b8d000e6b0e..0e53089eb05585 100644
--- a/clang/lib/Sema/CheckExprLifetime.cpp
+++ b/clang/lib/Sema/CheckExprLifetime.cpp
@@ -198,6 +198,7 @@ struct IndirectLocalPathEntry {
GslReferenceInit,
GslPointerInit,
GslPointerAssignment,
+ DefaultArg,
} Kind;
Expr *E;
union {
@@ -609,15 +610,22 @@ static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call,
for (unsigned I = 0,
N = std::min<unsigned>(Callee->getNumParams(), Args.size());
I != N; ++I) {
+ Expr *Arg = Args[I];
+ RevertToOldSizeRAII RAII(Path);
+ if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg)) {
+ Path.push_back(
+ {IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()});
+ Arg = DAE->getExpr();
+ }
if (CheckCoroCall || Callee->getParamDecl(I)->hasAttr<LifetimeBoundAttr>())
- VisitLifetimeBoundArg(Callee->getParamDecl(I), Args[I]);
+ VisitLifetimeBoundArg(Callee->getParamDecl(I), Arg);
else if (EnableGSLAnalysis && I == 0) {
// Perform GSL analysis for the first argument
if (shouldTrackFirstArgument(Callee)) {
- VisitGSLPointerArg(Callee, Args[0]);
+ VisitGSLPointerArg(Callee, Arg);
} else if (auto *Ctor = dyn_cast<CXXConstructExpr>(Call);
Ctor && shouldTrackFirstArgumentForConstructor(Ctor)) {
- VisitGSLPointerArg(Ctor->getConstructor(), Args[0]);
+ VisitGSLPointerArg(Ctor->getConstructor(), Arg);
}
}
}
@@ -1060,6 +1068,9 @@ static SourceRange nextPathEntryRange(const IndirectLocalPath &Path, unsigned I,
if (!Path[I].Capture->capturesVariable())
continue;
return Path[I].E->getSourceRange();
+
+ case IndirectLocalPathEntry::DefaultArg:
+ return cast<CXXDefaultArgExpr>(Path[I].E)->getUsedLocation();
}
}
return E->getSourceRange();
@@ -1370,7 +1381,7 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
break;
}
- case IndirectLocalPathEntry::LambdaCaptureInit:
+ case IndirectLocalPathEntry::LambdaCaptureInit: {
if (!Elem.Capture->capturesVariable())
break;
// FIXME: We can't easily tell apart an init-capture from a nested
@@ -1383,6 +1394,16 @@ static void checkExprLifetimeImpl(Sema &SemaRef,
<< nextPathEntryRange(Path, I + 1, L);
break;
}
+
+ case IndirectLocalPathEntry::DefaultArg: {
+ const auto *DAE = cast<CXXDefaultArgExpr>(Elem.E);
+ const ParmVarDecl *Param = DAE->getParam();
+ SemaRef.Diag(Param->getDefaultArgRange().getBegin(),
+ diag::note_init_with_default_argument)
+ << Param << nextPathEntryRange(Path, I + 1, L);
+ break;
+ }
+ }
}
// We didn't lifetime-extend, so don't go any further; we don't need more
diff --git a/clang/test/SemaCXX/attr-lifetimebound.cpp b/clang/test/SemaCXX/attr-lifetimebound.cpp
index bdc58171917375..d04bbb32433fb5 100644
--- a/clang/test/SemaCXX/attr-lifetimebound.cpp
+++ b/clang/test/SemaCXX/attr-lifetimebound.cpp
@@ -107,6 +107,37 @@ namespace std {
using std::operator""s;
using std::operator""sv;
+namespace default_args {
+ using IntArray = int[];
+ const int *defaultparam1(const int &def1 [[clang::lifetimebound]] = 0); // #def1
+ const int &defaultparam_array([[clang::lifetimebound]] const int *p = IntArray{1, 2, 3}); // #def2
+ struct A {
+ A(const char *, const int &def3 [[clang::lifetimebound]] = 0); // #def3
+ };
+ const int &defaultparam2(const int &def4 [[clang::lifetimebound]] = 0); // #def4
+ const int &defaultparam3(const int &def5 [[clang::lifetimebound]] = defaultparam2(), const int &def6 [[clang::lifetimebound]] = 0); // #def5 #def6
+ std::string_view defaultparam4(std::string_view s [[clang::lifetimebound]] = std::string()); // #def7
+
+ const int *test_default_args() {
+ 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}}
+ 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}}
+ 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}}
+ 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}} expected-warning {{temporary bound to local reference 't' will be destroyed at the end of the full-expression}} expected-note@#def6 {{initializing parameter 'def6' with default argument}}
+ 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}}
+ int local;
+ const int &v = defaultparam2(local); // no warning
+ const int &w = defaultparam2(1); // expected-warning {{temporary bound to local reference 'w' will be destroyed at the end of the full-expression}}
+ if (false) {
+ return &defaultparam2(); // expected-warning {{returning address of local temporary object}}
+ }
+ if (false) {
+ return &defaultparam2(0); // expected-warning {{returning address of local temporary object}} expected-note@#def4 {{initializing parameter 'def4' with default argument}}
+ }
+ std::string_view sv = defaultparam4(); // expected-warning {{temporary whose address is used as value of local variable 'sv' will be destroyed at the end of the full-expression}} expected-note@#def7 {{initializing parameter 's' with default argument}}
+ return nullptr;
+ }
+} // namespace default_args
+
namespace p0936r0_examples {
std::string_view s = "foo"s; // expected-warning {{temporary}}
More information about the cfe-commits
mailing list