FLANG
check-directive-structure.h
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/Semantics/semantics.h"
16#include "flang/Semantics/tools.h"
17#include "llvm/ADT/iterator_range.h"
18
19#include <functional>
20#include <set>
21#include <unordered_map>
22
23namespace Fortran::semantics {
24
25template <typename ClauseSetTy> struct DirectiveClauses {
26 const ClauseSetTy allowed;
27 const ClauseSetTy allowedOnce;
28 const ClauseSetTy allowedExclusive;
29 const ClauseSetTy requiredOneOf;
30};
31
32template <typename ClauseTy, typename ClauseSetTy>
33void IterateOverMembers(
34 const ClauseSetTy &set, std::function<void(ClauseTy)> func);
35
36// Generic branching checker for invalid branching out of OpenMP/OpenACC
37// directive.
38// typename D is the directive enumeration.
39template <typename D> class NoBranchingEnforce {
40public:
41 NoBranchingEnforce(SemanticsContext &context,
42 parser::CharBlock sourcePosition, D directive,
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 &) {}
49
50 template <typename T> bool Pre(const parser::Statement<T> &statement) {
51 currentStatementSourcePosition_ = statement.source;
52 return true;
53 }
54
55 bool Pre(const parser::DoConstruct &) {
56 numDoConstruct_++;
57 return true;
58 }
59 void Post(const parser::DoConstruct &) { numDoConstruct_--; }
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");
69 break;
70 default:
71 break;
72 }
73 }
74 }
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());
79 } else {
80 CheckConstructNameBranching("EXIT");
81 }
82 }
83 void Post(const parser::CycleStmt &cycleStmt) {
84 if (const auto &cycleName{cycleStmt.v}) {
85 CheckConstructNameBranching("CYCLE", cycleName.value());
86 } else {
87 if constexpr (std::is_same_v<D, llvm::omp::Directive>) {
88 switch ((llvm::omp::Directive)currentDirective_) {
89 // exclude directives which do not need a check for unlabelled CYCLES
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:
104 return;
105 default:
106 break;
107 }
108 } else if constexpr (std::is_same_v<D, llvm::acc::Directive>) {
109 switch ((llvm::acc::Directive)currentDirective_) {
110 // exclude loop directives which do not need a check for unlabelled
111 // CYCLES
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:
116 return;
117 default:
118 break;
119 }
120 }
121 CheckConstructNameBranching("CYCLE");
122 }
123 }
124
125private:
126 parser::MessageFormattedText GetEnclosingMsg() const {
127 return {"Enclosing %s construct"_en_US, upperCaseDirName_};
128 }
129
130 void EmitBranchOutError(const char *stmt) const {
131 context_
132 .Say(currentStatementSourcePosition_,
133 "%s statement is not allowed in a %s construct"_err_en_US, stmt,
134 upperCaseDirName_)
135 .Attach(sourcePosition_, GetEnclosingMsg());
136 }
137
138 void EmitBranchOutOfComputeConstructError(const char *stmt) const {
139 context_
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());
144 }
145
146 inline void EmitUnlabelledBranchOutError(const char *stmt) {
147 context_
148 .Say(currentStatementSourcePosition_,
149 "%s to construct outside of %s construct is not allowed"_err_en_US,
150 stmt, upperCaseDirName_)
151 .Attach(sourcePosition_, GetEnclosingMsg());
152 }
153
154 void EmitBranchOutErrorWithName(
155 const char *stmt, const parser::Name &toName) const {
156 const std::string branchingToName{toName.ToString()};
157 context_
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());
162 }
163
164 // Current semantic checker is not following OpenACC/OpenMP constructs as they
165 // are not Fortran constructs. Hence the ConstructStack doesn't capture
166 // OpenACC/OpenMP constructs. Apply an inverse way to figure out if a
167 // construct-name is branching out of an OpenACC/OpenMP construct. The control
168 // flow goes out of an OpenACC/OpenMP construct, if a construct-name from
169 // statement is found in ConstructStack.
170 void CheckConstructNameBranching(
171 const char *stmt, const parser::Name &stmtName) {
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)};
176 if (constructName) {
177 if (stmtName.source == constructName->source) {
178 EmitBranchOutErrorWithName(stmt, stmtName);
179 return;
180 }
181 }
182 }
183 }
184
185 // Check branching for unlabelled CYCLES and EXITs
186 void CheckConstructNameBranching(const char *stmt) {
187 // found an enclosing looping construct for the unlabelled EXIT/CYCLE
188 if (numDoConstruct_ > 0) {
189 return;
190 }
191 // did not found an enclosing looping construct within the OpenMP/OpenACC
192 // directive
193 EmitUnlabelledBranchOutError(stmt);
194 }
195
196 SemanticsContext &context_;
197 parser::CharBlock currentStatementSourcePosition_;
198 parser::CharBlock sourcePosition_;
199 std::string upperCaseDirName_;
200 D currentDirective_;
201 int numDoConstruct_; // tracks number of DoConstruct found AFTER encountering
202 // an OpenMP/OpenACC directive
203 std::set<parser::Label> labelsInBlock_;
204};
205
206// Generic structure checker for directives/clauses language such as OpenMP
207// and OpenACC.
208// typename D is the directive enumeration.
209// typename C is the clause enumeration.
210// typename PC is the parser class defined in parse-tree.h for the clauses.
211template <typename D, typename C, typename PC, typename ClauseSetTy>
212class DirectiveStructureChecker : public virtual BaseChecker {
213protected:
214 DirectiveStructureChecker(SemanticsContext &context,
215 const std::unordered_map<D, DirectiveClauses<ClauseSetTy>>
216 &directiveClausesMap)
217 : context_{context}, directiveClausesMap_(directiveClausesMap) {}
218 virtual ~DirectiveStructureChecker() {}
219
220 using ClauseMapTy = std::multimap<C, const PC *>;
221 struct DirectiveContext {
222 DirectiveContext(parser::CharBlock source, D d)
223 : directiveSource{source}, directive{d} {}
224
225 parser::CharBlock directiveSource{nullptr};
226 parser::CharBlock clauseSource{nullptr};
227 D directive;
228 ClauseSetTy allowedClauses{};
229 ClauseSetTy allowedOnceClauses{};
230 ClauseSetTy allowedExclusiveClauses{};
231 ClauseSetTy requiredClauses{};
232
233 const PC *clause{nullptr};
234 ClauseMapTy clauseInfo;
235 std::list<C> actualClauses;
236 std::list<C> endDirectiveClauses;
237 std::list<C> crtGroup;
238 };
239
240 // back() is the top of the stack
241 DirectiveContext &GetContext() {
242 CHECK(!dirContext_.empty());
243 return dirContext_.back();
244 }
245
246 DirectiveContext &GetContextParent() {
247 CHECK(dirContext_.size() >= 2);
248 return dirContext_[dirContext_.size() - 2];
249 }
250
251 void SetContextClause(const PC &clause) {
252 GetContext().clauseSource = clause.source;
253 GetContext().clause = &clause;
254 }
255
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 = {};
264 }
265
266 void SetContextDirectiveSource(const parser::CharBlock &directive) {
267 GetContext().directiveSource = directive;
268 }
269
270 void SetContextDirectiveEnum(D dir) { GetContext().directive = dir; }
271
272 void SetContextAllowed(const ClauseSetTy &allowed) {
273 GetContext().allowedClauses = allowed;
274 }
275
276 void SetContextAllowedOnce(const ClauseSetTy &allowedOnce) {
277 GetContext().allowedOnceClauses = allowedOnce;
278 }
279
280 void SetContextAllowedExclusive(const ClauseSetTy &allowedExclusive) {
281 GetContext().allowedExclusiveClauses = allowedExclusive;
282 }
283
284 void SetContextRequired(const ClauseSetTy &required) {
285 GetContext().requiredClauses = required;
286 }
287
288 void SetContextClauseInfo(C type) {
289 GetContext().clauseInfo.emplace(type, GetContext().clause);
290 }
291
292 void AddClauseToCrtContext(C type) {
293 GetContext().actualClauses.push_back(type);
294 }
295
296 void AddClauseToCrtGroupInContext(C type) {
297 GetContext().crtGroup.push_back(type);
298 }
299
300 void ResetCrtGroup() { GetContext().crtGroup.clear(); }
301
302 // Check if the given clause is present in the current context
303 const PC *FindClause(C type) { return FindClause(GetContext(), type); }
304
305 // Check if the given clause is present in the given context
306 const PC *FindClause(DirectiveContext &context, C type) {
307 auto it{context.clauseInfo.find(type)};
308 if (it != context.clauseInfo.end()) {
309 return it->second;
310 }
311 return nullptr;
312 }
313
314 // Check if the given clause is present in the parent context
315 const PC *FindClauseParent(C type) {
316 auto it{GetContextParent().clauseInfo.find(type)};
317 if (it != GetContextParent().clauseInfo.end()) {
318 return it->second;
319 }
320 return nullptr;
321 }
322
323 llvm::iterator_range<typename ClauseMapTy::iterator> FindClauses(C type) {
324 auto it{GetContext().clauseInfo.equal_range(type)};
325 return llvm::make_range(it);
326 }
327
328 DirectiveContext *GetEnclosingDirContext() {
329 CHECK(!dirContext_.empty());
330 auto it{dirContext_.rbegin()};
331 if (++it != dirContext_.rend()) {
332 return &(*it);
333 }
334 return nullptr;
335 }
336
337 void PushContext(const parser::CharBlock &source, D dir) {
338 dirContext_.emplace_back(source, dir);
339 }
340
341 DirectiveContext *GetEnclosingContextWithDir(D dir) {
342 CHECK(!dirContext_.empty());
343 auto it{dirContext_.rbegin()};
344 while (++it != dirContext_.rend()) {
345 if (it->directive == dir) {
346 return &(*it);
347 }
348 }
349 return nullptr;
350 }
351
352 bool CurrentDirectiveIsNested() { return dirContext_.size() > 1; };
353
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;
362 }
363 void PushContextAndClauseSets(const parser::CharBlock &source, D dir) {
364 PushContext(source, dir);
365 SetClauseSets(dir);
366 }
367
368 void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &);
369
370 template <typename B> void CheckMatching(const B &beginDir, const B &endDir) {
371 const auto &begin{beginDir.v};
372 const auto &end{endDir.v};
373 if (begin != end) {
374 SayNotMatching(beginDir.source, endDir.source);
375 }
376 }
377 // Check illegal branching out of `Parser::Block` for `Parser::Name` based
378 // nodes (example `Parser::ExitStmt`)
379 void CheckNoBranching(const parser::Block &block, D directive,
380 const parser::CharBlock &directiveSource);
381
382 // Check that only clauses in set are after the specific clauses.
383 void CheckOnlyAllowedAfter(C clause, ClauseSetTy set);
384
385 void CheckRequireAtLeastOneOf(bool warnInsteadOfError = false);
386
387 // Check if a clause is allowed on a directive. Returns true if is and
388 // false otherwise.
389 bool CheckAllowed(C clause, bool warnInsteadOfError = false);
390
391 // Check that the clause appears only once. The counter is reset when the
392 // separator clause appears.
393 void CheckAllowedOncePerGroup(C clause, C separator);
394
395 void CheckMutuallyExclusivePerGroup(C clause, C separator, ClauseSetTy set);
396
397 void CheckAtLeastOneClause();
398
399 void CheckNotAllowedIfClause(C clause, ClauseSetTy set);
400
401 std::string ContextDirectiveAsFortran();
402
403 void RequiresConstantPositiveParameter(
404 const C &clause, const parser::ScalarIntConstantExpr &i);
405
406 void RequiresPositiveParameter(const C &clause,
407 const parser::ScalarIntExpr &i, llvm::StringRef paramName = "parameter",
408 bool allowZero = true);
409
410 void OptionalConstantPositiveParameter(
411 const C &clause, const std::optional<parser::ScalarIntConstantExpr> &o);
412
413 virtual llvm::StringRef getClauseName(C clause) { return ""; };
414
415 virtual llvm::StringRef getDirectiveName(D directive) { return ""; };
416
417 SemanticsContext &context_;
418 std::vector<DirectiveContext> dirContext_; // used as a stack
419 std::unordered_map<D, DirectiveClauses<ClauseSetTy>> directiveClausesMap_;
420
421 std::string ClauseSetToString(const ClauseSetTy &set);
422};
423
424// Collect all labels defined in a block.
426 std::set<parser::Label> labels;
427 template <typename T> bool Pre(const T &) { return true; }
428 template <typename T> void Post(const T &) {}
429 template <typename T> bool Pre(const parser::Statement<T> &stmt) {
430 if (stmt.label)
431 labels.insert(*stmt.label);
432 return true;
433 }
434};
435
436template <typename D, typename C, typename PC, typename ClauseSetTy>
437void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckNoBranching(
438 const parser::Block &block, D directive,
439 const parser::CharBlock &directiveSource) {
440 LabelCollector labelCollector;
441 parser::Walk(block, labelCollector);
442 NoBranchingEnforce<D> noBranchingEnforce{
443 context_, directiveSource, directive, ContextDirectiveAsFortran()};
444 for (auto label : labelCollector.labels)
445 noBranchingEnforce.CollectLabel(label);
446 parser::Walk(block, noBranchingEnforce);
447}
448
449// Check that only clauses included in the given set are present after the given
450// clause.
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) {
456 if (cl == clause) {
457 enforceCheck = true;
458 continue;
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());
467 }
468 }
469}
470
471// Check that at least one clause is attached to the directive.
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());
478 }
479}
480
481template <typename D, typename C, typename PC, typename ClauseSetTy>
482std::string DirectiveStructureChecker<D, C, PC, ClauseSetTy>::ClauseSetToString(
483 const ClauseSetTy &set) {
484 std::string list;
485 std::function<void(C)> visitor{[&](C o) {
486 if (!list.empty())
487 list.append(", ");
488 list.append(parser::ToUpperCaseLetters(getClauseName(o).str()));
489 }};
490 IterateOverMembers(set, visitor);
491 return list;
492}
493
494// Check that at least one clause in the required set is present on the
495// directive.
496template <typename D, typename C, typename PC, typename ClauseSetTy>
497void DirectiveStructureChecker<D, C, PC, ClauseSetTy>::CheckRequireAtLeastOneOf(
498 bool warnInsteadOfError) {
499 if (GetContext().requiredClauses.empty()) {
500 return;
501 }
502 for (auto cl : GetContext().actualClauses) {
503 if (GetContext().requiredClauses.test(cl)) {
504 return;
505 }
506 }
507 // No clause matched in the actual clauses list
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());
514 } else {
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());
519 }
520}
521
522template <typename D, typename C, typename PC, typename ClauseSetTy>
523std::string
524DirectiveStructureChecker<D, C, PC, ClauseSetTy>::ContextDirectiveAsFortran() {
525 return parser::ToUpperCaseLetters(
526 getDirectiveName(GetContext().directive).str());
527}
528
529// Check that clauses present on the directive are allowed clauses.
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()));
543 } else {
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()));
548 }
549 return false;
550 }
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()));
558 return false;
559 }
560 if (GetContext().allowedExclusiveClauses.test(clause)) {
561 std::vector<C> others;
562 std::function<void(C)> visitor{[&](C o) {
563 if (FindClause(o)) {
564 others.emplace_back(o);
565 }
566 }};
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()));
575 }
576 if (!others.empty()) {
577 return false;
578 }
579 }
580 SetContextClauseInfo(clause);
581 AddClauseToCrtContext(clause);
582 AddClauseToCrtGroupInContext(clause);
583 return true;
584}
585
586// Enforce restriction where clauses in the given set are not allowed if the
587// given clause appears.
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)) {
592 return; // Clause is not present
593 }
594
595 for (auto cl : GetContext().actualClauses) {
596 if (set.test(cl)) {
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());
602 }
603 }
604}
605
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) {
611 if (cl == clause) {
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()));
618 } else {
619 clauseIsPresent = true;
620 }
621 }
622 if (cl == separator)
623 clauseIsPresent = false;
624 }
625}
626
627template <typename D, typename C, typename PC, typename ClauseSetTy>
628void DirectiveStructureChecker<D, C, PC,
629 ClauseSetTy>::CheckMutuallyExclusivePerGroup(C clause, C separator,
630 ClauseSetTy set) {
631
632 // Checking of there is any offending clauses before the first separator.
633 for (auto cl : GetContext().actualClauses) {
634 if (cl == separator) {
635 break;
636 }
637 if (set.test(cl)) {
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());
643 }
644 }
645
646 // Checking for mutually exclusive clauses in the current group.
647 for (auto cl : GetContext().crtGroup) {
648 if (set.test(cl)) {
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());
654 }
655 }
656}
657
658// Check the value of the clause is a constant positive integer.
659template <typename D, typename C, typename PC, typename ClauseSetTy>
660void DirectiveStructureChecker<D, C, PC,
661 ClauseSetTy>::RequiresConstantPositiveParameter(const C &clause,
662 const parser::ScalarIntConstantExpr &i) {
663 if (const auto v{GetIntValue(i)}) {
664 if (*v <= 0) {
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()));
669 }
670 }
671}
672
673// Check the value of the clause is a constant positive parameter.
674template <typename D, typename C, typename PC, typename ClauseSetTy>
675void DirectiveStructureChecker<D, C, PC,
676 ClauseSetTy>::OptionalConstantPositiveParameter(const C &clause,
677 const std::optional<parser::ScalarIntConstantExpr> &o) {
678 if (o != std::nullopt) {
679 RequiresConstantPositiveParameter(clause, o.value());
680 }
681}
682
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) {
686 context_
687 .Say(endSource, "Unmatched %s directive"_err_en_US,
688 parser::ToUpperCaseLetters(endSource.ToString()))
689 .Attach(beginSource, "Does not match directive"_en_US);
690}
691
692// Check the value of the clause is a positive parameter.
693template <typename D, typename C, typename PC, typename ClauseSetTy>
694void DirectiveStructureChecker<D, C, PC,
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,
702 paramName.str(),
703 parser::ToUpperCaseLetters(getClauseName(clause).str()));
704 }
705 }
706}
707
708} // namespace Fortran::semantics
709
710#endif // FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_
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