[flang][OpenMP][Semantics] Disallow NOWAIT and ORDERED with CANCEL (#135991)

NOWAIT was a tricky one because the clause can be on either the start or
the end directive. I couldn't find a convenient way to access the end
directive from the CANCEL directive nested inside of the construct, but
there are convenient ways to access the start directive. I have added a
list to the start directive context containing the clauses from the end
directive.
This commit is contained in:
Tom Eccles
2025-04-17 10:08:07 +01:00
committed by GitHub
parent 121cd7c6f0
commit c5e112eed7
4 changed files with 99 additions and 1 deletions

View File

@@ -202,6 +202,7 @@ protected:
const PC *clause{nullptr};
ClauseMapTy clauseInfo;
std::list<C> actualClauses;
std::list<C> endDirectiveClauses;
std::list<C> crtGroup;
Symbol *loopIV{nullptr};
};

View File

@@ -15,6 +15,7 @@
#include "flang/Semantics/expression.h"
#include "flang/Semantics/openmp-modifiers.h"
#include "flang/Semantics/tools.h"
#include "llvm/ADT/STLExtras.h"
#include <variant>
namespace Fortran::semantics {
@@ -682,11 +683,20 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeConstruct &x) {
ExitDirectiveNest(DeclarativeNest);
}
void OmpStructureChecker::AddEndDirectiveClauses(
const parser::OmpClauseList &clauses) {
for (const parser::OmpClause &clause : clauses.v) {
GetContext().endDirectiveClauses.push_back(clause.Id());
}
}
void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
loopStack_.push_back(&x);
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
PushContextAndClauseSets(beginDir.source, beginDir.v);
// check matching, End directive is optional
if (const auto &endLoopDir{
std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
@@ -694,9 +704,10 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
std::get<parser::OmpLoopDirective>(endLoopDir.value().t)};
CheckMatching<parser::OmpLoopDirective>(beginDir, endDir);
AddEndDirectiveClauses(std::get<parser::OmpClauseList>(endLoopDir->t));
}
PushContextAndClauseSets(beginDir.source, beginDir.v);
if (llvm::omp::allSimdSet.test(GetContext().directive)) {
EnterDirectiveNest(SIMDNest);
}
@@ -1429,6 +1440,8 @@ void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir);
PushContextAndClauseSets(beginDir.source, beginDir.v);
AddEndDirectiveClauses(std::get<parser::OmpClauseList>(endSectionsDir.t));
const auto &sectionBlocks{std::get<parser::OmpSectionBlocks>(x.t)};
for (const parser::OpenMPConstruct &block : sectionBlocks.v) {
CheckNoBranching(std::get<parser::OpenMPSectionConstruct>(block.u).v,
@@ -2288,6 +2301,37 @@ void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
if (auto maybeConstruct{GetCancelType(
llvm::omp::Directive::OMPD_cancel, x.source, maybeClauses)}) {
CheckCancellationNest(dirName.source, *maybeConstruct);
if (CurrentDirectiveIsNested()) {
// nowait can be put on the end directive rather than the start directive
// so we need to check both
auto getParentClauses{[&]() {
const DirectiveContext &parent{GetContextParent()};
return llvm::concat<const llvm::omp::Clause>(
parent.actualClauses, parent.endDirectiveClauses);
}};
if (llvm::omp::nestedCancelDoAllowedSet.test(*maybeConstruct)) {
for (llvm::omp::Clause clause : getParentClauses()) {
if (clause == llvm::omp::Clause::OMPC_nowait) {
context_.Say(dirName.source,
"The CANCEL construct cannot be nested inside of a worksharing construct with the NOWAIT clause"_err_en_US);
}
if (clause == llvm::omp::Clause::OMPC_ordered) {
context_.Say(dirName.source,
"The CANCEL construct cannot be nested inside of a worksharing construct with the ORDERED clause"_err_en_US);
}
}
} else if (llvm::omp::nestedCancelSectionsAllowedSet.test(
*maybeConstruct)) {
for (llvm::omp::Clause clause : getParentClauses()) {
if (clause == llvm::omp::Clause::OMPC_nowait) {
context_.Say(dirName.source,
"The CANCEL construct cannot be nested inside of a worksharing construct with the NOWAIT clause"_err_en_US);
}
}
}
}
}
}

View File

@@ -318,6 +318,8 @@ private:
void CheckAlignValue(const parser::OmpClause &);
void AddEndDirectiveClauses(const parser::OmpClauseList &clauses);
void EnterDirectiveNest(const int index) { directiveNest_[index]++; }
void ExitDirectiveNest(const int index) { directiveNest_[index]--; }
int GetDirectiveNest(const int index) { return directiveNest_[index]; }

View File

@@ -27,3 +27,54 @@ subroutine f03
!$omp cancellation point parallel parallel
!$omp end parallel
end
subroutine do_nowait1
!$omp parallel
!$omp do nowait
do i=1,2
!ERROR: The CANCEL construct cannot be nested inside of a worksharing construct with the NOWAIT clause
!$omp cancel do
enddo
!$omp end do
!$omp end parallel
end subroutine
subroutine do_nowait2
!$omp parallel
!$omp do
do i=1,2
!ERROR: The CANCEL construct cannot be nested inside of a worksharing construct with the NOWAIT clause
!$omp cancel do
enddo
!$omp end do nowait
!$omp end parallel
end subroutine
subroutine do_ordered
!$omp parallel do ordered
do i=1,2
!ERROR: The CANCEL construct cannot be nested inside of a worksharing construct with the ORDERED clause
!$omp cancel do
enddo
!$omp end parallel do
end subroutine
subroutine sections_nowait1
!$omp parallel
!$omp sections nowait
!$omp section
!ERROR: The CANCEL construct cannot be nested inside of a worksharing construct with the NOWAIT clause
!$omp cancel sections
!$omp end sections
!$omp end parallel
end subroutine
subroutine sections_nowait2
!$omp parallel
!$omp sections
!$omp section
!ERROR: The CANCEL construct cannot be nested inside of a worksharing construct with the NOWAIT clause
!$omp cancel sections
!$omp end sections nowait
!$omp end parallel
end subroutine