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/6] [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/6] [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 += {}) ; >From c613f596f7e1d9d92229d2a8f7d59df64cb2670b Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Mon, 19 Oct 2026 23:13:40 +0530 Subject: [PATCH 3/6] [Clang][OpenMP] Convert the empty-list zero implicitly instead of with a C-style cast --- clang/lib/Sema/SemaOpenMP.cpp | 7 +++---- clang/test/OpenMP/for_loop_messages.cpp | 5 +++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 4521fb422c6da2..36a5262af30976 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -8682,10 +8682,9 @@ static ExprResult buildInitListValue(Sema &SemaRef, Expr *E) { 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()); + return SemaRef.PerformImplicitConversion( + SemaRef.ActOnIntegerConstant(ILE->getLBraceLoc(), 0).get(), + ILE->getType(), AssignmentAction::Converting); } bool OpenMPIterationSpaceChecker::checkNonRectangularBound( diff --git a/clang/test/OpenMP/for_loop_messages.cpp b/clang/test/OpenMP/for_loop_messages.cpp index a6d92bfe43cf16..bf3bda16f225ec 100644 --- a/clang/test/OpenMP/for_loop_messages.cpp +++ b/clang/test/OpenMP/for_loop_messages.cpp @@ -424,6 +424,11 @@ int test_iteration_spaces() { for (int a = {}; a < 10; ++a) { } +#pragma omp parallel +#pragma omp for + for (int(*p)[4] = {}; p < lb + 8; ++p) { + } + return 0; } >From 7a03d42b244aa23d5c3b696665ba385213bda3b3 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Mon, 26 Oct 2026 23:37:16 +0530 Subject: [PATCH 4/6] [Clang][OpenMP] Handle braced lists in the IRBuilder loop path and for non-integer counters --- clang/docs/ReleaseNotes.md | 4 +- clang/lib/Sema/SemaOpenMP.cpp | 60 ++++++++++----------- clang/test/OpenMP/irbuilder_braced_init.cpp | 16 ++++++ clang/test/OpenMP/unroll_messages.cpp | 22 ++++++++ 4 files changed, 69 insertions(+), 33 deletions(-) create mode 100644 clang/test/OpenMP/irbuilder_braced_init.cpp diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 9e6872c3b40522..acb086e27f45b1 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -1383,8 +1383,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) +- 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 31082cc2979f6e..85079d7cc3722a 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -6020,6 +6020,26 @@ static QualType getIntTypeForBitwidthOrBitInt(ASTContext &C, unsigned Bits, return Ty; } +static Expr *buildInitListValue(Sema &SemaRef, Expr *E) { + auto *ILE = dyn_cast<InitListExpr>(E); + if (!ILE) + return E; + if (ILE->isTransparent()) + return buildInitListValue(SemaRef, ILE->getInit(0)); + ASTContext &Context = SemaRef.Context; + QualType Ty = ILE->getType(); + bool IsEmptyScalar = ILE->getNumInits() == 0 && Ty->isScalarType(); + if (!SemaRef.getLangOpts().CPlusPlus) + return IsEmptyScalar ? new (Context) ImplicitValueInitExpr(Ty) : E; + if (!IsEmptyScalar && !Ty->isRecordType()) + return E; + return CXXFunctionalCastExpr::Create( + Context, Ty, VK_PRValue, + Context.getTrivialTypeSourceInfo(Ty, ILE->getLBraceLoc()), CK_NoOp, ILE, + /*Path=*/nullptr, SemaRef.CurFPFeatureOverrides(), SourceLocation(), + SourceLocation()); +} + static VarDecl *precomputeExpr(Sema &Actions, SmallVectorImpl<Stmt *> &BodyStmts, Expr *E, StringRef Name) { @@ -6364,11 +6384,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)) { @@ -6382,11 +6402,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"); @@ -8701,19 +8722,6 @@ static const StmtExpr *findStmtExpr(const Expr *E) { return nullptr; } -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; - return SemaRef.PerformImplicitConversion( - SemaRef.ActOnIntegerConstant(ILE->getLBraceLoc(), 0).get(), - ILE->getType(), AssignmentAction::Converting); -} - bool OpenMPIterationSpaceChecker::checkNonRectangularBound( const Expr *E, bool IsInitializer) const { // Such a bound is emitted at every use, its declarations cannot be. @@ -8734,12 +8742,7 @@ bool OpenMPIterationSpaceChecker::setLCDeclAndLB(ValueDecl *NewLCDecl, UB == nullptr && Step == nullptr && !TestIsLessOp && !TestIsStrictOp); if (!NewLCDecl || !NewLB || NewLB->containsErrors()) return true; - if (!SemaRef.CurContext->isDependentContext()) { - ExprResult LBVal = buildInitListValue(SemaRef, NewLB); - if (LBVal.isInvalid()) - return true; - NewLB = LBVal.get(); - } + NewLB = buildInitListValue(SemaRef, NewLB); LCDecl = getCanonicalDecl(NewLCDecl); LCRef = NewLCRefExpr; if (auto *CE = dyn_cast_or_null<CXXConstructExpr>(NewLB)) @@ -8784,12 +8787,7 @@ bool OpenMPIterationSpaceChecker::setStep(Expr *NewStep, bool Subtract) { assert(LCDecl != nullptr && LB != nullptr && Step == nullptr); if (!NewStep || NewStep->containsErrors()) return true; - if (!SemaRef.CurContext->isDependentContext()) { - ExprResult StepVal = buildInitListValue(SemaRef, NewStep); - if (StepVal.isInvalid()) - return true; - NewStep = StepVal.get(); - } + NewStep = buildInitListValue(SemaRef, NewStep); if (!NewStep->isValueDependent()) { // Check that the step is integer expression. SourceLocation StepLoc = NewStep->getBeginLoc(); diff --git a/clang/test/OpenMP/irbuilder_braced_init.cpp b/clang/test/OpenMP/irbuilder_braced_init.cpp new file mode 100644 index 00000000000000..68eb12f8a57f79 --- /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/unroll_messages.cpp b/clang/test/OpenMP/unroll_messages.cpp index 87fc6ded58f9d2..2dd2242e9b42b7 100644 --- a/clang/test/OpenMP/unroll_messages.cpp +++ b/clang/test/OpenMP/unroll_messages.cpp @@ -196,4 +196,26 @@ template <typename T> void braced_init_templ() { ; } 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) + ; +} } >From da75974034ed7e5ef781699232baf8cb8500f1af Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Sat, 31 Oct 2026 18:46:48 +0530 Subject: [PATCH 5/6] [Clang][OpenMP] Build the braced-list value with Sema instead of hand-made nodes --- clang/lib/Sema/SemaOpenMP.cpp | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 85079d7cc3722a..53f036377e53aa 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -6026,18 +6026,29 @@ static Expr *buildInitListValue(Sema &SemaRef, Expr *E) { return E; if (ILE->isTransparent()) return buildInitListValue(SemaRef, ILE->getInit(0)); - ASTContext &Context = SemaRef.Context; QualType Ty = ILE->getType(); bool IsEmptyScalar = ILE->getNumInits() == 0 && Ty->isScalarType(); - if (!SemaRef.getLangOpts().CPlusPlus) - return IsEmptyScalar ? new (Context) ImplicitValueInitExpr(Ty) : E; - if (!IsEmptyScalar && !Ty->isRecordType()) + 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; + } + InitListExpr *Syntactic = ILE->getSyntacticForm(); + if ((!IsEmptyScalar && !Ty->isRecordType()) || !Syntactic) + return E; + Sema::TentativeAnalysisScope Trap(SemaRef); + ExprResult List = CaptureVars(SemaRef).TransformExpr(Syntactic); + if (List.isInvalid()) return E; - return CXXFunctionalCastExpr::Create( - Context, Ty, VK_PRValue, - Context.getTrivialTypeSourceInfo(Ty, ILE->getLBraceLoc()), CK_NoOp, ILE, - /*Path=*/nullptr, SemaRef.CurFPFeatureOverrides(), SourceLocation(), - SourceLocation()); + Expr *ListExpr = List.get(); + ExprResult Value = SemaRef.BuildCXXTypeConstructExpr( + SemaRef.Context.getTrivialTypeSourceInfo(Ty, LBrace), LBrace, ListExpr, + ILE->getRBraceLoc(), /*ListInitialization=*/true); + return Value.isUsable() ? Value.get() : E; } static VarDecl *precomputeExpr(Sema &Actions, >From 4b31851894adf1a4561023d7108b2c82e7263a58 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Sun, 1 Nov 2026 23:54:53 +0530 Subject: [PATCH 6/6] [Clang][OpenMP] Use the list as written in `buildInitListValue` and look through implicit nodes --- clang/lib/Sema/SemaOpenMP.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index a12e4f5483f3c4..9acf3b3724527c 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -6040,7 +6040,7 @@ static QualType getIntTypeForBitwidthOrBitInt(ASTContext &C, unsigned Bits, } static Expr *buildInitListValue(Sema &SemaRef, Expr *E) { - auto *ILE = dyn_cast<InitListExpr>(E); + auto *ILE = dyn_cast<InitListExpr>(E->IgnoreImplicit()); if (!ILE) return E; if (ILE->isTransparent()) @@ -6056,16 +6056,12 @@ static Expr *buildInitListValue(Sema &SemaRef, Expr *E) { AssignmentAction::Converting); return Zero.isUsable() ? Zero.get() : E; } - InitListExpr *Syntactic = ILE->getSyntacticForm(); + Expr *Syntactic = ILE->getSyntacticForm(); if ((!IsEmptyScalar && !Ty->isRecordType()) || !Syntactic) return E; Sema::TentativeAnalysisScope Trap(SemaRef); - ExprResult List = CaptureVars(SemaRef).TransformExpr(Syntactic); - if (List.isInvalid()) - return E; - Expr *ListExpr = List.get(); ExprResult Value = SemaRef.BuildCXXTypeConstructExpr( - SemaRef.Context.getTrivialTypeSourceInfo(Ty, LBrace), LBrace, ListExpr, + SemaRef.Context.getTrivialTypeSourceInfo(Ty, LBrace), LBrace, Syntactic, ILE->getRBraceLoc(), /*ListInitialization=*/true); return Value.isUsable() ? Value.get() : E; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
