[clang] f3b3187 - [clang][CFG] Fix lambda capture evaluation order (#211877)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jul 26 03:34:34 PDT 2026
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 at -1 {{Method called on moved-from object 's'}}
+ // expected-note at -2 {{Method called on moved-from object 's'}}
+ // expected-note at -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 {
More information about the cfe-commits
mailing list