[clang] 3621db6 - [Clang][OpenMP] Fix crash on `_BitInt` loop bounds and counters in loop analysis (#226464)

via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 25 09:57:45 PDT 2026


Author: Akash Manna
Date: 2026-09-25T12:57:39-04:00
New Revision: 3621db6aff701dcd9e6ff54ea281d24ee2f43448

URL: https://github.com/llvm/llvm-project/commit/3621db6aff701dcd9e6ff54ea281d24ee2f43448
DIFF: https://github.com/llvm/llvm-project/commit/3621db6aff701dcd9e6ff54ea281d24ee2f43448.diff

LOG: [Clang][OpenMP] Fix crash on `_BitInt` loop bounds and counters in loop analysis (#226464)

Fixes #140074

When building the iteration count of an OpenMP loop, Sema derives a few
integer types from bit widths, e.g. to promote the upper bound to an
unsigned type as wide as the wider bound so that `upper - lower` cannot
overflow. Those types came from `ASTContext::getIntTypeForBitwidth`,
which only knows the standard integer widths and returns a null type for
anything else. With a bound or loop counter of type `_BitInt(931)` the
request was for a 960-bit type, the result was null, and it went
straight into `PerformImplicitConversion`, which asserted. The
`collapse(2)` in the reduced reproducer is incidental; a plain `#pragma
omp for` on the same loop crashes the same way.

The loop analysis now goes through a small helper that asks for the
standard type first and otherwise builds the `_BitInt` type of that
width and signedness with `ASTContext::getBitIntType`. It is used at the
three places that derive a type from a loop width: the upper-bound
promotion in `calculateNumIters`, the conversion to the loop variable's
width in `buildNumIterations`, and the logical iteration type in
`ActOnOpenMPCanonicalLoop`. Wide `_BitInt` loop variables are then
narrowed to the runtime's 64-bit iteration variable with the existing
warning, the same as `__int128` today.

Added: 
    clang/test/OpenMP/for_loop_bitint_messages.c

Modified: 
    clang/docs/ReleaseNotes.md
    clang/lib/Sema/SemaOpenMP.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 48d9b05597868..4cd09971f5dcd 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -1014,6 +1014,8 @@ The `alpha.cplusplus.UseAfterLifetimeEnd` checker was renamed to `alpha.core.Use
 - 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.
+- Fixed a crash when the loop variable or a loop bound of an OpenMP loop has a
+  `_BitInt` type wider than any standard integer type. (#GH140074)
 
 - Added support for capturing structured bindings in OpenMP regions
   (a C++20 extension; warned as an extension in C++17). Individual bindings

diff  --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index 3bd68a9ace981..0c97400e31a8d 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -5969,6 +5969,16 @@ class CaptureVars : public TreeTransform<CaptureVars> {
 };
 } // namespace
 
+/// Like ASTContext::getIntTypeForBitwidth, but falls back to a _BitInt type
+/// when no standard integer type has the requested width.
+static QualType getIntTypeForBitwidthOrBitInt(ASTContext &C, unsigned Bits,
+                                              bool Signed) {
+  QualType Ty = C.getIntTypeForBitwidth(Bits, Signed);
+  if (Ty.isNull())
+    Ty = C.getBitIntType(/*IsUnsigned=*/!Signed, Bits);
+  return Ty;
+}
+
 static VarDecl *precomputeExpr(Sema &Actions,
                                SmallVectorImpl<Stmt *> &BodyStmts, Expr *E,
                                StringRef Name) {
@@ -6289,7 +6299,7 @@ StmtResult SemaOpenMP::ActOnOpenMPCanonicalLoop(Stmt *AStmt) {
   QualType LogicalTy = Ctx.getUnsignedPointerDiffType();
   if (CounterTy->isIntegerType()) {
     unsigned BitWidth = Ctx.getIntWidth(CounterTy);
-    LogicalTy = Ctx.getIntTypeForBitwidth(BitWidth, false);
+    LogicalTy = getIntTypeForBitwidthOrBitInt(Ctx, BitWidth, /*Signed=*/false);
   }
 
   // Analyze the loop increment.
@@ -9348,8 +9358,9 @@ calculateNumIters(Sema &SemaRef, Scope *S, SourceLocation DefaultLoc,
     uint64_t UpperSize = SemaRef.Context.getTypeSize(UpperTy);
     if ((LowerSize <= UpperSize && UpperTy->hasSignedIntegerRepresentation()) ||
         (LowerSize > UpperSize && LowerTy->hasSignedIntegerRepresentation())) {
-      QualType CastType = SemaRef.Context.getIntTypeForBitwidth(
-          LowerSize > UpperSize ? LowerSize : UpperSize, /*Signed=*/0);
+      QualType CastType = getIntTypeForBitwidthOrBitInt(
+          SemaRef.Context, LowerSize > UpperSize ? LowerSize : UpperSize,
+          /*Signed=*/false);
       Upper =
           SemaRef
               .PerformImplicitConversion(
@@ -9656,7 +9667,7 @@ Expr *OpenMPIterationSpaceChecker::buildNumIterations(
         UseVarType ? C.getTypeSize(VarType) : C.getTypeSize(Type);
     bool IsSigned = UseVarType ? VarType->hasSignedIntegerRepresentation()
                                : Type->hasSignedIntegerRepresentation();
-    Type = C.getIntTypeForBitwidth(NewSize, IsSigned);
+    Type = getIntTypeForBitwidthOrBitInt(C, NewSize, IsSigned);
     if (!SemaRef.Context.hasSameType(Diff.get()->getType(), Type)) {
       Diff = SemaRef.PerformImplicitConversion(Diff.get(), Type,
                                                AssignmentAction::Converting,

diff  --git a/clang/test/OpenMP/for_loop_bitint_messages.c b/clang/test/OpenMP/for_loop_bitint_messages.c
new file mode 100644
index 0000000000000..b40ec17598380
--- /dev/null
+++ b/clang/test/OpenMP/for_loop_bitint_messages.c
@@ -0,0 +1,46 @@
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -std=c23 -triple x86_64-unknown-unknown -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp -fopenmp-enable-irbuilder -std=c23 -triple x86_64-unknown-unknown -verify %s
+// RUN: %clang_cc1 -fsyntax-only -fopenmp-simd -std=c23 -triple x86_64-unknown-unknown -verify %s
+
+// RUN: %clang_cc1 -fopenmp -std=c23 -triple x86_64-unknown-unknown -emit-llvm -o - -DCODEGEN %s | FileCheck %s
+
+typedef _BitInt(931) B931;
+
+void sink(B931, B931);
+
+// GH140074
+// CHECK-LABEL: define {{.*}}void @gh140074_bound(
+// CHECK: call void @__kmpc_for_static_init_{{4|8u?}}(
+void gh140074_bound(int a, B931 b) {
+#pragma omp for
+  for (int i = a; i < b; i++)
+    sink(i, b);
+}
+
+#ifndef CODEGEN
+void gh140074_reduced(int a, B931 b) {
+#pragma omp for collapse(2) // expected-note {{as specified in 'collapse' clause}}
+  for (int i = a; i < b; i++)
+    sink(i, b); // expected-error {{expected 2 for loops after '#pragma omp for', but found only 1}}
+}
+#endif
+
+// CHECK-LABEL: define {{.*}}void @bitint_iv(
+// CHECK: call void @__kmpc_for_static_init_{{4|8u?}}(
+void bitint_iv(B931 x) {
+  // expected-warning at +2 {{OpenMP loop iteration variable cannot have more than 64 bits size and will be narrowed}}
+#pragma omp for
+  for (B931 i = 0; i < x; ++i)
+    sink(i, x);
+}
+
+// CHECK-LABEL: define {{.*}}void @bitint_collapse(
+// CHECK: call void @__kmpc_for_static_init_{{4|8u?}}(
+void bitint_collapse(B931 a, B931 b, B931 c, B931 d, B931 e, B931 f) {
+  // expected-warning at +3 {{OpenMP loop iteration variable cannot have more than 64 bits size and will be narrowed}}
+  // expected-warning at +3 {{OpenMP loop iteration variable cannot have more than 64 bits size and will be narrowed}}
+#pragma omp for collapse(2)
+  for (B931 i = a; i < b; i += c)
+    for (B931 j = d; j > e; j += f)
+      sink(i, j);
+}


        


More information about the cfe-commits mailing list