12#ifndef FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_
13#define FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_
15#include "flang/Semantics/semantics.h"
16#include "flang/Semantics/tools.h"
17#include "llvm/ADT/iterator_range.h"
21#include <unordered_map>
23namespace Fortran::semantics {
26 const ClauseSetTy allowed;
27 const ClauseSetTy allowedOnce;
28 const ClauseSetTy allowedExclusive;
29 const ClauseSetTy requiredOneOf;
32template <
typename ClauseTy,
typename ClauseSetTy>
33void IterateOverMembers(
34 const ClauseSetTy &set, std::function<
void(ClauseTy)> func);
39template <
typename D>
class NoBranchingEnforce {
43 std::string &&upperCaseDirName)
44 : context_{context}, sourcePosition_{sourcePosition},
45 upperCaseDirName_{std::move(upperCaseDirName)},
46 currentDirective_{directive}, numDoConstruct_{0} {}
47 template <
typename T>
bool Pre(
const T &) {
return true; }
48 template <
typename T>
void Post(
const T &) {}
51 currentStatementSourcePosition_ = statement.source;
60 void Post(
const parser::ReturnStmt &) { EmitBranchOutError(
"RETURN"); }
61 void Post(
const parser::GotoStmt &gotoStmt) {
62 if constexpr (std::is_same_v<D, llvm::acc::Directive>) {
63 switch ((llvm::acc::Directive)currentDirective_) {
64 case llvm::acc::Directive::ACCD_parallel:
65 case llvm::acc::Directive::ACCD_serial:
66 case llvm::acc::Directive::ACCD_kernels:
67 if (labelsInBlock_.count(gotoStmt.v) == 0)
68 EmitBranchOutOfComputeConstructError(
"GOTO");
75 void CollectLabel(parser::Label label) { labelsInBlock_.insert(label); }
76 void Post(
const parser::ExitStmt &exitStmt) {
77 if (
const auto &exitName{exitStmt.v}) {
78 CheckConstructNameBranching(
"EXIT", exitName.value());
80 CheckConstructNameBranching(
"EXIT");
83 void Post(
const parser::CycleStmt &cycleStmt) {
84 if (
const auto &cycleName{cycleStmt.v}) {
85 CheckConstructNameBranching(
"CYCLE", cycleName.value());
87 if constexpr (std::is_same_v<D, llvm::omp::Directive>) {
88 switch ((llvm::omp::Directive)currentDirective_) {
90 case llvm::omp::Directive::OMPD_do:
91 case llvm::omp::Directive::OMPD_simd:
92 case llvm::omp::Directive::OMPD_parallel_do:
93 case llvm::omp::Directive::OMPD_parallel_do_simd:
94 case llvm::omp::Directive::OMPD_distribute_parallel_do:
95 case llvm::omp::Directive::OMPD_distribute_parallel_do_simd:
96 case llvm::omp::Directive::OMPD_distribute_parallel_for:
97 case llvm::omp::Directive::OMPD_distribute_simd:
98 case llvm::omp::Directive::OMPD_distribute_parallel_for_simd:
99 case llvm::omp::Directive::OMPD_target_teams_distribute:
100 case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
101 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
102 case llvm::omp::Directive::
103 OMPD_target_teams_distribute_parallel_do_simd:
108 }
else if constexpr (std::is_same_v<D, llvm::acc::Directive>) {
109 switch ((llvm::acc::Directive)currentDirective_) {
112 case llvm::acc::Directive::ACCD_loop:
113 case llvm::acc::Directive::ACCD_kernels_loop:
114 case llvm::acc::Directive::ACCD_parallel_loop:
115 case llvm::acc::Directive::ACCD_serial_loop:
121 CheckConstructNameBranching(
"CYCLE");
127 return {
"Enclosing %s construct"_en_US, upperCaseDirName_};
130 void EmitBranchOutError(
const char *stmt)
const {
132 .Say(currentStatementSourcePosition_,
133 "%s statement is not allowed in a %s construct"_err_en_US, stmt,
135 .Attach(sourcePosition_, GetEnclosingMsg());
138 void EmitBranchOutOfComputeConstructError(
const char *stmt)
const {
140 .Say(currentStatementSourcePosition_,
141 "%s to a label outside of a %s construct is not allowed"_err_en_US,
142 stmt, upperCaseDirName_)
143 .Attach(sourcePosition_, GetEnclosingMsg());
146 inline void EmitUnlabelledBranchOutError(
const char *stmt) {
148 .Say(currentStatementSourcePosition_,
149 "%s to construct outside of %s construct is not allowed"_err_en_US,
150 stmt, upperCaseDirName_)
151 .Attach(sourcePosition_, GetEnclosingMsg());
154 void EmitBranchOutErrorWithName(
156 const std::string branchingToName{toName.ToString()};
158 .Say(currentStatementSourcePosition_,
159 "%s to construct '%s' outside of %s construct is not allowed"_err_en_US,
160 stmt, branchingToName, upperCaseDirName_)
161 .Attach(sourcePosition_, GetEnclosingMsg());
170 void CheckConstructNameBranching(
172 const ConstructStack &stack{context_.constructStack()};
173 for (
auto iter{stack.cend()}; iter-- != stack.cbegin();) {
174 const ConstructNode &construct{*iter};
175 const auto &constructName{MaybeGetNodeName(construct)};
177 if (stmtName.source == constructName->source) {
178 EmitBranchOutErrorWithName(stmt, stmtName);
186 void CheckConstructNameBranching(
const char *stmt) {
188 if (numDoConstruct_ > 0) {
193 EmitUnlabelledBranchOutError(stmt);
199 std::string upperCaseDirName_;
203 std::set<parser::Label> labelsInBlock_;
211template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
216 &directiveClausesMap)
217 : context_{context}, directiveClausesMap_(directiveClausesMap) {}
218 virtual ~DirectiveStructureChecker() {}
220 using ClauseMapTy = std::multimap<C, const PC *>;
221 struct DirectiveContext {
223 : directiveSource{source}, directive{d} {}
228 ClauseSetTy allowedClauses{};
229 ClauseSetTy allowedOnceClauses{};
230 ClauseSetTy allowedExclusiveClauses{};
231 ClauseSetTy requiredClauses{};
233 const PC *clause{
nullptr};
234 ClauseMapTy clauseInfo;
235 std::list<C> actualClauses;
236 std::list<C> endDirectiveClauses;
237 std::list<C> crtGroup;
242 CHECK(!dirContext_.empty());
243 return dirContext_.back();
246 DirectiveContext &GetContextParent() {
247 CHECK(dirContext_.size() >= 2);
248 return dirContext_[dirContext_.size() - 2];
251 void SetContextClause(
const PC &clause) {
252 GetContext().clauseSource = clause.source;
253 GetContext().clause = &clause;
256 void ResetPartialContext(
const parser::CharBlock &source) {
257 CHECK(!dirContext_.empty());
258 SetContextDirectiveSource(source);
259 GetContext().allowedClauses = {};
260 GetContext().allowedOnceClauses = {};
261 GetContext().allowedExclusiveClauses = {};
262 GetContext().requiredClauses = {};
263 GetContext().clauseInfo = {};
266 void SetContextDirectiveSource(
const parser::CharBlock &directive) {
267 GetContext().directiveSource = directive;
270 void SetContextDirectiveEnum(D dir) { GetContext().directive = dir; }
272 void SetContextAllowed(
const ClauseSetTy &allowed) {
273 GetContext().allowedClauses = allowed;
276 void SetContextAllowedOnce(
const ClauseSetTy &allowedOnce) {
277 GetContext().allowedOnceClauses = allowedOnce;
280 void SetContextAllowedExclusive(
const ClauseSetTy &allowedExclusive) {
281 GetContext().allowedExclusiveClauses = allowedExclusive;
284 void SetContextRequired(
const ClauseSetTy &required) {
285 GetContext().requiredClauses = required;
288 void SetContextClauseInfo(C type) {
289 GetContext().clauseInfo.emplace(type, GetContext().clause);
292 void AddClauseToCrtContext(C type) {
293 GetContext().actualClauses.push_back(type);
296 void AddClauseToCrtGroupInContext(C type) {
297 GetContext().crtGroup.push_back(type);
300 void ResetCrtGroup() { GetContext().crtGroup.clear(); }
303 const PC *FindClause(C type) {
return FindClause(GetContext(), type); }
307 auto it{context.clauseInfo.find(type)};
308 if (it != context.clauseInfo.end()) {
315 const PC *FindClauseParent(C type) {
316 auto it{GetContextParent().clauseInfo.find(type)};
317 if (it != GetContextParent().clauseInfo.end()) {
323 llvm::iterator_range<typename ClauseMapTy::iterator> FindClauses(C type) {
324 auto it{GetContext().clauseInfo.equal_range(type)};
325 return llvm::make_range(it);
329 CHECK(!dirContext_.empty());
330 auto it{dirContext_.rbegin()};
331 if (++it != dirContext_.rend()) {
337 void PushContext(
const parser::CharBlock &source, D dir) {
338 dirContext_.emplace_back(source, dir);
342 CHECK(!dirContext_.empty());
343 auto it{dirContext_.rbegin()};
344 while (++it != dirContext_.rend()) {
345 if (it->directive == dir) {
352 bool CurrentDirectiveIsNested() {
return dirContext_.size() > 1; };
354 void SetClauseSets(D dir) {
355 dirContext_.back().allowedClauses = directiveClausesMap_[dir].allowed;
356 dirContext_.back().allowedOnceClauses =
357 directiveClausesMap_[dir].allowedOnce;
358 dirContext_.back().allowedExclusiveClauses =
359 directiveClausesMap_[dir].allowedExclusive;
360 dirContext_.back().requiredClauses =
361 directiveClausesMap_[dir].requiredOneOf;
363 void PushContextAndClauseSets(
const parser::CharBlock &source, D dir) {
364 PushContext(source, dir);
368 void SayNotMatching(
const parser::CharBlock &,
const parser::CharBlock &);
370 template <
typename B>
void CheckMatching(
const B &beginDir,
const B &endDir) {
371 const auto &begin{beginDir.v};
372 const auto &end{endDir.v};
374 SayNotMatching(beginDir.source, endDir.source);
379 void CheckNoBranching(
const parser::Block &block, D directive,
380 const parser::CharBlock &directiveSource);
383 void CheckOnlyAllowedAfter(C clause, ClauseSetTy set);
385 void CheckRequireAtLeastOneOf(
bool warnInsteadOfError =
false);
389 bool CheckAllowed(C clause,
bool warnInsteadOfError =
false);
393 void CheckAllowedOncePerGroup(C clause, C separator);
395 void CheckMutuallyExclusivePerGroup(C clause, C separator, ClauseSetTy set);
397 void CheckAtLeastOneClause();
399 void CheckNotAllowedIfClause(C clause, ClauseSetTy set);
401 std::string ContextDirectiveAsFortran();
403 void RequiresConstantPositiveParameter(
404 const C &clause,
const parser::ScalarIntConstantExpr &i);
406 void RequiresPositiveParameter(
const C &clause,
407 const parser::ScalarIntExpr &i, llvm::StringRef paramName =
"parameter",
408 bool allowZero =
true);
410 void OptionalConstantPositiveParameter(
411 const C &clause,
const std::optional<parser::ScalarIntConstantExpr> &o);
413 virtual llvm::StringRef getClauseName(C clause) {
return ""; };
415 virtual llvm::StringRef getDirectiveName(D directive) {
return ""; };
417 SemanticsContext &context_;
418 std::vector<DirectiveContext> dirContext_;
419 std::unordered_map<D, DirectiveClauses<ClauseSetTy>> directiveClausesMap_;
421 std::string ClauseSetToString(
const ClauseSetTy &set);
426 std::set<parser::Label> labels;
427 template <
typename T>
bool Pre(
const T &) {
return true; }
428 template <
typename T>
void Post(
const T &) {}
431 labels.insert(*stmt.label);
436template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
437void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckNoBranching(
438 const parser::Block &block, D directive,
441 parser::Walk(block, labelCollector);
443 context_, directiveSource, directive, ContextDirectiveAsFortran()};
444 for (
auto label : labelCollector.labels)
445 noBranchingEnforce.CollectLabel(label);
446 parser::Walk(block, noBranchingEnforce);
451template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
452void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckOnlyAllowedAfter(
453 C clause, ClauseSetTy set) {
454 bool enforceCheck =
false;
455 for (
auto cl : GetContext().actualClauses) {
459 }
else if (enforceCheck && !set.test(cl)) {
460 auto parserClause = GetContext().clauseInfo.find(cl);
461 context_.Say(parserClause->second->source,
462 "Clause %s is not allowed after clause %s on the %s "
463 "directive"_err_en_US,
464 parser::ToUpperCaseLetters(getClauseName(cl).str()),
465 parser::ToUpperCaseLetters(getClauseName(clause).str()),
466 ContextDirectiveAsFortran());
472template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
473void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckAtLeastOneClause() {
474 if (GetContext().actualClauses.empty()) {
475 context_.Say(GetContext().directiveSource,
476 "At least one clause is required on the %s directive"_err_en_US,
477 ContextDirectiveAsFortran());
481template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
482std::string DirectiveStructureChecker<D, C, PC, ClauseSetTy>::ClauseSetToString(
483 const ClauseSetTy &set) {
485 std::function<void(C)> visitor{[&](C o) {
488 list.append(parser::ToUpperCaseLetters(getClauseName(o).str()));
490 IterateOverMembers(set, visitor);
496template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
497void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckRequireAtLeastOneOf(
498 bool warnInsteadOfError) {
499 if (GetContext().requiredClauses.empty()) {
502 for (
auto cl : GetContext().actualClauses) {
503 if (GetContext().requiredClauses.test(cl)) {
508 if (warnInsteadOfError) {
509 context_.Warn(common::UsageWarning::Portability,
510 GetContext().directiveSource,
511 "At least one of %s clause should appear on the %s directive"_port_en_US,
512 ClauseSetToString(GetContext().requiredClauses),
513 ContextDirectiveAsFortran());
515 context_.Say(GetContext().directiveSource,
516 "At least one of %s clause must appear on the %s directive"_err_en_US,
517 ClauseSetToString(GetContext().requiredClauses),
518 ContextDirectiveAsFortran());
522template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
524DirectiveStructureChecker<D, C, PC, ClauseSetTy>::ContextDirectiveAsFortran() {
525 return parser::ToUpperCaseLetters(
526 getDirectiveName(GetContext().directive).str());
530template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
531bool DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckAllowed(
532 C clause,
bool warnInsteadOfError) {
533 if (!GetContext().allowedClauses.test(clause) &&
534 !GetContext().allowedOnceClauses.test(clause) &&
535 !GetContext().allowedExclusiveClauses.test(clause) &&
536 !GetContext().requiredClauses.test(clause)) {
537 if (warnInsteadOfError) {
538 context_.Warn(common::UsageWarning::Portability,
539 GetContext().clauseSource,
540 "%s clause is not allowed on the %s directive and will be ignored"_port_en_US,
541 parser::ToUpperCaseLetters(getClauseName(clause).str()),
542 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
544 context_.Say(GetContext().clauseSource,
545 "%s clause is not allowed on the %s directive"_err_en_US,
546 parser::ToUpperCaseLetters(getClauseName(clause).str()),
547 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
551 if ((GetContext().allowedOnceClauses.test(clause) ||
552 GetContext().allowedExclusiveClauses.test(clause)) &&
553 FindClause(clause)) {
554 context_.Say(GetContext().clauseSource,
555 "At most one %s clause can appear on the %s directive"_err_en_US,
556 parser::ToUpperCaseLetters(getClauseName(clause).str()),
557 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
560 if (GetContext().allowedExclusiveClauses.test(clause)) {
561 std::vector<C> others;
562 std::function<void(C)> visitor{[&](C o) {
564 others.emplace_back(o);
567 IterateOverMembers(GetContext().allowedExclusiveClauses, visitor);
568 for (
const auto &e : others) {
569 context_.Say(GetContext().clauseSource,
570 "%s and %s clauses are mutually exclusive and may not appear on the "
571 "same %s directive"_err_en_US,
572 parser::ToUpperCaseLetters(getClauseName(clause).str()),
573 parser::ToUpperCaseLetters(getClauseName(e).str()),
574 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
576 if (!others.empty()) {
580 SetContextClauseInfo(clause);
581 AddClauseToCrtContext(clause);
582 AddClauseToCrtGroupInContext(clause);
588template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
589void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckNotAllowedIfClause(
590 C clause, ClauseSetTy set) {
591 if (!llvm::is_contained(GetContext().actualClauses, clause)) {
595 for (
auto cl : GetContext().actualClauses) {
597 context_.Say(GetContext().directiveSource,
598 "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,
599 parser::ToUpperCaseLetters(getClauseName(cl).str()),
600 parser::ToUpperCaseLetters(getClauseName(clause).str()),
601 ContextDirectiveAsFortran());
606template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
607void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckAllowedOncePerGroup(
608 C clause, C separator) {
609 bool clauseIsPresent =
false;
610 for (
auto cl : GetContext().actualClauses) {
612 if (clauseIsPresent) {
613 context_.Say(GetContext().clauseSource,
614 "At most one %s clause can appear on the %s directive or in group separated by the %s clause"_err_en_US,
615 parser::ToUpperCaseLetters(getClauseName(clause).str()),
616 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()),
617 parser::ToUpperCaseLetters(getClauseName(separator).str()));
619 clauseIsPresent =
true;
623 clauseIsPresent =
false;
627template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
629 ClauseSetTy>::CheckMutuallyExclusivePerGroup(C clause, C separator,
633 for (
auto cl : GetContext().actualClauses) {
634 if (cl == separator) {
638 context_.Say(GetContext().directiveSource,
639 "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,
640 parser::ToUpperCaseLetters(getClauseName(clause).str()),
641 parser::ToUpperCaseLetters(getClauseName(cl).str()),
642 ContextDirectiveAsFortran());
647 for (
auto cl : GetContext().crtGroup) {
649 context_.Say(GetContext().directiveSource,
650 "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,
651 parser::ToUpperCaseLetters(getClauseName(clause).str()),
652 parser::ToUpperCaseLetters(getClauseName(cl).str()),
653 ContextDirectiveAsFortran());
659template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
661 ClauseSetTy>::RequiresConstantPositiveParameter(
const C &clause,
662 const parser::ScalarIntConstantExpr &i) {
663 if (
const auto v{GetIntValue(i)}) {
665 context_.Say(GetContext().clauseSource,
666 "The parameter of the %s clause must be "
667 "a constant positive integer expression"_err_en_US,
668 parser::ToUpperCaseLetters(getClauseName(clause).str()));
674template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
676 ClauseSetTy>::OptionalConstantPositiveParameter(
const C &clause,
677 const std::optional<parser::ScalarIntConstantExpr> &o) {
678 if (o != std::nullopt) {
679 RequiresConstantPositiveParameter(clause, o.value());
683template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
684void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::SayNotMatching(
685 const parser::CharBlock &beginSource,
const parser::CharBlock &endSource) {
687 .Say(endSource,
"Unmatched %s directive"_err_en_US,
688 parser::ToUpperCaseLetters(endSource.ToString()))
689 .Attach(beginSource,
"Does not match directive"_en_US);
693template <
typename D,
typename C,
typename PC,
typename ClauseSetTy>
695 ClauseSetTy>::RequiresPositiveParameter(
const C &clause,
696 const parser::ScalarIntExpr &i, llvm::StringRef paramName,
bool allowZero) {
697 if (
const auto v{GetIntValue(i)}) {
698 if (*v < (allowZero ? 0 : 1)) {
699 context_.Say(GetContext().clauseSource,
700 "The %s of the %s clause must be "
701 "a positive integer expression"_err_en_US,
703 parser::ToUpperCaseLetters(getClauseName(clause).str()));
Definition char-block.h:26
Definition check-directive-structure.h:212
Definition check-directive-structure.h:39
Definition semantics.h:67
Definition parse-tree.h:2367
Definition parse-tree.h:591
Definition parse-tree.h:361
Definition semantics.h:468
Definition check-directive-structure.h:25
Definition check-directive-structure.h:221
Definition check-directive-structure.h:425