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