https://github.com/higher-performance updated https://github.com/llvm/llvm-project/pull/112047
>From 5a7cd2a5cf5275d408fac91dd03363137457b0e1 Mon Sep 17 00:00:00 2001 From: higher-performance <higher.performance.git...@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 | 31 ++++++++++++++++--- clang/test/SemaCXX/attr-lifetimebound.cpp | 11 +++++++ 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ce08bd60f76449..f49c874207585f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10107,6 +10107,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 f62e18543851c1..958f16a4c8a31e 100644 --- a/clang/lib/Sema/CheckExprLifetime.cpp +++ b/clang/lib/Sema/CheckExprLifetime.cpp @@ -194,6 +194,7 @@ struct IndirectLocalPathEntry { GslReferenceInit, GslPointerInit, GslPointerAssignment, + DefaultArg, } Kind; Expr *E; union { @@ -371,25 +372,33 @@ static bool implicitObjectParamIsLifetimeBound(const FunctionDecl *FD) { static void visitFunctionCallArguments(IndirectLocalPath &Path, Expr *Call, LocalVisitor Visit) { const FunctionDecl *Callee; - ArrayRef<Expr *> Args; + llvm::SmallVector<Expr *, 8> Args; if (auto *CE = dyn_cast<CallExpr>(Call)) { Callee = CE->getDirectCallee(); - Args = llvm::ArrayRef(CE->getArgs(), CE->getNumArgs()); + Args.append(CE->getArgs(), CE->getArgs() + CE->getNumArgs()); } else { auto *CCE = cast<CXXConstructExpr>(Call); Callee = CCE->getConstructor(); - Args = llvm::ArrayRef(CCE->getArgs(), CCE->getNumArgs()); + Args.append(CCE->getArgs(), CCE->getArgs() + CCE->getNumArgs()); } if (!Callee) return; + for (Expr *&Arg : Args) { + if (auto *DAE = dyn_cast<CXXDefaultArgExpr>(Arg)) { + Path.push_back( + {IndirectLocalPathEntry::DefaultArg, DAE, DAE->getParam()}); + Arg = DAE->getExpr(); + } + } + bool EnableGSLAnalysis = !Callee->getASTContext().getDiagnostics().isIgnored( diag::warn_dangling_lifetime_pointer, SourceLocation()); Expr *ObjectArg = nullptr; if (isa<CXXOperatorCallExpr>(Call) && Callee->isCXXInstanceMember()) { ObjectArg = Args[0]; - Args = Args.slice(1); + Args.erase(Args.begin() + 1); } else if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Call)) { ObjectArg = MCE->getImplicitObjectArgument(); } @@ -916,6 +925,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(); @@ -1221,7 +1233,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 @@ -1234,6 +1246,15 @@ static void checkExprLifetimeImpl(Sema &SemaRef, << nextPathEntryRange(Path, I + 1, L); break; } + + case IndirectLocalPathEntry::DefaultArg: { + const auto *DAE = cast<CXXDefaultArgExpr>(Elem.E); + SemaRef.Diag(DAE->getParam()->getDefaultArgRange().getBegin(), + diag::note_init_with_default_argument) + << DAE->getParam() << 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 0fb997a5671085..a1dba5c6a2473e 100644 --- a/clang/test/SemaCXX/attr-lifetimebound.cpp +++ b/clang/test/SemaCXX/attr-lifetimebound.cpp @@ -18,9 +18,13 @@ namespace usage_invalid { namespace usage_ok { struct IntRef { int *target; }; + using IntArray = int[]; + const int *defaultparam(const int &def1 [[clang::lifetimebound]] = 0); // #def1 int &refparam(int ¶m [[clang::lifetimebound]]); int &classparam(IntRef param [[clang::lifetimebound]]); + 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}} + const int &defaultparam_array([[clang::lifetimebound]] const int *p = IntArray{1, 2, 3}); // #def2 // Do not diagnose non-void return types; they can still be lifetime-bound. long long ptrintcast(int ¶m [[clang::lifetimebound]]) { @@ -34,13 +38,20 @@ namespace usage_ok { struct A { A(); A(int); + A(const char*, const int& def3 [[clang::lifetimebound]] = 0); // #def3 int *class_member() [[clang::lifetimebound]]; operator int*() [[clang::lifetimebound]]; + static const int &defaulted_param(const int &def4 [[clang::lifetimebound]] = 0); // #def4 + static const int &defaulted_param2(const int &def5 [[clang::lifetimebound]] = defaulted_param()); // #def5 }; 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}} 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}} 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}} + 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 = 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}} + 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}} + 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}} void test_assignment() { p = A().class_member(); // expected-warning {{object backing the pointer p will be destroyed at the end of the full-expression}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits