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

Previously, loop constructs were parsed in a piece-wise manner: the begin 
directive, the body, and the end directive were parsed separately. Later on in 
canonicalization they were all coalesced into a loop construct. To facilitate 
that end-loop directives were given a special treatment, namely they were 
parsed as OpenMP constructs. As a result syntax errors caused by misplaced 
end-loop directives were handled differently from those cause by misplaced 
non-loop end directives.

The new loop nest parser constructs the complete loop construct, removing the 
need for the canonicalization step. Additionally, it is the basis for parsing 
loop-sequence-associated constructs in the future.

It also removes the need for the special treatment of end-loop directives. 
While this patch temporarily degrades the error messaging for misplaced 
end-loop directives, it enables uniform handling of any misplaced 
end-directives in the future.

>From aaea8e6b47808333581569d40e3721050be52bf1 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <[email protected]>
Date: Fri, 14 Nov 2025 15:52:11 -0600
Subject: [PATCH] [flang][OpenMP] Implement loop nest parser

Previously, loop constructs were parsed in a piece-wise manner: the
begin directive, the body, and the end directive were parsed separately.
Later on in canonicalization they were all coalesced into a loop
construct. To facilitate that end-loop directives were given a special
treatment, namely they were parsed as OpenMP constructs. As a result
syntax errors caused by misplaced end-loop directives were handled
differently from those cause by misplaced non-loop end directives.

The new loop nest parser constructs the complete loop construct,
removing the need for the canonicalization step. Additionally, it is
the basis for parsing loop-sequence-associated constructs in the future.

It also removes the need for the special treatment of end-loop
directives. While this patch temporarily degrades the error messaging
for misplaced end-loop directives, it enables uniform handling of any
misplaced end-directives in the future.
---
 flang/include/flang/Parser/parse-tree.h       |   3 +-
 flang/lib/Parser/executable-parsers.cpp       |   1 -
 flang/lib/Parser/openmp-parsers.cpp           | 143 ++++++++++++++-
 flang/lib/Parser/parse-tree.cpp               |  35 +++-
 flang/lib/Semantics/canonicalize-omp.cpp      | 163 ------------------
 flang/lib/Semantics/check-omp-loop.cpp        |  74 ++++++++
 flang/lib/Semantics/check-omp-structure.h     |   4 +
 flang/lib/Semantics/resolve-directives.cpp    |   7 -
 ...nested-loop-transformation-construct02.f90 |   2 +-
 .../loop-transformation-construct01.f90       |   7 +-
 .../loop-transformation-construct02.f90       |   7 +-
 flang/test/Parser/OpenMP/tile-fail.f90        |   5 +-
 flang/test/Semantics/OpenMP/do21.f90          |  10 +-
 .../Semantics/OpenMP/loop-association.f90     |  33 ++--
 .../loop-transformation-construct01.f90       |  40 +++--
 flang/test/Semantics/OpenMP/tile02.f90        |   2 +-
 16 files changed, 293 insertions(+), 243 deletions(-)

diff --git a/flang/include/flang/Parser/parse-tree.h 
b/flang/include/flang/Parser/parse-tree.h
index 60d2ad0b764b9..9795a0d2ae25e 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -271,7 +271,6 @@ struct OpenACCRoutineConstruct;
 struct OpenMPConstruct;
 struct OpenMPLoopConstruct;
 struct OpenMPDeclarativeConstruct;
-struct OmpEndLoopDirective;
 struct CUFKernelDoConstruct;
 
 // Cooked character stream locations
@@ -539,7 +538,6 @@ struct ExecutableConstruct {
       common::Indirection<OpenACCConstruct>,
       common::Indirection<AccEndCombinedDirective>,
       common::Indirection<OpenMPConstruct>,
-      common::Indirection<OmpEndLoopDirective>,
       common::Indirection<CUFKernelDoConstruct>>
       u;
 };
@@ -5359,6 +5357,7 @@ struct OpenMPLoopConstruct {
   const DoConstruct *GetNestedLoop() const;
   const OpenMPLoopConstruct *GetNestedConstruct() const;
 
+  CharBlock source;
   std::tuple<OmpBeginLoopDirective, Block, std::optional<OmpEndLoopDirective>>
       t;
 };
diff --git a/flang/lib/Parser/executable-parsers.cpp 
b/flang/lib/Parser/executable-parsers.cpp
index fadec1f11d1db..8d777a6671495 100644
--- a/flang/lib/Parser/executable-parsers.cpp
+++ b/flang/lib/Parser/executable-parsers.cpp
@@ -49,7 +49,6 @@ constexpr auto executableConstruct{first(
     construct<ExecutableConstruct>(indirect(Parser<SelectTypeConstruct>{})),
     construct<ExecutableConstruct>(indirect(whereConstruct)),
     construct<ExecutableConstruct>(indirect(forallConstruct)),
-    construct<ExecutableConstruct>(indirect(ompEndLoopDirective)),
     construct<ExecutableConstruct>(indirect(openmpConstruct)),
     construct<ExecutableConstruct>(indirect(Parser<OpenACCConstruct>{})),
     construct<ExecutableConstruct>(indirect(compilerDirective)),
diff --git a/flang/lib/Parser/openmp-parsers.cpp 
b/flang/lib/Parser/openmp-parsers.cpp
index e2da60ed19de8..d50f45794230b 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -17,6 +17,7 @@
 #include "type-parser-implementation.h"
 #include "flang/Parser/openmp-utils.h"
 #include "flang/Parser/parse-tree.h"
+#include "flang/Parser/tools.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/Bitset.h"
 #include "llvm/ADT/STLExtras.h"
@@ -30,6 +31,7 @@
 #include <iterator>
 #include <list>
 #include <optional>
+#include <set>
 #include <string>
 #include <tuple>
 #include <type_traits>
@@ -1656,6 +1658,100 @@ struct LooselyStructuredBlockParser {
   }
 };
 
+struct NonBlockDoConstructParser {
+  using resultType = Block;
+
+  std::optional<resultType> Parse(ParseState &state) const {
+    std::set<Label> labels;
+    Block body;
+
+    // Parse nests like
+    // do 20 i = 1, n     LabelDoStmt.t<Label> = 20
+    //   do 10 j = 1, m
+    //     ...
+    //   10 continue      Statement<...>.label = 10
+    // 20 continue
+
+    // Keep parsing ExecutionPartConstructs until the set of open label-do
+    // statements becomes empty, or until the EPC parser fails.
+    while (auto &&epc{attempt(executionPartConstruct).Parse(state)}) {
+      if (auto &&label{GetStatementLabel(*epc)}) {
+        labels.erase(*label);
+      }
+      if (auto *labelDo{Unwrap<LabelDoStmt>(*epc)}) {
+        labels.insert(std::get<Label>(labelDo->t));
+      }
+      body.push_back(std::move(*epc));
+      if (labels.empty()) {
+        break;
+      }
+    }
+
+    if (!body.empty()) {
+      return std::move(body);
+    }
+    return std::nullopt;
+  }
+
+private:
+  // Is the template argument "Statement<T>" for some T?
+  template <typename T> struct IsStatement {
+    static constexpr bool value{false};
+  };
+  template <typename T> struct IsStatement<Statement<T>> {
+    static constexpr bool value{true};
+  };
+
+  // Get the Label from a Statement<...> contained in an 
ExecutionPartConstruct,
+  // or std::nullopt, if there is no Statement<...> contained in there.
+  template <typename T>
+  static std::optional<Label> GetStatementLabel(const T &stmt) {
+    if constexpr (IsStatement<T>::value) {
+      return stmt.label;
+    } else if constexpr (WrapperTrait<T>) {
+      return GetStatementLabel(stmt.v);
+    } else if constexpr (UnionTrait<T>) {
+      return common::visit(
+          [&](auto &&s) { return GetStatementLabel(s); }, stmt.u);
+    }
+    return std::nullopt;
+  }
+};
+
+struct LoopNestParser {
+  using resultType = Block;
+
+  std::optional<resultType> Parse(ParseState &state) const {
+    // Parse !$DIR as an ExecutionPartConstruct
+    auto fortranDirective{predicated(executionPartConstruct,
+        [](auto &epc) { return Unwrap<CompilerDirective>(epc); })};
+    // Parse DO loop as an ExecutionPartConstruct
+    auto fortranDoConstruct{predicated(executionPartConstruct,
+        [&](auto &epc) { return Unwrap<DoConstruct>(epc); })};
+    ParseState backtrack{state};
+
+    Block body;
+    llvm::move(*many(fortranDirective).Parse(state), std::back_inserter(body));
+
+    if (auto &&doLoop{attempt(fortranDoConstruct).Parse(state)}) {
+      body.push_back(std::move(*doLoop));
+      return std::move(body);
+    }
+    if (auto &&labelDo{attempt(NonBlockDoConstructParser{}).Parse(state)}) {
+      llvm::move(*labelDo, std::back_inserter(body));
+      return std::move(body);
+    }
+    if (auto &&sblock{attempt(StrictlyStructuredBlockParser{}).Parse(state)}) {
+      llvm::move(*sblock, std::back_inserter(body));
+      return std::move(body);
+    }
+    // If it's neither a DO-loop, nor a BLOCK, undo the parsing of the
+    // directives and fail.
+    state = backtrack;
+    return std::nullopt;
+  }
+};
+
 TYPE_PARSER(construct<OmpErrorDirective>(
     predicated(Parser<OmpDirectiveName>{},
         IsDirective(llvm::omp::Directive::OMPD_error)) >=
@@ -1783,6 +1879,43 @@ struct OmpBlockConstructParser {
   llvm::omp::Directive dir_;
 };
 
+struct OmpLoopConstructParser {
+  using resultType = OpenMPLoopConstruct;
+
+  constexpr OmpLoopConstructParser(DirectiveSet dirs) : dirs_(dirs) {}
+
+  std::optional<resultType> Parse(ParseState &state) const {
+    if (auto &&begin{OmpBeginDirectiveParser(dirs_).Parse(state)}) {
+      if (auto &&nest{attempt(LoopNestParser{}).Parse(state)}) {
+        auto end{maybe(OmpEndDirectiveParser{dirs_}).Parse(state)};
+        return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
+            std::move(*nest),
+            llvm::transformOptional(std::move(*end),
+                [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};
+      } else {
+        // Parse a nested OpenMPLoopConstruct as the body.
+        auto ompLoopConstruct{predicated(executionPartConstruct,
+            [](auto &epc) { return Unwrap<OpenMPLoopConstruct>(epc); })};
+
+        // Allow empty body.
+        Block body;
+        if (auto &&omp{attempt(ompLoopConstruct).Parse(state)}) {
+          body.push_back(std::move(*omp));
+        }
+        auto end{maybe(OmpEndDirectiveParser{dirs_}).Parse(state)};
+        return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
+            std::move(body),
+            llvm::transformOptional(std::move(*end),
+                [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};
+      }
+    }
+    return std::nullopt;
+  }
+
+private:
+  DirectiveSet dirs_;
+};
+
 struct OmpDeclarativeAllocateParser {
   using resultType = OmpAllocateDirective;
 
@@ -2266,13 +2399,7 @@ static constexpr DirectiveSet GetLoopDirectives() {
   return loopDirectives;
 }
 
-TYPE_PARSER(sourced(construct<OmpBeginLoopDirective>(
-    sourced(OmpBeginDirectiveParser(GetLoopDirectives())))))
-
-// END OMP Loop directives
-TYPE_PARSER(sourced(construct<OmpEndLoopDirective>(
-    sourced(OmpEndDirectiveParser(GetLoopDirectives())))))
+TYPE_PARSER(sourced(construct<OpenMPLoopConstruct>(
+    OmpLoopConstructParser(GetLoopDirectives()))))
 
-TYPE_PARSER(construct<OpenMPLoopConstruct>(
-    Parser<OmpBeginLoopDirective>{} / endOmpLine))
 } // namespace Fortran::parser
diff --git a/flang/lib/Parser/parse-tree.cpp b/flang/lib/Parser/parse-tree.cpp
index 60e51895cdcea..53d4e4e680caa 100644
--- a/flang/lib/Parser/parse-tree.cpp
+++ b/flang/lib/Parser/parse-tree.cpp
@@ -435,17 +435,36 @@ const OmpClauseList &OmpDirectiveSpecification::Clauses() 
const {
 }
 
 const DoConstruct *OpenMPLoopConstruct::GetNestedLoop() const {
-  if (auto &body{std::get<Block>(t)}; !body.empty()) {
-    return Unwrap<DoConstruct>(body.front());
-  }
-  return nullptr;
+  auto getFromBlock{[](const Block &body, auto self) -> const DoConstruct * {
+    for (auto &stmt : body) {
+      if (auto *block{Unwrap<BlockConstruct>(&stmt)}) {
+        return self(std::get<Block>(block->t), self);
+      }
+      if (auto *loop{Unwrap<DoConstruct>(&stmt)}) {
+        return loop;
+      }
+    }
+    return nullptr;
+  }};
+
+  return getFromBlock(std::get<Block>(t), getFromBlock);
 }
 
 const OpenMPLoopConstruct *OpenMPLoopConstruct::GetNestedConstruct() const {
-  if (auto &body{std::get<Block>(t)}; !body.empty()) {
-    return Unwrap<OpenMPLoopConstruct>(body.front());
-  }
-  return nullptr;
+  auto getFromBlock{
+      [](const Block &body, auto self) -> const OpenMPLoopConstruct * {
+        for (auto &stmt : body) {
+          if (auto *block{Unwrap<BlockConstruct>(&stmt)}) {
+            return self(std::get<Block>(block->t), self);
+          }
+          if (auto *omp{Unwrap<OpenMPLoopConstruct>(&stmt)}) {
+            return omp;
+          }
+        }
+        return nullptr;
+      }};
+
+  return getFromBlock(std::get<Block>(t), getFromBlock);
 }
 
 static bool InitCharBlocksFromStrings(llvm::MutableArrayRef<CharBlock> blocks,
diff --git a/flang/lib/Semantics/canonicalize-omp.cpp 
b/flang/lib/Semantics/canonicalize-omp.cpp
index 0cec1969e0978..8a45cc3a88f45 100644
--- a/flang/lib/Semantics/canonicalize-omp.cpp
+++ b/flang/lib/Semantics/canonicalize-omp.cpp
@@ -31,26 +31,6 @@ class CanonicalizationOfOmp {
   CanonicalizationOfOmp(SemanticsContext &context)
       : context_{context}, messages_{context.messages()} {}
 
-  void Post(parser::Block &block) {
-    for (auto it{block.begin()}; it != block.end(); ++it) {
-      if (auto *ompCons{GetConstructIf<parser::OpenMPConstruct>(*it)}) {
-        // OpenMPLoopConstruct
-        if (auto *ompLoop{
-                std::get_if<parser::OpenMPLoopConstruct>(&ompCons->u)}) {
-          RewriteOpenMPLoopConstruct(*ompLoop, block, it);
-        }
-      } else if (auto *endDir{
-                     GetConstructIf<parser::OmpEndLoopDirective>(*it)}) {
-        // Unmatched OmpEndLoopDirective
-        const parser::OmpDirectiveName &endName{endDir->DirName()};
-        messages_.Say(endName.source,
-            "The %s directive must follow the DO loop associated with the "
-            "loop construct"_err_en_US,
-            parser::ToUpperCaseLetters(endName.source.ToString()));
-      }
-    } // Block list
-  }
-
   // Pre-visit all constructs that have both a specification part and
   // an execution part, and store the connection between the two.
   bool Pre(parser::BlockConstruct &x) {
@@ -92,149 +72,6 @@ class CanonicalizationOfOmp {
   void Post(parser::OmpMapClause &map) { CanonicalizeMapModifiers(map); }
 
 private:
-  template <typename T> T *GetConstructIf(parser::ExecutionPartConstruct &x) {
-    if (auto *y{std::get_if<parser::ExecutableConstruct>(&x.u)}) {
-      if (auto *z{std::get_if<common::Indirection<T>>(&y->u)}) {
-        return &z->value();
-      }
-    }
-    return nullptr;
-  }
-
-  template <typename T> T *GetOmpIf(parser::ExecutionPartConstruct &x) {
-    if (auto *construct{GetConstructIf<parser::OpenMPConstruct>(x)}) {
-      if (auto *omp{std::get_if<T>(&construct->u)}) {
-        return omp;
-      }
-    }
-    return nullptr;
-  }
-
-  void RewriteOpenMPLoopConstruct(parser::OpenMPLoopConstruct &x,
-      parser::Block &block, parser::Block::iterator it) {
-    // Check the sequence of DoConstruct and OmpEndLoopDirective
-    // in the same iteration
-    //
-    // Original:
-    //   ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
-    //     OmpBeginLoopDirective
-    //   ExecutableConstruct -> DoConstruct
-    //   ExecutableConstruct -> OmpEndLoopDirective (if available)
-    //
-    // After rewriting:
-    //   ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
-    //     OmpBeginLoopDirective
-    //     DoConstruct
-    //     OmpEndLoopDirective (if available)
-    parser::Block::iterator nextIt;
-    const parser::OmpDirectiveSpecification &beginDir{x.BeginDir()};
-    const parser::OmpDirectiveName &beginName{beginDir.DirName()};
-
-    auto missingDoConstruct = [](const parser::OmpDirectiveName &dirName,
-                                  parser::Messages &messages) {
-      messages.Say(dirName.source,
-          "A DO loop must follow the %s directive"_err_en_US,
-          parser::ToUpperCaseLetters(dirName.source.ToString()));
-    };
-    auto tileUnrollError = [](const parser::OmpDirectiveName &dirName,
-                               parser::Messages &messages) {
-      messages.Say(dirName.source,
-          "If a loop construct has been fully unrolled, it cannot then be 
tiled"_err_en_US,
-          parser::ToUpperCaseLetters(dirName.source.ToString()));
-    };
-
-    auto &body{std::get<parser::Block>(x.t)};
-
-    nextIt = it;
-    while (++nextIt != block.end()) {
-      // Ignore compiler directives.
-      if (GetConstructIf<parser::CompilerDirective>(*nextIt))
-        continue;
-
-      if (auto *doCons{GetConstructIf<parser::DoConstruct>(*nextIt)}) {
-        if (doCons->GetLoopControl()) {
-          // move DoConstruct
-          body.push_back(std::move(*nextIt));
-          nextIt = block.erase(nextIt);
-          // try to match OmpEndLoopDirective
-          if (nextIt != block.end()) {
-            if (auto *endDir{
-                    GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) {
-              std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) =
-                  std::move(*endDir);
-              nextIt = block.erase(nextIt);
-            }
-          }
-        } else {
-          messages_.Say(beginName.source,
-              "DO loop after the %s directive must have loop 
control"_err_en_US,
-              parser::ToUpperCaseLetters(beginName.source.ToString()));
-        }
-      } else if (auto *ompLoopCons{
-                     GetOmpIf<parser::OpenMPLoopConstruct>(*nextIt)}) {
-        // We should allow UNROLL and TILE constructs to be inserted between an
-        // OpenMP Loop Construct and the DO loop itself
-        auto &nestedBeginDirective = ompLoopCons->BeginDir();
-        auto &nestedBeginName = nestedBeginDirective.DirName();
-        if ((nestedBeginName.v == llvm::omp::Directive::OMPD_unroll ||
-                nestedBeginName.v == llvm::omp::Directive::OMPD_tile) &&
-            !(nestedBeginName.v == llvm::omp::Directive::OMPD_unroll &&
-                beginName.v == llvm::omp::Directive::OMPD_tile)) {
-          // iterate through the remaining block items to find the end 
directive
-          // for the unroll/tile directive.
-          parser::Block::iterator endIt;
-          endIt = nextIt;
-          while (endIt != block.end()) {
-            if (auto *endDir{
-                    GetConstructIf<parser::OmpEndLoopDirective>(*endIt)}) {
-              auto &endDirName = endDir->DirName();
-              if (endDirName.v == beginName.v) {
-                std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) =
-                    std::move(*endDir);
-                endIt = block.erase(endIt);
-                continue;
-              }
-            }
-            ++endIt;
-          }
-          RewriteOpenMPLoopConstruct(*ompLoopCons, block, nextIt);
-          body.push_back(std::move(*nextIt));
-          nextIt = block.erase(nextIt);
-        } else if (nestedBeginName.v == llvm::omp::Directive::OMPD_unroll &&
-            beginName.v == llvm::omp::Directive::OMPD_tile) {
-          // if a loop has been unrolled, the user can not then tile that loop
-          // as it has been unrolled
-          const parser::OmpClauseList &unrollClauseList{
-              nestedBeginDirective.Clauses()};
-          if (unrollClauseList.v.empty()) {
-            // if the clause list is empty for an unroll construct, we assume
-            // the loop is being fully unrolled
-            tileUnrollError(beginName, messages_);
-          } else {
-            // parse the clauses for the unroll directive to find the full
-            // clause
-            for (auto &clause : unrollClauseList.v) {
-              if (clause.Id() == llvm::omp::OMPC_full) {
-                tileUnrollError(beginName, messages_);
-              }
-            }
-          }
-        } else {
-          messages_.Say(nestedBeginName.source,
-              "Only Loop Transformation Constructs or Loop Nests can be nested 
within Loop Constructs"_err_en_US,
-              parser::ToUpperCaseLetters(nestedBeginName.source.ToString()));
-        }
-      } else {
-        missingDoConstruct(beginName, messages_);
-      }
-      // If we get here, we either found a loop, or issued an error message.
-      return;
-    }
-    if (nextIt == block.end()) {
-      missingDoConstruct(beginName, messages_);
-    }
-  }
-
   // Canonicalization of allocate directives
   //
   // In OpenMP 5.0 and 5.1 the allocate directive could either be a declarative
diff --git a/flang/lib/Semantics/check-omp-loop.cpp 
b/flang/lib/Semantics/check-omp-loop.cpp
index 3d3596b500880..9798420eb8086 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -245,6 +245,78 @@ void OmpStructureChecker::CheckSIMDNest(const 
parser::OpenMPConstruct &c) {
   }
 }
 
+static bool IsLoopTransforming(llvm::omp::Directive dir) {
+  switch (dir) {
+  // TODO case llvm::omp::Directive::OMPD_flatten:
+  case llvm::omp::Directive::OMPD_fuse:
+  case llvm::omp::Directive::OMPD_interchange:
+  case llvm::omp::Directive::OMPD_nothing:
+  case llvm::omp::Directive::OMPD_reverse:
+  // TODO case llvm::omp::Directive::OMPD_split:
+  case llvm::omp::Directive::OMPD_stripe:
+  case llvm::omp::Directive::OMPD_tile:
+  case llvm::omp::Directive::OMPD_unroll:
+    return true;
+  default:
+    return false;
+  }
+}
+
+void OmpStructureChecker::CheckNestedBlock(const parser::OpenMPLoopConstruct 
&x,
+    const parser::Block &body, size_t &nestedCount) {
+  for (auto &stmt : body) {
+    if (auto *dir{parser::Unwrap<parser::CompilerDirective>(stmt)}) {
+      context_.Say(dir->source,
+          "Compiler directives are not allowed inside OpenMP loop 
constructs"_err_en_US);
+    } else if (parser::Unwrap<parser::DoConstruct>(stmt)) {
+      ++nestedCount;
+    } else if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(stmt)}) {
+      if (!IsLoopTransforming(omp->BeginDir().DirName().v)) {
+        context_.Say(omp->source,
+            "Only loop-transforming OpenMP constructs are allowed inside 
OpenMP loop constructs"_err_en_US);
+      }
+      ++nestedCount;
+    } else if (auto *block{parser::Unwrap<parser::BlockConstruct>(stmt)}) {
+      CheckNestedBlock(x, std::get<parser::Block>(block->t), nestedCount);
+    } else {
+      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);
+    }
+  }
+}
+
+void OmpStructureChecker::CheckNestedConstruct(
+    const parser::OpenMPLoopConstruct &x) {
+  size_t nestedCount{0};
+
+  auto &body{std::get<parser::Block>(x.t)};
+  if (body.empty()) {
+    context_.Say(x.source,
+        "OpenMP loop construct should contain a DO-loop or a 
loop-nest-generating OpenMP construct"_err_en_US);
+  } else {
+    CheckNestedBlock(x, body, nestedCount);
+  }
+}
+
+void OmpStructureChecker::CheckFullUnroll(
+    const parser::OpenMPLoopConstruct &x) {
+  // If the nested construct is a full unroll, then this construct is invalid
+  // since it won't contain a loop.
+  if (const parser::OpenMPLoopConstruct *nested{x.GetNestedConstruct()}) {
+    auto &nestedSpec{nested->BeginDir()};
+    if (nestedSpec.DirName().v == llvm::omp::Directive::OMPD_unroll) {
+      bool isPartial{
+          llvm::any_of(nestedSpec.Clauses().v, [](const parser::OmpClause &c) {
+            return c.Id() == llvm::omp::Clause::OMPC_partial;
+          })};
+      if (!isPartial) {
+        context_.Say(x.source, "OpenMP loop construct cannot apply to a fully 
unrolled loop"_err_en_US);
+      }
+    }
+  }
+}
+
 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
   loopStack_.push_back(&x);
 
@@ -290,6 +362,8 @@ void OmpStructureChecker::Enter(const 
parser::OpenMPLoopConstruct &x) {
     CheckNoBranching(doBlock, beginName.v, beginName.source);
   }
   CheckLoopItrVariableIsInt(x);
+  CheckNestedConstruct(x);
+  CheckFullUnroll(x);
   CheckAssociatedLoopConstraints(x);
   HasInvalidDistributeNesting(x);
   HasInvalidLoopBinding(x);
diff --git a/flang/lib/Semantics/check-omp-structure.h 
b/flang/lib/Semantics/check-omp-structure.h
index 1b84bc5dda471..7139f475e91d6 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -318,6 +318,10 @@ class OmpStructureChecker : public OmpStructureCheckerBase 
{
 
   void CheckDistLinear(const parser::OpenMPLoopConstruct &x);
   void CheckSIMDNest(const parser::OpenMPConstruct &x);
+  void CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
+      const parser::Block &body, size_t &nestedCount);
+  void CheckNestedConstruct(const parser::OpenMPLoopConstruct &x);
+  void CheckFullUnroll(const parser::OpenMPLoopConstruct &x);
   void CheckTargetNest(const parser::OpenMPConstruct &x);
   void CheckTargetUpdate();
   void CheckTaskgraph(const parser::OmpBlockConstruct &x);
diff --git a/flang/lib/Semantics/resolve-directives.cpp 
b/flang/lib/Semantics/resolve-directives.cpp
index c4d103613b587..f69fce8a6b17a 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2352,7 +2352,6 @@ void 
OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop(
 //     construct with multiple associated do-loops are lastprivate.
 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
     const parser::OpenMPLoopConstruct &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
   std::int64_t level{GetContext().associatedLoopLevel};
   if (level <= 0) {
     return;
@@ -2410,12 +2409,6 @@ void 
OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
       }
     }
     CheckAssocLoopLevel(level, GetAssociatedClause());
-  } else {
-    context_.Say(GetContext().directiveSource,
-        "A DO loop must follow the %s directive"_err_en_US,
-        parser::ToUpperCaseLetters(
-            llvm::omp::getOpenMPDirectiveName(GetContext().directive, version)
-                .str()));
   }
 }
 
diff --git a/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90 
b/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90
index cdc628a3b2e64..5200777e54cda 100644
--- a/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90
+++ b/flang/test/Lower/OpenMP/nested-loop-transformation-construct02.f90
@@ -9,7 +9,7 @@ program loop_transformation_construct
   integer :: y(I)
 
   !$omp do
-  !$omp unroll
+  !$omp unroll partial(2)
   do x = 1, I
     y(x) = y(x) * 5
   end do
diff --git a/flang/test/Parser/OpenMP/loop-transformation-construct01.f90 
b/flang/test/Parser/OpenMP/loop-transformation-construct01.f90
index 8b314d8d823db..979dd0c57e8b5 100644
--- a/flang/test/Parser/OpenMP/loop-transformation-construct01.f90
+++ b/flang/test/Parser/OpenMP/loop-transformation-construct01.f90
@@ -10,7 +10,7 @@ subroutine loop_transformation_construct
   integer :: y(I)
 
   !$omp do
-  !$omp unroll
+  !$omp unroll partial(1)
   do i = 1, I
     y(i) = y(i) * 5
   end do
@@ -28,7 +28,8 @@ subroutine loop_transformation_construct
 !CHECK-PARSE-NEXT: | | | | ExecutionPartConstruct -> ExecutableConstruct -> 
OpenMPConstruct -> OpenMPLoopConstruct
 !CHECK-PARSE-NEXT: | | | | | OmpBeginLoopDirective
 !CHECK-PARSE-NEXT: | | | | | | OmpDirectiveName -> llvm::omp::Directive = 
unroll
-!CHECK-PARSE-NEXT: | | | | | | OmpClauseList ->
+!CHECK-PARSE-NEXT: | | | | | | OmpClauseList -> OmpClause -> Partial -> Scalar 
-> Integer -> Constant -> Expr = '1_4'
+!CHECK-PARSE-NEXT: | | | | | | | LiteralConstant -> IntLiteralConstant = '1'
 !CHECK-PARSE-NEXT: | | | | | | Flags = None
 !CHECK-PARSE-NEXT: | | | | | Block
 !CHECK-PARSE-NEXT: | | | | | | ExecutionPartConstruct -> ExecutableConstruct 
-> DoConstruct
@@ -71,7 +72,7 @@ subroutine loop_transformation_construct
 !CHECK-UNPARSE-NEXT:  INTEGER x
 !CHECK-UNPARSE-NEXT:  INTEGER y(i)
 !CHECK-UNPARSE-NEXT: !$OMP DO
-!CHECK-UNPARSE-NEXT: !$OMP UNROLL
+!CHECK-UNPARSE-NEXT: !$OMP UNROLL PARTIAL(1_4)
 !CHECK-UNPARSE-NEXT:  DO i=1_4,i
 !CHECK-UNPARSE-NEXT:    y(int(i,kind=8))=5_4*y(int(i,kind=8))
 !CHECK-UNPARSE-NEXT:  END DO
diff --git a/flang/test/Parser/OpenMP/loop-transformation-construct02.f90 
b/flang/test/Parser/OpenMP/loop-transformation-construct02.f90
index 5b5b591b35f8f..814a885f14a18 100644
--- a/flang/test/Parser/OpenMP/loop-transformation-construct02.f90
+++ b/flang/test/Parser/OpenMP/loop-transformation-construct02.f90
@@ -10,7 +10,7 @@ subroutine loop_transformation_construct
   integer :: y(I)
 
   !$omp do
-  !$omp unroll
+  !$omp unroll partial(1)
   !$omp tile sizes(2)
   do i = 1, I
     y(i) = y(i) * 5
@@ -30,7 +30,8 @@ subroutine loop_transformation_construct
 !CHECK-PARSE-NEXT: | | | | ExecutionPartConstruct -> ExecutableConstruct -> 
OpenMPConstruct -> OpenMPLoopConstruct
 !CHECK-PARSE-NEXT: | | | | | OmpBeginLoopDirective
 !CHECK-PARSE-NEXT: | | | | | | OmpDirectiveName -> llvm::omp::Directive = 
unroll
-!CHECK-PARSE-NEXT: | | | | | | OmpClauseList ->
+!CHECK-PARSE-NEXT: | | | | | | OmpClauseList -> OmpClause -> Partial -> Scalar 
-> Integer -> Constant -> Expr = '1_4'
+!CHECK-PARSE-NEXT: | | | | | | | LiteralConstant -> IntLiteralConstant = '1'
 !CHECK-PARSE-NEXT: | | | | | | Flags = None
 !CHECK-PARSE-NEXT: | | | | | Block
 !CHECK-PARSE-NEXT: | | | | | | ExecutionPartConstruct -> ExecutableConstruct 
-> OpenMPConstruct -> OpenMPLoopConstruct
@@ -84,7 +85,7 @@ subroutine loop_transformation_construct
 !CHECK-UNPARSE-NEXT:  INTEGER x
 !CHECK-UNPARSE-NEXT:  INTEGER y(i)
 !CHECK-UNPARSE-NEXT: !$OMP DO
-!CHECK-UNPARSE-NEXT: !$OMP UNROLL
+!CHECK-UNPARSE-NEXT: !$OMP UNROLL PARTIAL(1_4)
 !CHECK-UNPARSE-NEXT: !$OMP TILE
 !CHECK-UNPARSE-NEXT:  DO i=1_4,i
 !CHECK-UNPARSE-NEXT:    y(int(i,kind=8))=5_4*y(int(i,kind=8))
diff --git a/flang/test/Parser/OpenMP/tile-fail.f90 
b/flang/test/Parser/OpenMP/tile-fail.f90
index 0a92e5bcb6570..3cb0ea96975c8 100644
--- a/flang/test/Parser/OpenMP/tile-fail.f90
+++ b/flang/test/Parser/OpenMP/tile-fail.f90
@@ -14,11 +14,10 @@ subroutine stray_end1
 
 
 !--- stray_end2.f90
-! Semantic error
 
 subroutine stray_end2
   print *
-  !CHECK: error: The END TILE directive must follow the DO loop associated 
with the loop construct
+  !CHECK: error: expected 'END'
   !$omp end tile
 end subroutine
 
@@ -26,7 +25,7 @@ subroutine stray_end2
 !--- stray_begin.f90
 
 subroutine stray_begin
-  !CHECK: error: A DO loop must follow the TILE directive
+  !CHECK: error: OpenMP loop construct should contain a DO-loop or a 
loop-nest-generating OpenMP construct
   !$omp tile sizes(2)
 end subroutine
 
diff --git a/flang/test/Semantics/OpenMP/do21.f90 
b/flang/test/Semantics/OpenMP/do21.f90
index 2f5815c10c11a..e6fe7dd39dd3e 100644
--- a/flang/test/Semantics/OpenMP/do21.f90
+++ b/flang/test/Semantics/OpenMP/do21.f90
@@ -2,26 +2,26 @@
 ! Check for existence of loop following a DO directive
 
 subroutine do1
-  !ERROR: A DO loop must follow the DO directive
+  !ERROR: OpenMP loop construct should contain a DO-loop or a 
loop-nest-generating OpenMP construct
   !$omp do
 end subroutine
 
 subroutine do2
-  !ERROR: A DO loop must follow the PARALLEL DO directive
+  !ERROR: OpenMP loop construct should contain a DO-loop or a 
loop-nest-generating OpenMP construct
   !$omp parallel do
 end subroutine
 
 subroutine do3
-  !ERROR: A DO loop must follow the SIMD directive
+  !ERROR: OpenMP loop construct should contain a DO-loop or a 
loop-nest-generating OpenMP construct
   !$omp simd
 end subroutine
 
 subroutine do4
-  !ERROR: A DO loop must follow the DO SIMD directive
+  !ERROR: OpenMP loop construct should contain a DO-loop or a 
loop-nest-generating OpenMP construct
   !$omp do simd
 end subroutine
 
 subroutine do5
-  !ERROR: A DO loop must follow the LOOP directive
+  !ERROR: OpenMP loop construct should contain a DO-loop or a 
loop-nest-generating OpenMP construct
   !$omp loop
 end subroutine
diff --git a/flang/test/Semantics/OpenMP/loop-association.f90 
b/flang/test/Semantics/OpenMP/loop-association.f90
index 9fac508e6128a..0a3462048000e 100644
--- a/flang/test/Semantics/OpenMP/loop-association.f90
+++ b/flang/test/Semantics/OpenMP/loop-association.f90
@@ -17,11 +17,13 @@
   !$omp end parallel
 
   !$omp parallel do
+  !ERROR: DO CONCURRENT loops cannot form part of a loop nest.
   DO CONCURRENT (i = 1:N)
      a = 3.14
   END DO
 
   !$omp parallel do simd
+  !ERROR: The associated loop of a loop-associated directive cannot be a DO 
WHILE.
   outer: DO WHILE (c > 1)
      inner: do while (b > 100)
         a = 3.14
@@ -32,6 +34,8 @@
 
   ! Accept directives between parallel do and actual loop.
   !$OMP PARALLEL DO
+  !WARNING: Unrecognized compiler directive was ignored [-Wignored-directive]
+  !ERROR: Compiler directives are not allowed inside OpenMP loop constructs
   !DIR$ VECTOR ALIGNED
   DO 20 i=1,N
      a = a + 0.5
@@ -39,11 +43,14 @@
   !$OMP END PARALLEL DO
 
   c = 16
-  !ERROR: DO loop after the PARALLEL DO directive must have loop control
   !$omp parallel do
+  !ERROR: Loop control is not present in the DO LOOP
+  !ERROR: The associated loop of a loop-associated directive cannot be a DO 
without control.
   do
      a = 3.14
      c = c - 1
+     !ERROR: EXIT to construct outside of PARALLEL DO construct is not allowed
+     !ERROR: EXIT statement terminates associated loop of an OpenMP DO 
construct
      if (c < 1) exit
   enddo
 
@@ -57,9 +64,9 @@
      do 100 j=1, N
         a = 3.14
 100     continue
-    !ERROR: The ENDDO directive must follow the DO loop associated with the 
loop construct
     !$omp enddo
 
+  !ERROR: Non-THREADPRIVATE object 'a' in COPYIN clause
   !$omp parallel do copyin(a)
   do i = 1, N
      !$omp parallel do
@@ -74,8 +81,6 @@
   do i = 1, N
   enddo
   !$omp end parallel do
-  !ERROR: The END PARALLEL DO directive must follow the DO loop associated 
with the loop construct
-  !$omp end parallel do
 
   !$omp parallel
   a = 3.0
@@ -84,27 +89,22 @@
   enddo
   !$omp end do simd
 
+  !ERROR: Non-THREADPRIVATE object 'a' in COPYIN clause
   !$omp parallel do copyin(a)
   do i = 1, N
   enddo
   !$omp end parallel
 
   a = 0.0
-  !ERROR: The END PARALLEL DO directive must follow the DO loop associated 
with the loop construct
-  !$omp end parallel do
   !$omp parallel do private(c)
   do i = 1, N
      do j = 1, N
-        !ERROR: A DO loop must follow the PARALLEL DO directive
         !$omp parallel do shared(b)
+        !ERROR: OpenMP loop construct can only contain DO loops or 
loop-nest-generating OpenMP constructs
         a = 3.14
      enddo
-     !ERROR: The END PARALLEL DO directive must follow the DO loop associated 
with the loop construct
-     !$omp end parallel do
   enddo
   a = 1.414
-  !ERROR: The END PARALLEL DO directive must follow the DO loop associated 
with the loop construct
-  !$omp end parallel do
 
   do i = 1, N
      !$omp parallel do
@@ -112,29 +112,22 @@
         a = 3.14
      enddo
   enddo
-  !ERROR: The END PARALLEL DO directive must follow the DO loop associated 
with the loop construct
-  !$omp end parallel do
 
-  !ERROR: A DO loop must follow the PARALLEL DO directive
   !$omp parallel do private(c)
+  !ERROR: OpenMP loop construct can only contain DO loops or 
loop-nest-generating OpenMP constructs
 5 FORMAT (1PE12.4, I10)
   do i=1, N
      a = 3.14
   enddo
-  !ERROR: The END PARALLEL DO directive must follow the DO loop associated 
with the loop construct
-  !$omp end parallel do
 
   !$omp parallel do simd
   do i = 1, N
      a = 3.14
   enddo
   !$omp end parallel do simd
-  !ERROR: The END PARALLEL DO SIMD directive must follow the DO loop 
associated with the loop construct
-  !$omp end parallel do simd
 
-  !ERROR: A DO loop must follow the SIMD directive
   !$omp simd
+    !ERROR: OpenMP loop construct can only contain DO loops or 
loop-nest-generating OpenMP constructs
     a = i + 1
-  !ERROR: The END SIMD directive must follow the DO loop associated with the 
loop construct
   !$omp end simd
 end
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 
b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
index f4628c9533db8..caa8f3f216fec 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
@@ -5,45 +5,45 @@
 subroutine loop_transformation_construct1
   implicit none
 
+  !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop
   !$omp do
-  !ERROR: A DO loop must follow the UNROLL directive
+  !ERROR: OpenMP loop construct should contain a DO-loop or a 
loop-nest-generating OpenMP construct
   !$omp unroll
 end subroutine
 
 subroutine loop_transformation_construct2
   implicit none
-  integer :: i = 5
+  integer, parameter :: i = 5
   integer :: x
   integer :: v(i)
 
   !$omp do
+  !ERROR: At least one of SIZES clause must appear on the TILE directive
   !$omp tile
   do x = 1, i
     v(x) = v(x) * 2
   end do
   !$omp end tile
   !$omp end do
-  !ERROR: The END TILE directive must follow the DO loop associated with the 
loop construct
-  !$omp end tile
 end subroutine
 
-subroutine loop_transformation_construct2
+subroutine loop_transformation_construct3
   implicit none
-  integer :: i = 5
+  integer, parameter :: i = 5
   integer :: x
   integer :: v(i)
 
   !$omp do
-  !ERROR: Only Loop Transformation Constructs or Loop Nests can be nested 
within Loop Constructs
+  !ERROR: Only loop-transforming OpenMP constructs are allowed inside OpenMP 
loop constructs
   !$omp parallel do
   do x = 1, i
     v(x) = v(x) * 2
   end do
 end subroutine
 
-subroutine loop_transformation_construct3
+subroutine loop_transformation_construct4
   implicit none
-  integer :: i = 5
+  integer, parameter :: i = 5
   integer :: x
   integer :: v(i)
 
@@ -51,18 +51,20 @@ subroutine loop_transformation_construct3
   do x = 1, i
     v(x) = v(x) * 2
   end do
-  !ERROR: A DO loop must follow the TILE directive
+  !ERROR: OpenMP loop construct should contain a DO-loop or a 
loop-nest-generating OpenMP construct
+  !ERROR: At least one of SIZES clause must appear on the TILE directive
   !$omp tile
 end subroutine
 
-subroutine loop_transformation_construct4
+subroutine loop_transformation_construct5
   implicit none
-  integer :: i = 5
+  integer, parameter :: i = 5
   integer :: x
   integer :: v(i)
 
   !$omp do
-  !ERROR: If a loop construct has been fully unrolled, it cannot then be tiled
+  !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop
+  !ERROR: At least one of SIZES clause must appear on the TILE directive
   !$omp tile
   !$omp unroll full
   do x = 1, i
@@ -70,14 +72,15 @@ subroutine loop_transformation_construct4
   end do
 end subroutine
 
-subroutine loop_transformation_construct5
+subroutine loop_transformation_construct6
   implicit none
-  integer :: i = 5
+  integer, parameter :: i = 5
   integer :: x
   integer :: v(i)
 
   !$omp do
-  !ERROR: If a loop construct has been fully unrolled, it cannot then be tiled
+  !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop
+  !ERROR: At least one of SIZES clause must appear on the TILE directive
   !$omp tile
   !$omp unroll
   do x = 1, i
@@ -85,13 +88,14 @@ subroutine loop_transformation_construct5
   end do
 end subroutine
 
-subroutine loop_transformation_construct6
+subroutine loop_transformation_construct7
   implicit none
-  integer :: i = 5
+  integer, parameter :: i = 5
   integer :: x
   integer :: v(i)
 
   !$omp do
+  !ERROR: At least one of SIZES clause must appear on the TILE directive
   !$omp tile
   !$omp unroll partial(2)
   do x = 1, i
diff --git a/flang/test/Semantics/OpenMP/tile02.f90 
b/flang/test/Semantics/OpenMP/tile02.f90
index 676796375353f..5b70f94afb09e 100644
--- a/flang/test/Semantics/OpenMP/tile02.f90
+++ b/flang/test/Semantics/OpenMP/tile02.f90
@@ -6,7 +6,7 @@ subroutine on_unroll
   implicit none
   integer i
 
-  !ERROR: If a loop construct has been fully unrolled, it cannot then be tiled
+  !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop
   !$omp tile sizes(2)
   !$omp unroll
   do i = 1, 5

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to