Author: Balázs Benics
Date: 2026-07-26T11:34:29+01:00
New Revision: f3b31871e9b8e0fe07c92c91854861365ea189a4

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

LOG: [clang][CFG] Fix lambda capture evaluation order (#211877)

Previously, the captures were emitted in their spelling order - but in
the CFG that gets translared in a reversed order.
Because of this, we need to emit them in reversed order to get them
appear in their natural order.

Fixes rdar://183140177

Added: 
    clang/test/Analysis/lambda-capture-init-order.cpp

Modified: 
    clang/lib/Analysis/CFG.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index bb8e98a894dab..efd1da9c9a4db 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -3617,11 +3617,12 @@ CFGBlock *CFGBuilder::VisitBlockExpr(BlockExpr *E, 
AddStmtChoice asc) {
 CFGBlock *CFGBuilder::VisitLambdaExpr(LambdaExpr *E, AddStmtChoice asc) {
   CFGBlock *LastBlock = VisitNoRecurse(E, asc);
 
-  unsigned Idx = 0;
-  for (LambdaExpr::capture_init_iterator it = E->capture_init_begin(),
-                                         et = E->capture_init_end();
-       it != et; ++it, ++Idx) {
-    if (Expr *Init = *it) {
+  // Visit the capture initializers in reverse order so they appear in
+  // left-to-right (natural) order in the CFG.
+  unsigned Idx = E->capture_size();
+  for (Expr *Init : reverse(E->capture_inits())) {
+    --Idx;
+    if (Init) {
       // If the initializer is an ArrayInitLoopExpr, we want to extract the
       // initializer, that's used for each element.
       auto *AILEInit = extractElementInitializerFromNestedAILE(

diff  --git a/clang/test/Analysis/lambda-capture-init-order.cpp 
b/clang/test/Analysis/lambda-capture-init-order.cpp
new file mode 100644
index 0000000000000..051bab3fed8c8
--- /dev/null
+++ b/clang/test/Analysis/lambda-capture-init-order.cpp
@@ -0,0 +1,63 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG \
+// RUN:   -std=c++14 %s 2>&1 | FileCheck %s
+
+// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=core,cplusplus.Move \
+// RUN:   -analyzer-output=text -verify %s
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+struct S {
+  int v;
+  int use(S &) { return v; }
+};
+
+void safe_useThenMove() {
+  S s{7};
+  auto lam = [x = s.use(s), y = std::move(s)]() mutable {
+    (void)x;
+    (void)y;
+  };
+  lam(); // no-warning: the object is used before it is moved from.
+}
+
+// CHECK-LABEL: void safe_useThenMove()
+// CHECK:          1: 7
+// CHECK-NEXT:     2: {[B1.1]}
+// CHECK-NEXT:     3: S s{7};
+// CHECK-NEXT:     4: s
+// CHECK-NEXT:     5: [B1.4].use
+// CHECK-NEXT:     6: s
+// CHECK-NEXT:     7: [B1.5]([B1.6])
+// CHECK-NEXT:     8: std::move
+// CHECK-NEXT:     9: [B1.8] (ImplicitCastExpr, BuiltinFnToFnPtr, typename 
remove_reference<S &>::type &&(*)(struct S &))
+// CHECK-NEXT:    10: s
+// CHECK-NEXT:    11: [B1.9]([B1.10])
+// CHECK-NEXT:    12: [B1.11] (CXXConstructExpr{{.*}}, typename 
remove_reference<S &>::type)
+// CHECK-NEXT:    13: [x = [B1.7], y = [B1.12]]() mutable {
+
+void unsafe_moveThenUse() {
+  S s{7};
+  auto lam = [y = std::move(s), x = s.use(s)]() mutable {
+    // expected-warning@-1 {{Method called on moved-from object 's'}}
+    // expected-note@-2    {{Method called on moved-from object 's'}}
+    // expected-note@-3    {{Object 's' is moved}}
+    (void)x;
+    (void)y;
+  };
+  lam();
+}
+
+// CHECK-LABEL: void unsafe_moveThenUse()
+// CHECK:          1: 7
+// CHECK-NEXT:     2: {[B1.1]}
+// CHECK-NEXT:     3: S s{7};
+// CHECK-NEXT:     4: std::move
+// CHECK-NEXT:     5: [B1.4] (ImplicitCastExpr, BuiltinFnToFnPtr, typename 
remove_reference<S &>::type &&(*)(struct S &))
+// CHECK-NEXT:     6: s
+// CHECK-NEXT:     7: [B1.5]([B1.6])
+// CHECK-NEXT:     8: [B1.7] (CXXConstructExpr{{.*}}, typename 
remove_reference<S &>::type)
+// CHECK-NEXT:     9: s
+// CHECK-NEXT:    10: [B1.9].use
+// CHECK-NEXT:    11: s
+// CHECK-NEXT:    12: [B1.10]([B1.11])
+// CHECK-NEXT:    13: [y = [B1.8], x = [B1.12]]() mutable {


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

Reply via email to