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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // Directive structure validity checks common to OpenMP, OpenACC and other
10 // 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 <unordered_map>
19 
20 namespace Fortran::semantics {
21 
22 template <typename C, std::size_t ClauseEnumSize> struct DirectiveClauses {
23   const common::EnumSet<C, ClauseEnumSize> allowed;
24   const common::EnumSet<C, ClauseEnumSize> allowedOnce;
25   const common::EnumSet<C, ClauseEnumSize> allowedExclusive;
26   const common::EnumSet<C, ClauseEnumSize> requiredOneOf;
27 };
28 
29 // Generic branching checker for invalid branching out of OpenMP/OpenACC
30 // directive.
31 // typename D is the directive enumeration.
32 template <typename D> class NoBranchingEnforce {
33 public:
NoBranchingEnforce(SemanticsContext & context,parser::CharBlock sourcePosition,D directive,std::string && upperCaseDirName)34   NoBranchingEnforce(SemanticsContext &context,
35       parser::CharBlock sourcePosition, D directive,
36       std::string &&upperCaseDirName)
37       : context_{context}, sourcePosition_{sourcePosition},
38         upperCaseDirName_{std::move(upperCaseDirName)},
39         currentDirective_{directive}, numDoConstruct_{0} {}
Pre(const T &)40   template <typename T> bool Pre(const T &) { return true; }
Post(const T &)41   template <typename T> void Post(const T &) {}
42 
Pre(const parser::Statement<T> & statement)43   template <typename T> bool Pre(const parser::Statement<T> &statement) {
44     currentStatementSourcePosition_ = statement.source;
45     return true;
46   }
47 
Pre(const parser::DoConstruct &)48   bool Pre(const parser::DoConstruct &) {
49     numDoConstruct_++;
50     return true;
51   }
Post(const parser::DoConstruct &)52   void Post(const parser::DoConstruct &) { numDoConstruct_--; }
Post(const parser::ReturnStmt &)53   void Post(const parser::ReturnStmt &) { EmitBranchOutError("RETURN"); }
Post(const parser::ExitStmt & exitStmt)54   void Post(const parser::ExitStmt &exitStmt) {
55     if (const auto &exitName{exitStmt.v}) {
56       CheckConstructNameBranching("EXIT", exitName.value());
57     } else {
58       CheckConstructNameBranching("EXIT");
59     }
60   }
Post(const parser::StopStmt &)61   void Post(const parser::StopStmt &) { EmitBranchOutError("STOP"); }
Post(const parser::CycleStmt & cycleStmt)62   void Post(const parser::CycleStmt &cycleStmt) {
63     if (const auto &cycleName{cycleStmt.v}) {
64       CheckConstructNameBranching("CYCLE", cycleName.value());
65     } else {
66       switch ((llvm::omp::Directive)currentDirective_) {
67       // exclude directives which do not need a check for unlabelled CYCLES
68       case llvm::omp::Directive::OMPD_do:
69         return;
70       case llvm::omp::Directive::OMPD_simd:
71         return;
72       default:
73         break;
74       }
75       CheckConstructNameBranching("CYCLE");
76     }
77   }
78 
79 private:
GetEnclosingMsg()80   parser::MessageFormattedText GetEnclosingMsg() const {
81     return {"Enclosing %s construct"_en_US, upperCaseDirName_};
82   }
83 
EmitBranchOutError(const char * stmt)84   void EmitBranchOutError(const char *stmt) const {
85     context_
86         .Say(currentStatementSourcePosition_,
87             "%s statement is not allowed in a %s construct"_err_en_US, stmt,
88             upperCaseDirName_)
89         .Attach(sourcePosition_, GetEnclosingMsg());
90   }
91 
EmitUnlabelledBranchOutError(const char * stmt)92   inline void EmitUnlabelledBranchOutError(const char *stmt) {
93     context_
94         .Say(currentStatementSourcePosition_,
95             "%s to construct outside of %s construct is not allowed"_err_en_US,
96             stmt, upperCaseDirName_)
97         .Attach(sourcePosition_, GetEnclosingMsg());
98   }
99 
EmitBranchOutErrorWithName(const char * stmt,const parser::Name & toName)100   void EmitBranchOutErrorWithName(
101       const char *stmt, const parser::Name &toName) const {
102     const std::string branchingToName{toName.ToString()};
103     context_
104         .Say(currentStatementSourcePosition_,
105             "%s to construct '%s' outside of %s construct is not allowed"_err_en_US,
106             stmt, branchingToName, upperCaseDirName_)
107         .Attach(sourcePosition_, GetEnclosingMsg());
108   }
109 
110   // Current semantic checker is not following OpenACC/OpenMP constructs as they
111   // are not Fortran constructs. Hence the ConstructStack doesn't capture
112   // OpenACC/OpenMP constructs. Apply an inverse way to figure out if a
113   // construct-name is branching out of an OpenACC/OpenMP construct. The control
114   // flow goes out of an OpenACC/OpenMP construct, if a construct-name from
115   // statement is found in ConstructStack.
CheckConstructNameBranching(const char * stmt,const parser::Name & stmtName)116   void CheckConstructNameBranching(
117       const char *stmt, const parser::Name &stmtName) {
118     const ConstructStack &stack{context_.constructStack()};
119     for (auto iter{stack.cend()}; iter-- != stack.cbegin();) {
120       const ConstructNode &construct{*iter};
121       const auto &constructName{MaybeGetNodeName(construct)};
122       if (constructName) {
123         if (stmtName.source == constructName->source) {
124           EmitBranchOutErrorWithName(stmt, stmtName);
125           return;
126         }
127       }
128     }
129   }
130 
131   // Check branching for unlabelled CYCLES and EXITs
CheckConstructNameBranching(const char * stmt)132   void CheckConstructNameBranching(const char *stmt) {
133     // found an enclosing looping construct for the unlabelled EXIT/CYCLE
134     if (numDoConstruct_ > 0) {
135       return;
136     }
137     // did not found an enclosing looping construct within the OpenMP/OpenACC
138     // directive
139     EmitUnlabelledBranchOutError(stmt);
140     return;
141   }
142 
143   SemanticsContext &context_;
144   parser::CharBlock currentStatementSourcePosition_;
145   parser::CharBlock sourcePosition_;
146   std::string upperCaseDirName_;
147   D currentDirective_;
148   int numDoConstruct_; // tracks number of DoConstruct found AFTER encountering
149                        // an OpenMP/OpenACC directive
150 };
151 
152 // Generic structure checker for directives/clauses language such as OpenMP
153 // and OpenACC.
154 // typename D is the directive enumeration.
155 // tyepname C is the clause enumeration.
156 // typename PC is the parser class defined in parse-tree.h for the clauses.
157 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
158 class DirectiveStructureChecker : public virtual BaseChecker {
159 protected:
DirectiveStructureChecker(SemanticsContext & context,std::unordered_map<D,DirectiveClauses<C,ClauseEnumSize>> directiveClausesMap)160   DirectiveStructureChecker(SemanticsContext &context,
161       std::unordered_map<D, DirectiveClauses<C, ClauseEnumSize>>
162           directiveClausesMap)
163       : context_{context}, directiveClausesMap_(directiveClausesMap) {}
~DirectiveStructureChecker()164   virtual ~DirectiveStructureChecker() {}
165 
166   using ClauseMapTy = std::multimap<C, const PC *>;
167   struct DirectiveContext {
DirectiveContextDirectiveContext168     DirectiveContext(parser::CharBlock source, D d)
169         : directiveSource{source}, directive{d} {}
170 
171     parser::CharBlock directiveSource{nullptr};
172     parser::CharBlock clauseSource{nullptr};
173     D directive;
174     common::EnumSet<C, ClauseEnumSize> allowedClauses{};
175     common::EnumSet<C, ClauseEnumSize> allowedOnceClauses{};
176     common::EnumSet<C, ClauseEnumSize> allowedExclusiveClauses{};
177     common::EnumSet<C, ClauseEnumSize> requiredClauses{};
178 
179     const PC *clause{nullptr};
180     ClauseMapTy clauseInfo;
181     std::list<C> actualClauses;
182     Symbol *loopIV{nullptr};
183   };
184 
SetLoopIv(Symbol * symbol)185   void SetLoopIv(Symbol *symbol) { GetContext().loopIV = symbol; }
186 
187   // back() is the top of the stack
GetContext()188   DirectiveContext &GetContext() {
189     CHECK(!dirContext_.empty());
190     return dirContext_.back();
191   }
192 
GetContextParent()193   DirectiveContext &GetContextParent() {
194     CHECK(dirContext_.size() >= 2);
195     return dirContext_[dirContext_.size() - 2];
196   }
197 
SetContextClause(const PC & clause)198   void SetContextClause(const PC &clause) {
199     GetContext().clauseSource = clause.source;
200     GetContext().clause = &clause;
201   }
202 
ResetPartialContext(const parser::CharBlock & source)203   void ResetPartialContext(const parser::CharBlock &source) {
204     CHECK(!dirContext_.empty());
205     SetContextDirectiveSource(source);
206     GetContext().allowedClauses = {};
207     GetContext().allowedOnceClauses = {};
208     GetContext().allowedExclusiveClauses = {};
209     GetContext().requiredClauses = {};
210     GetContext().clauseInfo = {};
211     GetContext().loopIV = {nullptr};
212   }
213 
SetContextDirectiveSource(const parser::CharBlock & directive)214   void SetContextDirectiveSource(const parser::CharBlock &directive) {
215     GetContext().directiveSource = directive;
216   }
217 
SetContextDirectiveEnum(D dir)218   void SetContextDirectiveEnum(D dir) { GetContext().directive = dir; }
219 
SetContextAllowed(const common::EnumSet<C,ClauseEnumSize> & allowed)220   void SetContextAllowed(const common::EnumSet<C, ClauseEnumSize> &allowed) {
221     GetContext().allowedClauses = allowed;
222   }
223 
SetContextAllowedOnce(const common::EnumSet<C,ClauseEnumSize> & allowedOnce)224   void SetContextAllowedOnce(
225       const common::EnumSet<C, ClauseEnumSize> &allowedOnce) {
226     GetContext().allowedOnceClauses = allowedOnce;
227   }
228 
SetContextAllowedExclusive(const common::EnumSet<C,ClauseEnumSize> & allowedExclusive)229   void SetContextAllowedExclusive(
230       const common::EnumSet<C, ClauseEnumSize> &allowedExclusive) {
231     GetContext().allowedExclusiveClauses = allowedExclusive;
232   }
233 
SetContextRequired(const common::EnumSet<C,ClauseEnumSize> & required)234   void SetContextRequired(const common::EnumSet<C, ClauseEnumSize> &required) {
235     GetContext().requiredClauses = required;
236   }
237 
SetContextClauseInfo(C type)238   void SetContextClauseInfo(C type) {
239     GetContext().clauseInfo.emplace(type, GetContext().clause);
240   }
241 
AddClauseToCrtContext(C type)242   void AddClauseToCrtContext(C type) {
243     GetContext().actualClauses.push_back(type);
244   }
245 
246   // Check if the given clause is present in the current context
FindClause(C type)247   const PC *FindClause(C type) {
248     auto it{GetContext().clauseInfo.find(type)};
249     if (it != GetContext().clauseInfo.end()) {
250       return it->second;
251     }
252     return nullptr;
253   }
254 
255   // Check if the given clause is present in the parent context
FindClauseParent(C type)256   const PC *FindClauseParent(C type) {
257     auto it{GetContextParent().clauseInfo.find(type)};
258     if (it != GetContextParent().clauseInfo.end()) {
259       return it->second;
260     }
261     return nullptr;
262   }
263 
264   std::pair<typename ClauseMapTy::iterator, typename ClauseMapTy::iterator>
FindClauses(C type)265   FindClauses(C type) {
266     auto it{GetContext().clauseInfo.equal_range(type)};
267     return it;
268   }
269 
GetEnclosingDirContext()270   DirectiveContext *GetEnclosingDirContext() {
271     CHECK(!dirContext_.empty());
272     auto it{dirContext_.rbegin()};
273     if (++it != dirContext_.rend()) {
274       return &(*it);
275     }
276     return nullptr;
277   }
278 
PushContext(const parser::CharBlock & source,D dir)279   void PushContext(const parser::CharBlock &source, D dir) {
280     dirContext_.emplace_back(source, dir);
281   }
282 
GetEnclosingContextWithDir(D dir)283   DirectiveContext *GetEnclosingContextWithDir(D dir) {
284     CHECK(!dirContext_.empty());
285     auto it{dirContext_.rbegin()};
286     while (++it != dirContext_.rend()) {
287       if (it->directive == dir) {
288         return &(*it);
289       }
290     }
291     return nullptr;
292   }
293 
CurrentDirectiveIsNested()294   bool CurrentDirectiveIsNested() { return dirContext_.size() > 1; };
295 
SetClauseSets(D dir)296   void SetClauseSets(D dir) {
297     dirContext_.back().allowedClauses = directiveClausesMap_[dir].allowed;
298     dirContext_.back().allowedOnceClauses =
299         directiveClausesMap_[dir].allowedOnce;
300     dirContext_.back().allowedExclusiveClauses =
301         directiveClausesMap_[dir].allowedExclusive;
302     dirContext_.back().requiredClauses =
303         directiveClausesMap_[dir].requiredOneOf;
304   }
PushContextAndClauseSets(const parser::CharBlock & source,D dir)305   void PushContextAndClauseSets(const parser::CharBlock &source, D dir) {
306     PushContext(source, dir);
307     SetClauseSets(dir);
308   }
309 
310   void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &);
311 
CheckMatching(const B & beginDir,const B & endDir)312   template <typename B> void CheckMatching(const B &beginDir, const B &endDir) {
313     const auto &begin{beginDir.v};
314     const auto &end{endDir.v};
315     if (begin != end) {
316       SayNotMatching(beginDir.source, endDir.source);
317     }
318   }
319   // Check illegal branching out of `Parser::Block` for `Parser::Name` based
320   // nodes (example `Parser::ExitStmt`)
321   void CheckNoBranching(const parser::Block &block, D directive,
322       const parser::CharBlock &directiveSource);
323 
324   // Check that only clauses in set are after the specific clauses.
325   void CheckOnlyAllowedAfter(C clause, common::EnumSet<C, ClauseEnumSize> set);
326 
327   void CheckRequireAtLeastOneOf();
328 
329   void CheckAllowed(C clause);
330 
331   void CheckAtLeastOneClause();
332 
333   void CheckNotAllowedIfClause(
334       C clause, common::EnumSet<C, ClauseEnumSize> set);
335 
336   std::string ContextDirectiveAsFortran();
337 
338   void RequiresConstantPositiveParameter(
339       const C &clause, const parser::ScalarIntConstantExpr &i);
340 
341   void RequiresPositiveParameter(const C &clause,
342       const parser::ScalarIntExpr &i, llvm::StringRef paramName = "parameter");
343 
344   void OptionalConstantPositiveParameter(
345       const C &clause, const std::optional<parser::ScalarIntConstantExpr> &o);
346 
getClauseName(C clause)347   virtual llvm::StringRef getClauseName(C clause) { return ""; };
348 
getDirectiveName(D directive)349   virtual llvm::StringRef getDirectiveName(D directive) { return ""; };
350 
351   SemanticsContext &context_;
352   std::vector<DirectiveContext> dirContext_; // used as a stack
353   std::unordered_map<D, DirectiveClauses<C, ClauseEnumSize>>
354       directiveClausesMap_;
355 
356   std::string ClauseSetToString(const common::EnumSet<C, ClauseEnumSize> set);
357 };
358 
359 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
CheckNoBranching(const parser::Block & block,D directive,const parser::CharBlock & directiveSource)360 void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckNoBranching(
361     const parser::Block &block, D directive,
362     const parser::CharBlock &directiveSource) {
363   NoBranchingEnforce<D> noBranchingEnforce{
364       context_, directiveSource, directive, ContextDirectiveAsFortran()};
365   parser::Walk(block, noBranchingEnforce);
366 }
367 
368 // Check that only clauses included in the given set are present after the given
369 // clause.
370 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
CheckOnlyAllowedAfter(C clause,common::EnumSet<C,ClauseEnumSize> set)371 void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckOnlyAllowedAfter(
372     C clause, common::EnumSet<C, ClauseEnumSize> set) {
373   bool enforceCheck = false;
374   for (auto cl : GetContext().actualClauses) {
375     if (cl == clause) {
376       enforceCheck = true;
377       continue;
378     } else if (enforceCheck && !set.test(cl)) {
379       auto parserClause = GetContext().clauseInfo.find(cl);
380       context_.Say(parserClause->second->source,
381           "Clause %s is not allowed after clause %s on the %s "
382           "directive"_err_en_US,
383           parser::ToUpperCaseLetters(getClauseName(cl).str()),
384           parser::ToUpperCaseLetters(getClauseName(clause).str()),
385           ContextDirectiveAsFortran());
386     }
387   }
388 }
389 
390 // Check that at least one clause is attached to the directive.
391 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
392 void DirectiveStructureChecker<D, C, PC,
CheckAtLeastOneClause()393     ClauseEnumSize>::CheckAtLeastOneClause() {
394   if (GetContext().actualClauses.empty()) {
395     context_.Say(GetContext().directiveSource,
396         "At least one clause is required on the %s directive"_err_en_US,
397         ContextDirectiveAsFortran());
398   }
399 }
400 
401 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
402 std::string
ClauseSetToString(const common::EnumSet<C,ClauseEnumSize> set)403 DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::ClauseSetToString(
404     const common::EnumSet<C, ClauseEnumSize> set) {
405   std::string list;
406   set.IterateOverMembers([&](C o) {
407     if (!list.empty())
408       list.append(", ");
409     list.append(parser::ToUpperCaseLetters(getClauseName(o).str()));
410   });
411   return list;
412 }
413 
414 // Check that at least one clause in the required set is present on the
415 // directive.
416 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
417 void DirectiveStructureChecker<D, C, PC,
CheckRequireAtLeastOneOf()418     ClauseEnumSize>::CheckRequireAtLeastOneOf() {
419   if (GetContext().requiredClauses.empty())
420     return;
421   for (auto cl : GetContext().actualClauses) {
422     if (GetContext().requiredClauses.test(cl))
423       return;
424   }
425   // No clause matched in the actual clauses list
426   context_.Say(GetContext().directiveSource,
427       "At least one of %s clause must appear on the %s directive"_err_en_US,
428       ClauseSetToString(GetContext().requiredClauses),
429       ContextDirectiveAsFortran());
430 }
431 
432 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
433 std::string DirectiveStructureChecker<D, C, PC,
ContextDirectiveAsFortran()434     ClauseEnumSize>::ContextDirectiveAsFortran() {
435   return parser::ToUpperCaseLetters(
436       getDirectiveName(GetContext().directive).str());
437 }
438 
439 // Check that clauses present on the directive are allowed clauses.
440 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
CheckAllowed(C clause)441 void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckAllowed(
442     C clause) {
443   if (!GetContext().allowedClauses.test(clause) &&
444       !GetContext().allowedOnceClauses.test(clause) &&
445       !GetContext().allowedExclusiveClauses.test(clause) &&
446       !GetContext().requiredClauses.test(clause)) {
447     context_.Say(GetContext().clauseSource,
448         "%s clause is not allowed on the %s directive"_err_en_US,
449         parser::ToUpperCaseLetters(getClauseName(clause).str()),
450         parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
451     return;
452   }
453   if ((GetContext().allowedOnceClauses.test(clause) ||
454           GetContext().allowedExclusiveClauses.test(clause)) &&
455       FindClause(clause)) {
456     context_.Say(GetContext().clauseSource,
457         "At most one %s clause can appear on the %s directive"_err_en_US,
458         parser::ToUpperCaseLetters(getClauseName(clause).str()),
459         parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
460     return;
461   }
462   if (GetContext().allowedExclusiveClauses.test(clause)) {
463     std::vector<C> others;
464     GetContext().allowedExclusiveClauses.IterateOverMembers([&](C o) {
465       if (FindClause(o)) {
466         others.emplace_back(o);
467       }
468     });
469     for (const auto &e : others) {
470       context_.Say(GetContext().clauseSource,
471           "%s and %s clauses are mutually exclusive and may not appear on the "
472           "same %s directive"_err_en_US,
473           parser::ToUpperCaseLetters(getClauseName(clause).str()),
474           parser::ToUpperCaseLetters(getClauseName(e).str()),
475           parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
476     }
477     if (!others.empty()) {
478       return;
479     }
480   }
481   SetContextClauseInfo(clause);
482   AddClauseToCrtContext(clause);
483 }
484 
485 // Enforce restriction where clauses in the given set are not allowed if the
486 // given clause appears.
487 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
488 void DirectiveStructureChecker<D, C, PC,
CheckNotAllowedIfClause(C clause,common::EnumSet<C,ClauseEnumSize> set)489     ClauseEnumSize>::CheckNotAllowedIfClause(C clause,
490     common::EnumSet<C, ClauseEnumSize> set) {
491   if (std::find(GetContext().actualClauses.begin(),
492           GetContext().actualClauses.end(),
493           clause) == GetContext().actualClauses.end()) {
494     return; // Clause is not present
495   }
496 
497   for (auto cl : GetContext().actualClauses) {
498     if (set.test(cl)) {
499       context_.Say(GetContext().directiveSource,
500           "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,
501           parser::ToUpperCaseLetters(getClauseName(cl).str()),
502           parser::ToUpperCaseLetters(getClauseName(clause).str()),
503           ContextDirectiveAsFortran());
504     }
505   }
506 }
507 
508 // Check the value of the clause is a constant positive integer.
509 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
510 void DirectiveStructureChecker<D, C, PC,
RequiresConstantPositiveParameter(const C & clause,const parser::ScalarIntConstantExpr & i)511     ClauseEnumSize>::RequiresConstantPositiveParameter(const C &clause,
512     const parser::ScalarIntConstantExpr &i) {
513   if (const auto v{GetIntValue(i)}) {
514     if (*v <= 0) {
515       context_.Say(GetContext().clauseSource,
516           "The parameter of the %s clause must be "
517           "a constant positive integer expression"_err_en_US,
518           parser::ToUpperCaseLetters(getClauseName(clause).str()));
519     }
520   }
521 }
522 
523 // Check the value of the clause is a constant positive parameter.
524 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
525 void DirectiveStructureChecker<D, C, PC,
OptionalConstantPositiveParameter(const C & clause,const std::optional<parser::ScalarIntConstantExpr> & o)526     ClauseEnumSize>::OptionalConstantPositiveParameter(const C &clause,
527     const std::optional<parser::ScalarIntConstantExpr> &o) {
528   if (o != std::nullopt) {
529     RequiresConstantPositiveParameter(clause, o.value());
530   }
531 }
532 
533 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
SayNotMatching(const parser::CharBlock & beginSource,const parser::CharBlock & endSource)534 void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::SayNotMatching(
535     const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
536   context_
537       .Say(endSource, "Unmatched %s directive"_err_en_US,
538           parser::ToUpperCaseLetters(endSource.ToString()))
539       .Attach(beginSource, "Does not match directive"_en_US);
540 }
541 
542 // Check the value of the clause is a positive parameter.
543 template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
544 void DirectiveStructureChecker<D, C, PC,
RequiresPositiveParameter(const C & clause,const parser::ScalarIntExpr & i,llvm::StringRef paramName)545     ClauseEnumSize>::RequiresPositiveParameter(const C &clause,
546     const parser::ScalarIntExpr &i, llvm::StringRef paramName) {
547   if (const auto v{GetIntValue(i)}) {
548     if (*v <= 0) {
549       context_.Say(GetContext().clauseSource,
550           "The %s of the %s clause must be "
551           "a positive integer expression"_err_en_US,
552           paramName.str(),
553           parser::ToUpperCaseLetters(getClauseName(clause).str()));
554     }
555   }
556 }
557 
558 } // namespace Fortran::semantics
559 
560 #endif // FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_
561