https://github.com/Inconnu08 updated https://github.com/llvm/llvm-project/pull/234467
>From a3be0abd1d651f16b1b027107f3022f086f25c0c Mon Sep 17 00:00:00 2001 From: inconnu08 <[email protected]> Date: Wed, 21 Oct 2026 22:51:43 -0400 Subject: [PATCH] [clang][CFG] Fix range-for iterator initialization order --- clang/lib/Analysis/CFG.cpp | 5 +- .../Analysis/auto-obj-dtors-cfg-output.cpp | 12 ++--- .../Analysis/cfg-range-for-init-order.cpp | 51 +++++++++++++++++++ clang/test/Analysis/scopes-cfg-output.cpp | 22 ++++---- .../Sema/LifetimeSafety/invalidations.cpp | 4 +- 5 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 clang/test/Analysis/cfg-range-for-init-order.cpp diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 556e08f5c1df80..4d6bf0ba8bd9b5 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -4987,10 +4987,11 @@ CFGBlock *CFGBuilder::VisitCXXForRangeStmt(CXXForRangeStmt *S) { // false branch). addSuccessor(ConditionBlock, KnownVal.isTrue() ? nullptr : LoopSuccessor); - // Add the initialization statements. + // Add the initialization statements in reverse execution order so that + // begin is initialized before end. Block = createBlock(); - addStmt(S->getBeginStmt()); addStmt(S->getEndStmt()); + addStmt(S->getBeginStmt()); CFGBlock *Head = addStmt(S->getRangeStmt()); if (S->getInit()) Head = addStmt(S->getInit()); diff --git a/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp b/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp index 96b9a5508cc084..4a39b70f8ab335 100644 --- a/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp +++ b/clang/test/Analysis/auto-obj-dtors-cfg-output.cpp @@ -1117,12 +1117,12 @@ void test_for_implicit_scope() { // CHECK-NEXT: 3: auto &&__range1 = nums; // CHECK-NEXT: 4: __range1 // CHECK-NEXT: 5: [B4.4] (ImplicitCastExpr, ArrayToPointerDecay, int *) -// CHECK-NEXT: 6: 4{{L*}} -// CHECK-NEXT: 7: [B4.5] + [B4.6] -// CHECK-NEXT: 8: auto __end1 = __range1 + 4{{L*}}; -// CHECK-NEXT: 9: __range1 -// CHECK-NEXT: 10: [B4.9] (ImplicitCastExpr, ArrayToPointerDecay, int *) -// CHECK-NEXT: 11: auto __begin1 = __range1; +// CHECK-NEXT: 6: auto __begin1 = __range1; +// CHECK-NEXT: 7: __range1 +// CHECK-NEXT: 8: [B4.7] (ImplicitCastExpr, ArrayToPointerDecay, int *) +// CHECK-NEXT: 9: 4{{L*}} +// CHECK-NEXT: 10: [B4.8] + [B4.9] +// CHECK-NEXT: 11: auto __end1 = __range1 + 4{{L*}}; // CHECK-NEXT: Preds (1): B5 // CHECK-NEXT: Succs (1): B1 // CHECK: [B0 (EXIT)] diff --git a/clang/test/Analysis/cfg-range-for-init-order.cpp b/clang/test/Analysis/cfg-range-for-init-order.cpp new file mode 100644 index 00000000000000..eb93ffc53d5273 --- /dev/null +++ b/clang/test/Analysis/cfg-range-for-init-order.cpp @@ -0,0 +1,51 @@ +// RUN: %clang_analyze_cc1 -std=c++17 -analyzer-checker=debug.DumpCFG -analyzer-config cfg-rich-constructors=false %s 2>&1 | FileCheck %s +// RUN: %clang_analyze_cc1 -std=c++17 -analyzer-checker=debug.DumpCFG -analyzer-config cfg-rich-constructors=true %s 2>&1 | FileCheck %s + +struct Begin { + ~Begin(); +}; + +struct End { + ~End(); +}; + +bool operator!=(const Begin &, const End &); +void operator++(Begin &); +int &operator*(const Begin &); + +struct Range { + Begin begin(); + End end(); +}; + +// The cleanup block appears before the initialization block in the dump. +// Iterators must initialize begin before end and be destroyed in reverse order. +// +// CHECK-LABEL: void range_for_init_order( +// CHECK: [B1] +// CHECK-NEXT: 1: [B5.12].~End() (Implicit destructor) +// CHECK-NEXT: 2: [B5.7].~Begin() (Implicit destructor) +// CHECK-NEXT: Preds (1): B2 +// CHECK-NEXT: Succs (1): B0 +// CHECK-EMPTY: +// +// CHECK: [B5] +// CHECK-NEXT: 1: range +// CHECK-NEXT: 2: auto &&__range1 = range; +// CHECK-NEXT: 3: __range1 +// CHECK-NEXT: 4: [B5.3].begin +// CHECK-NEXT: 5: [B5.4](){{.*}} +// CHECK-NEXT: 6: [B5.5] (BindTemporary) +// CHECK-NEXT: 7: auto __begin1 = __range1.begin(); +// CHECK-NEXT: 8: __range1 +// CHECK-NEXT: 9: [B5.8].end +// CHECK-NEXT: 10: [B5.9](){{.*}} +// CHECK-NEXT: 11: [B5.10] (BindTemporary) +// CHECK-NEXT: 12: auto __end1 = __range1.end(); +// CHECK-NEXT: Preds (1): B6 +// CHECK-NEXT: Succs (1): B2 +// CHECK-EMPTY: +void range_for_init_order(Range range) { + for (int &value : range) + (void)value; +} diff --git a/clang/test/Analysis/scopes-cfg-output.cpp b/clang/test/Analysis/scopes-cfg-output.cpp index ac8ff1179a9945..13c6c42555ffae 100644 --- a/clang/test/Analysis/scopes-cfg-output.cpp +++ b/clang/test/Analysis/scopes-cfg-output.cpp @@ -890,7 +890,7 @@ void test_for_compound_and_break() { // CHECK-NEXT: 3: __end1 // CHECK-NEXT: 4: [B2.3] (ImplicitCastExpr, LValueToRValue, A *) // CHECK-NEXT: 5: [B2.2] != [B2.4] -// CHECK-NEXT: T: for (auto &i : [B5.4]) +// CHECK-NEXT: T: for (auto &i : [B5.5]) // CHECK: [B4.11]; // CHECK: Preds (2): B3 B5 // CHECK-NEXT: Succs (2): B4 B1 @@ -918,17 +918,19 @@ void test_for_compound_and_break() { // CHECK-NEXT: 1: CFGScopeBegin(a) // CHECK-NEXT: 2: (CXXConstructExpr, [B5.3], A[10]) // CHECK-NEXT: 3: A a[10]; -// CHECK-NEXT: 4: a -// CHECK-NEXT: 5: auto &&__range1 = a; -// CHECK-NEXT: 6: CFGScopeBegin(__end1) -// CHECK-NEXT: 7: __range1 -// CHECK-NEXT: 8: [B5.7] (ImplicitCastExpr, ArrayToPointerDecay, A *) -// CHECK-NEXT: 9: 10 -// CHECK-NEXT: 10: [B5.8] + [B5.9] -// CHECK-NEXT: 11: auto __end1 = __range1 + 10 +// CHECK-NEXT: 4: CFGScopeBegin(__range1) +// CHECK-NEXT: 5: a +// CHECK-NEXT: 6: auto &&__range1 = a; +// CHECK-NEXT: 7: CFGScopeBegin(__begin1) +// CHECK-NEXT: 8: __range1 +// CHECK-NEXT: 9: [B5.8] (ImplicitCastExpr, ArrayToPointerDecay, A *) +// CHECK-NEXT: 10: auto __begin1 = __range1; +// CHECK-NEXT: 11: CFGScopeBegin(__end1) // CHECK-NEXT: 12: __range1 // CHECK-NEXT: 13: [B5.12] (ImplicitCastExpr, ArrayToPointerDecay, A *) -// CHECK-NEXT: 14: auto __begin1 = __range1; +// CHECK-NEXT: 14: 10{{L*}} +// CHECK-NEXT: 15: [B5.13] + [B5.14] +// CHECK-NEXT: 16: auto __end1 = __range1 + 10{{L*}}; // CHECK-NEXT: Preds (1): B6 // CHECK-NEXT: Succs (1): B2 // CHECK: [B0 (EXIT)] diff --git a/clang/test/Sema/LifetimeSafety/invalidations.cpp b/clang/test/Sema/LifetimeSafety/invalidations.cpp index f96e8259b64ebc..4bed13b2cc844d 100644 --- a/clang/test/Sema/LifetimeSafety/invalidations.cpp +++ b/clang/test/Sema/LifetimeSafety/invalidations.cpp @@ -164,7 +164,7 @@ void NoIteratorInvalidationInAWhileLoopErase(std::unordered_map<int, int> mp) { void IteratorInvalidationInAForeachLoop(std::vector<int> v) { for (int& x : v) { // expected-warning {{parameter 'v' is later invalidated}} \ // expected-note {{later used here}} \ - // expected-note {{result of call to 'end' aliases the storage of parameter 'v'}} + // expected-note {{result of call to 'begin' aliases the storage of parameter 'v'}} if (x % 2 == 0) { v.erase(std::find(v.begin(), v.end(), 1)); // expected-note {{parameter 'v' is invalidated here}} } @@ -556,7 +556,7 @@ void IteratorFromPointerToContainerIsInvalidated() { void ChangingRegionOwnedByContainerIsOk() { std::vector<std::string> subdirs; for (std::string& path : subdirs) // expected-warning {{local variable 'subdirs' is later invalidated}} expected-note {{later used here}} \ - // expected-note {{result of call to 'end' aliases the storage of local variable 'subdirs'}} + // expected-note {{result of call to 'begin' aliases the storage of local variable 'subdirs'}} path = std::string(); // expected-note {{local variable 'subdirs' is invalidated here}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
