[flang-commits] [flang] [flang][OpenMP] Better diagnostics for invalid or misplaced directives (PR #168885)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Sat Nov 22 10:54:58 PST 2025
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/168885
>From 1e13a5c75fb694d94c057fe267a3b62dd8545d3e Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Mon, 17 Nov 2025 09:36:28 -0600
Subject: [PATCH 01/11] [flang][OpenMP] Fix some typo-like things in test case
---
.../loop-transformation-construct01.f90 | 24 +++++++++----------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
index f718efc32aabf..f4628c9533db8 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
@@ -13,13 +13,13 @@ subroutine loop_transformation_construct1
subroutine loop_transformation_construct2
implicit none
integer :: i = 5
- integer :: y
+ integer :: x
integer :: v(i)
!$omp do
!$omp tile
do x = 1, i
- v(x) = x(x) * 2
+ v(x) = v(x) * 2
end do
!$omp end tile
!$omp end do
@@ -30,26 +30,26 @@ subroutine loop_transformation_construct2
subroutine loop_transformation_construct2
implicit none
integer :: i = 5
- integer :: y
+ integer :: x
integer :: v(i)
!$omp do
!ERROR: Only Loop Transformation Constructs or Loop Nests can be nested within Loop Constructs
!$omp parallel do
do x = 1, i
- v(x) = x(x) * 2
+ v(x) = v(x) * 2
end do
end subroutine
subroutine loop_transformation_construct3
implicit none
integer :: i = 5
- integer :: y
+ integer :: x
integer :: v(i)
!$omp do
do x = 1, i
- v(x) = x(x) * 2
+ v(x) = v(x) * 2
end do
!ERROR: A DO loop must follow the TILE directive
!$omp tile
@@ -58,7 +58,7 @@ subroutine loop_transformation_construct3
subroutine loop_transformation_construct4
implicit none
integer :: i = 5
- integer :: y
+ integer :: x
integer :: v(i)
!$omp do
@@ -66,14 +66,14 @@ subroutine loop_transformation_construct4
!$omp tile
!$omp unroll full
do x = 1, i
- v(x) = x(x) * 2
+ v(x) = v(x) * 2
end do
end subroutine
subroutine loop_transformation_construct5
implicit none
integer :: i = 5
- integer :: y
+ integer :: x
integer :: v(i)
!$omp do
@@ -81,20 +81,20 @@ subroutine loop_transformation_construct5
!$omp tile
!$omp unroll
do x = 1, i
- v(x) = x(x) * 2
+ v(x) = v(x) * 2
end do
end subroutine
subroutine loop_transformation_construct6
implicit none
integer :: i = 5
- integer :: y
+ integer :: x
integer :: v(i)
!$omp do
!$omp tile
!$omp unroll partial(2)
do x = 1, i
- v(x) = x(x) * 2
+ v(x) = v(x) * 2
end do
end subroutine
>From aaea8e6b47808333581569d40e3721050be52bf1 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Fri, 14 Nov 2025 15:52:11 -0600
Subject: [PATCH 02/11] [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
>From 72c046f755c26e7696ccff0ba4e25701293be726 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 19 Nov 2025 14:41:07 -0600
Subject: [PATCH 03/11] [flang][OpenMP] Better diagnostics for invalid or
misplaced directives
Add two more AST nodes, one for a misplaced end-directive, and one for
an invalid string following the OpenMP sentinel (e.g. "!$OMP XYZ").
Emit error messages when either node is encountered in semantic analysis.
---
flang/include/flang/Parser/dump-parse-tree.h | 2 +
flang/include/flang/Parser/parse-tree.h | 20 +++++++-
flang/lib/Parser/executable-parsers.cpp | 2 +
flang/lib/Parser/openmp-parsers.cpp | 51 ++++++++++++-------
flang/lib/Parser/program-parsers.cpp | 3 ++
flang/lib/Parser/type-parsers.h | 3 +-
flang/lib/Parser/unparse.cpp | 10 ++++
flang/lib/Semantics/check-omp-structure.cpp | 23 +++++++++
flang/lib/Semantics/check-omp-structure.h | 6 +++
flang/lib/Semantics/resolve-directives.cpp | 3 ++
flang/test/Parser/OpenMP/fail-construct2.f90 | 2 +-
flang/test/Parser/OpenMP/tile-fail.f90 | 4 +-
.../Semantics/OpenMP/loop-association.f90 | 14 +++++
13 files changed, 119 insertions(+), 24 deletions(-)
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 5ca9deeb9b7f6..32fcd4182bed7 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -755,7 +755,9 @@ class ParseTreeDumper {
NODE(parser, OpenMPDispatchConstruct)
NODE(parser, OpenMPFlushConstruct)
NODE(parser, OpenMPGroupprivate)
+ NODE(parser, OpenMPInvalidDirective)
NODE(parser, OpenMPLoopConstruct)
+ NODE(parser, OpenMPMisplacedEndDirective)
NODE(parser, OpenMPRequiresConstruct)
NODE(parser, OpenMPSectionConstruct)
NODE(parser, OpenMPSectionsConstruct)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 9795a0d2ae25e..003d11721908e 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -269,8 +269,9 @@ struct AccEndCombinedDirective;
struct OpenACCDeclarativeConstruct;
struct OpenACCRoutineConstruct;
struct OpenMPConstruct;
-struct OpenMPLoopConstruct;
struct OpenMPDeclarativeConstruct;
+struct OpenMPInvalidDirective;
+struct OpenMPMisplacedEndDirective;
struct CUFKernelDoConstruct;
// Cooked character stream locations
@@ -406,6 +407,8 @@ struct SpecificationConstruct {
common::Indirection<StructureDef>,
common::Indirection<OpenACCDeclarativeConstruct>,
common::Indirection<OpenMPDeclarativeConstruct>,
+ common::Indirection<OpenMPMisplacedEndDirective>,
+ common::Indirection<OpenMPInvalidDirective>,
common::Indirection<CompilerDirective>>
u;
};
@@ -538,6 +541,8 @@ struct ExecutableConstruct {
common::Indirection<OpenACCConstruct>,
common::Indirection<AccEndCombinedDirective>,
common::Indirection<OpenMPConstruct>,
+ common::Indirection<OpenMPMisplacedEndDirective>,
+ common::Indirection<OpenMPInvalidDirective>,
common::Indirection<CUFKernelDoConstruct>>
u;
};
@@ -5379,6 +5384,19 @@ struct OpenMPConstruct {
u;
};
+// Orphaned !$OMP END <directive>, i.e. not being a part of a valid OpenMP
+// construct.
+struct OpenMPMisplacedEndDirective : public OmpEndDirective {
+ INHERITED_TUPLE_CLASS_BOILERPLATE(
+ OpenMPMisplacedEndDirective, OmpEndDirective);
+};
+
+// Unrecognized string after the !$OMP sentinel.
+struct OpenMPInvalidDirective {
+ using EmptyTrait = std::true_type;
+ CharBlock source;
+};
+
// Parse tree nodes for OpenACC 3.3 directives and clauses
struct AccObject {
diff --git a/flang/lib/Parser/executable-parsers.cpp b/flang/lib/Parser/executable-parsers.cpp
index 8d777a6671495..2241c04f5d26d 100644
--- a/flang/lib/Parser/executable-parsers.cpp
+++ b/flang/lib/Parser/executable-parsers.cpp
@@ -50,6 +50,8 @@ constexpr auto executableConstruct{first(
construct<ExecutableConstruct>(indirect(whereConstruct)),
construct<ExecutableConstruct>(indirect(forallConstruct)),
construct<ExecutableConstruct>(indirect(openmpConstruct)),
+ construct<ExecutableConstruct>(indirect(openmpMisplacedEndDirective)),
+ construct<ExecutableConstruct>(indirect(openmpInvalidDirective)),
construct<ExecutableConstruct>(indirect(Parser<OpenACCConstruct>{})),
construct<ExecutableConstruct>(indirect(compilerDirective)),
construct<ExecutableConstruct>(indirect(Parser<CUFKernelDoConstruct>{})))};
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index d50f45794230b..6592b87f8c62d 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -1573,6 +1573,14 @@ static inline constexpr auto IsMemberOf(const DirectiveSet &dirs) {
};
}
+constexpr auto validEPC{//
+ predicated(executionPartConstruct, [](auto &epc) {
+ return !Unwrap<OpenMPMisplacedEndDirective>(epc) &&
+ !Unwrap<OpenMPMisplacedEndDirective>(epc);
+ })};
+
+constexpr auto validBlock{many(validEPC)};
+
TYPE_PARSER(sourced(construct<OmpDirectiveName>(OmpDirectiveNameParser{})))
OmpDirectiveSpecification static makeFlushFromOldSyntax(Verbatim &&text,
@@ -1630,7 +1638,7 @@ struct StrictlyStructuredBlockParser {
std::optional<resultType> Parse(ParseState &state) const {
// Detect BLOCK construct without parsing the entire thing.
if (lookAhead(skipStuffBeforeStatement >> "BLOCK"_tok).Parse(state)) {
- if (auto epc{Parser<ExecutionPartConstruct>{}.Parse(state)}) {
+ if (auto &&epc{executionPartConstruct.Parse(state)}) {
if (GetFortranBlockConstruct(*epc) != nullptr) {
Block body;
body.emplace_back(std::move(*epc));
@@ -1650,7 +1658,7 @@ struct LooselyStructuredBlockParser {
if (lookAhead(skipStuffBeforeStatement >> "BLOCK"_tok).Parse(state)) {
return std::nullopt;
}
- if (auto &&body{block.Parse(state)}) {
+ if (auto &&body{validBlock.Parse(state)}) {
// Empty body is ok.
return std::move(body);
}
@@ -1674,7 +1682,7 @@ struct NonBlockDoConstructParser {
// 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)}) {
+ while (auto &&epc{attempt(validEPC).Parse(state)}) {
if (auto &&label{GetStatementLabel(*epc)}) {
labels.erase(*label);
}
@@ -1825,7 +1833,7 @@ struct OmpStatementConstructParser {
std::optional<resultType> Parse(ParseState &state) const {
if (auto begin{OmpBeginDirectiveParser(dir_).Parse(state)}) {
Block body;
- if (auto stmt{attempt(Parser<ExecutionPartConstruct>{}).Parse(state)}) {
+ if (auto stmt{attempt(validEPC).Parse(state)}) {
body.emplace_back(std::move(*stmt));
}
// Allow empty block. Check for this in semantics.
@@ -1995,11 +2003,9 @@ struct OmpAtomicConstructParser {
return std::nullopt;
}
- auto exec{Parser<ExecutionPartConstruct>{}};
- auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_atomic}};
TailType tail;
- if (ParseOne(exec, end, tail, state)) {
+ if (ParseOne(tail, state)) {
if (!tail.first.empty()) {
if (auto &&rest{attempt(LimitedTailParser(BodyLimit)).Parse(state)}) {
for (auto &&s : rest->first) {
@@ -2026,13 +2032,12 @@ struct OmpAtomicConstructParser {
// Parse either an ExecutionPartConstruct, or atomic end-directive. When
// successful, record the result in the "tail" provided, otherwise fail.
- static std::optional<Success> ParseOne( //
- Parser<ExecutionPartConstruct> &exec, OmpEndDirectiveParser &end,
- TailType &tail, ParseState &state) {
- auto isRecovery{[](const ExecutionPartConstruct &e) {
- return std::holds_alternative<ErrorRecovery>(e.u);
+ static std::optional<Success> ParseOne(TailType &tail, ParseState &state) {
+ auto isUsable{[](const std::optional<ExecutionPartConstruct> &e) {
+ return e && !std::holds_alternative<ErrorRecovery>(e->u);
}};
- if (auto &&stmt{attempt(exec).Parse(state)}; stmt && !isRecovery(*stmt)) {
+ auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_atomic}};
+ if (auto &&stmt{attempt(validEPC).Parse(state)}; isUsable(stmt)) {
tail.first.emplace_back(std::move(*stmt));
} else if (auto &&dir{attempt(end).Parse(state)}) {
tail.second = std::move(*dir);
@@ -2048,12 +2053,10 @@ struct OmpAtomicConstructParser {
constexpr LimitedTailParser(size_t count) : count_(count) {}
std::optional<resultType> Parse(ParseState &state) const {
- auto exec{Parser<ExecutionPartConstruct>{}};
- auto end{OmpEndDirectiveParser{llvm::omp::Directive::OMPD_atomic}};
TailType tail;
for (size_t i{0}; i != count_; ++i) {
- if (ParseOne(exec, end, tail, state)) {
+ if (ParseOne(tail, state)) {
if (tail.second) {
// Return when the end-directive was parsed.
return std::move(tail);
@@ -2325,9 +2328,9 @@ TYPE_PARSER(sourced(construct<OpenMPSectionsConstruct>(
Parser<OmpBeginSectionsDirective>{} / endOmpLine,
cons( //
construct<OpenMPConstruct>(sourced(
- construct<OpenMPSectionConstruct>(maybe(sectionDir), block))),
- many(construct<OpenMPConstruct>(
- sourced(construct<OpenMPSectionConstruct>(sectionDir, block))))),
+ construct<OpenMPSectionConstruct>(maybe(sectionDir), validBlock))),
+ many(construct<OpenMPConstruct>(sourced(
+ construct<OpenMPSectionConstruct>(sectionDir, validBlock))))),
maybe(Parser<OmpEndSectionsDirective>{} / endOmpLine))))
static bool IsExecutionPart(const OmpDirectiveName &name) {
@@ -2402,4 +2405,14 @@ static constexpr DirectiveSet GetLoopDirectives() {
TYPE_PARSER(sourced(construct<OpenMPLoopConstruct>(
OmpLoopConstructParser(GetLoopDirectives()))))
+static constexpr DirectiveSet GetAllDirectives() { //
+ return ~DirectiveSet{};
+}
+
+TYPE_PARSER(construct<OpenMPMisplacedEndDirective>(
+ OmpEndDirectiveParser{GetAllDirectives()}))
+
+TYPE_PARSER( //
+ startOmpLine >> sourced(construct<OpenMPInvalidDirective>(
+ !OmpDirectiveNameParser{} >> SkipTo<'\n'>{})))
} // namespace Fortran::parser
diff --git a/flang/lib/Parser/program-parsers.cpp b/flang/lib/Parser/program-parsers.cpp
index 740dbbfab9db7..303335934a37a 100644
--- a/flang/lib/Parser/program-parsers.cpp
+++ b/flang/lib/Parser/program-parsers.cpp
@@ -201,6 +201,9 @@ TYPE_CONTEXT_PARSER("specification construct"_en_US,
construct<SpecificationConstruct>(
indirect(openaccDeclarativeConstruct)),
construct<SpecificationConstruct>(indirect(openmpDeclarativeConstruct)),
+ construct<SpecificationConstruct>(
+ indirect(openmpMisplacedEndDirective)),
+ construct<SpecificationConstruct>(indirect(openmpInvalidDirective)),
construct<SpecificationConstruct>(indirect(compilerDirective))))
// R513 other-specification-stmt ->
diff --git a/flang/lib/Parser/type-parsers.h b/flang/lib/Parser/type-parsers.h
index 3900c5a86c874..142aa226893b6 100644
--- a/flang/lib/Parser/type-parsers.h
+++ b/flang/lib/Parser/type-parsers.h
@@ -139,7 +139,8 @@ constexpr Parser<OpenACCDeclarativeConstruct> openaccDeclarativeConstruct;
constexpr Parser<OpenMPConstruct> openmpConstruct;
constexpr Parser<OpenMPExecDirective> openmpExecDirective;
constexpr Parser<OpenMPDeclarativeConstruct> openmpDeclarativeConstruct;
-constexpr Parser<OmpEndLoopDirective> ompEndLoopDirective;
+constexpr Parser<OpenMPMisplacedEndDirective> openmpMisplacedEndDirective;
+constexpr Parser<OpenMPInvalidDirective> openmpInvalidDirective;
constexpr Parser<IntrinsicVectorTypeSpec> intrinsicVectorTypeSpec; // Extension
constexpr Parser<VectorTypeSpec> vectorTypeSpec; // Extension
constexpr Parser<UnsignedTypeSpec> unsignedTypeSpec; // Extension
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index f81200d092b11..3854d33d46d48 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2706,6 +2706,16 @@ class UnparseVisitor {
Put("\n");
EndOpenMP();
}
+ void Unparse(const OpenMPMisplacedEndDirective &x) {
+ Unparse(static_cast<const OmpEndDirective &>(x));
+ }
+ void Unparse(const OpenMPInvalidDirective &x) {
+ BeginOpenMP();
+ Word("!$OMP ");
+ Put(parser::ToUpperCaseLetters(x.source.ToString()));
+ Put("\n");
+ EndOpenMP();
+ }
void Unparse(const BasedPointer &x) {
Put('('), Walk(std::get<0>(x.t)), Put(","), Walk(std::get<1>(x.t));
Walk("(", std::get<std::optional<ArraySpec>>(x.t), ")"), Put(')');
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 37b4404cc598f..6c18b7a8ac4bd 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5474,6 +5474,29 @@ void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) {
}
}
+void OmpStructureChecker::Enter(const parser::OpenMPMisplacedEndDirective &x) {
+ invalidState_ = true;
+ context_.Say(x.DirName().source, "Misplaced OpenMP end-directive"_err_en_US);
+ PushContextAndClauseSets(
+ x.DirName().source, llvm::omp::Directive::OMPD_unknown);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPMisplacedEndDirective &x) {
+ dirContext_.pop_back();
+ invalidState_ = false;
+}
+
+void OmpStructureChecker::Enter(const parser::OpenMPInvalidDirective &x) {
+ invalidState_ = true;
+ context_.Say(x.source, "Invalid OpenMP directive"_err_en_US);
+ PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_unknown);
+}
+
+void OmpStructureChecker::Leave(const parser::OpenMPInvalidDirective &x) {
+ dirContext_.pop_back();
+ invalidState_ = false;
+}
+
// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
#define CHECK_SIMPLE_CLAUSE(X, Y) \
void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 7139f475e91d6..965f18bfbb352 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -94,6 +94,11 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void Enter(const parser::OpenMPDeclarativeConstruct &);
void Leave(const parser::OpenMPDeclarativeConstruct &);
+ void Enter(const parser::OpenMPMisplacedEndDirective &);
+ void Leave(const parser::OpenMPMisplacedEndDirective &);
+ void Enter(const parser::OpenMPInvalidDirective &);
+ void Leave(const parser::OpenMPInvalidDirective &);
+
void Enter(const parser::OpenMPLoopConstruct &);
void Leave(const parser::OpenMPLoopConstruct &);
void Enter(const parser::OmpEndLoopDirective &);
@@ -389,6 +394,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
std::vector<LoopConstruct> loopStack_;
// Scopes for scoping units.
std::vector<const Scope *> scopeStack_;
+ bool invalidState_{false}; // Set during visiting OpenMPMisplacedEndDirective
enum class PartKind : int {
// There are also other "parts", such as internal-subprogram-part, etc,
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index f69fce8a6b17a..4fadcf73c5cbb 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -529,6 +529,9 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
void Post(const parser::OmpBeginLoopDirective &) {
GetContext().withinConstruct = true;
}
+ bool Pre(const parser::OpenMPMisplacedEndDirective &x) { return false; }
+ bool Pre(const parser::OpenMPInvalidDirective &x) { return false; }
+
bool Pre(const parser::DoConstruct &);
bool Pre(const parser::OpenMPSectionsConstruct &);
diff --git a/flang/test/Parser/OpenMP/fail-construct2.f90 b/flang/test/Parser/OpenMP/fail-construct2.f90
index b7f5736d1329b..3798c3dae3f0d 100644
--- a/flang/test/Parser/OpenMP/fail-construct2.f90
+++ b/flang/test/Parser/OpenMP/fail-construct2.f90
@@ -1,5 +1,5 @@
! RUN: not %flang_fc1 -fsyntax-only -fopenmp %s 2>&1 | FileCheck %s
-! CHECK: error: expected OpenMP construct
+! CHECK: error: Invalid OpenMP directive
!$omp dummy
end
diff --git a/flang/test/Parser/OpenMP/tile-fail.f90 b/flang/test/Parser/OpenMP/tile-fail.f90
index 3cb0ea96975c8..a69261a927961 100644
--- a/flang/test/Parser/OpenMP/tile-fail.f90
+++ b/flang/test/Parser/OpenMP/tile-fail.f90
@@ -8,7 +8,7 @@
! Parser error
subroutine stray_end1
- !CHECK: error: expected OpenMP construct
+ !CHECK: error: Misplaced OpenMP end-directive
!$omp end tile
end subroutine
@@ -17,7 +17,7 @@ subroutine stray_end1
subroutine stray_end2
print *
- !CHECK: error: expected 'END'
+ !CHECK: error: Misplaced OpenMP end-directive
!$omp end tile
end subroutine
diff --git a/flang/test/Semantics/OpenMP/loop-association.f90 b/flang/test/Semantics/OpenMP/loop-association.f90
index 0a3462048000e..603bfdcc0a4e9 100644
--- a/flang/test/Semantics/OpenMP/loop-association.f90
+++ b/flang/test/Semantics/OpenMP/loop-association.f90
@@ -81,6 +81,8 @@
do i = 1, N
enddo
!$omp end parallel do
+ !ERROR: Misplaced OpenMP end-directive
+ !$omp end parallel do
!$omp parallel
a = 3.0
@@ -96,6 +98,8 @@
!$omp end parallel
a = 0.0
+ !ERROR: Misplaced OpenMP end-directive
+ !$omp end parallel do
!$omp parallel do private(c)
do i = 1, N
do j = 1, N
@@ -103,8 +107,12 @@
!ERROR: OpenMP loop construct can only contain DO loops or loop-nest-generating OpenMP constructs
a = 3.14
enddo
+ !ERROR: Misplaced OpenMP end-directive
+ !$omp end parallel do
enddo
a = 1.414
+ !ERROR: Misplaced OpenMP end-directive
+ !$omp end parallel do
do i = 1, N
!$omp parallel do
@@ -112,6 +120,8 @@
a = 3.14
enddo
enddo
+ !ERROR: Misplaced OpenMP end-directive
+ !$omp end parallel do
!$omp parallel do private(c)
!ERROR: OpenMP loop construct can only contain DO loops or loop-nest-generating OpenMP constructs
@@ -119,12 +129,16 @@
do i=1, N
a = 3.14
enddo
+ !ERROR: Misplaced OpenMP end-directive
+ !$omp end parallel do
!$omp parallel do simd
do i = 1, N
a = 3.14
enddo
!$omp end parallel do simd
+ !ERROR: Misplaced OpenMP end-directive
+ !$omp end parallel do simd
!$omp simd
!ERROR: OpenMP loop construct can only contain DO loops or loop-nest-generating OpenMP constructs
>From 50ec7a3478b2cc0cd2c258ef2ce12dc3eda70d95 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 20 Nov 2025 09:17:30 -0600
Subject: [PATCH 04/11] format
---
flang/lib/Semantics/check-omp-loop.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 9798420eb8086..b321012fc5421 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -311,7 +311,8 @@ void OmpStructureChecker::CheckFullUnroll(
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);
+ context_.Say(x.source,
+ "OpenMP loop construct cannot apply to a fully unrolled loop"_err_en_US);
}
}
}
>From 4d92961ae2ff6f2c45a26fca889c7e097d3fd4d4 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 20 Nov 2025 11:04:52 -0600
Subject: [PATCH 05/11] Remove invalidState_
---
flang/lib/Semantics/check-omp-structure.cpp | 4 ----
flang/lib/Semantics/check-omp-structure.h | 1 -
2 files changed, 5 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 6c18b7a8ac4bd..3bae781ab2446 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5475,7 +5475,6 @@ void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) {
}
void OmpStructureChecker::Enter(const parser::OpenMPMisplacedEndDirective &x) {
- invalidState_ = true;
context_.Say(x.DirName().source, "Misplaced OpenMP end-directive"_err_en_US);
PushContextAndClauseSets(
x.DirName().source, llvm::omp::Directive::OMPD_unknown);
@@ -5483,18 +5482,15 @@ void OmpStructureChecker::Enter(const parser::OpenMPMisplacedEndDirective &x) {
void OmpStructureChecker::Leave(const parser::OpenMPMisplacedEndDirective &x) {
dirContext_.pop_back();
- invalidState_ = false;
}
void OmpStructureChecker::Enter(const parser::OpenMPInvalidDirective &x) {
- invalidState_ = true;
context_.Say(x.source, "Invalid OpenMP directive"_err_en_US);
PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_unknown);
}
void OmpStructureChecker::Leave(const parser::OpenMPInvalidDirective &x) {
dirContext_.pop_back();
- invalidState_ = false;
}
// Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 965f18bfbb352..2990710f92d1d 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -394,7 +394,6 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
std::vector<LoopConstruct> loopStack_;
// Scopes for scoping units.
std::vector<const Scope *> scopeStack_;
- bool invalidState_{false}; // Set during visiting OpenMPMisplacedEndDirective
enum class PartKind : int {
// There are also other "parts", such as internal-subprogram-part, etc,
>From a032a55d4e3f4e54324a027b6a73507a6cd3b19f Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Fri, 21 Nov 2025 14:33:15 -0600
Subject: [PATCH 06/11] Rebase + parser updates to reflect changes in main
---
flang/lib/Parser/openmp-parsers.cpp | 102 +++++++++++++++------
flang/lib/Semantics/check-omp-loop.cpp | 4 +-
flang/lib/Semantics/resolve-directives.cpp | 1 +
3 files changed, 80 insertions(+), 27 deletions(-)
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 68720913ac2ca..a0c94296de5ed 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -58,6 +58,35 @@ constexpr auto endOmpLine = space >> endOfLine;
constexpr auto logicalConstantExpr{logical(constantExpr)};
constexpr auto scalarLogicalConstantExpr{scalar(logicalConstantExpr)};
+// Parser that wraps the result of another parser into a Block. If the given
+// parser succeeds, the result is a block containing the ExecutionPartConstruct
+// result of the argument parser. Otherwise the parser fails.
+template <typename ExecParser> struct AsBlockParser {
+ using resultType = Block;
+ static_assert(
+ std::is_same_v<typename ExecParser::resultType, ExecutionPartConstruct>);
+
+ constexpr AsBlockParser(ExecParser epc) : epc_(epc) {}
+ std::optional<resultType> Parse(ParseState &state) const {
+ if (auto &&exec{attempt(epc_).Parse(state)}) {
+ Block body;
+ body.push_back(std::move(*exec));
+ return body;
+ }
+ return std::nullopt;
+ }
+
+private:
+ const ExecParser epc_;
+};
+
+template <typename ExecParser,
+ typename = std::enable_if<std::is_same_v<typename ExecParser::resultType,
+ ExecutionPartConstruct>>>
+constexpr auto asBlock(ExecParser epc) {
+ return AsBlockParser<ExecParser>(epc);
+}
+
// Given a parser for a single element, and a parser for a list of elements
// of the same type, create a parser that constructs the entire list by having
// the single element be the head of the list, and the rest be the tail.
@@ -1674,16 +1703,26 @@ struct NonBlockDoConstructParser {
// 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)}) {
+ auto processEpc{[&](ExecutionPartConstruct &&epc) {
+ if (auto &&label{GetStatementLabel(epc)}) {
labels.erase(*label);
}
- if (auto *labelDo{Unwrap<LabelDoStmt>(*epc)}) {
+ if (auto *labelDo{Unwrap<LabelDoStmt>(epc)}) {
labels.insert(std::get<Label>(labelDo->t));
}
- body.push_back(std::move(*epc));
- if (labels.empty()) {
- break;
+ body.push_back(std::move(epc));
+ }};
+
+ auto nonBlockDo{predicated(executionPartConstruct,
+ [](auto &epc) { return Unwrap<LabelDoStmt>(epc); })};
+
+ if (auto &&nbd{nonBlockDo.Parse(state)}) {
+ processEpc(std::move(*nbd));
+ while (auto &&epc{attempt(executionPartConstruct).Parse(state)}) {
+ processEpc(std::move(*epc));
+ if (labels.empty()) {
+ break;
+ }
}
}
@@ -1885,28 +1924,39 @@ struct OmpLoopConstructParser {
constexpr OmpLoopConstructParser(DirectiveSet dirs) : dirs_(dirs) {}
std::optional<resultType> Parse(ParseState &state) const {
+ auto ompLoopConstruct{asBlock(predicated(executionPartConstruct,
+ [](auto &epc) { return Unwrap<OpenMPLoopConstruct>(epc); }))};
+ auto loopItem{LoopNestParser{} || ompLoopConstruct};
+
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 loopDir{begin->DirName().v};
+ auto assoc{llvm::omp::getDirectiveAssociation(loopDir)};
+ if (assoc == llvm::omp::Association::LoopNest) {
+ if (auto &&item{attempt(loopItem).Parse(state)}) {
+ auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};
+ return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
+ std::move(*item),
+ llvm::transformOptional(std::move(*end),
+ [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};
+ } else if (auto &&empty{pure<Block>().Parse(state)}) {
+ // Allow empty body.
+ auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};
+ return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
+ std::move(*empty),
+ llvm::transformOptional(std::move(*end),
+ [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};
}
- 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)); })};
+ } else if (assoc == llvm::omp::Association::LoopSeq) {
+ // Parse loop sequence as a block.
+ if (auto &&body{block.Parse(state)}) {
+ auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};
+ return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
+ std::move(*body),
+ llvm::transformOptional(std::move(*end),
+ [](auto &&s) { return OmpEndLoopDirective(std::move(s)); })};
+ }
+ } else {
+ llvm_unreachable("Unexpected association");
}
}
return std::nullopt;
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index f5885c700f445..6d83ee62c3b73 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -550,7 +550,9 @@ void OmpStructureChecker::CheckLooprangeBounds(
void OmpStructureChecker::CheckNestedFuse(
const parser::OpenMPLoopConstruct &x) {
auto &loopConsList{std::get<parser::Block>(x.t)};
- assert(loopConsList.size() == 1 && "Not Expecting a loop sequence");
+ if (loopConsList.empty()) {
+ return;
+ }
const auto *ompConstruct{parser::omp::GetOmpLoop(loopConsList.front())};
if (!ompConstruct) {
return;
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 6aa606e9524b4..bd6c1f8e4c776 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2417,6 +2417,7 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
}
CheckAssocLoopLevel(level, GetAssociatedClause());
} else {
+ unsigned version{context_.langOptions().OpenMPVersion};
context_.Say(GetContext().directiveSource,
"A DO loop must follow the %s directive"_err_en_US,
parser::ToUpperCaseLetters(
>From 13f734504f83238b257ad1131314a4201de2a95c Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Sat, 22 Nov 2025 09:13:21 -0600
Subject: [PATCH 07/11] Temporary test changes
---
flang/test/Parser/OpenMP/fail-looprange.f90 | 6 +++---
flang/test/Semantics/OpenMP/clause-validity01.f90 | 5 +----
flang/test/Semantics/OpenMP/loop-association.f90 | 9 +++++----
.../Semantics/OpenMP/loop-transformation-construct02.f90 | 1 +
.../Semantics/OpenMP/loop-transformation-construct03.f90 | 1 +
5 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/flang/test/Parser/OpenMP/fail-looprange.f90 b/flang/test/Parser/OpenMP/fail-looprange.f90
index ebe3480b44f12..1c8a080871389 100644
--- a/flang/test/Parser/OpenMP/fail-looprange.f90
+++ b/flang/test/Parser/OpenMP/fail-looprange.f90
@@ -1,11 +1,11 @@
! RUN: not %flang_fc1 -fsyntax-only -fopenmp %s 2>&1 | FileCheck %s
-! CHECK: error: expected end of line
+! CHECK: error:
!$omp fuse looprange
-! CHECK: error: expected end of line
+! CHECK: error:
!$omp fuse looprange(1)
-! CHECK: error: expected end of line
+! CHECK: error:
!$omp fuse looprange(1,2,3)
end
diff --git a/flang/test/Semantics/OpenMP/clause-validity01.f90 b/flang/test/Semantics/OpenMP/clause-validity01.f90
index 5f74978f38279..fda15eb9876ef 100644
--- a/flang/test/Semantics/OpenMP/clause-validity01.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity01.f90
@@ -252,8 +252,6 @@
!$omp parallel do if(target:a>1.)
do i = 1, N
enddo
- !ERROR: Unmatched END SIMD directive
- !$omp end simd
! 2.7.2 sections-clause -> private-clause |
! firstprivate-clause |
@@ -574,8 +572,7 @@
do i = 1, N
a = a + 3.14
enddo
- !ERROR: Unmatched END TASKLOOP directive
- !$omp end taskloop
+ !$omp end taskloop simd
!ERROR: GRAINSIZE and NUM_TASKS clauses are mutually exclusive and may not appear on the same TASKLOOP SIMD directive
!$omp taskloop simd num_tasks(3) grainsize(2)
diff --git a/flang/test/Semantics/OpenMP/loop-association.f90 b/flang/test/Semantics/OpenMP/loop-association.f90
index 0a3462048000e..b2898d3967a25 100644
--- a/flang/test/Semantics/OpenMP/loop-association.f90
+++ b/flang/test/Semantics/OpenMP/loop-association.f90
@@ -33,6 +33,7 @@
END DO outer
! Accept directives between parallel do and actual loop.
+ !ERROR: A DO loop must follow the PARALLEL DO directive
!$OMP PARALLEL DO
!WARNING: Unrecognized compiler directive was ignored [-Wignored-directive]
!ERROR: Compiler directives are not allowed inside OpenMP loop constructs
@@ -99,8 +100,8 @@
!$omp parallel do private(c)
do i = 1, N
do j = 1, N
+ !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp parallel do shared(b)
- !ERROR: OpenMP loop construct can only contain DO loops or loop-nest-generating OpenMP constructs
a = 3.14
enddo
enddo
@@ -113,8 +114,8 @@
enddo
enddo
+ !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$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
@@ -126,8 +127,8 @@
enddo
!$omp end parallel do simd
+ !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp simd
- !ERROR: OpenMP loop construct can only contain DO loops or loop-nest-generating OpenMP constructs
- a = i + 1
!$omp end simd
+ a = i + 1
end
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
index 7cf7b15c41a62..3c07d6d08508a 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
@@ -2,6 +2,7 @@
! nested Loop Transformation Constructs
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+!XFAIL: *
subroutine loop_transformation_construct1
implicit none
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90
index 88c3bd2bae4e0..ab6b1bd7d67f9 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90
@@ -1,6 +1,7 @@
! Testing the Semantic failure of forming loop sequences under regular OpenMP directives
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+!XFAIL: *
subroutine loop_transformation_construct1
implicit none
>From da686bef8fa571cfbda3aa48eed74057d8e8ef61 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Sat, 22 Nov 2025 09:47:17 -0600
Subject: [PATCH 08/11] Restore tests
---
flang/test/Semantics/OpenMP/clause-validity01.f90 | 5 ++++-
flang/test/Semantics/OpenMP/loop-association.f90 | 3 ++-
.../OpenMP/loop-transformation-construct02.f90 | 12 ++++++------
.../OpenMP/loop-transformation-construct03.f90 | 5 ++---
4 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/flang/test/Semantics/OpenMP/clause-validity01.f90 b/flang/test/Semantics/OpenMP/clause-validity01.f90
index fda15eb9876ef..ad0b764b2cc28 100644
--- a/flang/test/Semantics/OpenMP/clause-validity01.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity01.f90
@@ -252,6 +252,8 @@
!$omp parallel do if(target:a>1.)
do i = 1, N
enddo
+ !ERROR: Misplaced OpenMP end-directive
+ !$omp end simd
! 2.7.2 sections-clause -> private-clause |
! firstprivate-clause |
@@ -572,7 +574,8 @@
do i = 1, N
a = a + 3.14
enddo
- !$omp end taskloop simd
+ !ERROR: Misplaced OpenMP end-directive
+ !$omp end taskloop
!ERROR: GRAINSIZE and NUM_TASKS clauses are mutually exclusive and may not appear on the same TASKLOOP SIMD directive
!$omp taskloop simd num_tasks(3) grainsize(2)
diff --git a/flang/test/Semantics/OpenMP/loop-association.f90 b/flang/test/Semantics/OpenMP/loop-association.f90
index 8f696546c05e0..6ca95496c0e9c 100644
--- a/flang/test/Semantics/OpenMP/loop-association.f90
+++ b/flang/test/Semantics/OpenMP/loop-association.f90
@@ -143,6 +143,7 @@
!ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp simd
+ a = i + 1
+ !ERROR: Misplaced OpenMP end-directive
!$omp end simd
- a = i + 1
end
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
index 3c07d6d08508a..1b15c938915cd 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
@@ -2,13 +2,12 @@
! nested Loop Transformation Constructs
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
-!XFAIL: *
subroutine loop_transformation_construct1
implicit none
!$omp do
- !ERROR: The FUSE construct requires the END FUSE directive
+ !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp fuse
end subroutine
@@ -16,7 +15,7 @@ subroutine loop_transformation_construct2
implicit none
!$omp do
- !ERROR: A DO loop must follow the FUSE directive
+ !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp fuse
!$omp end fuse
end subroutine
@@ -37,7 +36,7 @@ subroutine loop_transformation_construct3
end do
!$omp end fuse
!$omp end do
- !ERROR: The END FUSE directive must follow the DO loop associated with the loop construct
+ !ERROR: Misplaced OpenMP end-directive
!$omp end fuse
end subroutine
@@ -51,7 +50,7 @@ subroutine loop_transformation_construct4
do x = 1, i
v(x) = v(x) * 2
end do
- !ERROR: A DO loop must follow the FUSE directive
+ !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp fuse
!$omp end fuse
end subroutine
@@ -63,7 +62,7 @@ subroutine loop_transformation_construct5
integer :: v(i)
!$omp do
- !ERROR: If a loop construct has been fully unrolled, it cannot then be further transformed
+ !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop
!$omp fuse
!$omp unroll full
do x = 1, i
@@ -81,6 +80,7 @@ subroutine loop_transformation_construct6
integer :: x
integer :: v(i)
+ !ERROR: The loop sequence following the DO construct must be fully fused first.
!$omp do
!$omp fuse looprange(1,1)
!$omp unroll partial(2)
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90
index ab6b1bd7d67f9..578c55b3c0aff 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90
@@ -1,7 +1,6 @@
! Testing the Semantic failure of forming loop sequences under regular OpenMP directives
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
-!XFAIL: *
subroutine loop_transformation_construct1
implicit none
@@ -17,7 +16,7 @@ subroutine loop_transformation_construct1
do x = 1, i
v(x) = v(x) * 2
end do
- !ERROR: The END DO directive must follow the DO loop associated with the loop construct
+ !ERROR: Misplaced OpenMP end-directive
!$omp end do
end subroutine
@@ -35,6 +34,6 @@ subroutine loop_transformation_construct2
do x = 1, i
v(x) = v(x) * 2
end do
- !ERROR: The END TILE directive must follow the DO loop associated with the loop construct
+ !ERROR: Misplaced OpenMP end-directive
!$omp end tile
end subroutine
>From 31578653d009a528b5e0f4a738840cbb83e3c372 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Sat, 22 Nov 2025 11:26:08 -0600
Subject: [PATCH 09/11] Update loop construct parser
---
flang/lib/Parser/openmp-parsers.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 993b8c5f2ec07..77b0e3f5435ce 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -1956,7 +1956,7 @@ struct OmpLoopConstructParser {
}
} else if (assoc == llvm::omp::Association::LoopSeq) {
// Parse loop sequence as a block.
- if (auto &&body{block.Parse(state)}) {
+ if (auto &&body{validBlock.Parse(state)}) {
auto end{maybe(OmpEndDirectiveParser{loopDir}).Parse(state)};
return OpenMPLoopConstruct{OmpBeginLoopDirective(std::move(*begin)),
std::move(*body),
>From 4a0b1a0f4d8a5452de8652717f7209b9b6ae7879 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Sat, 22 Nov 2025 12:54:24 -0600
Subject: [PATCH 10/11] Update loop-transformation-construct02.f90
---
flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 | 1 -
1 file changed, 1 deletion(-)
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
index 974f31b928883..1b15c938915cd 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
@@ -2,7 +2,6 @@
! nested Loop Transformation Constructs
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
-!XFAIL: *
subroutine loop_transformation_construct1
implicit none
>From e37a530dbc0a5ccc4bb3c3a65033f6dbb9ba1f69 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Sat, 22 Nov 2025 12:54:46 -0600
Subject: [PATCH 11/11] Update loop-transformation-construct03.f90
---
flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 | 1 -
1 file changed, 1 deletion(-)
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90
index 1e37d6105b2c8..578c55b3c0aff 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct03.f90
@@ -1,7 +1,6 @@
! Testing the Semantic failure of forming loop sequences under regular OpenMP directives
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
-!XFAIL: *
subroutine loop_transformation_construct1
implicit none
More information about the flang-commits
mailing list