llvm-project/flang/lib/Semantics/check-omp-structure.cpp
sameeran joshi d95d3d2a42 [Flang][OpenMP] Rework parser changes for OpenMP atomic construct.
`OmpStructureChecker` is supposed to work only with `parser::OmpClause`
after tablegen changes for OpenMP and OpenACC were introduced.
Hence `OmpMemoryOrderClause`, `OmpAtomicMemoryOrderClause` and similar ones were failing
to catch semantic errors, inspite of having code for semantic checks.
This patch tries to change parser for `OmpMemoryOrderClause` and similar dependent ones
and use `OmpClauseList` which resides/comes from common tablegen for OpenMP/OpenACC eventually using `parser::OmpClause`.

This patch also tries to :
1. Change `OmpCriticalDirective` in `openmp-parsers.cpp` to support `OmpClauseList`.
2. Check-flang regresses when changes were introduced due to missing semantic checks in OmpCritical, patch implements them at the minimal level to pass the regression.
3. Change tablegen to support Hint clause.
4. Adds missing source locations `CharBlock Source` in each atomic construct.
5. Remove dead code realted to `memory-order-clauses` after moving to `OmpClauseList`.

Reviewed By: kiranchandramohan

Differential Revision: https://reviews.llvm.org/D88965
2020-10-14 14:19:49 +05:30

639 lines
24 KiB
C++

//===-- lib/Semantics/check-omp-structure.cpp -----------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "check-omp-structure.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/tools.h"
namespace Fortran::semantics {
bool OmpStructureChecker::HasInvalidWorksharingNesting(
const parser::CharBlock &source, const OmpDirectiveSet &set) {
// set contains all the invalid closely nested directives
// for the given directive (`source` here)
if (CurrentDirectiveIsNested() && set.test(GetContext().directive)) {
context_.Say(source,
"A worksharing region may not be closely nested inside a "
"worksharing, explicit task, taskloop, critical, ordered, atomic, or "
"master region"_err_en_US);
return true;
}
return false;
}
void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) {
// 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check
}
void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
// check matching, End directive is optional
if (const auto &endLoopDir{
std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
const auto &endDir{
std::get<parser::OmpLoopDirective>(endLoopDir.value().t)};
CheckMatching<parser::OmpLoopDirective>(beginDir, endDir);
}
if (beginDir.v != llvm::omp::Directive::OMPD_do) {
PushContextAndClauseSets(beginDir.source, beginDir.v);
} else {
// 2.7.1 do-clause -> private-clause |
// firstprivate-clause |
// lastprivate-clause |
// linear-clause |
// reduction-clause |
// schedule-clause |
// collapse-clause |
// ordered-clause
// nesting check
HasInvalidWorksharingNesting(beginDir.source,
{llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_sections,
llvm::omp::Directive::OMPD_single,
llvm::omp::Directive::OMPD_workshare,
llvm::omp::Directive::OMPD_task,
llvm::omp::Directive::OMPD_taskloop,
llvm::omp::Directive::OMPD_critical,
llvm::omp::Directive::OMPD_ordered,
llvm::omp::Directive::OMPD_atomic,
llvm::omp::Directive::OMPD_master});
PushContextAndClauseSets(beginDir.source, llvm::omp::Directive::OMPD_do);
}
}
void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
const auto &dir{std::get<parser::OmpLoopDirective>(x.t)};
ResetPartialContext(dir.source);
switch (dir.v) {
// 2.7.1 end-do -> END DO [nowait-clause]
// 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
case llvm::omp::Directive::OMPD_do:
case llvm::omp::Directive::OMPD_do_simd:
SetClauseSets(dir.v);
break;
default:
// no clauses are allowed
break;
}
}
void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)};
const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)};
CheckMatching<parser::OmpBlockDirective>(beginDir, endDir);
PushContextAndClauseSets(beginDir.source, beginDir.v);
}
void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
const auto &beginSectionsDir{
std::get<parser::OmpBeginSectionsDirective>(x.t)};
const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)};
const auto &beginDir{
std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)};
CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir);
PushContextAndClauseSets(beginDir.source, beginDir.v);
}
void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)};
ResetPartialContext(dir.source);
switch (dir.v) {
// 2.7.2 end-sections -> END SECTIONS [nowait-clause]
case llvm::omp::Directive::OMPD_sections:
SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_sections);
SetContextAllowed(OmpClauseSet{llvm::omp::Clause::OMPC_nowait});
break;
default:
// no clauses are allowed
break;
}
}
void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
const auto &dir{std::get<parser::Verbatim>(x.t)};
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd);
}
void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
const auto &dir{std::get<parser::Verbatim>(x.t)};
PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target);
const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) {
SetContextAllowed(
OmpClauseSet{llvm::omp::Clause::OMPC_to, llvm::omp::Clause::OMPC_link});
}
}
void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(
const parser::OpenMPSimpleStandaloneConstruct &x) {
const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
PushContextAndClauseSets(dir.source, dir.v);
}
void OmpStructureChecker::Leave(
const parser::OpenMPSimpleStandaloneConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
const auto &dir{std::get<parser::Verbatim>(x.t)};
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_flush);
}
void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
const auto &dir{std::get<parser::Verbatim>(x.t)};
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel);
}
void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) {
const auto &dir{std::get<parser::OmpCriticalDirective>(x.t)};
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_critical);
}
void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(
const parser::OpenMPCancellationPointConstruct &x) {
const auto &dir{std::get<parser::Verbatim>(x.t)};
PushContextAndClauseSets(
dir.source, llvm::omp::Directive::OMPD_cancellation_point);
}
void OmpStructureChecker::Leave(
const parser::OpenMPCancellationPointConstruct &) {
dirContext_.pop_back();
}
void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
const auto &dir{std::get<parser::OmpBlockDirective>(x.t)};
ResetPartialContext(dir.source);
switch (dir.v) {
// 2.7.3 end-single-clause -> copyprivate-clause |
// nowait-clause
case llvm::omp::Directive::OMPD_single: {
SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_single);
OmpClauseSet allowed{llvm::omp::Clause::OMPC_copyprivate};
SetContextAllowed(allowed);
OmpClauseSet allowedOnce{llvm::omp::Clause::OMPC_nowait};
SetContextAllowedOnce(allowedOnce);
} break;
// 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
case llvm::omp::Directive::OMPD_workshare:
SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_workshare);
SetContextAllowed(OmpClauseSet{llvm::omp::Clause::OMPC_nowait});
break;
default:
// no clauses are allowed
break;
}
}
void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
// 2.7 Loop Construct Restriction
if (llvm::omp::doSet.test(GetContext().directive)) {
if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) {
// only one schedule clause is allowed
const auto &schedClause{std::get<parser::OmpScheduleClause>(clause->u)};
if (ScheduleModifierHasType(schedClause,
parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
if (FindClause(llvm::omp::Clause::OMPC_ordered)) {
context_.Say(clause->source,
"The NONMONOTONIC modifier cannot be specified "
"if an ORDERED clause is specified"_err_en_US);
}
if (ScheduleModifierHasType(schedClause,
parser::OmpScheduleModifierType::ModType::Monotonic)) {
context_.Say(clause->source,
"The MONOTONIC and NONMONOTONIC modifiers "
"cannot be both specified"_err_en_US);
}
}
}
if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) {
// only one ordered clause is allowed
const auto &orderedClause{
std::get<parser::OmpClause::Ordered>(clause->u)};
if (orderedClause.v) {
if (FindClause(llvm::omp::Clause::OMPC_linear)) {
context_.Say(clause->source,
"A loop directive may not have both a LINEAR clause and "
"an ORDERED clause with a parameter"_err_en_US);
}
if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) {
const auto &collapseClause{
std::get<parser::OmpClause::Collapse>(clause2->u)};
// ordered and collapse both have parameters
if (const auto orderedValue{GetIntValue(orderedClause.v)}) {
if (const auto collapseValue{GetIntValue(collapseClause.v)}) {
if (*orderedValue > 0 && *orderedValue < *collapseValue) {
context_.Say(clause->source,
"The parameter of the ORDERED clause must be "
"greater than or equal to "
"the parameter of the COLLAPSE clause"_err_en_US);
}
}
}
}
}
// TODO: ordered region binding check (requires nesting implementation)
}
} // doSet
// 2.8.1 Simd Construct Restriction
if (llvm::omp::simdSet.test(GetContext().directive)) {
if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) {
if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) {
const auto &simdlenClause{
std::get<parser::OmpClause::Simdlen>(clause->u)};
const auto &safelenClause{
std::get<parser::OmpClause::Safelen>(clause2->u)};
// simdlen and safelen both have parameters
if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) {
if (const auto safelenValue{GetIntValue(safelenClause.v)}) {
if (*safelenValue > 0 && *simdlenValue > *safelenValue) {
context_.Say(clause->source,
"The parameter of the SIMDLEN clause must be less than or "
"equal to the parameter of the SAFELEN clause"_err_en_US);
}
}
}
}
}
// TODO: A list-item cannot appear in more than one aligned clause
} // SIMD
// 2.7.3 Single Construct Restriction
if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) {
if (auto *clause{FindClause(llvm::omp::Clause::OMPC_copyprivate)}) {
if (FindClause(llvm::omp::Clause::OMPC_nowait)) {
context_.Say(clause->source,
"The COPYPRIVATE clause must not be used with "
"the NOWAIT clause"_err_en_US);
}
}
}
GetContext().requiredClauses.IterateOverMembers(
[this](llvm::omp::Clause c) { CheckRequired(c); });
}
void OmpStructureChecker::Enter(const parser::OmpClause &x) {
SetContextClause(x);
}
void OmpStructureChecker::Enter(const parser::OmpNowait &) {
CheckAllowed(llvm::omp::Clause::OMPC_nowait);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Inbranch &) {
CheckAllowed(llvm::omp::Clause::OMPC_inbranch);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Mergeable &) {
CheckAllowed(llvm::omp::Clause::OMPC_mergeable);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Nogroup &) {
CheckAllowed(llvm::omp::Clause::OMPC_nogroup);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Notinbranch &) {
CheckAllowed(llvm::omp::Clause::OMPC_notinbranch);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Untied &) {
CheckAllowed(llvm::omp::Clause::OMPC_untied);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Collapse &x) {
CheckAllowed(llvm::omp::Clause::OMPC_collapse);
// collapse clause must have a parameter
RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_collapse, x.v);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &) {
CheckAllowed(llvm::omp::Clause::OMPC_copyin);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &) {
CheckAllowed(llvm::omp::Clause::OMPC_copyprivate);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Device &) {
CheckAllowed(llvm::omp::Clause::OMPC_device);
}
void OmpStructureChecker::Enter(const parser::OmpDistScheduleClause &) {
CheckAllowed(llvm::omp::Clause::OMPC_dist_schedule);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Final &) {
CheckAllowed(llvm::omp::Clause::OMPC_final);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &) {
CheckAllowed(llvm::omp::Clause::OMPC_firstprivate);
}
void OmpStructureChecker::Enter(const parser::OmpClause::From &) {
CheckAllowed(llvm::omp::Clause::OMPC_from);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Grainsize &x) {
CheckAllowed(llvm::omp::Clause::OMPC_grainsize);
RequiresPositiveParameter(llvm::omp::Clause::OMPC_grainsize, x.v);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &) {
CheckAllowed(llvm::omp::Clause::OMPC_lastprivate);
}
void OmpStructureChecker::Enter(const parser::OmpClause::NumTasks &x) {
CheckAllowed(llvm::omp::Clause::OMPC_num_tasks);
RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_tasks, x.v);
}
void OmpStructureChecker::Enter(const parser::OmpClause::NumTeams &x) {
CheckAllowed(llvm::omp::Clause::OMPC_num_teams);
RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_teams, x.v);
}
void OmpStructureChecker::Enter(const parser::OmpClause::NumThreads &x) {
CheckAllowed(llvm::omp::Clause::OMPC_num_threads);
RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_threads, x.v);
// if parameter is variable, defer to Expression Analysis
}
void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
CheckAllowed(llvm::omp::Clause::OMPC_ordered);
// the parameter of ordered clause is optional
if (const auto &expr{x.v}) {
RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr);
// 2.8.3 Loop SIMD Construct Restriction
if (llvm::omp::doSimdSet.test(GetContext().directive)) {
context_.Say(GetContext().clauseSource,
"No ORDERED clause with a parameter can be specified "
"on the %s directive"_err_en_US,
ContextDirectiveAsFortran());
}
}
}
void OmpStructureChecker::Enter(const parser::OmpClause::Priority &x) {
CheckAllowed(llvm::omp::Clause::OMPC_priority);
RequiresPositiveParameter(llvm::omp::Clause::OMPC_priority, x.v);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Private &) {
CheckAllowed(llvm::omp::Clause::OMPC_private);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Safelen &x) {
CheckAllowed(llvm::omp::Clause::OMPC_safelen);
RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_safelen, x.v);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Shared &) {
CheckAllowed(llvm::omp::Clause::OMPC_shared);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Simdlen &x) {
CheckAllowed(llvm::omp::Clause::OMPC_simdlen);
RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_simdlen, x.v);
}
void OmpStructureChecker::Enter(const parser::OmpClause::ThreadLimit &x) {
CheckAllowed(llvm::omp::Clause::OMPC_thread_limit);
RequiresPositiveParameter(llvm::omp::Clause::OMPC_thread_limit, x.v);
}
void OmpStructureChecker::Enter(const parser::OmpClause::To &) {
CheckAllowed(llvm::omp::Clause::OMPC_to);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Link &) {
CheckAllowed(llvm::omp::Clause::OMPC_link);
}
void OmpStructureChecker::Enter(const parser::OmpClause::Uniform &) {
CheckAllowed(llvm::omp::Clause::OMPC_uniform);
}
void OmpStructureChecker::Enter(const parser::OmpClause::UseDevicePtr &) {
CheckAllowed(llvm::omp::Clause::OMPC_use_device_ptr);
}
void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr &) {
CheckAllowed(llvm::omp::Clause::OMPC_is_device_ptr);
}
void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) {
CheckAllowed(llvm::omp::Clause::OMPC_aligned);
if (const auto &expr{
std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) {
if (const auto v{GetIntValue(*expr)}) {
if (*v <= 0) {
context_.Say(GetContext().clauseSource,
"The ALIGNMENT parameter of the ALIGNED clause must be "
"a constant positive integer expression"_err_en_US);
}
}
}
// 2.8.1 TODO: list-item attribute check
}
void OmpStructureChecker::Enter(const parser::OmpAllocateClause &) {
CheckAllowed(llvm::omp::Clause::OMPC_allocate);
}
void OmpStructureChecker::Enter(const parser::OmpDefaultClause &) {
CheckAllowed(llvm::omp::Clause::OMPC_default);
}
void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) {
CheckAllowed(llvm::omp::Clause::OMPC_defaultmap);
using VariableCategory = parser::OmpDefaultmapClause::VariableCategory;
if (!std::get<std::optional<VariableCategory>>(x.t)) {
context_.Say(GetContext().clauseSource,
"The argument TOFROM:SCALAR must be specified on the DEFAULTMAP "
"clause"_err_en_US);
}
}
void OmpStructureChecker::Enter(const parser::OmpDependClause &) {
CheckAllowed(llvm::omp::Clause::OMPC_depend);
}
void OmpStructureChecker::Enter(const parser::OmpIfClause &x) {
CheckAllowed(llvm::omp::Clause::OMPC_if);
using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier;
static std::unordered_map<dirNameModifier, OmpDirectiveSet>
dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet},
{dirNameModifier::Target, llvm::omp::targetSet},
{dirNameModifier::TargetEnterData,
{llvm::omp::Directive::OMPD_target_enter_data}},
{dirNameModifier::TargetExitData,
{llvm::omp::Directive::OMPD_target_exit_data}},
{dirNameModifier::TargetData,
{llvm::omp::Directive::OMPD_target_data}},
{dirNameModifier::TargetUpdate,
{llvm::omp::Directive::OMPD_target_update}},
{dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}},
{dirNameModifier::Taskloop, llvm::omp::taskloopSet}};
if (const auto &directiveName{
std::get<std::optional<dirNameModifier>>(x.t)}) {
auto search{dirNameModifierMap.find(*directiveName)};
if (search == dirNameModifierMap.end() ||
!search->second.test(GetContext().directive)) {
context_
.Say(GetContext().clauseSource,
"Unmatched directive name modifier %s on the IF clause"_err_en_US,
parser::ToUpperCaseLetters(
parser::OmpIfClause::EnumToString(*directiveName)))
.Attach(
GetContext().directiveSource, "Cannot apply to directive"_en_US);
}
}
}
void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) {
CheckAllowed(llvm::omp::Clause::OMPC_linear);
// 2.7 Loop Construct Restriction
if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) {
if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) {
context_.Say(GetContext().clauseSource,
"A modifier may not be specified in a LINEAR clause "
"on the %s directive"_err_en_US,
ContextDirectiveAsFortran());
}
}
}
void OmpStructureChecker::Enter(const parser::OmpMapClause &x) {
CheckAllowed(llvm::omp::Clause::OMPC_map);
if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) {
using Type = parser::OmpMapType::Type;
const Type &type{std::get<Type>(maptype->t)};
switch (GetContext().directive) {
case llvm::omp::Directive::OMPD_target:
case llvm::omp::Directive::OMPD_target_teams:
case llvm::omp::Directive::OMPD_target_teams_distribute:
case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
case llvm::omp::Directive::OMPD_target_data: {
if (type != Type::To && type != Type::From && type != Type::Tofrom &&
type != Type::Alloc) {
context_.Say(GetContext().clauseSource,
"Only the TO, FROM, TOFROM, or ALLOC map types are permitted "
"for MAP clauses on the %s directive"_err_en_US,
ContextDirectiveAsFortran());
}
} break;
case llvm::omp::Directive::OMPD_target_enter_data: {
if (type != Type::To && type != Type::Alloc) {
context_.Say(GetContext().clauseSource,
"Only the TO or ALLOC map types are permitted "
"for MAP clauses on the %s directive"_err_en_US,
ContextDirectiveAsFortran());
}
} break;
case llvm::omp::Directive::OMPD_target_exit_data: {
if (type != Type::Delete && type != Type::Release && type != Type::From) {
context_.Say(GetContext().clauseSource,
"Only the FROM, RELEASE, or DELETE map types are permitted "
"for MAP clauses on the %s directive"_err_en_US,
ContextDirectiveAsFortran());
}
} break;
default:
break;
}
}
}
void OmpStructureChecker::Enter(const parser::OmpProcBindClause &) {
CheckAllowed(llvm::omp::Clause::OMPC_proc_bind);
}
void OmpStructureChecker::Enter(const parser::OmpReductionClause &) {
CheckAllowed(llvm::omp::Clause::OMPC_reduction);
}
bool OmpStructureChecker::ScheduleModifierHasType(
const parser::OmpScheduleClause &x,
const parser::OmpScheduleModifierType::ModType &type) {
const auto &modifier{
std::get<std::optional<parser::OmpScheduleModifier>>(x.t)};
if (modifier) {
const auto &modType1{
std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)};
const auto &modType2{
std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(
modifier->t)};
if (modType1.v.v == type || (modType2 && modType2->v.v == type)) {
return true;
}
}
return false;
}
void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) {
CheckAllowed(llvm::omp::Clause::OMPC_schedule);
// 2.7 Loop Construct Restriction
if (llvm::omp::doSet.test(GetContext().directive)) {
const auto &kind{std::get<1>(x.t)};
const auto &chunk{std::get<2>(x.t)};
if (chunk) {
if (kind == parser::OmpScheduleClause::ScheduleType::Runtime ||
kind == parser::OmpScheduleClause::ScheduleType::Auto) {
context_.Say(GetContext().clauseSource,
"When SCHEDULE clause has %s specified, "
"it must not have chunk size specified"_err_en_US,
parser::ToUpperCaseLetters(
parser::OmpScheduleClause::EnumToString(kind)));
}
}
if (ScheduleModifierHasType(
x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic &&
kind != parser::OmpScheduleClause::ScheduleType::Guided) {
context_.Say(GetContext().clauseSource,
"The NONMONOTONIC modifier can only be specified with "
"SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US);
}
}
}
}
llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
return llvm::omp::getOpenMPClauseName(clause);
}
llvm::StringRef OmpStructureChecker::getDirectiveName(
llvm::omp::Directive directive) {
return llvm::omp::getOpenMPDirectiveName(directive);
}
} // namespace Fortran::semantics