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