Author: higher-performance
Date: 2024-10-15T13:41:52-07:00
New Revision: dd47920ce97e7db1ddeec34acdd9cb0ca7dcd7c4

URL: 
https://github.com/llvm/llvm-project/commit/dd47920ce97e7db1ddeec34acdd9cb0ca7dcd7c4
DIFF: 
https://github.com/llvm/llvm-project/commit/dd47920ce97e7db1ddeec34acdd9cb0ca7dcd7c4.diff

LOG: Make [[clang::lifetimebound]] work for expressions coming from default 
arguments (#112047)

Fixes #68596.

Added: 
    

Modified: 
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/Sema/CheckExprLifetime.cpp
    clang/test/SemaCXX/attr-lifetimebound.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e78acc8dc8c57b..e9111394bcd398 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 9b3894767d8629..8caeae5fcf9f8e 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}}
 


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to