[clang] [Clang][OpenMP] Fix crash on a `braced-init-list` in the `init` or increment of a transformed loop (PR #233248)

via cfe-commits cfe-commits at lists.llvm.org
Sun Oct 18 09:22:35 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Akash Manna (akash-manna-sky)

<details>
<summary>Changes</summary>

Fixes #<!-- -->202562

The loop analysis kept the initializer of `int i = {0}` as the lower bound as written, a braced-init-list, and did the same with the step of `i += {1}`. Plain loop directives got away with that, but `unroll partial`, `reverse`, `interchange`, `flatten` and `fuse` copy the trip count through a `TreeTransform`, which rebuilds the list without a type, so `{0} - 1` fails to build and the transform asserts.

The analysis now looks through a list that merely wraps one expression of the counter's type, so `int i = {0}`, `int i{0}`, `i = {0}` and `i += {1}` are analysed as `0` and `1`. A list that wraps nothing, like `{}`, is rejected as a non-canonical init or increment.

Assisted-by : LLM tools, reviewed and testes by me. 


---
Full diff: https://github.com/llvm/llvm-project/pull/233248.diff


6 Files Affected:

- (modified) clang/docs/ReleaseNotes.md (+2) 
- (modified) clang/lib/Sema/SemaOpenMP.cpp (+26) 
- (modified) clang/test/OpenMP/for_loop_messages.cpp (+11) 
- (modified) clang/test/OpenMP/reverse_messages.cpp (+9) 
- (added) clang/test/OpenMP/unroll_codegen_braced_init.cpp (+18) 
- (modified) clang/test/OpenMP/unroll_messages.cpp (+61) 


``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 3f0c48cf36644d..ae98d4dd777b9f 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1318,6 +1318,8 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
   matching the behavior of such maps outside a mapper.
 - Fixed a crash when the `safelen` and `simdlen` clauses of an OpenMP directive
   have arguments of different integer types. (#GH108367)
+- Fixed a crash when the init or increment of a loop under an OpenMP loop
+  transformation directive uses a braced list, such as `int i = {0}`. (#GH202562)
 - The `holds` clause on the `assume` directive now lowers side-effect-free
   conditions to `llvm.assume`, enabling downstream optimizations. Previously
   the clause was parsed but its condition was discarded without effect.
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index a3be416d2da24f..7aa05e46f7bfaa 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -8674,6 +8674,15 @@ static const StmtExpr *findStmtExpr(const Expr *E) {
   return nullptr;
 }
 
+static Expr *ignoreTransparentInitLists(Expr *E) {
+  while (auto *ILE = dyn_cast<InitListExpr>(E)) {
+    if (!ILE->isTransparent())
+      break;
+    E = ILE->getInit(0);
+  }
+  return E;
+}
+
 bool OpenMPIterationSpaceChecker::checkNonRectangularBound(
     const Expr *E, bool IsInitializer) const {
   // Such a bound is emitted at every use, its declarations cannot be.
@@ -8694,6 +8703,15 @@ bool OpenMPIterationSpaceChecker::setLCDeclAndLB(ValueDecl *NewLCDecl,
          UB == nullptr && Step == nullptr && !TestIsLessOp && !TestIsStrictOp);
   if (!NewLCDecl || !NewLB || NewLB->containsErrors())
     return true;
+  if (!SemaRef.CurContext->isDependentContext()) {
+    NewLB = ignoreTransparentInitLists(NewLB);
+    if (auto *ILE = dyn_cast<InitListExpr>(NewLB)) {
+      if (EmitDiags)
+        SemaRef.Diag(ILE->getBeginLoc(), diag::err_omp_loop_not_canonical_init)
+            << ILE->getSourceRange();
+      return true;
+    }
+  }
   LCDecl = getCanonicalDecl(NewLCDecl);
   LCRef = NewLCRefExpr;
   if (auto *CE = dyn_cast_or_null<CXXConstructExpr>(NewLB))
@@ -8738,6 +8756,14 @@ bool OpenMPIterationSpaceChecker::setStep(Expr *NewStep, bool Subtract) {
   assert(LCDecl != nullptr && LB != nullptr && Step == nullptr);
   if (!NewStep || NewStep->containsErrors())
     return true;
+  if (!SemaRef.CurContext->isDependentContext()) {
+    NewStep = ignoreTransparentInitLists(NewStep);
+    if (auto *ILE = dyn_cast<InitListExpr>(NewStep)) {
+      SemaRef.Diag(ILE->getBeginLoc(), diag::err_omp_loop_not_canonical_incr)
+          << ILE->getSourceRange() << LCDecl;
+      return true;
+    }
+  }
   if (!NewStep->isValueDependent()) {
     // Check that the step is integer expression.
     SourceLocation StepLoc = NewStep->getBeginLoc();
diff --git a/clang/test/OpenMP/for_loop_messages.cpp b/clang/test/OpenMP/for_loop_messages.cpp
index d2fbeea106a513..e10952887b0759 100644
--- a/clang/test/OpenMP/for_loop_messages.cpp
+++ b/clang/test/OpenMP/for_loop_messages.cpp
@@ -414,6 +414,17 @@ int test_iteration_spaces() {
   for (int a{0}; a < 10; ++a) {
   }
 
+#pragma omp parallel
+#pragma omp for
+  for (int a = {0}; a < 10; ++a) {
+  }
+
+#pragma omp parallel
+// expected-error at +2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
+#pragma omp for
+  for (int a = {}; a < 10; ++a) {
+  }
+
   return 0;
 }
 
diff --git a/clang/test/OpenMP/reverse_messages.cpp b/clang/test/OpenMP/reverse_messages.cpp
index 9636a70bf2753b..07a9010a584735 100644
--- a/clang/test/OpenMP/reverse_messages.cpp
+++ b/clang/test/OpenMP/reverse_messages.cpp
@@ -36,5 +36,14 @@ void func() {
   for (int i = 0; i < 7; ++i)
     ;
 
+  #pragma omp reverse
+  for (int i = {0}; i < 7; ++i)
+    ;
+
+  // expected-error at +2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
+  #pragma omp reverse
+  for (int i = {}; i < 7; ++i)
+    ;
+
 }
 
diff --git a/clang/test/OpenMP/unroll_codegen_braced_init.cpp b/clang/test/OpenMP/unroll_codegen_braced_init.cpp
new file mode 100644
index 00000000000000..dae66b579b520d
--- /dev/null
+++ b/clang/test/OpenMP/unroll_codegen_braced_init.cpp
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -verify -triple x86_64-pc-linux-gnu -fopenmp -emit-llvm %s -o - | FileCheck %s
+// expected-no-diagnostics
+
+extern "C" void body(int);
+
+// CHECK-LABEL: define {{.*}}void @_Z11braced_initv(
+// CHECK:         store i32 3, ptr %[[I:.+]], align 4
+// CHECK:         icmp sgt i32 %{{.+}}, 1
+// CHECK:         icmp slt i32 %{{.+}}, 4
+// CHECK:         %[[MUL:.+]] = mul nsw i32 %{{.+}}, 1
+// CHECK-NEXT:    %[[ADD:.+]] = add nsw i32 3, %[[MUL]]
+// CHECK-NEXT:    store i32 %[[ADD]], ptr %[[I]], align 4
+void braced_init() {
+#pragma omp for
+#pragma omp unroll partial(2)
+  for (int i = {3}; i < 7; i++)
+    body(i);
+}
diff --git a/clang/test/OpenMP/unroll_messages.cpp b/clang/test/OpenMP/unroll_messages.cpp
index 17d5ed83e162f2..b21a71b37d2183 100644
--- a/clang/test/OpenMP/unroll_messages.cpp
+++ b/clang/test/OpenMP/unroll_messages.cpp
@@ -137,3 +137,64 @@ void f(void) {
     ;
 }
 }
+
+namespace GH202562 {
+void braced_init() {
+#pragma omp unroll partial
+  for (int i = {0}; i < 2; i++)
+    ;
+
+#pragma omp unroll partial(2)
+  for (int i = {0}; i < 2; i++)
+    ;
+
+  // expected-warning at +2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
+#pragma omp unroll partial
+  for (int i{0}; i < 2; i++)
+    ;
+
+  // expected-warning at +2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
+#pragma omp unroll partial
+  for (int i(0); i < 2; i++)
+    ;
+
+  int j;
+#pragma omp unroll partial
+  for (j = {0}; j < 2; j++)
+    ;
+
+  // expected-error at +2 {{initialization clause of OpenMP for loop is not in canonical form ('var = init' or 'T var = init')}}
+#pragma omp unroll partial
+  for (int i = {}; i < 2; i++)
+    ;
+
+  // expected-warning at +2 {{too many braces around scalar initializer}}
+#pragma omp unroll partial
+  for (int i = {{0}}; i < 2; i++)
+    ;
+
+#pragma omp unroll partial
+  for (int i = 0; i < 2; i += {1})
+    ;
+
+#pragma omp unroll partial
+  for (int i = 0; i < 2; i -= {-1})
+    ;
+
+  // expected-error at +2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'i'}}
+#pragma omp unroll partial
+  for (int i = 0; i < 2; i += {})
+    ;
+
+#pragma omp unroll partial
+  for (int i = 0; i < 2; i++)
+    ;
+}
+
+template <typename T> void braced_init_templ() {
+#pragma omp unroll partial
+  for (T i = {0}; i < 2; i++)
+    ;
+}
+void braced_init_inst() { braced_init_templ<int>(); }
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/233248


More information about the cfe-commits mailing list