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
