[flang-commits] [flang] 6e01ea4 - [flang][OpenMP] Generalize checks of loop construct structure (#170735)
via flang-commits
flang-commits at lists.llvm.org
Mon Dec 15 05:20:10 PST 2025
Author: Krzysztof Parzyszek
Date: 2025-12-15T07:20:05-06:00
New Revision: 6e01ea4babc8b5f41cc1541ab73af4388b1744f3
URL: https://github.com/llvm/llvm-project/commit/6e01ea4babc8b5f41cc1541ab73af4388b1744f3
DIFF: https://github.com/llvm/llvm-project/commit/6e01ea4babc8b5f41cc1541ab73af4388b1744f3.diff
LOG: [flang][OpenMP] Generalize checks of loop construct structure (#170735)
For an OpenMP loop construct, count how many loops will effectively be
contained in its associated block. For constructs that are loop-nest
associated this number should be 1. Report cases where this number is
different.
Take into account that the block associated with a loop construct can
contain compiler directives.
Added:
Modified:
flang/lib/Semantics/check-omp-loop.cpp
flang/lib/Semantics/check-omp-structure.h
flang/test/Parser/OpenMP/tile-fail.f90
flang/test/Semantics/OpenMP/do21.f90
flang/test/Semantics/OpenMP/loop-association.f90
flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90
flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
flang/test/Semantics/OpenMP/loop-transformation-construct04.f90
Removed:
################################################################################
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 726dbe865834d..bc96544a8a9c7 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -37,6 +37,14 @@
#include <tuple>
#include <variant>
+namespace Fortran::semantics {
+static bool IsLoopTransforming(llvm::omp::Directive dir);
+static bool IsFullUnroll(const parser::OpenMPLoopConstruct &x);
+static std::optional<size_t> CountGeneratedLoops(
+ const parser::ExecutionPartConstruct &epc);
+static std::optional<size_t> CountGeneratedLoops(const parser::Block &block);
+} // namespace Fortran::semantics
+
namespace {
using namespace Fortran;
@@ -262,23 +270,20 @@ static bool IsLoopTransforming(llvm::omp::Directive dir) {
}
}
-void OmpStructureChecker::CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
- const parser::Block &body, size_t &nestedCount) {
+void OmpStructureChecker::CheckNestedBlock(
+ const parser::OpenMPLoopConstruct &x, const parser::Block &body) {
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"_warn_en_US);
- } else if (parser::Unwrap<parser::DoConstruct>(stmt)) {
- ++nestedCount;
} else if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(stmt)}) {
if (!IsLoopTransforming(omp->BeginDir().DirId())) {
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 {
+ CheckNestedBlock(x, std::get<parser::Block>(block->t));
+ } else if (!parser::Unwrap<parser::DoConstruct>(stmt)) {
parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)};
context_.Say(source,
"OpenMP loop construct can only contain DO loops or loop-nest-generating OpenMP constructs"_err_en_US);
@@ -286,9 +291,78 @@ void OmpStructureChecker::CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
}
}
+static bool IsFullUnroll(const parser::OpenMPLoopConstruct &x) {
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
+
+ if (beginSpec.DirName().v == llvm::omp::Directive::OMPD_unroll) {
+ return llvm::none_of(beginSpec.Clauses().v, [](const parser::OmpClause &c) {
+ return c.Id() == llvm::omp::Clause::OMPC_partial;
+ });
+ }
+ return false;
+}
+
+static std::optional<size_t> CountGeneratedLoops(
+ const parser::ExecutionPartConstruct &epc) {
+ if (parser::Unwrap<parser::DoConstruct>(epc)) {
+ return 1;
+ }
+
+ auto &omp{DEREF(parser::Unwrap<parser::OpenMPLoopConstruct>(epc))};
+ const parser::OmpDirectiveSpecification &beginSpec{omp.BeginDir()};
+ llvm::omp::Directive dir{beginSpec.DirName().v};
+
+ // TODO: Handle split, apply.
+ if (IsFullUnroll(omp)) {
+ return std::nullopt;
+ }
+ if (dir == llvm::omp::Directive::OMPD_fuse) {
+ auto rangeAt{
+ llvm::find_if(beginSpec.Clauses().v, [](const parser::OmpClause &c) {
+ return c.Id() == llvm::omp::Clause::OMPC_looprange;
+ })};
+ if (rangeAt == beginSpec.Clauses().v.end()) {
+ return std::nullopt;
+ }
+
+ auto *loopRange{parser::Unwrap<parser::OmpLooprangeClause>(*rangeAt)};
+ std::optional<int64_t> count{GetIntValue(std::get<1>(loopRange->t))};
+ if (!count || *count <= 0) {
+ return std::nullopt;
+ }
+ if (auto nestedCount{CountGeneratedLoops(std::get<parser::Block>(omp.t))}) {
+ if (static_cast<size_t>(*count) <= *nestedCount)
+ return 1 + *nestedCount - static_cast<size_t>(*count);
+ }
+ return std::nullopt;
+ }
+
+ // For every other loop construct return 1.
+ return 1;
+}
+
+static std::optional<size_t> CountGeneratedLoops(const parser::Block &block) {
+ // Count the number of loops in the associated block. If there are any
+ // malformed construct in there, getting the number may be meaningless.
+ // These issues will be diagnosed elsewhere, and we should not emit any
+ // messages about a potentially incorrect loop count.
+ // In such cases reset the count to nullopt. Once it becomes nullopt,
+ // keep it that way.
+ std::optional<size_t> numLoops{0};
+ for (auto &epc : parser::omp::LoopRange(block)) {
+ if (auto genCount{CountGeneratedLoops(epc)}) {
+ *numLoops += *genCount;
+ } else {
+ numLoops = std::nullopt;
+ break;
+ }
+ }
+ return numLoops;
+}
+
void OmpStructureChecker::CheckNestedConstruct(
const parser::OpenMPLoopConstruct &x) {
- size_t nestedCount{0};
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
// End-directive is not allowed in such cases:
// do 100 i = ...
@@ -296,7 +370,6 @@ void OmpStructureChecker::CheckNestedConstruct(
// do 100 j = ...
// 100 continue
// !$omp end do ! error
- const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
auto &flags{std::get<parser::OmpDirectiveSpecification::Flags>(beginSpec.t)};
if (flags.test(parser::OmpDirectiveSpecification::Flag::CrossesLabelDo)) {
if (auto &endSpec{x.EndDir()}) {
@@ -310,11 +383,23 @@ void OmpStructureChecker::CheckNestedConstruct(
}
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);
+
+ CheckNestedBlock(x, body);
+
+ // Check if a loop-nest-associated construct has only one top-level loop
+ // in it.
+ if (std::optional<size_t> numLoops{CountGeneratedLoops(body)}) {
+ if (*numLoops == 0) {
+ context_.Say(beginSpec.DirName().source,
+ "This construct should contain a DO-loop or a loop-nest-generating OpenMP construct"_err_en_US);
+ } else {
+ auto assoc{llvm::omp::getDirectiveAssociation(beginSpec.DirName().v)};
+ if (*numLoops > 1 && assoc == llvm::omp::Association::LoopNest) {
+ context_.Say(beginSpec.DirName().source,
+ "This construct applies to a loop nest, but has a loop sequence of length %zu"_err_en_US,
+ *numLoops);
+ }
+ }
}
}
@@ -323,16 +408,9 @@ void OmpStructureChecker::CheckFullUnroll(
// 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.DirId() == 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);
- }
+ if (IsFullUnroll(*nested)) {
+ context_.Say(x.source,
+ "OpenMP loop construct cannot apply to a fully unrolled loop"_err_en_US);
}
}
}
@@ -406,11 +484,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
beginName.v == llvm::omp::Directive::OMPD_distribute_simd) {
CheckDistLinear(x);
}
- if (beginName.v == llvm::omp::Directive::OMPD_fuse) {
- CheckLooprangeBounds(x);
- } else {
- CheckNestedFuse(x);
- }
}
const parser::Name OmpStructureChecker::GetLoopIndex(
@@ -550,57 +623,20 @@ void OmpStructureChecker::CheckDistLinear(
void OmpStructureChecker::CheckLooprangeBounds(
const parser::OpenMPLoopConstruct &x) {
- const parser::OmpClauseList &clauseList{x.BeginDir().Clauses()};
- if (clauseList.v.empty()) {
- return;
- }
- for (auto &clause : clauseList.v) {
- if (const auto *lrClause{
- std::get_if<parser::OmpClause::Looprange>(&clause.u)}) {
- auto first{GetIntValue(std::get<0>((lrClause->v).t))};
- auto count{GetIntValue(std::get<1>((lrClause->v).t))};
- if (!first || !count) {
+ for (const parser::OmpClause &clause : x.BeginDir().Clauses().v) {
+ if (auto *lrClause{parser::Unwrap<parser::OmpLooprangeClause>(clause)}) {
+ auto first{GetIntValue(std::get<0>(lrClause->t))};
+ auto count{GetIntValue(std::get<1>(lrClause->t))};
+ if (!first || !count || *first <= 0 || *count <= 0) {
return;
}
- auto &loopConsList{std::get<parser::Block>(x.t)};
- if (*first > 0 && *count > 0 &&
- loopConsList.size() < (unsigned)(*first + *count - 1)) {
- context_.Say(clause.source,
- "The loop range indicated in the %s clause must not be out of the bounds of the Loop Sequence following the construct."_err_en_US,
- parser::ToUpperCaseLetters(clause.source.ToString()));
- }
- return;
- }
- }
-}
-
-void OmpStructureChecker::CheckNestedFuse(
- const parser::OpenMPLoopConstruct &x) {
- auto &loopConsList{std::get<parser::Block>(x.t)};
- if (loopConsList.empty()) {
- return;
- }
- const auto *ompConstruct{parser::omp::GetOmpLoop(loopConsList.front())};
- if (!ompConstruct) {
- return;
- }
- const parser::OmpClauseList &clauseList{ompConstruct->BeginDir().Clauses()};
- if (clauseList.v.empty()) {
- return;
- }
- for (auto &clause : clauseList.v) {
- if (const auto *lrClause{
- std::get_if<parser::OmpClause::Looprange>(&clause.u)}) {
- auto count{GetIntValue(std::get<1>((lrClause->v).t))};
- if (!count) {
- return;
- }
- auto &nestedLoopConsList{std::get<parser::Block>(ompConstruct->t)};
- if (nestedLoopConsList.size() > (unsigned)(*count)) {
- context_.Say(x.BeginDir().DirName().source,
- "The loop sequence following the %s construct must be fully fused first."_err_en_US,
- parser::ToUpperCaseLetters(
- x.BeginDir().DirName().source.ToString()));
+ auto requiredCount{static_cast<size_t>(*first + *count - 1)};
+ if (auto loopCount{CountGeneratedLoops(std::get<parser::Block>(x.t))}) {
+ if (*loopCount < requiredCount) {
+ context_.Say(clause.source,
+ "The specified loop range requires %zu loops, but the loop sequence has a length of %zu"_err_en_US,
+ requiredCount, *loopCount);
+ }
}
return;
}
@@ -644,18 +680,21 @@ void OmpStructureChecker::CheckScanModifier(
}
void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) {
- const parser::OmpClauseList &clauseList{x.BeginDir().Clauses()};
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
// A few semantic checks for InScan reduction are performed below as SCAN
// constructs inside LOOP may add the relevant information. Scan reduction is
// supported only in loop constructs, so same checks are not applicable to
// other directives.
- for (const auto &clause : clauseList.v) {
+ for (const auto &clause : beginSpec.Clauses().v) {
if (auto *reduction{std::get_if<parser::OmpClause::Reduction>(&clause.u)}) {
CheckScanModifier(*reduction);
}
}
- if (llvm::omp::allSimdSet.test(GetContext().directive)) {
+ if (beginSpec.DirName().v == llvm::omp::Directive::OMPD_fuse) {
+ CheckLooprangeBounds(x);
+ }
+ if (llvm::omp::allSimdSet.test(beginSpec.DirName().v)) {
ExitDirectiveNest(SIMDNest);
}
dirContext_.pop_back();
@@ -801,8 +840,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {
void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_looprange);
auto &[first, count]{x.v.t};
- RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, count);
RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, first);
+ RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, count);
}
void OmpStructureChecker::Enter(const parser::DoConstruct &x) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index d9a860490b4aa..4222259a56edb 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -325,11 +325,10 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void CheckScanModifier(const parser::OmpClause::Reduction &x);
void CheckLooprangeBounds(const parser::OpenMPLoopConstruct &x);
- void CheckNestedFuse(const parser::OpenMPLoopConstruct &x);
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 CheckNestedBlock(
+ const parser::OpenMPLoopConstruct &x, const parser::Block &body);
void CheckNestedConstruct(const parser::OpenMPLoopConstruct &x);
void CheckFullUnroll(const parser::OpenMPLoopConstruct &x);
void CheckTargetNest(const parser::OpenMPConstruct &x);
diff --git a/flang/test/Parser/OpenMP/tile-fail.f90 b/flang/test/Parser/OpenMP/tile-fail.f90
index a69261a927961..d5ff39cd1037c 100644
--- a/flang/test/Parser/OpenMP/tile-fail.f90
+++ b/flang/test/Parser/OpenMP/tile-fail.f90
@@ -1,7 +1,7 @@
! RUN: split-file %s %t
-! RUN: not %flang_fc1 -fsyntax-only -fopenmp %t/stray_end1.f90 2>&1 | FileCheck %t/stray_end1.f90
-! RUN: not %flang_fc1 -fsyntax-only -fopenmp %t/stray_end2.f90 2>&1 | FileCheck %t/stray_end2.f90
-! RUN: not %flang_fc1 -fsyntax-only -fopenmp %t/stray_begin.f90 2>&1 | FileCheck %t/stray_begin.f90
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=60 %t/stray_end1.f90 2>&1 | FileCheck %t/stray_end1.f90
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=60 %t/stray_end2.f90 2>&1 | FileCheck %t/stray_end2.f90
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=60 %t/stray_begin.f90 2>&1 | FileCheck %t/stray_begin.f90
!--- stray_end1.f90
@@ -25,7 +25,7 @@ subroutine stray_end2
!--- stray_begin.f90
subroutine stray_begin
- !CHECK: error: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !CHECK: error: This 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 e6fe7dd39dd3e..683118a5b2182 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: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp do
end subroutine
subroutine do2
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp parallel do
end subroutine
subroutine do3
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp simd
end subroutine
subroutine do4
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp do simd
end subroutine
subroutine do5
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This 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 7070ff5638c72..7235099210f77 100644
--- a/flang/test/Semantics/OpenMP/loop-association.f90
+++ b/flang/test/Semantics/OpenMP/loop-association.f90
@@ -104,7 +104,7 @@
!$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
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp parallel do shared(b)
a = 3.14
enddo
@@ -124,7 +124,7 @@
!ERROR: Misplaced OpenMP end-directive
!$omp end parallel do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp parallel do private(c)
5 FORMAT (1PE12.4, I10)
do i=1, N
@@ -141,7 +141,7 @@
!ERROR: Misplaced OpenMP end-directive
!$omp end parallel do simd
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp simd
a = i + 1
!ERROR: Misplaced OpenMP end-directive
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90
index 9ca0e8cfc9af1..5e3d32d7c6eff 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90
@@ -20,7 +20,7 @@ subroutine loop_transformation_construct1
end do
!$omp end fuse
- !ERROR: The loop range indicated in the LOOPRANGE(5,2) clause must not be out of the bounds of the Loop Sequence following the construct.
+ !ERROR: The specified loop range requires 6 loops, but the loop sequence has a length of 2
!$omp fuse looprange(5,2)
do x = 1, i
v(x) = x * 2
@@ -63,4 +63,18 @@ subroutine loop_transformation_construct1
v(x) = x * 2
end do
!$omp end fuse
+
+ ! This is ok aside from the warnings about compiler directives
+ !$omp fuse looprange(1,3)
+ do x = 1, 10; end do ! 1 loop
+ !WARNING: Compiler directives are not allowed inside OpenMP loop constructs
+ !dir$ novector
+ !$omp fuse looprange(1,2) ! 2 loops
+ do x = 1, 10; end do
+ !WARNING: Compiler directives are not allowed inside OpenMP loop constructs
+ !dir$ nounroll
+ do x = 1, 10; end do
+ do x = 1, 10; end do
+ !$omp end fuse
+ !$omp end fuse
end subroutine
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
index caa8f3f216fec..4eeb7330ea589 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
@@ -7,7 +7,7 @@ subroutine loop_transformation_construct1
!ERROR: OpenMP loop construct cannot apply to a fully unrolled loop
!$omp do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp unroll
end subroutine
@@ -51,7 +51,7 @@ subroutine loop_transformation_construct4
do x = 1, i
v(x) = v(x) * 2
end do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This 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
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
index 1b15c938915cd..25247c3896cae 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
@@ -7,7 +7,7 @@ subroutine loop_transformation_construct1
implicit none
!$omp do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp fuse
end subroutine
@@ -15,7 +15,7 @@ subroutine loop_transformation_construct2
implicit none
!$omp do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp fuse
!$omp end fuse
end subroutine
@@ -50,7 +50,7 @@ subroutine loop_transformation_construct4
do x = 1, i
v(x) = v(x) * 2
end do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp fuse
!$omp end fuse
end subroutine
@@ -80,7 +80,7 @@ subroutine loop_transformation_construct6
integer :: x
integer :: v(i)
- !ERROR: The loop sequence following the DO construct must be fully fused first.
+ !ERROR: This construct applies to a loop nest, but has a loop sequence of length 2
!$omp do
!$omp fuse looprange(1,1)
!$omp unroll partial(2)
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90
index 2856247329f3b..158b030906e07 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90
@@ -8,7 +8,7 @@ subroutine loop_transformation_construct3
integer :: x
integer :: v(i)
- !ERROR: The loop sequence following the DO construct must be fully fused first.
+ !ERROR: This construct applies to a loop nest, but has a loop sequence of length 2
!$omp do
!$omp fuse looprange(1,2)
do x = 1, i
@@ -30,7 +30,7 @@ subroutine loop_transformation_construct4
integer :: x
integer :: v(i)
- !ERROR: The loop sequence following the TILE construct must be fully fused first.
+ !ERROR: This construct applies to a loop nest, but has a loop sequence of length 2
!$omp tile sizes(2)
!$omp fuse looprange(1,2)
do x = 1, i
More information about the flang-commits
mailing list