Author: NeKon69
Date: 2026-03-23T17:53:34+01:00
New Revision: b32b31e6a7d003a96d8110c3debd891ba6977d25

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

LOG: [LifetimeSafety] Fix compiler crash with `static operator()` (#187853)

This PR removes the first argument from the `Args` list (which is `S()`)
before doing lifetime safety checks to ensure correct indexing.

It also adds a test to prevent regressions in the future

Fixes #187426
<details>
<summary>Bug details</summary>

When calling a `static operator()` directly (with `S()(...)`), we also
store `S()` in `Args` as the first argument, so all indexing is off by
one.
The most interesting part is that `S::operator()(...)` works correctly
and does not add `S()` at the beginning of the argument list, so it does
not crash during lifetime checks.
This solution is probably not the cleanest, but I would love to hear
feedback on where to put it!
</details>

Added: 
    

Modified: 
    clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
    clang/test/Sema/warn-lifetime-safety.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp 
b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
index 3259505584c9f..fc1e311b4920c 100644
--- a/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
+++ b/clang/lib/Analysis/LifetimeSafety/FactsGenerator.cpp
@@ -20,6 +20,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/Origins.h"
 #include "clang/Analysis/Analyses/PostOrderCFGView.h"
 #include "clang/Analysis/CFG.h"
+#include "clang/Basic/OperatorKinds.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/Casting.h"
@@ -401,7 +402,13 @@ void FactsGenerator::VisitCXXOperatorCallExpr(const 
CXXOperatorCallExpr *OCE) {
     handleAssignment(OCE->getArg(0), OCE->getArg(1));
     return;
   }
-  VisitCallExpr(OCE);
+
+  ArrayRef Args = {OCE->getArgs(), OCE->getNumArgs()};
+  // For `static operator()`, the first argument is the object argument,
+  // remove it from the argument list to avoid off-by-one errors.
+  if (OCE->getOperator() == OO_Call && OCE->getDirectCallee()->isStatic())
+    Args = Args.slice(1);
+  handleFunctionCall(OCE, OCE->getDirectCallee(), Args);
 }
 
 void FactsGenerator::VisitCXXFunctionalCastExpr(

diff  --git a/clang/test/Sema/warn-lifetime-safety.cpp 
b/clang/test/Sema/warn-lifetime-safety.cpp
index bd09bb70e9a11..7013b8ceebdaa 100644
--- a/clang/test/Sema/warn-lifetime-safety.cpp
+++ b/clang/test/Sema/warn-lifetime-safety.cpp
@@ -2102,3 +2102,27 @@ void pointer_in_array_use_after_scope() {
 }
 
 } // namespace array
+
+namespace static_call_operator {
+// https://github.com/llvm/llvm-project/issues/187426
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++23-extensions"
+
+struct S {
+  static S operator()(int, int&&);
+  static S& operator()(std::string&&,
+                       const int& a [[clang::lifetimebound]],
+                       const int& b [[clang::lifetimebound]]);
+};
+
+void indexing_with_static_operator() {
+  S()(1, 2);
+  S& x = S()("1",
+             2,  // expected-warning {{object whose reference is captured does 
not live long enough}} expected-note {{destroyed here}}
+             3); // expected-warning {{object whose reference is captured does 
not live long enough}} expected-note {{destroyed here}}
+
+  (void)x; // expected-note 2 {{later used here}}
+
+}
+} // namespace static_call_operator


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to