1 //===-- lib/Semantics/check-acc-structure.cpp -----------------------------===//
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 #include "check-acc-structure.h"
10 #include "flang/Parser/parse-tree.h"
11 #include "flang/Semantics/tools.h"
12 
13 #define CHECK_SIMPLE_CLAUSE(X, Y) \
14   void AccStructureChecker::Enter(const parser::AccClause::X &) { \
15     CheckAllowed(llvm::acc::Clause::Y); \
16   }
17 
18 #define CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(X, Y) \
19   void AccStructureChecker::Enter(const parser::AccClause::X &c) { \
20     CheckAllowed(llvm::acc::Clause::Y); \
21     RequiresConstantPositiveParameter(llvm::acc::Clause::Y, c.v); \
22   }
23 
24 namespace Fortran::semantics {
25 
26 static constexpr inline AccClauseSet
27     parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses{
28         llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
29         llvm::acc::Clause::ACCC_num_gangs, llvm::acc::Clause::ACCC_num_workers,
30         llvm::acc::Clause::ACCC_vector_length};
31 
32 static constexpr inline AccClauseSet serialOnlyAllowedAfterDeviceTypeClauses{
33     llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait};
34 
35 static constexpr inline AccClauseSet loopOnlyAllowedAfterDeviceTypeClauses{
36     llvm::acc::Clause::ACCC_auto, llvm::acc::Clause::ACCC_collapse,
37     llvm::acc::Clause::ACCC_independent, llvm::acc::Clause::ACCC_gang,
38     llvm::acc::Clause::ACCC_seq, llvm::acc::Clause::ACCC_tile,
39     llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker};
40 
41 static constexpr inline AccClauseSet updateOnlyAllowedAfterDeviceTypeClauses{
42     llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait};
43 
44 static constexpr inline AccClauseSet routineOnlyAllowedAfterDeviceTypeClauses{
45     llvm::acc::Clause::ACCC_bind, llvm::acc::Clause::ACCC_gang,
46     llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker};
47 
48 class NoBranchingEnforce {
49 public:
NoBranchingEnforce(SemanticsContext & context,parser::CharBlock sourcePosition,llvm::acc::Directive directive)50   NoBranchingEnforce(SemanticsContext &context,
51       parser::CharBlock sourcePosition, llvm::acc::Directive directive)
52       : context_{context}, sourcePosition_{sourcePosition}, currentDirective_{
53                                                                 directive} {}
Pre(const T &)54   template <typename T> bool Pre(const T &) { return true; }
Post(const T &)55   template <typename T> void Post(const T &) {}
56 
Pre(const parser::Statement<T> & statement)57   template <typename T> bool Pre(const parser::Statement<T> &statement) {
58     currentStatementSourcePosition_ = statement.source;
59     return true;
60   }
61 
Post(const parser::ReturnStmt &)62   void Post(const parser::ReturnStmt &) { emitBranchOutError("RETURN"); }
Post(const parser::ExitStmt &)63   void Post(const parser::ExitStmt &) { emitBranchOutError("EXIT"); }
Post(const parser::StopStmt &)64   void Post(const parser::StopStmt &) { emitBranchOutError("STOP"); }
65 
66 private:
GetEnclosingMsg()67   parser::MessageFixedText GetEnclosingMsg() {
68     return "Enclosing block construct"_en_US;
69   }
70 
emitBranchOutError(const char * stmt)71   void emitBranchOutError(const char *stmt) {
72     context_
73         .Say(currentStatementSourcePosition_,
74             "%s statement is not allowed in a %s construct"_err_en_US, stmt,
75             parser::ToUpperCaseLetters(
76                 llvm::acc::getOpenACCDirectiveName(currentDirective_).str()))
77         .Attach(sourcePosition_, GetEnclosingMsg());
78   }
79 
80   SemanticsContext &context_;
81   parser::CharBlock currentStatementSourcePosition_;
82   parser::CharBlock sourcePosition_;
83   llvm::acc::Directive currentDirective_;
84 };
85 
Enter(const parser::AccClause & x)86 void AccStructureChecker::Enter(const parser::AccClause &x) {
87   SetContextClause(x);
88 }
89 
Leave(const parser::AccClauseList &)90 void AccStructureChecker::Leave(const parser::AccClauseList &) {}
91 
Enter(const parser::OpenACCBlockConstruct & x)92 void AccStructureChecker::Enter(const parser::OpenACCBlockConstruct &x) {
93   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
94   const auto &endBlockDir{std::get<parser::AccEndBlockDirective>(x.t)};
95   const auto &beginAccBlockDir{
96       std::get<parser::AccBlockDirective>(beginBlockDir.t)};
97 
98   CheckMatching(beginAccBlockDir, endBlockDir.v);
99   PushContextAndClauseSets(beginAccBlockDir.source, beginAccBlockDir.v);
100 }
101 
Leave(const parser::OpenACCBlockConstruct & x)102 void AccStructureChecker::Leave(const parser::OpenACCBlockConstruct &x) {
103   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
104   const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
105   const parser::Block &block{std::get<parser::Block>(x.t)};
106   switch (blockDir.v) {
107   case llvm::acc::Directive::ACCD_kernels:
108   case llvm::acc::Directive::ACCD_parallel:
109     // Restriction - 880-881 (KERNELS)
110     // Restriction - 843-844 (PARALLEL)
111     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
112         parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses);
113     // Restriction - 877 (KERNELS)
114     // Restriction - 840 (PARALLEL)
115     CheckNoBranching(block, GetContext().directive, blockDir.source);
116     break;
117   case llvm::acc::Directive::ACCD_serial:
118     // Restriction - 919
119     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
120         serialOnlyAllowedAfterDeviceTypeClauses);
121     // Restriction - 916
122     CheckNoBranching(block, llvm::acc::Directive::ACCD_serial, blockDir.source);
123     break;
124   case llvm::acc::Directive::ACCD_data:
125     // Restriction - 1117-1118
126     CheckRequireAtLeastOneOf();
127     break;
128   case llvm::acc::Directive::ACCD_host_data:
129     // Restriction - 1578
130     CheckRequireAtLeastOneOf();
131     break;
132   default:
133     break;
134   }
135   accContext_.pop_back();
136 }
137 
CheckNoBranching(const parser::Block & block,const llvm::acc::Directive directive,const parser::CharBlock & directiveSource) const138 void AccStructureChecker::CheckNoBranching(const parser::Block &block,
139     const llvm::acc::Directive directive,
140     const parser::CharBlock &directiveSource) const {
141   NoBranchingEnforce noBranchingEnforce{context_, directiveSource, directive};
142   parser::Walk(block, noBranchingEnforce);
143 }
144 
Enter(const parser::OpenACCStandaloneDeclarativeConstruct & x)145 void AccStructureChecker::Enter(
146     const parser::OpenACCStandaloneDeclarativeConstruct &x) {
147   const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)};
148   PushContextAndClauseSets(declarativeDir.source, declarativeDir.v);
149 }
150 
Leave(const parser::OpenACCStandaloneDeclarativeConstruct &)151 void AccStructureChecker::Leave(
152     const parser::OpenACCStandaloneDeclarativeConstruct &) {
153   // Restriction - 2075
154   CheckAtLeastOneClause();
155   accContext_.pop_back();
156 }
157 
Enter(const parser::OpenACCCombinedConstruct & x)158 void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) {
159   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
160   const auto &combinedDir{
161       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
162   PushContextAndClauseSets(combinedDir.source, combinedDir.v);
163 }
164 
Leave(const parser::OpenACCCombinedConstruct & x)165 void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) {
166   const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
167   const auto &combinedDir{
168       std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
169   switch (combinedDir.v) {
170   case llvm::acc::Directive::ACCD_kernels_loop:
171   case llvm::acc::Directive::ACCD_parallel_loop:
172     // Restriction - 1962 -> (880-881) (KERNELS LOOP)
173     // Restriction - 1962 -> (843-844) (PARALLEL LOOP)
174     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
175         {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
176             llvm::acc::Clause::ACCC_num_gangs,
177             llvm::acc::Clause::ACCC_num_workers,
178             llvm::acc::Clause::ACCC_vector_length});
179     break;
180   case llvm::acc::Directive::ACCD_serial_loop:
181     // Restriction - 1962 -> (919) (SERIAL LOOP)
182     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
183         {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait});
184     break;
185   default:
186     break;
187   }
188   accContext_.pop_back();
189 }
190 
ContextDirectiveAsFortran()191 std::string AccStructureChecker::ContextDirectiveAsFortran() {
192   return parser::ToUpperCaseLetters(
193       llvm::acc::getOpenACCDirectiveName(GetContext().directive).str());
194 }
195 
Enter(const parser::OpenACCLoopConstruct & x)196 void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) {
197   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
198   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
199   PushContextAndClauseSets(loopDir.source, loopDir.v);
200 }
201 
Leave(const parser::OpenACCLoopConstruct & x)202 void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) {
203   const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
204   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
205   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
206     // Restriction - 1615-1616
207     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
208         loopOnlyAllowedAfterDeviceTypeClauses);
209     // Restriction - 1622
210     CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq,
211         {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector,
212             llvm::acc::Clause::ACCC_worker});
213   }
214   accContext_.pop_back();
215 }
216 
Enter(const parser::OpenACCStandaloneConstruct & x)217 void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) {
218   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
219   PushContextAndClauseSets(standaloneDir.source, standaloneDir.v);
220 }
221 
Leave(const parser::OpenACCStandaloneConstruct & x)222 void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) {
223   const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
224   switch (standaloneDir.v) {
225   case llvm::acc::Directive::ACCD_enter_data:
226   case llvm::acc::Directive::ACCD_exit_data:
227   case llvm::acc::Directive::ACCD_set:
228     // Restriction - 1117-1118 (ENTER DATA)
229     // Restriction - 1161-1162 (EXIT DATA)
230     // Restriction - 2254 (SET)
231     CheckRequireAtLeastOneOf();
232     break;
233   case llvm::acc::Directive::ACCD_update:
234     // Restriction - 2301
235     CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
236         updateOnlyAllowedAfterDeviceTypeClauses);
237     break;
238   default:
239     break;
240   }
241   accContext_.pop_back();
242 }
243 
Enter(const parser::OpenACCRoutineConstruct & x)244 void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) {
245   PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine);
246 }
Leave(const parser::OpenACCRoutineConstruct &)247 void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) {
248   // Restriction - 2409
249   CheckRequireAtLeastOneOf();
250   // Restriction - 2407-2408
251   CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
252       routineOnlyAllowedAfterDeviceTypeClauses);
253   accContext_.pop_back();
254 }
255 
256 // Clause checkers
CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse,ACCC_collapse)257 CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse, ACCC_collapse)
258 
259 CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto)
260 CHECK_SIMPLE_CLAUSE(Async, ACCC_async)
261 CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach)
262 CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind)
263 CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture)
264 CHECK_SIMPLE_CLAUSE(Copy, ACCC_copy)
265 CHECK_SIMPLE_CLAUSE(Default, ACCC_default)
266 CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async)
267 CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete)
268 CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach)
269 CHECK_SIMPLE_CLAUSE(Device, ACCC_device)
270 CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num)
271 CHECK_SIMPLE_CLAUSE(DevicePtr, ACCC_deviceptr)
272 CHECK_SIMPLE_CLAUSE(DeviceResident, ACCC_device_resident)
273 CHECK_SIMPLE_CLAUSE(DeviceType, ACCC_device_type)
274 CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize)
275 CHECK_SIMPLE_CLAUSE(FirstPrivate, ACCC_firstprivate)
276 CHECK_SIMPLE_CLAUSE(Gang, ACCC_gang)
277 CHECK_SIMPLE_CLAUSE(Host, ACCC_host)
278 CHECK_SIMPLE_CLAUSE(If, ACCC_if)
279 CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present)
280 CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent)
281 CHECK_SIMPLE_CLAUSE(Link, ACCC_link)
282 CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create)
283 CHECK_SIMPLE_CLAUSE(NoHost, ACCC_nohost)
284 CHECK_SIMPLE_CLAUSE(NumGangs, ACCC_num_gangs)
285 CHECK_SIMPLE_CLAUSE(NumWorkers, ACCC_num_workers)
286 CHECK_SIMPLE_CLAUSE(Present, ACCC_present)
287 CHECK_SIMPLE_CLAUSE(Private, ACCC_private)
288 CHECK_SIMPLE_CLAUSE(Read, ACCC_read)
289 CHECK_SIMPLE_CLAUSE(Reduction, ACCC_reduction)
290 CHECK_SIMPLE_CLAUSE(Self, ACCC_self)
291 CHECK_SIMPLE_CLAUSE(Seq, ACCC_seq)
292 CHECK_SIMPLE_CLAUSE(Tile, ACCC_tile)
293 CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device)
294 CHECK_SIMPLE_CLAUSE(Vector, ACCC_vector)
295 CHECK_SIMPLE_CLAUSE(VectorLength, ACCC_vector_length)
296 CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait)
297 CHECK_SIMPLE_CLAUSE(Worker, ACCC_worker)
298 CHECK_SIMPLE_CLAUSE(Write, ACCC_write)
299 
300 void AccStructureChecker::Enter(const parser::AccClause::Create &c) {
301   CheckAllowed(llvm::acc::Clause::ACCC_create);
302   const auto &modifierClause{c.v};
303   if (const auto &modifier{
304           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
305     if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
306       context_.Say(GetContext().clauseSource,
307           "Only the ZERO modifier is allowed for the %s clause "
308           "on the %s directive"_err_en_US,
309           parser::ToUpperCaseLetters(
310               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create)
311                   .str()),
312           ContextDirectiveAsFortran());
313     }
314   }
315 }
316 
Enter(const parser::AccClause::Copyin & c)317 void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) {
318   CheckAllowed(llvm::acc::Clause::ACCC_copyin);
319   const auto &modifierClause{c.v};
320   if (const auto &modifier{
321           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
322     if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) {
323       context_.Say(GetContext().clauseSource,
324           "Only the READONLY modifier is allowed for the %s clause "
325           "on the %s directive"_err_en_US,
326           parser::ToUpperCaseLetters(
327               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin)
328                   .str()),
329           ContextDirectiveAsFortran());
330     }
331   }
332 }
333 
Enter(const parser::AccClause::Copyout & c)334 void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) {
335   CheckAllowed(llvm::acc::Clause::ACCC_copyout);
336   const auto &modifierClause{c.v};
337   if (const auto &modifier{
338           std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
339     if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
340       context_.Say(GetContext().clauseSource,
341           "Only the ZERO modifier is allowed for the %s clause "
342           "on the %s directive"_err_en_US,
343           parser::ToUpperCaseLetters(
344               llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout)
345                   .str()),
346           ContextDirectiveAsFortran());
347     }
348   }
349 }
350 
CheckAllowed(llvm::acc::Clause clause)351 void AccStructureChecker::CheckAllowed(llvm::acc::Clause clause) {
352   if (!GetContext().allowedClauses.test(clause) &&
353       !GetContext().allowedOnceClauses.test(clause) &&
354       !GetContext().allowedExclusiveClauses.test(clause) &&
355       !GetContext().requiredClauses.test(clause)) {
356     context_.Say(GetContext().clauseSource,
357         "%s clause is not allowed on the %s directive"_err_en_US,
358         parser::ToUpperCaseLetters(
359             llvm::acc::getOpenACCClauseName(clause).str()),
360         parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
361     return;
362   }
363   if ((GetContext().allowedOnceClauses.test(clause) ||
364           GetContext().allowedExclusiveClauses.test(clause)) &&
365       FindClause(clause)) {
366     context_.Say(GetContext().clauseSource,
367         "At most one %s clause can appear on the %s directive"_err_en_US,
368         parser::ToUpperCaseLetters(
369             llvm::acc::getOpenACCClauseName(clause).str()),
370         parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
371     return;
372   }
373   if (GetContext().allowedExclusiveClauses.test(clause)) {
374     std::vector<llvm::acc::Clause> others;
375     GetContext().allowedExclusiveClauses.IterateOverMembers(
376         [&](llvm::acc::Clause o) {
377           if (FindClause(o)) {
378             others.emplace_back(o);
379           }
380         });
381     for (const auto &e : others) {
382       context_.Say(GetContext().clauseSource,
383           "%s and %s clauses are mutually exclusive and may not appear on the "
384           "same %s directive"_err_en_US,
385           parser::ToUpperCaseLetters(
386               llvm::acc::getOpenACCClauseName(clause).str()),
387           parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(e).str()),
388           parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
389     }
390     if (!others.empty()) {
391       return;
392     }
393   }
394   SetContextClauseInfo(clause);
395   AddClauseToCrtContext(clause);
396 }
397 
CheckOnlyAllowedAfter(llvm::acc::Clause clause,AccClauseSet set)398 void AccStructureChecker::CheckOnlyAllowedAfter(
399     llvm::acc::Clause clause, AccClauseSet set) {
400   bool enforceCheck = false;
401   for (auto cl : GetContext().actualClauses) {
402     if (cl == clause) {
403       enforceCheck = true;
404       continue;
405     } else if (enforceCheck && !set.test(cl)) {
406       auto parserClause = GetContext().clauseInfo.find(cl);
407       context_.Say(parserClause->second->source,
408           "Clause %s is not allowed after clause %s on the %s "
409           "directive"_err_en_US,
410           parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()),
411           parser::ToUpperCaseLetters(
412               llvm::acc::getOpenACCClauseName(clause).str()),
413           ContextDirectiveAsFortran());
414     }
415   }
416 }
417 
CheckRequireAtLeastOneOf()418 void AccStructureChecker::CheckRequireAtLeastOneOf() {
419   for (auto cl : GetContext().actualClauses) {
420     if (GetContext().requiredClauses.test(cl))
421       return;
422   }
423   // No clause matched in the actual clauses list
424   context_.Say(GetContext().directiveSource,
425       "At least one of %s clause must appear on the %s directive"_err_en_US,
426       ClauseSetToString(GetContext().requiredClauses),
427       ContextDirectiveAsFortran());
428 }
429 
CheckAtLeastOneClause()430 void AccStructureChecker::CheckAtLeastOneClause() {
431   if (GetContext().actualClauses.empty()) {
432     context_.Say(GetContext().directiveSource,
433         "At least one clause is required on the %s directive"_err_en_US,
434         ContextDirectiveAsFortran());
435   }
436 }
437 
438 // Enforce restriction where clauses in the given set are not allowed if the
439 // given clause appears.
CheckNotAllowedIfClause(llvm::acc::Clause clause,AccClauseSet set)440 void AccStructureChecker::CheckNotAllowedIfClause(
441     llvm::acc::Clause clause, AccClauseSet set) {
442   if (std::find(GetContext().actualClauses.begin(),
443           GetContext().actualClauses.end(),
444           clause) == GetContext().actualClauses.end()) {
445     return; // Clause is not present
446   }
447 
448   for (auto cl : GetContext().actualClauses) {
449     if (set.test(cl)) {
450       context_.Say(GetContext().directiveSource,
451           "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,
452           parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()),
453           parser::ToUpperCaseLetters(
454               llvm::acc::getOpenACCClauseName(clause).str()),
455           ContextDirectiveAsFortran());
456     }
457   }
458 }
459 
RequiresConstantPositiveParameter(const llvm::acc::Clause & clause,const parser::ScalarIntConstantExpr & i)460 void AccStructureChecker::RequiresConstantPositiveParameter(
461     const llvm::acc::Clause &clause, const parser::ScalarIntConstantExpr &i) {
462   if (const auto v{GetIntValue(i)}) {
463     if (*v <= 0) {
464       context_.Say(GetContext().clauseSource,
465           "The parameter of the %s clause on the %s directive must be "
466           "a constant positive integer expression"_err_en_US,
467           parser::ToUpperCaseLetters(
468               llvm::acc::getOpenACCClauseName(clause).str()),
469           ContextDirectiveAsFortran());
470     }
471   }
472 }
473 
OptionalConstantPositiveParameter(const llvm::acc::Clause & clause,const std::optional<parser::ScalarIntConstantExpr> & o)474 void AccStructureChecker::OptionalConstantPositiveParameter(
475     const llvm::acc::Clause &clause,
476     const std::optional<parser::ScalarIntConstantExpr> &o) {
477   if (o != std::nullopt) {
478     RequiresConstantPositiveParameter(clause, o.value());
479   }
480 }
481 
ClauseSetToString(const AccClauseSet set)482 std::string AccStructureChecker::ClauseSetToString(const AccClauseSet set) {
483   std::string list;
484   set.IterateOverMembers([&](llvm::acc::Clause o) {
485     if (!list.empty())
486       list.append(", ");
487     list.append(
488         parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(o).str()));
489   });
490   return list;
491 }
492 
SayNotMatching(const parser::CharBlock & beginSource,const parser::CharBlock & endSource)493 void AccStructureChecker::SayNotMatching(
494     const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
495   context_
496       .Say(endSource, "Unmatched %s directive"_err_en_US,
497           parser::ToUpperCaseLetters(endSource.ToString()))
498       .Attach(beginSource, "Does not match directive"_en_US);
499 }
500 
501 } // namespace Fortran::semantics
502