[llvm-branch-commits] [flang] [flang][OpenM] Check if loop nest/sequence is well-formed (PR #188025)

2026-03-23 Thread Jack Styles via llvm-branch-commits

Stylie777 wrote:

nit: Commit message should be [Flang][OpenMP]

https://github.com/llvm/llvm-project/pull/188025
___
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits


[llvm-branch-commits] [flang] [flang][OpenM] Check if loop nest/sequence is well-formed (PR #188025)

2026-03-23 Thread Jack Styles via llvm-branch-commits


@@ -268,93 +258,83 @@ void OmpStructureChecker::CheckNestedConstruct(
 
   // Check constructs contained in the body of the loop construct.
   auto &body{std::get(x.t)};
+
   for (auto &stmt : BlockRange(body, BlockRange::Step::Over)) {
 if (auto *d{parser::Unwrap(stmt)}) {
   context_.Say(d->source,
   "Compiler directives are not allowed inside OpenMP loop 
constructs"_warn_en_US);
-} else if (auto *omp{parser::Unwrap(stmt)}) {
-  if (!IsLoopTransforming(omp->BeginDir().DirId())) {
-context_.Say(omp->source,
-"Only loop-transforming OpenMP constructs are allowed inside 
OpenMP loop constructs"_err_en_US);
-  }
-  if (IsFullUnroll(*omp)) {
-context_.Say(x.source,
-"OpenMP loop construct cannot apply to a fully unrolled 
loop"_err_en_US);
-  }
-} else if (!parser::Unwrap(stmt)) {
-  parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)};
-  context_.Say(source,
-  "OpenMP loop construct can only contain DO loops or 
loop-nest-generating OpenMP constructs"_err_en_US);
 }
   }
 
   LoopSequence sequence(body, version, true);
 
-  // Check if a loop-nest-associated construct has only one top-level loop
-  // in it.
+  auto assoc{llvm::omp::getDirectiveAssociation(dir)};
   auto needRange{GetAffectedLoopRangeWithReason(beginSpec, version)};
+  auto haveLength{sequence.length()};
 
-  if (auto haveLength{sequence.length()}) {
-if (*haveLength.value == 0) {
+  if (assoc == llvm::omp::Association::LoopNest) {
+if (sequence.children().size() == 0) {
   context_.Say(beginSource,
-  "This construct should contain a DO-loop or a loop-nest-generating 
OpenMP construct"_err_en_US);
-} else {
-  auto assoc{llvm::omp::getDirectiveAssociation(dir)};
-  if (*haveLength.value > 1 && assoc == llvm::omp::Association::LoopNest) {
-auto &msg{context_.Say(beginSource,
-"This construct applies to a loop nest, but has a loop sequence of 
"
-"length %" PRId64 ""_err_en_US,
-*haveLength.value)};
-haveLength.reason.AttachTo(msg);
-  }
-  if (assoc == llvm::omp::Association::LoopSeq) {
-if (auto requiredCount{GetRequiredCount(needRange.value)}) {
-  if (*requiredCount > 0 && *haveLength.value < *requiredCount) {
-auto &msg{context_.Say(beginSource,
-"This construct requires a sequence of %" PRId64
-" loops, but the loop sequence has a length of %" PRId64
-""_err_en_US,
-*requiredCount, *haveLength.value)};
-haveLength.reason.AttachTo(msg);
-needRange.reason.AttachTo(msg);
-  }
-}
-  }
+  "This construct should contain a DO-loop or a loop-nest-generating 
construct"_err_en_US);
+} else if (haveLength.value > 1) {
+  auto &msg{context_.Say(beginSource,
+  "This construct applies to a loop nest, but has a loop sequence of "
+  "length %" PRId64 ""_err_en_US,
+  *haveLength.value)};
+  haveLength.reason.AttachTo(msg);
+}
+auto [isWellFormed, whyNot]{sequence.isWellFormedNest()};
+if (isWellFormed && !*isWellFormed) {
+  auto &msg{context_.Say(beginSource,
+  "This construct requires a canonical loop nest"_err_en_US)};
+  whyNot.AttachTo(msg);
 }
-  }
 
-  // Check requirements on nest depth.
-  auto [needDepth, needPerfect]{
-  GetAffectedNestDepthWithReason(beginSpec, version)};
-  auto &[haveSema, havePerf]{sequence.depth()};
+// Check requirements on nest depth.
+auto [needDepth, needPerfect]{
+GetAffectedNestDepthWithReason(beginSpec, version)};
+auto &[haveSema, havePerf]{sequence.depth()};
+
+auto haveDepth{needPerfect ? havePerf : haveSema};
+std::string_view perfectTxt{needPerfect ? " perfect" : ""};
 
-  if (dir != llvm::omp::Directive::OMPD_fuse) {
-auto haveDepth = needPerfect ? havePerf : haveSema;
 // If the present depth is 0, it's likely that the construct doesn't
 // have any loops in it, which would be diagnosed above.
 if (needDepth && haveDepth.value > 0) {
   if (*needDepth.value > *haveDepth.value) {
-if (needPerfect) {
+auto &msg{context_.Say(beginSource,
+"This construct requires a%s nest of depth %" PRId64
+", but the associated nest is a%s nest of depth %" PRId64
+""_err_en_US,
+perfectTxt, *needDepth.value, perfectTxt, *haveDepth.value)};
+haveDepth.reason.AttachTo(msg);
+needDepth.reason.AttachTo(msg);
+  }
+}
+
+  } else if (assoc == llvm::omp::Association::LoopSeq) {
+if (haveLength.value == 0) {
+  context_.Say(beginSource,
+  "This construct should contain a DO-loop or a 
loop-sequence-generating construct"_err_en_US);

Stylie777 wrote:

This looks to be repeated, to reduce maintenance can we put this in a lambd

[llvm-branch-commits] [flang] [flang][OpenM] Check if loop nest/sequence is well-formed (PR #188025)

2026-03-23 Thread Jack Styles via llvm-branch-commits


@@ -268,93 +258,83 @@ void OmpStructureChecker::CheckNestedConstruct(
 
   // Check constructs contained in the body of the loop construct.
   auto &body{std::get(x.t)};
+

Stylie777 wrote:

nit: unnecessary change

https://github.com/llvm/llvm-project/pull/188025
___
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits


[llvm-branch-commits] [flang] [flang][OpenM] Check if loop nest/sequence is well-formed (PR #188025)

2026-03-23 Thread Jack Styles via llvm-branch-commits


@@ -268,93 +258,83 @@ void OmpStructureChecker::CheckNestedConstruct(
 
   // Check constructs contained in the body of the loop construct.
   auto &body{std::get(x.t)};
+
   for (auto &stmt : BlockRange(body, BlockRange::Step::Over)) {
 if (auto *d{parser::Unwrap(stmt)}) {
   context_.Say(d->source,
   "Compiler directives are not allowed inside OpenMP loop 
constructs"_warn_en_US);
-} else if (auto *omp{parser::Unwrap(stmt)}) {
-  if (!IsLoopTransforming(omp->BeginDir().DirId())) {
-context_.Say(omp->source,
-"Only loop-transforming OpenMP constructs are allowed inside 
OpenMP loop constructs"_err_en_US);
-  }
-  if (IsFullUnroll(*omp)) {
-context_.Say(x.source,
-"OpenMP loop construct cannot apply to a fully unrolled 
loop"_err_en_US);
-  }
-} else if (!parser::Unwrap(stmt)) {
-  parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)};
-  context_.Say(source,
-  "OpenMP loop construct can only contain DO loops or 
loop-nest-generating OpenMP constructs"_err_en_US);
 }
   }
 
   LoopSequence sequence(body, version, true);
 
-  // Check if a loop-nest-associated construct has only one top-level loop
-  // in it.
+  auto assoc{llvm::omp::getDirectiveAssociation(dir)};
   auto needRange{GetAffectedLoopRangeWithReason(beginSpec, version)};
+  auto haveLength{sequence.length()};
 
-  if (auto haveLength{sequence.length()}) {
-if (*haveLength.value == 0) {
+  if (assoc == llvm::omp::Association::LoopNest) {

Stylie777 wrote:

Is there a risk here that things may be missed if we are now checking only for 
loop nest associated directives? 

https://github.com/llvm/llvm-project/pull/188025
___
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits


[llvm-branch-commits] [flang] [flang][OpenM] Check if loop nest/sequence is well-formed (PR #188025)

2026-03-23 Thread Krzysztof Parzyszek via llvm-branch-commits

https://github.com/kparzysz updated 
https://github.com/llvm/llvm-project/pull/188025

>From fba1271b9b74d00f153d5f1eaa0887e61c64e02d Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek 
Date: Fri, 20 Mar 2026 11:33:45 -0500
Subject: [PATCH] [flang][OpenM] Check if loop nest/sequence is well-formed

Check if the code associated with a nest or sequence construct is well
formed. Emit diagnostic messages if not.

Make a clearer separation for checks of loop-nest-associated and loop-
sequence-associated constructs.

Unify structure of some of the more common messages.

Issue: https://github.com/llvm/llvm-project/issues/185287
---
 flang/include/flang/Semantics/openmp-utils.h  |   3 +
 flang/lib/Semantics/check-omp-loop.cpp| 126 ++
 flang/lib/Semantics/openmp-utils.cpp  | 162 +-
 flang/lib/Semantics/resolve-directives.cpp|   5 -
 flang/test/Parser/OpenMP/interchange-fail.f90 |  31 
 flang/test/Parser/OpenMP/tile-fail.f90|  31 
 flang/test/Semantics/OpenMP/do-collapse.f90   |   3 +-
 .../OpenMP/do-concurrent-collapse.f90 |  11 +-
 flang/test/Semantics/OpenMP/do09.f90  |   6 +-
 flang/test/Semantics/OpenMP/do13.f90  |  10 +-
 flang/test/Semantics/OpenMP/do21.f90  |  10 +-
 flang/test/Semantics/OpenMP/fuse1.f90 |   2 +-
 .../Semantics/OpenMP/interchange-fail.f90 |  18 ++
 flang/test/Semantics/OpenMP/interchange01.f90 |   8 +-
 .../Semantics/OpenMP/loop-association.f90 |  15 +-
 .../loop-transformation-construct01.f90   |  17 +-
 .../loop-transformation-construct02.f90   |  11 +-
 .../loop-transformation-construct04.f90   |   4 +-
 flang/test/Semantics/OpenMP/tile-fail.f90 |  18 ++
 flang/test/Semantics/OpenMP/tile02.f90|   3 +-
 flang/test/Semantics/OpenMP/tile03.f90|   3 +-
 flang/test/Semantics/OpenMP/tile08.f90|   4 +-
 flang/test/Semantics/OpenMP/tile09.f90|   3 +-
 23 files changed, 269 insertions(+), 235 deletions(-)
 delete mode 100644 flang/test/Parser/OpenMP/interchange-fail.f90
 delete mode 100644 flang/test/Parser/OpenMP/tile-fail.f90
 create mode 100644 flang/test/Semantics/OpenMP/interchange-fail.f90
 create mode 100644 flang/test/Semantics/OpenMP/tile-fail.f90

diff --git a/flang/include/flang/Semantics/openmp-utils.h 
b/flang/include/flang/Semantics/openmp-utils.h
index 7c95edf81ada2..bf35598177e17 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -202,6 +202,9 @@ struct LoopSequence {
   const Depth &depth() const { return depth_; }
   const std::vector &children() const { return children_; }
 
+  WithReason isWellFormedSequence() const;
+  WithReason isWellFormedNest() const;
+
 private:
   using Construct = ExecutionPartIterator::Construct;
 
diff --git a/flang/lib/Semantics/check-omp-loop.cpp 
b/flang/lib/Semantics/check-omp-loop.cpp
index df9797ac8e56a..962537496e580 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -61,16 +61,6 @@ class AssociatedLoopChecker {
   constructNamesAndLevels_.emplace(
   constructName.value().ToString(), level_);
 }
-if (level_ >= 0) {
-  if (dc.IsDoWhile()) {
-context_.Say(doStmt.source,
-"The associated loop of a loop-associated directive cannot be a DO 
WHILE."_err_en_US);
-  }
-  if (!dc.GetLoopControl()) {
-context_.Say(doStmt.source,
-"The associated loop of a loop-associated directive cannot be a DO 
without control."_err_en_US);
-  }
-}
 return true;
   }
 
@@ -268,93 +258,83 @@ void OmpStructureChecker::CheckNestedConstruct(
 
   // Check constructs contained in the body of the loop construct.
   auto &body{std::get(x.t)};
+
   for (auto &stmt : BlockRange(body, BlockRange::Step::Over)) {
 if (auto *d{parser::Unwrap(stmt)}) {
   context_.Say(d->source,
   "Compiler directives are not allowed inside OpenMP loop 
constructs"_warn_en_US);
-} else if (auto *omp{parser::Unwrap(stmt)}) {
-  if (!IsLoopTransforming(omp->BeginDir().DirId())) {
-context_.Say(omp->source,
-"Only loop-transforming OpenMP constructs are allowed inside 
OpenMP loop constructs"_err_en_US);
-  }
-  if (IsFullUnroll(*omp)) {
-context_.Say(x.source,
-"OpenMP loop construct cannot apply to a fully unrolled 
loop"_err_en_US);
-  }
-} else if (!parser::Unwrap(stmt)) {
-  parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)};
-  context_.Say(source,
-  "OpenMP loop construct can only contain DO loops or 
loop-nest-generating OpenMP constructs"_err_en_US);
 }
   }
 
   LoopSequence sequence(body, version, true);
 
-  // Check if a loop-nest-associated construct has only one top-level loop
-  // in it.
+  auto assoc{llvm::omp::getDirectiveAssociation(dir)};
   auto needRange{GetAffectedLoopRangeWithReason(beginSpec, version)};
+  aut

[llvm-branch-commits] [flang] [flang][OpenM] Check if loop nest/sequence is well-formed (PR #188025)

2026-03-23 Thread via llvm-branch-commits

llvmbot wrote:




@llvm/pr-subscribers-flang-openmp

Author: Krzysztof Parzyszek (kparzysz)


Changes

Check if the code associated with a nest or sequence construct is well formed. 
Emit diagnostic messages if not.

Make a clearer separation for checks of loop-nest-associated and loop- 
sequence-associated constructs.

Unify structure of some of the more common messages.

Issue: https://github.com/llvm/llvm-project/issues/185287

---

Patch is 41.74 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/188025.diff


23 Files Affected:

- (modified) flang/include/flang/Semantics/openmp-utils.h (+3) 
- (modified) flang/lib/Semantics/check-omp-loop.cpp (+53-73) 
- (modified) flang/lib/Semantics/openmp-utils.cpp (+115-47) 
- (modified) flang/lib/Semantics/resolve-directives.cpp (-5) 
- (removed) flang/test/Parser/OpenMP/interchange-fail.f90 (-31) 
- (removed) flang/test/Parser/OpenMP/tile-fail.f90 (-31) 
- (modified) flang/test/Semantics/OpenMP/do-collapse.f90 (+1-2) 
- (modified) flang/test/Semantics/OpenMP/do-concurrent-collapse.f90 (+6-5) 
- (modified) flang/test/Semantics/OpenMP/do09.f90 (+4-2) 
- (modified) flang/test/Semantics/OpenMP/do13.f90 (+5-5) 
- (modified) flang/test/Semantics/OpenMP/do21.f90 (+5-5) 
- (modified) flang/test/Semantics/OpenMP/fuse1.f90 (+1-1) 
- (added) flang/test/Semantics/OpenMP/interchange-fail.f90 (+18) 
- (modified) flang/test/Semantics/OpenMP/interchange01.f90 (+5-3) 
- (modified) flang/test/Semantics/OpenMP/loop-association.f90 (+9-6) 
- (modified) flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 
(+10-7) 
- (modified) flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 
(+6-5) 
- (modified) flang/test/Semantics/OpenMP/loop-transformation-construct04.f90 
(+2-2) 
- (added) flang/test/Semantics/OpenMP/tile-fail.f90 (+18) 
- (modified) flang/test/Semantics/OpenMP/tile02.f90 (+2-1) 
- (modified) flang/test/Semantics/OpenMP/tile03.f90 (+2-1) 
- (modified) flang/test/Semantics/OpenMP/tile08.f90 (+2-2) 
- (modified) flang/test/Semantics/OpenMP/tile09.f90 (+2-1) 


``diff
diff --git a/flang/include/flang/Semantics/openmp-utils.h 
b/flang/include/flang/Semantics/openmp-utils.h
index 7c95edf81ada2..bf35598177e17 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -202,6 +202,9 @@ struct LoopSequence {
   const Depth &depth() const { return depth_; }
   const std::vector &children() const { return children_; }
 
+  WithReason isWellFormedSequence() const;
+  WithReason isWellFormedNest() const;
+
 private:
   using Construct = ExecutionPartIterator::Construct;
 
diff --git a/flang/lib/Semantics/check-omp-loop.cpp 
b/flang/lib/Semantics/check-omp-loop.cpp
index df9797ac8e56a..962537496e580 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -61,16 +61,6 @@ class AssociatedLoopChecker {
   constructNamesAndLevels_.emplace(
   constructName.value().ToString(), level_);
 }
-if (level_ >= 0) {
-  if (dc.IsDoWhile()) {
-context_.Say(doStmt.source,
-"The associated loop of a loop-associated directive cannot be a DO 
WHILE."_err_en_US);
-  }
-  if (!dc.GetLoopControl()) {
-context_.Say(doStmt.source,
-"The associated loop of a loop-associated directive cannot be a DO 
without control."_err_en_US);
-  }
-}
 return true;
   }
 
@@ -268,93 +258,83 @@ void OmpStructureChecker::CheckNestedConstruct(
 
   // Check constructs contained in the body of the loop construct.
   auto &body{std::get(x.t)};
+
   for (auto &stmt : BlockRange(body, BlockRange::Step::Over)) {
 if (auto *d{parser::Unwrap(stmt)}) {
   context_.Say(d->source,
   "Compiler directives are not allowed inside OpenMP loop 
constructs"_warn_en_US);
-} else if (auto *omp{parser::Unwrap(stmt)}) {
-  if (!IsLoopTransforming(omp->BeginDir().DirId())) {
-context_.Say(omp->source,
-"Only loop-transforming OpenMP constructs are allowed inside 
OpenMP loop constructs"_err_en_US);
-  }
-  if (IsFullUnroll(*omp)) {
-context_.Say(x.source,
-"OpenMP loop construct cannot apply to a fully unrolled 
loop"_err_en_US);
-  }
-} else if (!parser::Unwrap(stmt)) {
-  parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)};
-  context_.Say(source,
-  "OpenMP loop construct can only contain DO loops or 
loop-nest-generating OpenMP constructs"_err_en_US);
 }
   }
 
   LoopSequence sequence(body, version, true);
 
-  // Check if a loop-nest-associated construct has only one top-level loop
-  // in it.
+  auto assoc{llvm::omp::getDirectiveAssociation(dir)};
   auto needRange{GetAffectedLoopRangeWithReason(beginSpec, version)};
+  auto haveLength{sequence.length()};
 
-  if (auto haveLength{sequence.length()}) {
-if (*haveLength.value == 0) {
+  if (assoc == llvm::omp::Association::Lo

[llvm-branch-commits] [flang] [flang][OpenM] Check if loop nest/sequence is well-formed (PR #188025)

2026-03-23 Thread via llvm-branch-commits

llvmbot wrote:




@llvm/pr-subscribers-flang-semantics

Author: Krzysztof Parzyszek (kparzysz)


Changes

Check if the code associated with a nest or sequence construct is well formed. 
Emit diagnostic messages if not.

Make a clearer separation for checks of loop-nest-associated and loop- 
sequence-associated constructs.

Unify structure of some of the more common messages.

Issue: https://github.com/llvm/llvm-project/issues/185287

---

Patch is 41.74 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/188025.diff


23 Files Affected:

- (modified) flang/include/flang/Semantics/openmp-utils.h (+3) 
- (modified) flang/lib/Semantics/check-omp-loop.cpp (+53-73) 
- (modified) flang/lib/Semantics/openmp-utils.cpp (+115-47) 
- (modified) flang/lib/Semantics/resolve-directives.cpp (-5) 
- (removed) flang/test/Parser/OpenMP/interchange-fail.f90 (-31) 
- (removed) flang/test/Parser/OpenMP/tile-fail.f90 (-31) 
- (modified) flang/test/Semantics/OpenMP/do-collapse.f90 (+1-2) 
- (modified) flang/test/Semantics/OpenMP/do-concurrent-collapse.f90 (+6-5) 
- (modified) flang/test/Semantics/OpenMP/do09.f90 (+4-2) 
- (modified) flang/test/Semantics/OpenMP/do13.f90 (+5-5) 
- (modified) flang/test/Semantics/OpenMP/do21.f90 (+5-5) 
- (modified) flang/test/Semantics/OpenMP/fuse1.f90 (+1-1) 
- (added) flang/test/Semantics/OpenMP/interchange-fail.f90 (+18) 
- (modified) flang/test/Semantics/OpenMP/interchange01.f90 (+5-3) 
- (modified) flang/test/Semantics/OpenMP/loop-association.f90 (+9-6) 
- (modified) flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 
(+10-7) 
- (modified) flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 
(+6-5) 
- (modified) flang/test/Semantics/OpenMP/loop-transformation-construct04.f90 
(+2-2) 
- (added) flang/test/Semantics/OpenMP/tile-fail.f90 (+18) 
- (modified) flang/test/Semantics/OpenMP/tile02.f90 (+2-1) 
- (modified) flang/test/Semantics/OpenMP/tile03.f90 (+2-1) 
- (modified) flang/test/Semantics/OpenMP/tile08.f90 (+2-2) 
- (modified) flang/test/Semantics/OpenMP/tile09.f90 (+2-1) 


``diff
diff --git a/flang/include/flang/Semantics/openmp-utils.h 
b/flang/include/flang/Semantics/openmp-utils.h
index 7c95edf81ada2..bf35598177e17 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -202,6 +202,9 @@ struct LoopSequence {
   const Depth &depth() const { return depth_; }
   const std::vector &children() const { return children_; }
 
+  WithReason isWellFormedSequence() const;
+  WithReason isWellFormedNest() const;
+
 private:
   using Construct = ExecutionPartIterator::Construct;
 
diff --git a/flang/lib/Semantics/check-omp-loop.cpp 
b/flang/lib/Semantics/check-omp-loop.cpp
index df9797ac8e56a..962537496e580 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -61,16 +61,6 @@ class AssociatedLoopChecker {
   constructNamesAndLevels_.emplace(
   constructName.value().ToString(), level_);
 }
-if (level_ >= 0) {
-  if (dc.IsDoWhile()) {
-context_.Say(doStmt.source,
-"The associated loop of a loop-associated directive cannot be a DO 
WHILE."_err_en_US);
-  }
-  if (!dc.GetLoopControl()) {
-context_.Say(doStmt.source,
-"The associated loop of a loop-associated directive cannot be a DO 
without control."_err_en_US);
-  }
-}
 return true;
   }
 
@@ -268,93 +258,83 @@ void OmpStructureChecker::CheckNestedConstruct(
 
   // Check constructs contained in the body of the loop construct.
   auto &body{std::get(x.t)};
+
   for (auto &stmt : BlockRange(body, BlockRange::Step::Over)) {
 if (auto *d{parser::Unwrap(stmt)}) {
   context_.Say(d->source,
   "Compiler directives are not allowed inside OpenMP loop 
constructs"_warn_en_US);
-} else if (auto *omp{parser::Unwrap(stmt)}) {
-  if (!IsLoopTransforming(omp->BeginDir().DirId())) {
-context_.Say(omp->source,
-"Only loop-transforming OpenMP constructs are allowed inside 
OpenMP loop constructs"_err_en_US);
-  }
-  if (IsFullUnroll(*omp)) {
-context_.Say(x.source,
-"OpenMP loop construct cannot apply to a fully unrolled 
loop"_err_en_US);
-  }
-} else if (!parser::Unwrap(stmt)) {
-  parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)};
-  context_.Say(source,
-  "OpenMP loop construct can only contain DO loops or 
loop-nest-generating OpenMP constructs"_err_en_US);
 }
   }
 
   LoopSequence sequence(body, version, true);
 
-  // Check if a loop-nest-associated construct has only one top-level loop
-  // in it.
+  auto assoc{llvm::omp::getDirectiveAssociation(dir)};
   auto needRange{GetAffectedLoopRangeWithReason(beginSpec, version)};
+  auto haveLength{sequence.length()};
 
-  if (auto haveLength{sequence.length()}) {
-if (*haveLength.value == 0) {
+  if (assoc == llvm::omp::Association:

[llvm-branch-commits] [flang] [flang][OpenM] Check if loop nest/sequence is well-formed (PR #188025)

2026-03-23 Thread Krzysztof Parzyszek via llvm-branch-commits

https://github.com/kparzysz created 
https://github.com/llvm/llvm-project/pull/188025

Check if the code associated with a nest or sequence construct is well formed. 
Emit diagnostic messages if not.

Make a clearer separation for checks of loop-nest-associated and loop- 
sequence-associated constructs.

Unify structure of some of the more common messages.

Issue: https://github.com/llvm/llvm-project/issues/185287

>From fba1271b9b74d00f153d5f1eaa0887e61c64e02d Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek 
Date: Fri, 20 Mar 2026 11:33:45 -0500
Subject: [PATCH] [flang][OpenM] Check if loop nest/sequence is well-formed

Check if the code associated with a nest or sequence construct is well
formed. Emit diagnostic messages if not.

Make a clearer separation for checks of loop-nest-associated and loop-
sequence-associated constructs.

Unify structure of some of the more common messages.

Issue: https://github.com/llvm/llvm-project/issues/185287
---
 flang/include/flang/Semantics/openmp-utils.h  |   3 +
 flang/lib/Semantics/check-omp-loop.cpp| 126 ++
 flang/lib/Semantics/openmp-utils.cpp  | 162 +-
 flang/lib/Semantics/resolve-directives.cpp|   5 -
 flang/test/Parser/OpenMP/interchange-fail.f90 |  31 
 flang/test/Parser/OpenMP/tile-fail.f90|  31 
 flang/test/Semantics/OpenMP/do-collapse.f90   |   3 +-
 .../OpenMP/do-concurrent-collapse.f90 |  11 +-
 flang/test/Semantics/OpenMP/do09.f90  |   6 +-
 flang/test/Semantics/OpenMP/do13.f90  |  10 +-
 flang/test/Semantics/OpenMP/do21.f90  |  10 +-
 flang/test/Semantics/OpenMP/fuse1.f90 |   2 +-
 .../Semantics/OpenMP/interchange-fail.f90 |  18 ++
 flang/test/Semantics/OpenMP/interchange01.f90 |   8 +-
 .../Semantics/OpenMP/loop-association.f90 |  15 +-
 .../loop-transformation-construct01.f90   |  17 +-
 .../loop-transformation-construct02.f90   |  11 +-
 .../loop-transformation-construct04.f90   |   4 +-
 flang/test/Semantics/OpenMP/tile-fail.f90 |  18 ++
 flang/test/Semantics/OpenMP/tile02.f90|   3 +-
 flang/test/Semantics/OpenMP/tile03.f90|   3 +-
 flang/test/Semantics/OpenMP/tile08.f90|   4 +-
 flang/test/Semantics/OpenMP/tile09.f90|   3 +-
 23 files changed, 269 insertions(+), 235 deletions(-)
 delete mode 100644 flang/test/Parser/OpenMP/interchange-fail.f90
 delete mode 100644 flang/test/Parser/OpenMP/tile-fail.f90
 create mode 100644 flang/test/Semantics/OpenMP/interchange-fail.f90
 create mode 100644 flang/test/Semantics/OpenMP/tile-fail.f90

diff --git a/flang/include/flang/Semantics/openmp-utils.h 
b/flang/include/flang/Semantics/openmp-utils.h
index 7c95edf81ada2..bf35598177e17 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -202,6 +202,9 @@ struct LoopSequence {
   const Depth &depth() const { return depth_; }
   const std::vector &children() const { return children_; }
 
+  WithReason isWellFormedSequence() const;
+  WithReason isWellFormedNest() const;
+
 private:
   using Construct = ExecutionPartIterator::Construct;
 
diff --git a/flang/lib/Semantics/check-omp-loop.cpp 
b/flang/lib/Semantics/check-omp-loop.cpp
index df9797ac8e56a..962537496e580 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -61,16 +61,6 @@ class AssociatedLoopChecker {
   constructNamesAndLevels_.emplace(
   constructName.value().ToString(), level_);
 }
-if (level_ >= 0) {
-  if (dc.IsDoWhile()) {
-context_.Say(doStmt.source,
-"The associated loop of a loop-associated directive cannot be a DO 
WHILE."_err_en_US);
-  }
-  if (!dc.GetLoopControl()) {
-context_.Say(doStmt.source,
-"The associated loop of a loop-associated directive cannot be a DO 
without control."_err_en_US);
-  }
-}
 return true;
   }
 
@@ -268,93 +258,83 @@ void OmpStructureChecker::CheckNestedConstruct(
 
   // Check constructs contained in the body of the loop construct.
   auto &body{std::get(x.t)};
+
   for (auto &stmt : BlockRange(body, BlockRange::Step::Over)) {
 if (auto *d{parser::Unwrap(stmt)}) {
   context_.Say(d->source,
   "Compiler directives are not allowed inside OpenMP loop 
constructs"_warn_en_US);
-} else if (auto *omp{parser::Unwrap(stmt)}) {
-  if (!IsLoopTransforming(omp->BeginDir().DirId())) {
-context_.Say(omp->source,
-"Only loop-transforming OpenMP constructs are allowed inside 
OpenMP loop constructs"_err_en_US);
-  }
-  if (IsFullUnroll(*omp)) {
-context_.Say(x.source,
-"OpenMP loop construct cannot apply to a fully unrolled 
loop"_err_en_US);
-  }
-} else if (!parser::Unwrap(stmt)) {
-  parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)};
-  context_.Say(source,
-  "OpenMP loop construct can only contain DO loops or 
loop-