https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/233248
>From db2ead9b54115ca237f9a390575acab653bdd6cb Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Wed, 14 Oct 2026 23:24:32 +0530 Subject: [PATCH 1/2] [Clang][OpenMP] Fix crash on a braced-init-list in the init or increment of a transformed loop 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. LLM tools were used for this contribution. I've reviewed, built, and tested the change myself before pushing to GitHub. --- clang/docs/ReleaseNotes.md | 2 + clang/lib/Sema/SemaOpenMP.cpp | 26 ++++++++ clang/test/OpenMP/for_loop_messages.cpp | 11 ++++ clang/test/OpenMP/reverse_messages.cpp | 9 +++ .../OpenMP/unroll_codegen_braced_init.cpp | 18 ++++++ clang/test/OpenMP/unroll_messages.cpp | 61 +++++++++++++++++++ 6 files changed, 127 insertions(+) create mode 100644 clang/test/OpenMP/unroll_codegen_braced_init.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 1dc1764978ea27..a65ed7fcd5e878 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -1243,6 +1243,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@+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@+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@+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++) + ; + + // expected-error@+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@+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 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>(); } +} >From bf41815523914d9786c349524588107acd91fdb7 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Mon, 19 Oct 2026 08:24:11 +0530 Subject: [PATCH 2/2] [Clang][OpenMP] Handle an empty braced list as value-initialization instead of rejecting it --- clang/lib/Sema/SemaOpenMP.cpp | 36 +++++++++---------- clang/test/OpenMP/for_loop_messages.cpp | 1 - clang/test/OpenMP/reverse_messages.cpp | 1 - .../OpenMP/unroll_codegen_braced_init.cpp | 14 ++++++++ clang/test/OpenMP/unroll_messages.cpp | 3 +- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 7aa05e46f7bfaa..4521fb422c6da2 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -8674,13 +8674,18 @@ 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; +static ExprResult buildInitListValue(Sema &SemaRef, Expr *E) { + auto *ILE = dyn_cast<InitListExpr>(E); + if (!ILE) + return E; + if (ILE->isTransparent()) + return buildInitListValue(SemaRef, ILE->getInit(0)); + if (ILE->getNumInits() != 0 || !ILE->getType()->isScalarType()) + return E; + SourceLocation Loc = ILE->getLBraceLoc(); + return SemaRef.BuildCStyleCastExpr( + Loc, SemaRef.Context.getTrivialTypeSourceInfo(ILE->getType(), Loc), Loc, + SemaRef.ActOnIntegerConstant(Loc, 0).get()); } bool OpenMPIterationSpaceChecker::checkNonRectangularBound( @@ -8704,13 +8709,10 @@ bool OpenMPIterationSpaceChecker::setLCDeclAndLB(ValueDecl *NewLCDecl, 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(); + ExprResult LBVal = buildInitListValue(SemaRef, NewLB); + if (LBVal.isInvalid()) return true; - } + NewLB = LBVal.get(); } LCDecl = getCanonicalDecl(NewLCDecl); LCRef = NewLCRefExpr; @@ -8757,12 +8759,10 @@ bool OpenMPIterationSpaceChecker::setStep(Expr *NewStep, bool Subtract) { 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; + ExprResult StepVal = buildInitListValue(SemaRef, NewStep); + if (StepVal.isInvalid()) return true; - } + NewStep = StepVal.get(); } if (!NewStep->isValueDependent()) { // Check that the step is integer expression. diff --git a/clang/test/OpenMP/for_loop_messages.cpp b/clang/test/OpenMP/for_loop_messages.cpp index e10952887b0759..a6d92bfe43cf16 100644 --- a/clang/test/OpenMP/for_loop_messages.cpp +++ b/clang/test/OpenMP/for_loop_messages.cpp @@ -420,7 +420,6 @@ int test_iteration_spaces() { } #pragma omp parallel -// expected-error@+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) { } diff --git a/clang/test/OpenMP/reverse_messages.cpp b/clang/test/OpenMP/reverse_messages.cpp index 07a9010a584735..df62ce2902b142 100644 --- a/clang/test/OpenMP/reverse_messages.cpp +++ b/clang/test/OpenMP/reverse_messages.cpp @@ -40,7 +40,6 @@ void func() { for (int i = {0}; i < 7; ++i) ; - // expected-error@+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 index dae66b579b520d..034556cf090383 100644 --- a/clang/test/OpenMP/unroll_codegen_braced_init.cpp +++ b/clang/test/OpenMP/unroll_codegen_braced_init.cpp @@ -16,3 +16,17 @@ void braced_init() { 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 b21a71b37d2183..87fc6ded58f9d2 100644 --- a/clang/test/OpenMP/unroll_messages.cpp +++ b/clang/test/OpenMP/unroll_messages.cpp @@ -163,7 +163,6 @@ void braced_init() { for (j = {0}; j < 2; j++) ; - // expected-error@+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++) ; @@ -181,7 +180,7 @@ void braced_init() { for (int i = 0; i < 2; i -= {-1}) ; - // expected-error@+2 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'i'}} + // 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 += {}) ; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
