676 lines · c
1//===-- lib/Semantics/check-directive-structure.h ---------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// Directive structure validity checks common to OpenMP, OpenACC and other10// directive language.11 12#ifndef FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_13#define FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_14 15#include "flang/Common/enum-set.h"16#include "flang/Semantics/semantics.h"17#include "flang/Semantics/tools.h"18#include "llvm/ADT/iterator_range.h"19 20#include <unordered_map>21 22namespace Fortran::semantics {23 24template <typename C, std::size_t ClauseEnumSize> struct DirectiveClauses {25 const common::EnumSet<C, ClauseEnumSize> allowed;26 const common::EnumSet<C, ClauseEnumSize> allowedOnce;27 const common::EnumSet<C, ClauseEnumSize> allowedExclusive;28 const common::EnumSet<C, ClauseEnumSize> requiredOneOf;29};30 31// Generic branching checker for invalid branching out of OpenMP/OpenACC32// directive.33// typename D is the directive enumeration.34template <typename D> class NoBranchingEnforce {35public:36 NoBranchingEnforce(SemanticsContext &context,37 parser::CharBlock sourcePosition, D directive,38 std::string &&upperCaseDirName)39 : context_{context}, sourcePosition_{sourcePosition},40 upperCaseDirName_{std::move(upperCaseDirName)},41 currentDirective_{directive}, numDoConstruct_{0} {}42 template <typename T> bool Pre(const T &) { return true; }43 template <typename T> void Post(const T &) {}44 45 template <typename T> bool Pre(const parser::Statement<T> &statement) {46 currentStatementSourcePosition_ = statement.source;47 return true;48 }49 50 bool Pre(const parser::DoConstruct &) {51 numDoConstruct_++;52 return true;53 }54 void Post(const parser::DoConstruct &) { numDoConstruct_--; }55 void Post(const parser::ReturnStmt &) { EmitBranchOutError("RETURN"); }56 void Post(const parser::ExitStmt &exitStmt) {57 if (const auto &exitName{exitStmt.v}) {58 CheckConstructNameBranching("EXIT", exitName.value());59 } else {60 CheckConstructNameBranching("EXIT");61 }62 }63 void Post(const parser::CycleStmt &cycleStmt) {64 if (const auto &cycleName{cycleStmt.v}) {65 CheckConstructNameBranching("CYCLE", cycleName.value());66 } else {67 if constexpr (std::is_same_v<D, llvm::omp::Directive>) {68 switch ((llvm::omp::Directive)currentDirective_) {69 // exclude directives which do not need a check for unlabelled CYCLES70 case llvm::omp::Directive::OMPD_do:71 case llvm::omp::Directive::OMPD_simd:72 case llvm::omp::Directive::OMPD_parallel_do:73 case llvm::omp::Directive::OMPD_parallel_do_simd:74 case llvm::omp::Directive::OMPD_distribute_parallel_do:75 case llvm::omp::Directive::OMPD_distribute_parallel_do_simd:76 case llvm::omp::Directive::OMPD_distribute_parallel_for:77 case llvm::omp::Directive::OMPD_distribute_simd:78 case llvm::omp::Directive::OMPD_distribute_parallel_for_simd:79 case llvm::omp::Directive::OMPD_target_teams_distribute:80 case llvm::omp::Directive::OMPD_target_teams_distribute_simd:81 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:82 case llvm::omp::Directive::83 OMPD_target_teams_distribute_parallel_do_simd:84 return;85 default:86 break;87 }88 } else if constexpr (std::is_same_v<D, llvm::acc::Directive>) {89 switch ((llvm::acc::Directive)currentDirective_) {90 // exclude loop directives which do not need a check for unlabelled91 // CYCLES92 case llvm::acc::Directive::ACCD_loop:93 case llvm::acc::Directive::ACCD_kernels_loop:94 case llvm::acc::Directive::ACCD_parallel_loop:95 case llvm::acc::Directive::ACCD_serial_loop:96 return;97 default:98 break;99 }100 }101 CheckConstructNameBranching("CYCLE");102 }103 }104 105private:106 parser::MessageFormattedText GetEnclosingMsg() const {107 return {"Enclosing %s construct"_en_US, upperCaseDirName_};108 }109 110 void EmitBranchOutError(const char *stmt) const {111 context_112 .Say(currentStatementSourcePosition_,113 "%s statement is not allowed in a %s construct"_err_en_US, stmt,114 upperCaseDirName_)115 .Attach(sourcePosition_, GetEnclosingMsg());116 }117 118 inline void EmitUnlabelledBranchOutError(const char *stmt) {119 context_120 .Say(currentStatementSourcePosition_,121 "%s to construct outside of %s construct is not allowed"_err_en_US,122 stmt, upperCaseDirName_)123 .Attach(sourcePosition_, GetEnclosingMsg());124 }125 126 void EmitBranchOutErrorWithName(127 const char *stmt, const parser::Name &toName) const {128 const std::string branchingToName{toName.ToString()};129 context_130 .Say(currentStatementSourcePosition_,131 "%s to construct '%s' outside of %s construct is not allowed"_err_en_US,132 stmt, branchingToName, upperCaseDirName_)133 .Attach(sourcePosition_, GetEnclosingMsg());134 }135 136 // Current semantic checker is not following OpenACC/OpenMP constructs as they137 // are not Fortran constructs. Hence the ConstructStack doesn't capture138 // OpenACC/OpenMP constructs. Apply an inverse way to figure out if a139 // construct-name is branching out of an OpenACC/OpenMP construct. The control140 // flow goes out of an OpenACC/OpenMP construct, if a construct-name from141 // statement is found in ConstructStack.142 void CheckConstructNameBranching(143 const char *stmt, const parser::Name &stmtName) {144 const ConstructStack &stack{context_.constructStack()};145 for (auto iter{stack.cend()}; iter-- != stack.cbegin();) {146 const ConstructNode &construct{*iter};147 const auto &constructName{MaybeGetNodeName(construct)};148 if (constructName) {149 if (stmtName.source == constructName->source) {150 EmitBranchOutErrorWithName(stmt, stmtName);151 return;152 }153 }154 }155 }156 157 // Check branching for unlabelled CYCLES and EXITs158 void CheckConstructNameBranching(const char *stmt) {159 // found an enclosing looping construct for the unlabelled EXIT/CYCLE160 if (numDoConstruct_ > 0) {161 return;162 }163 // did not found an enclosing looping construct within the OpenMP/OpenACC164 // directive165 EmitUnlabelledBranchOutError(stmt);166 }167 168 SemanticsContext &context_;169 parser::CharBlock currentStatementSourcePosition_;170 parser::CharBlock sourcePosition_;171 std::string upperCaseDirName_;172 D currentDirective_;173 int numDoConstruct_; // tracks number of DoConstruct found AFTER encountering174 // an OpenMP/OpenACC directive175};176 177// Generic structure checker for directives/clauses language such as OpenMP178// and OpenACC.179// typename D is the directive enumeration.180// typename C is the clause enumeration.181// typename PC is the parser class defined in parse-tree.h for the clauses.182template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>183class DirectiveStructureChecker : public virtual BaseChecker {184protected:185 DirectiveStructureChecker(SemanticsContext &context,186 const std::unordered_map<D, DirectiveClauses<C, ClauseEnumSize>>187 &directiveClausesMap)188 : context_{context}, directiveClausesMap_(directiveClausesMap) {}189 virtual ~DirectiveStructureChecker() {}190 191 using ClauseMapTy = std::multimap<C, const PC *>;192 struct DirectiveContext {193 DirectiveContext(parser::CharBlock source, D d)194 : directiveSource{source}, directive{d} {}195 196 parser::CharBlock directiveSource{nullptr};197 parser::CharBlock clauseSource{nullptr};198 D directive;199 common::EnumSet<C, ClauseEnumSize> allowedClauses{};200 common::EnumSet<C, ClauseEnumSize> allowedOnceClauses{};201 common::EnumSet<C, ClauseEnumSize> allowedExclusiveClauses{};202 common::EnumSet<C, ClauseEnumSize> requiredClauses{};203 204 const PC *clause{nullptr};205 ClauseMapTy clauseInfo;206 std::list<C> actualClauses;207 std::list<C> endDirectiveClauses;208 std::list<C> crtGroup;209 Symbol *loopIV{nullptr};210 };211 212 void SetLoopIv(Symbol *symbol) { GetContext().loopIV = symbol; }213 214 // back() is the top of the stack215 DirectiveContext &GetContext() {216 CHECK(!dirContext_.empty());217 return dirContext_.back();218 }219 220 DirectiveContext &GetContextParent() {221 CHECK(dirContext_.size() >= 2);222 return dirContext_[dirContext_.size() - 2];223 }224 225 void SetContextClause(const PC &clause) {226 GetContext().clauseSource = clause.source;227 GetContext().clause = &clause;228 }229 230 void ResetPartialContext(const parser::CharBlock &source) {231 CHECK(!dirContext_.empty());232 SetContextDirectiveSource(source);233 GetContext().allowedClauses = {};234 GetContext().allowedOnceClauses = {};235 GetContext().allowedExclusiveClauses = {};236 GetContext().requiredClauses = {};237 GetContext().clauseInfo = {};238 GetContext().loopIV = {nullptr};239 }240 241 void SetContextDirectiveSource(const parser::CharBlock &directive) {242 GetContext().directiveSource = directive;243 }244 245 void SetContextDirectiveEnum(D dir) { GetContext().directive = dir; }246 247 void SetContextAllowed(const common::EnumSet<C, ClauseEnumSize> &allowed) {248 GetContext().allowedClauses = allowed;249 }250 251 void SetContextAllowedOnce(252 const common::EnumSet<C, ClauseEnumSize> &allowedOnce) {253 GetContext().allowedOnceClauses = allowedOnce;254 }255 256 void SetContextAllowedExclusive(257 const common::EnumSet<C, ClauseEnumSize> &allowedExclusive) {258 GetContext().allowedExclusiveClauses = allowedExclusive;259 }260 261 void SetContextRequired(const common::EnumSet<C, ClauseEnumSize> &required) {262 GetContext().requiredClauses = required;263 }264 265 void SetContextClauseInfo(C type) {266 GetContext().clauseInfo.emplace(type, GetContext().clause);267 }268 269 void AddClauseToCrtContext(C type) {270 GetContext().actualClauses.push_back(type);271 }272 273 void AddClauseToCrtGroupInContext(C type) {274 GetContext().crtGroup.push_back(type);275 }276 277 void ResetCrtGroup() { GetContext().crtGroup.clear(); }278 279 // Check if the given clause is present in the current context280 const PC *FindClause(C type) { return FindClause(GetContext(), type); }281 282 // Check if the given clause is present in the given context283 const PC *FindClause(DirectiveContext &context, C type) {284 auto it{context.clauseInfo.find(type)};285 if (it != context.clauseInfo.end()) {286 return it->second;287 }288 return nullptr;289 }290 291 // Check if the given clause is present in the parent context292 const PC *FindClauseParent(C type) {293 auto it{GetContextParent().clauseInfo.find(type)};294 if (it != GetContextParent().clauseInfo.end()) {295 return it->second;296 }297 return nullptr;298 }299 300 llvm::iterator_range<typename ClauseMapTy::iterator> FindClauses(C type) {301 auto it{GetContext().clauseInfo.equal_range(type)};302 return llvm::make_range(it);303 }304 305 DirectiveContext *GetEnclosingDirContext() {306 CHECK(!dirContext_.empty());307 auto it{dirContext_.rbegin()};308 if (++it != dirContext_.rend()) {309 return &(*it);310 }311 return nullptr;312 }313 314 void PushContext(const parser::CharBlock &source, D dir) {315 dirContext_.emplace_back(source, dir);316 }317 318 DirectiveContext *GetEnclosingContextWithDir(D dir) {319 CHECK(!dirContext_.empty());320 auto it{dirContext_.rbegin()};321 while (++it != dirContext_.rend()) {322 if (it->directive == dir) {323 return &(*it);324 }325 }326 return nullptr;327 }328 329 bool CurrentDirectiveIsNested() { return dirContext_.size() > 1; };330 331 void SetClauseSets(D dir) {332 dirContext_.back().allowedClauses = directiveClausesMap_[dir].allowed;333 dirContext_.back().allowedOnceClauses =334 directiveClausesMap_[dir].allowedOnce;335 dirContext_.back().allowedExclusiveClauses =336 directiveClausesMap_[dir].allowedExclusive;337 dirContext_.back().requiredClauses =338 directiveClausesMap_[dir].requiredOneOf;339 }340 void PushContextAndClauseSets(const parser::CharBlock &source, D dir) {341 PushContext(source, dir);342 SetClauseSets(dir);343 }344 345 void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &);346 347 template <typename B> void CheckMatching(const B &beginDir, const B &endDir) {348 const auto &begin{beginDir.v};349 const auto &end{endDir.v};350 if (begin != end) {351 SayNotMatching(beginDir.source, endDir.source);352 }353 }354 // Check illegal branching out of `Parser::Block` for `Parser::Name` based355 // nodes (example `Parser::ExitStmt`)356 void CheckNoBranching(const parser::Block &block, D directive,357 const parser::CharBlock &directiveSource);358 359 // Check that only clauses in set are after the specific clauses.360 void CheckOnlyAllowedAfter(C clause, common::EnumSet<C, ClauseEnumSize> set);361 362 void CheckRequireAtLeastOneOf(bool warnInsteadOfError = false);363 364 // Check if a clause is allowed on a directive. Returns true if is and365 // false otherwise.366 bool CheckAllowed(C clause, bool warnInsteadOfError = false);367 368 // Check that the clause appears only once. The counter is reset when the369 // separator clause appears.370 void CheckAllowedOncePerGroup(C clause, C separator);371 372 void CheckMutuallyExclusivePerGroup(373 C clause, C separator, common::EnumSet<C, ClauseEnumSize> set);374 375 void CheckAtLeastOneClause();376 377 void CheckNotAllowedIfClause(378 C clause, common::EnumSet<C, ClauseEnumSize> set);379 380 std::string ContextDirectiveAsFortran();381 382 void RequiresConstantPositiveParameter(383 const C &clause, const parser::ScalarIntConstantExpr &i);384 385 void RequiresPositiveParameter(const C &clause,386 const parser::ScalarIntExpr &i, llvm::StringRef paramName = "parameter",387 bool allowZero = true);388 389 void OptionalConstantPositiveParameter(390 const C &clause, const std::optional<parser::ScalarIntConstantExpr> &o);391 392 virtual llvm::StringRef getClauseName(C clause) { return ""; };393 394 virtual llvm::StringRef getDirectiveName(D directive) { return ""; };395 396 SemanticsContext &context_;397 std::vector<DirectiveContext> dirContext_; // used as a stack398 std::unordered_map<D, DirectiveClauses<C, ClauseEnumSize>>399 directiveClausesMap_;400 401 std::string ClauseSetToString(const common::EnumSet<C, ClauseEnumSize> set);402};403 404template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>405void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckNoBranching(406 const parser::Block &block, D directive,407 const parser::CharBlock &directiveSource) {408 NoBranchingEnforce<D> noBranchingEnforce{409 context_, directiveSource, directive, ContextDirectiveAsFortran()};410 parser::Walk(block, noBranchingEnforce);411}412 413// Check that only clauses included in the given set are present after the given414// clause.415template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>416void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckOnlyAllowedAfter(417 C clause, common::EnumSet<C, ClauseEnumSize> set) {418 bool enforceCheck = false;419 for (auto cl : GetContext().actualClauses) {420 if (cl == clause) {421 enforceCheck = true;422 continue;423 } else if (enforceCheck && !set.test(cl)) {424 auto parserClause = GetContext().clauseInfo.find(cl);425 context_.Say(parserClause->second->source,426 "Clause %s is not allowed after clause %s on the %s "427 "directive"_err_en_US,428 parser::ToUpperCaseLetters(getClauseName(cl).str()),429 parser::ToUpperCaseLetters(getClauseName(clause).str()),430 ContextDirectiveAsFortran());431 }432 }433}434 435// Check that at least one clause is attached to the directive.436template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>437void DirectiveStructureChecker<D, C, PC,438 ClauseEnumSize>::CheckAtLeastOneClause() {439 if (GetContext().actualClauses.empty()) {440 context_.Say(GetContext().directiveSource,441 "At least one clause is required on the %s directive"_err_en_US,442 ContextDirectiveAsFortran());443 }444}445 446template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>447std::string448DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::ClauseSetToString(449 const common::EnumSet<C, ClauseEnumSize> set) {450 std::string list;451 set.IterateOverMembers([&](C o) {452 if (!list.empty())453 list.append(", ");454 list.append(parser::ToUpperCaseLetters(getClauseName(o).str()));455 });456 return list;457}458 459// Check that at least one clause in the required set is present on the460// directive.461template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>462void DirectiveStructureChecker<D, C, PC,463 ClauseEnumSize>::CheckRequireAtLeastOneOf(bool warnInsteadOfError) {464 if (GetContext().requiredClauses.empty()) {465 return;466 }467 for (auto cl : GetContext().actualClauses) {468 if (GetContext().requiredClauses.test(cl)) {469 return;470 }471 }472 // No clause matched in the actual clauses list473 if (warnInsteadOfError) {474 context_.Warn(common::UsageWarning::Portability,475 GetContext().directiveSource,476 "At least one of %s clause should appear on the %s directive"_port_en_US,477 ClauseSetToString(GetContext().requiredClauses),478 ContextDirectiveAsFortran());479 } else {480 context_.Say(GetContext().directiveSource,481 "At least one of %s clause must appear on the %s directive"_err_en_US,482 ClauseSetToString(GetContext().requiredClauses),483 ContextDirectiveAsFortran());484 }485}486 487template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>488std::string DirectiveStructureChecker<D, C, PC,489 ClauseEnumSize>::ContextDirectiveAsFortran() {490 return parser::ToUpperCaseLetters(491 getDirectiveName(GetContext().directive).str());492}493 494// Check that clauses present on the directive are allowed clauses.495template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>496bool DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckAllowed(497 C clause, bool warnInsteadOfError) {498 if (!GetContext().allowedClauses.test(clause) &&499 !GetContext().allowedOnceClauses.test(clause) &&500 !GetContext().allowedExclusiveClauses.test(clause) &&501 !GetContext().requiredClauses.test(clause)) {502 if (warnInsteadOfError) {503 context_.Warn(common::UsageWarning::Portability,504 GetContext().clauseSource,505 "%s clause is not allowed on the %s directive and will be ignored"_port_en_US,506 parser::ToUpperCaseLetters(getClauseName(clause).str()),507 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));508 } else {509 context_.Say(GetContext().clauseSource,510 "%s clause is not allowed on the %s directive"_err_en_US,511 parser::ToUpperCaseLetters(getClauseName(clause).str()),512 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));513 }514 return false;515 }516 if ((GetContext().allowedOnceClauses.test(clause) ||517 GetContext().allowedExclusiveClauses.test(clause)) &&518 FindClause(clause)) {519 context_.Say(GetContext().clauseSource,520 "At most one %s clause can appear on the %s directive"_err_en_US,521 parser::ToUpperCaseLetters(getClauseName(clause).str()),522 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));523 return false;524 }525 if (GetContext().allowedExclusiveClauses.test(clause)) {526 std::vector<C> others;527 GetContext().allowedExclusiveClauses.IterateOverMembers([&](C o) {528 if (FindClause(o)) {529 others.emplace_back(o);530 }531 });532 for (const auto &e : others) {533 context_.Say(GetContext().clauseSource,534 "%s and %s clauses are mutually exclusive and may not appear on the "535 "same %s directive"_err_en_US,536 parser::ToUpperCaseLetters(getClauseName(clause).str()),537 parser::ToUpperCaseLetters(getClauseName(e).str()),538 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));539 }540 if (!others.empty()) {541 return false;542 }543 }544 SetContextClauseInfo(clause);545 AddClauseToCrtContext(clause);546 AddClauseToCrtGroupInContext(clause);547 return true;548}549 550// Enforce restriction where clauses in the given set are not allowed if the551// given clause appears.552template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>553void DirectiveStructureChecker<D, C, PC,554 ClauseEnumSize>::CheckNotAllowedIfClause(C clause,555 common::EnumSet<C, ClauseEnumSize> set) {556 if (!llvm::is_contained(GetContext().actualClauses, clause)) {557 return; // Clause is not present558 }559 560 for (auto cl : GetContext().actualClauses) {561 if (set.test(cl)) {562 context_.Say(GetContext().directiveSource,563 "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,564 parser::ToUpperCaseLetters(getClauseName(cl).str()),565 parser::ToUpperCaseLetters(getClauseName(clause).str()),566 ContextDirectiveAsFortran());567 }568 }569}570 571template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>572void DirectiveStructureChecker<D, C, PC,573 ClauseEnumSize>::CheckAllowedOncePerGroup(C clause, C separator) {574 bool clauseIsPresent = false;575 for (auto cl : GetContext().actualClauses) {576 if (cl == clause) {577 if (clauseIsPresent) {578 context_.Say(GetContext().clauseSource,579 "At most one %s clause can appear on the %s directive or in group separated by the %s clause"_err_en_US,580 parser::ToUpperCaseLetters(getClauseName(clause).str()),581 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()),582 parser::ToUpperCaseLetters(getClauseName(separator).str()));583 } else {584 clauseIsPresent = true;585 }586 }587 if (cl == separator)588 clauseIsPresent = false;589 }590}591 592template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>593void DirectiveStructureChecker<D, C, PC,594 ClauseEnumSize>::CheckMutuallyExclusivePerGroup(C clause, C separator,595 common::EnumSet<C, ClauseEnumSize> set) {596 597 // Checking of there is any offending clauses before the first separator.598 for (auto cl : GetContext().actualClauses) {599 if (cl == separator) {600 break;601 }602 if (set.test(cl)) {603 context_.Say(GetContext().directiveSource,604 "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,605 parser::ToUpperCaseLetters(getClauseName(clause).str()),606 parser::ToUpperCaseLetters(getClauseName(cl).str()),607 ContextDirectiveAsFortran());608 }609 }610 611 // Checking for mutually exclusive clauses in the current group.612 for (auto cl : GetContext().crtGroup) {613 if (set.test(cl)) {614 context_.Say(GetContext().directiveSource,615 "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,616 parser::ToUpperCaseLetters(getClauseName(clause).str()),617 parser::ToUpperCaseLetters(getClauseName(cl).str()),618 ContextDirectiveAsFortran());619 }620 }621}622 623// Check the value of the clause is a constant positive integer.624template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>625void DirectiveStructureChecker<D, C, PC,626 ClauseEnumSize>::RequiresConstantPositiveParameter(const C &clause,627 const parser::ScalarIntConstantExpr &i) {628 if (const auto v{GetIntValue(i)}) {629 if (*v <= 0) {630 context_.Say(GetContext().clauseSource,631 "The parameter of the %s clause must be "632 "a constant positive integer expression"_err_en_US,633 parser::ToUpperCaseLetters(getClauseName(clause).str()));634 }635 }636}637 638// Check the value of the clause is a constant positive parameter.639template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>640void DirectiveStructureChecker<D, C, PC,641 ClauseEnumSize>::OptionalConstantPositiveParameter(const C &clause,642 const std::optional<parser::ScalarIntConstantExpr> &o) {643 if (o != std::nullopt) {644 RequiresConstantPositiveParameter(clause, o.value());645 }646}647 648template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>649void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::SayNotMatching(650 const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {651 context_652 .Say(endSource, "Unmatched %s directive"_err_en_US,653 parser::ToUpperCaseLetters(endSource.ToString()))654 .Attach(beginSource, "Does not match directive"_en_US);655}656 657// Check the value of the clause is a positive parameter.658template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>659void DirectiveStructureChecker<D, C, PC,660 ClauseEnumSize>::RequiresPositiveParameter(const C &clause,661 const parser::ScalarIntExpr &i, llvm::StringRef paramName, bool allowZero) {662 if (const auto v{GetIntValue(i)}) {663 if (*v < (allowZero ? 0 : 1)) {664 context_.Say(GetContext().clauseSource,665 "The %s of the %s clause must be "666 "a positive integer expression"_err_en_US,667 paramName.str(),668 parser::ToUpperCaseLetters(getClauseName(clause).str()));669 }670 }671}672 673} // namespace Fortran::semantics674 675#endif // FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_676