Author: Akash Manna
Date: 2026-11-01T19:11:38Z
New Revision: 95870937da12763ab2ba0c9832bb9c4f17f5a140

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

LOG: [Clang][OpenMP] Fix crashes on a `braced-init-list` in the `init` or 
increment of a loop (#233248)

Fixes #202562

The loop analysis kept the braced-init-list of `int i = {0}` (and of `i
+= {1}`) as the bound or step as written. The loop transformations copy
the trip count through a `TreeTransform`, which rebuilds a list without
a type, so `{0} - 1` fails to build and the transform asserts. Pointer
and aggregate-iterator counters hit a related assert even under plain
`omp for`, and the IRBuilder path crashed on a braced step.

The analysis now never keeps a bare list: a list wrapping one expression
becomes that expression, an empty or class-type list becomes the
`T{...}` node Sema builds for a functional cast. The IRBuilder loop path
uses the same helper for its step.

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

Added: 
    clang/test/OpenMP/irbuilder_braced_init.cpp
    clang/test/OpenMP/unroll_codegen_braced_init.cpp

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/Sema/SemaOpenMP.cpp
    clang/test/OpenMP/for_loop_messages.cpp
    clang/test/OpenMP/reverse_messages.cpp
    clang/test/OpenMP/unroll_messages.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index d2df8fe262fbf..a367196a601bc 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1495,6 +1495,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 
diff erent integer types. (#GH108367)
+- Fixed a crash when the init or increment of an OpenMP loop 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 eb2ffea24bcb2..9acf3b3724527 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -6039,6 +6039,33 @@ static QualType getIntTypeForBitwidthOrBitInt(ASTContext 
&C, unsigned Bits,
   return Ty;
 }
 
+static Expr *buildInitListValue(Sema &SemaRef, Expr *E) {
+  auto *ILE = dyn_cast<InitListExpr>(E->IgnoreImplicit());
+  if (!ILE)
+    return E;
+  if (ILE->isTransparent())
+    return buildInitListValue(SemaRef, ILE->getInit(0));
+  QualType Ty = ILE->getType();
+  bool IsEmptyScalar = ILE->getNumInits() == 0 && Ty->isScalarType();
+  SourceLocation LBrace = ILE->getLBraceLoc();
+  if (!SemaRef.getLangOpts().CPlusPlus) {
+    if (!IsEmptyScalar)
+      return E;
+    ExprResult Zero = SemaRef.PerformImplicitConversion(
+        SemaRef.ActOnIntegerConstant(LBrace, 0).get(), Ty,
+        AssignmentAction::Converting);
+    return Zero.isUsable() ? Zero.get() : E;
+  }
+  Expr *Syntactic = ILE->getSyntacticForm();
+  if ((!IsEmptyScalar && !Ty->isRecordType()) || !Syntactic)
+    return E;
+  Sema::TentativeAnalysisScope Trap(SemaRef);
+  ExprResult Value = SemaRef.BuildCXXTypeConstructExpr(
+      SemaRef.Context.getTrivialTypeSourceInfo(Ty, LBrace), LBrace, Syntactic,
+      ILE->getRBraceLoc(), /*ListInitialization=*/true);
+  return Value.isUsable() ? Value.get() : E;
+}
+
 static VarDecl *precomputeExpr(Sema &Actions,
                                SmallVectorImpl<Stmt *> &BodyStmts, Expr *E,
                                StringRef Name) {
@@ -6383,11 +6410,11 @@ StmtResult SemaOpenMP::ActOnOpenMPCanonicalLoop(Stmt 
*AStmt) {
         llvm::APInt(Ctx.getIntWidth(LogicalTy), Direction, /*isSigned=*/true),
         LogicalTy, {});
   } else if (auto *IncBin = dyn_cast<BinaryOperator>(Inc)) {
+    Expr *IncRHS = buildInitListValue(SemaRef, IncBin->getRHS());
     if (IncBin->getOpcode() == BO_AddAssign) {
-      Step = IncBin->getRHS();
+      Step = IncRHS;
     } else if (IncBin->getOpcode() == BO_SubAssign) {
-      Step = AssertSuccess(
-          SemaRef.BuildUnaryOp(nullptr, {}, UO_Minus, IncBin->getRHS()));
+      Step = AssertSuccess(SemaRef.BuildUnaryOp(nullptr, {}, UO_Minus, 
IncRHS));
     } else
       llvm_unreachable("unhandled binary increment operator");
   } else if (auto *CondCXXOp = dyn_cast<CXXOperatorCallExpr>(Inc)) {
@@ -6401,11 +6428,12 @@ StmtResult SemaOpenMP::ActOnOpenMPCanonicalLoop(Stmt 
*AStmt) {
           Ctx, llvm::APInt(Ctx.getIntWidth(LogicalTy), -1), LogicalTy, {});
       break;
     case OO_PlusEqual:
-      Step = CondCXXOp->getArg(1);
+      Step = buildInitListValue(SemaRef, CondCXXOp->getArg(1));
       break;
     case OO_MinusEqual:
-      Step = AssertSuccess(
-          SemaRef.BuildUnaryOp(nullptr, {}, UO_Minus, CondCXXOp->getArg(1)));
+      Step = AssertSuccess(SemaRef.BuildUnaryOp(
+          nullptr, {}, UO_Minus,
+          buildInitListValue(SemaRef, CondCXXOp->getArg(1))));
       break;
     default:
       llvm_unreachable("unhandled overloaded increment operator");
@@ -8885,6 +8913,7 @@ bool 
OpenMPIterationSpaceChecker::setLCDeclAndLB(ValueDecl *NewLCDecl,
          UB == nullptr && Step == nullptr && !TestIsLessOp && !TestIsStrictOp);
   if (!NewLCDecl || !NewLB || NewLB->containsErrors())
     return true;
+  NewLB = buildInitListValue(SemaRef, NewLB);
   LCDecl = getCanonicalDecl(NewLCDecl);
   LCRef = NewLCRefExpr;
   if (auto *CE = dyn_cast_or_null<CXXConstructExpr>(NewLB))
@@ -8929,6 +8958,7 @@ bool OpenMPIterationSpaceChecker::setStep(Expr *NewStep, 
bool Subtract) {
   assert(LCDecl != nullptr && LB != nullptr && Step == nullptr);
   if (!NewStep || NewStep->containsErrors())
     return true;
+  NewStep = buildInitListValue(SemaRef, NewStep);
   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 d2fbeea106a51..bf3bda16f225e 100644
--- a/clang/test/OpenMP/for_loop_messages.cpp
+++ b/clang/test/OpenMP/for_loop_messages.cpp
@@ -414,6 +414,21 @@ 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
+#pragma omp for
+  for (int a = {}; a < 10; ++a) {
+  }
+
+#pragma omp parallel
+#pragma omp for
+  for (int(*p)[4] = {}; p < lb + 8; ++p) {
+  }
+
   return 0;
 }
 

diff  --git a/clang/test/OpenMP/irbuilder_braced_init.cpp 
b/clang/test/OpenMP/irbuilder_braced_init.cpp
new file mode 100644
index 0000000000000..68eb12f8a57f7
--- /dev/null
+++ b/clang/test/OpenMP/irbuilder_braced_init.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -fopenmp -fopenmp-enable-irbuilder -x c++ -triple 
x86_64-unknown-unknown -fsyntax-only -verify %s
+
+void braced_init(int n) {
+#pragma omp for
+  for (int i = {0}; i < n; i += {1})
+    ;
+
+#pragma omp for
+  for (int i = {}; i < n; i -= {-1})
+    ;
+
+  // expected-error@+2 {{increment expression must cause 'i' to increase on 
each iteration of OpenMP for loop}} expected-note@+2 {{loop step is expected to 
be positive due to this condition}}
+#pragma omp for
+  for (int i = 0; i < n; i += {})
+    ;
+}

diff  --git a/clang/test/OpenMP/reverse_messages.cpp 
b/clang/test/OpenMP/reverse_messages.cpp
index 9636a70bf2753..df62ce2902b14 100644
--- a/clang/test/OpenMP/reverse_messages.cpp
+++ b/clang/test/OpenMP/reverse_messages.cpp
@@ -36,5 +36,13 @@ void func() {
   for (int i = 0; i < 7; ++i)
     ;
 
+  #pragma omp reverse
+  for (int i = {0}; i < 7; ++i)
+    ;
+
+  #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 0000000000000..034556cf09038
--- /dev/null
+++ b/clang/test/OpenMP/unroll_codegen_braced_init.cpp
@@ -0,0 +1,32 @@
+// 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);
+}
+
+// CHECK-LABEL: define {{.*}}void @_Z10empty_initv(
+// CHECK:         store i32 0, 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 0, %[[MUL]]
+// CHECK-NEXT:    store i32 %[[ADD]], ptr %[[I]], align 4
+void empty_init() {
+#pragma omp for
+#pragma omp unroll partial(2)
+  for (int i = {}; i < 4; i++)
+    body(i);
+}

diff  --git a/clang/test/OpenMP/unroll_messages.cpp 
b/clang/test/OpenMP/unroll_messages.cpp
index 17d5ed83e162f..2dd2242e9b42b 100644
--- a/clang/test/OpenMP/unroll_messages.cpp
+++ b/clang/test/OpenMP/unroll_messages.cpp
@@ -137,3 +137,85 @@ 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@+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@+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++)
+    ;
+
+#pragma omp unroll partial
+  for (int i = {}; i < 2; i++)
+    ;
+
+  // expected-warning@+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@+2 {{increment expression must cause 'i' to increase on 
each iteration of OpenMP for loop}} expected-note@+2 {{loop step is expected to 
be positive due to this condition}}
+#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>(); }
+
+struct It {
+  int *p;
+  It &operator++() { ++p; return *this; }
+  It &operator+=(int n) { p += n; return *this; }
+  friend bool operator<(It a, It b) { return a.p < b.p; }
+  friend int operator-(It a, It b) { return a.p - b.p; }
+  friend It operator+(It a, int n) { return {a.p + n}; }
+};
+void aggregate_counter(int *q) {
+#pragma omp for
+  for (It it = {q}; it < It{q + 4}; ++it)
+    ;
+
+#pragma omp unroll partial
+  for (It it = {q}; it < It{q + 4}; ++it)
+    ;
+
+#pragma omp unroll partial
+  for (It it = {}; it < It{q + 4}; ++it)
+    ;
+}
+}


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

Reply via email to