1 // Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "check-omp-structure.h"
16 #include "tools.h"
17 #include "../parser/parse-tree.h"
18 #include <unordered_map>
19 
20 namespace Fortran::semantics {
21 
22 static constexpr OmpDirectiveSet parallelSet{
23     OmpDirective::DISTRIBUTE_PARALLEL_DO,
24     OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL,
25     OmpDirective::PARALLEL_DO, OmpDirective::PARALLEL_DO_SIMD,
26     OmpDirective::PARALLEL_SECTIONS, OmpDirective::PARALLEL_WORKSHARE,
27     OmpDirective::TARGET_PARALLEL, OmpDirective::TARGET_PARALLEL_DO,
28     OmpDirective::TARGET_PARALLEL_DO_SIMD,
29     OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO,
30     OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
31     OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO,
32     OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD};
33 static constexpr OmpDirectiveSet doSet{OmpDirective::DISTRIBUTE_PARALLEL_DO,
34     OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL_DO,
35     OmpDirective::PARALLEL_DO_SIMD, OmpDirective::DO, OmpDirective::DO_SIMD,
36     OmpDirective::TARGET_PARALLEL_DO, OmpDirective::TARGET_PARALLEL_DO_SIMD,
37     OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO,
38     OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
39     OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO,
40     OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD};
41 static constexpr OmpDirectiveSet simdSet{
42     OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::DISTRIBUTE_SIMD,
43     OmpDirective::PARALLEL_DO_SIMD, OmpDirective::DO_SIMD, OmpDirective::SIMD,
44     OmpDirective::TARGET_PARALLEL_DO_SIMD,
45     OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
46     OmpDirective::TARGET_TEAMS_DISTRIBUTE_SIMD, OmpDirective::TARGET_SIMD,
47     OmpDirective::TASKLOOP_SIMD,
48     OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
49     OmpDirective::TEAMS_DISTRIBUTE_SIMD};
50 static constexpr OmpDirectiveSet doSimdSet{
51     OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL_DO_SIMD,
52     OmpDirective::DO_SIMD, OmpDirective::TARGET_PARALLEL_DO_SIMD,
53     OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
54     OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD};
55 static constexpr OmpDirectiveSet taskloopSet{
56     OmpDirective::TASKLOOP, OmpDirective::TASKLOOP_SIMD};
57 static constexpr OmpDirectiveSet targetSet{OmpDirective::TARGET,
58     OmpDirective::TARGET_PARALLEL, OmpDirective::TARGET_PARALLEL_DO,
59     OmpDirective::TARGET_PARALLEL_DO_SIMD, OmpDirective::TARGET_SIMD,
60     OmpDirective::TARGET_TEAMS, OmpDirective::TARGET_TEAMS_DISTRIBUTE,
61     OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO,
62     OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
63     OmpDirective::TARGET_TEAMS_DISTRIBUTE_SIMD};
64 
ContextDirectiveAsFortran()65 std::string OmpStructureChecker::ContextDirectiveAsFortran() {
66   auto dir{EnumToString(GetContext().directive)};
67   std::replace(dir.begin(), dir.end(), '_', ' ');
68   return dir;
69 }
70 
SayNotMatching(const parser::CharBlock & beginSource,const parser::CharBlock & endSource)71 void OmpStructureChecker::SayNotMatching(
72     const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
73   context_
74       .Say(endSource, "Unmatched %s directive"_err_en_US,
75           parser::ToUpperCaseLetters(endSource.ToString()))
76       .Attach(beginSource, "Does not match directive"_en_US);
77 }
78 
HasInvalidWorksharingNesting(const parser::CharBlock & source,const OmpDirectiveSet & set)79 bool OmpStructureChecker::HasInvalidWorksharingNesting(
80     const parser::CharBlock &source, const OmpDirectiveSet &set) {
81   // set contains all the invalid closely nested directives
82   // for the given directive (`source` here)
83   if (CurrentDirectiveIsNested() && set.test(GetContext().directive)) {
84     context_.Say(source,
85         "A worksharing region may not be closely nested inside a "
86         "worksharing, explicit task, taskloop, critical, ordered, atomic, or "
87         "master region"_err_en_US);
88     return true;
89   }
90   return false;
91 }
92 
CheckAllowed(OmpClause type)93 void OmpStructureChecker::CheckAllowed(OmpClause type) {
94   if (!GetContext().allowedClauses.test(type) &&
95       !GetContext().allowedOnceClauses.test(type) &&
96       !GetContext().allowedExclusiveClauses.test(type)) {
97     context_.Say(GetContext().clauseSource,
98         "%s clause is not allowed on the %s directive"_err_en_US,
99         EnumToString(type),
100         parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
101     return;
102   }
103   if ((GetContext().allowedOnceClauses.test(type) ||
104           GetContext().allowedExclusiveClauses.test(type)) &&
105       FindClause(type)) {
106     context_.Say(GetContext().clauseSource,
107         "At most one %s clause can appear on the %s directive"_err_en_US,
108         EnumToString(type),
109         parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
110     return;
111   }
112   if (GetContext().allowedExclusiveClauses.test(type)) {
113     std::vector<OmpClause> others;
114     GetContext().allowedExclusiveClauses.IterateOverMembers([&](OmpClause o) {
115       if (FindClause(o)) {
116         others.emplace_back(o);
117       }
118     });
119     for (const auto &e : others) {
120       context_.Say(GetContext().clauseSource,
121           "%s and %s are mutually exclusive and may not appear on the "
122           "same %s directive"_err_en_US,
123           EnumToString(type), EnumToString(e),
124           parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
125     }
126     if (!others.empty()) {
127       return;
128     }
129   }
130   SetContextClauseInfo(type);
131 }
132 
CheckRequired(OmpClause c)133 void OmpStructureChecker::CheckRequired(OmpClause c) {
134   if (!FindClause(c)) {
135     context_.Say(GetContext().directiveSource,
136         "At least one %s clause must appear on the %s directive"_err_en_US,
137         EnumToString(c), ContextDirectiveAsFortran());
138   }
139 }
140 
RequiresConstantPositiveParameter(const OmpClause & clause,const parser::ScalarIntConstantExpr & i)141 void OmpStructureChecker::RequiresConstantPositiveParameter(
142     const OmpClause &clause, const parser::ScalarIntConstantExpr &i) {
143   if (const auto v{GetIntValue(i)}) {
144     if (*v <= 0) {
145       context_.Say(GetContext().clauseSource,
146           "The parameter of the %s clause must be "
147           "a constant positive integer expression"_err_en_US,
148           EnumToString(clause));
149     }
150   }
151 }
152 
RequiresPositiveParameter(const OmpClause & clause,const parser::ScalarIntExpr & i)153 void OmpStructureChecker::RequiresPositiveParameter(
154     const OmpClause &clause, const parser::ScalarIntExpr &i) {
155   if (const auto v{GetIntValue(i)}) {
156     if (*v <= 0) {
157       context_.Say(GetContext().clauseSource,
158           "The parameter of the %s clause must be "
159           "a positive integer expression"_err_en_US,
160           EnumToString(clause));
161     }
162   }
163 }
164 
Enter(const parser::OpenMPConstruct &)165 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) {
166   // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check
167 }
168 
Enter(const parser::OpenMPLoopConstruct & x)169 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
170   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
171   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
172 
173   // check matching, End directive is optional
174   if (const auto &endLoopDir{
175           std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
176     CheckMatching<parser::OmpLoopDirective>(beginLoopDir, *endLoopDir);
177   }
178 
179   switch (beginDir.v) {
180   // 2.7.1 do-clause -> private-clause |
181   //                    firstprivate-clause |
182   //                    lastprivate-clause |
183   //                    linear-clause |
184   //                    reduction-clause |
185   //                    schedule-clause |
186   //                    collapse-clause |
187   //                    ordered-clause
188   case parser::OmpLoopDirective::Directive::Do: {
189     // nesting check
190     HasInvalidWorksharingNesting(beginDir.source,
191         {OmpDirective::DO, OmpDirective::SECTIONS, OmpDirective::SINGLE,
192             OmpDirective::WORKSHARE, OmpDirective::TASK, OmpDirective::TASKLOOP,
193             OmpDirective::CRITICAL, OmpDirective::ORDERED, OmpDirective::ATOMIC,
194             OmpDirective::MASTER});
195 
196     PushContext(beginDir.source, OmpDirective::DO);
197     OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
198         OmpClause::LASTPRIVATE, OmpClause::LINEAR, OmpClause::REDUCTION};
199     SetContextAllowed(allowed);
200     OmpClauseSet allowedOnce{
201         OmpClause::SCHEDULE, OmpClause::COLLAPSE, OmpClause::ORDERED};
202     SetContextAllowedOnce(allowedOnce);
203   } break;
204 
205   // 2.11.1 parallel-do-clause -> parallel-clause |
206   //                              do-clause
207   case parser::OmpLoopDirective::Directive::ParallelDo: {
208     PushContext(beginDir.source, OmpDirective::PARALLEL_DO);
209     OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE,
210         OmpClause::FIRSTPRIVATE, OmpClause::SHARED, OmpClause::COPYIN,
211         OmpClause::REDUCTION, OmpClause::LASTPRIVATE, OmpClause::LINEAR};
212     SetContextAllowed(allowed);
213     OmpClauseSet allowedOnce{OmpClause::IF, OmpClause::NUM_THREADS,
214         OmpClause::PROC_BIND, OmpClause::SCHEDULE, OmpClause::COLLAPSE,
215         OmpClause::ORDERED};
216     SetContextAllowedOnce(allowedOnce);
217   } break;
218 
219   // 2.8.1 simd-clause -> safelen-clause |
220   //                      simdlen-clause |
221   //                      linear-clause |
222   //                      aligned-clause |
223   //                      private-clause |
224   //                      lastprivate-clause |
225   //                      reduction-clause |
226   //                      collapse-clause
227   case parser::OmpLoopDirective::Directive::Simd: {
228     PushContext(beginDir.source, OmpDirective::SIMD);
229     OmpClauseSet allowed{OmpClause::LINEAR, OmpClause::ALIGNED,
230         OmpClause::PRIVATE, OmpClause::LASTPRIVATE, OmpClause::REDUCTION};
231     SetContextAllowed(allowed);
232     OmpClauseSet allowedOnce{
233         OmpClause::COLLAPSE, OmpClause::SAFELEN, OmpClause::SIMDLEN};
234     SetContextAllowedOnce(allowedOnce);
235   } break;
236 
237   // 2.8.3 do-simd-clause -> do-clause |
238   //                         simd-clause
239   case parser::OmpLoopDirective::Directive::DoSimd: {
240     PushContext(beginDir.source, OmpDirective::DO_SIMD);
241     OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
242         OmpClause::LASTPRIVATE, OmpClause::LINEAR, OmpClause::REDUCTION,
243         OmpClause::ALIGNED};
244     SetContextAllowed(allowed);
245     OmpClauseSet allowedOnce{OmpClause::SCHEDULE, OmpClause::COLLAPSE,
246         OmpClause::ORDERED, OmpClause::SAFELEN, OmpClause::SIMDLEN};
247     SetContextAllowedOnce(allowedOnce);
248   } break;
249 
250   // 2.11.4 parallel-do-simd-clause -> parallel-clause |
251   //                                   do-simd-clause
252   case parser::OmpLoopDirective::Directive::ParallelDoSimd: {
253     PushContext(beginDir.source, OmpDirective::PARALLEL_DO_SIMD);
254     OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE,
255         OmpClause::FIRSTPRIVATE, OmpClause::SHARED, OmpClause::COPYIN,
256         OmpClause::REDUCTION, OmpClause::LASTPRIVATE, OmpClause::LINEAR,
257         OmpClause::ALIGNED};
258     SetContextAllowed(allowed);
259     OmpClauseSet allowedOnce{OmpClause::IF, OmpClause::NUM_THREADS,
260         OmpClause::PROC_BIND, OmpClause::SCHEDULE, OmpClause::COLLAPSE,
261         OmpClause::ORDERED, OmpClause::SAFELEN, OmpClause::SIMDLEN};
262     SetContextAllowedOnce(allowedOnce);
263   } break;
264 
265   // 2.9.2 taskloop-clause -> if-clause |
266   //                          shared-clause |
267   //                          private-clause |
268   //                          firstprivate-clause |
269   //                          lastprivate-clause |
270   //                          default-clause |
271   //                          grainsize-clause |
272   //                          num-tasks-clause |
273   //                          collapse-clause |
274   //                          final-clause |
275   //                          priority-clause |
276   //                          untied-clause |
277   //                          mergeable-clause |
278   //                          nogroup-clause
279   case parser::OmpLoopDirective::Directive::Taskloop: {
280     PushContext(beginDir.source, OmpDirective::TASKLOOP);
281     OmpClauseSet allowed{OmpClause::SHARED, OmpClause::PRIVATE,
282         OmpClause::FIRSTPRIVATE, OmpClause::LASTPRIVATE, OmpClause::DEFAULT,
283         OmpClause::UNTIED, OmpClause::MERGEABLE, OmpClause::NOGROUP};
284     SetContextAllowed(allowed);
285     OmpClauseSet allowedOnce{OmpClause::COLLAPSE, OmpClause::IF,
286         OmpClause::FINAL, OmpClause::PRIORITY};
287     SetContextAllowedOnce(allowedOnce);
288     OmpClauseSet allowedExclusive{OmpClause::GRAINSIZE, OmpClause::NUM_TASKS};
289     SetContextAllowedExclusive(allowedExclusive);
290   } break;
291 
292   // 2.9.3 taskloop-simd-clause -> taskloop-clause |
293   //                               simd-clause
294   case parser::OmpLoopDirective::Directive::TaskloopSimd: {
295     PushContext(beginDir.source, OmpDirective::TASKLOOP_SIMD);
296     OmpClauseSet allowed{OmpClause::LINEAR, OmpClause::ALIGNED,
297         OmpClause::SHARED, OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
298         OmpClause::LASTPRIVATE, OmpClause::DEFAULT, OmpClause::UNTIED,
299         OmpClause::MERGEABLE, OmpClause::NOGROUP};
300     SetContextAllowed(allowed);
301     OmpClauseSet allowedOnce{OmpClause::COLLAPSE, OmpClause::SAFELEN,
302         OmpClause::SIMDLEN, OmpClause::IF, OmpClause::FINAL,
303         OmpClause::PRIORITY};
304     SetContextAllowedOnce(allowedOnce);
305     OmpClauseSet allowedExclusive{OmpClause::GRAINSIZE, OmpClause::NUM_TASKS};
306     SetContextAllowedExclusive(allowedExclusive);
307   } break;
308 
309   // 2.10.8 distribute-clause -> private-clause |
310   //                             firstprivate-clause |
311   //                             lastprivate-clause |
312   //                             collapse-clause |
313   //                             dist-schedule-clause
314   case parser::OmpLoopDirective::Directive::Distribute: {
315     PushContext(beginDir.source, OmpDirective::DISTRIBUTE);
316     OmpClauseSet allowed{
317         OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE, OmpClause::LASTPRIVATE};
318     SetContextAllowed(allowed);
319     OmpClauseSet allowedOnce{OmpClause::COLLAPSE, OmpClause::DIST_SCHEDULE};
320     SetContextAllowedOnce(allowedOnce);
321   } break;
322 
323   default:
324     // TODO others
325     break;
326   }
327 }
328 
Leave(const parser::OpenMPLoopConstruct &)329 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
330   ompContext_.pop_back();
331 }
332 
Enter(const parser::OmpEndLoopDirective & x)333 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
334   const auto &dir{std::get<parser::OmpLoopDirective>(x.t)};
335   ResetPartialContext(dir.source);
336   switch (dir.v) {
337   // 2.7.1 end-do -> END DO [nowait-clause]
338   // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
339   case parser::OmpLoopDirective::Directive::Do:
340     SetContextDirectiveEnum(OmpDirective::END_DO);
341     SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT});
342     break;
343   case parser::OmpLoopDirective::Directive::DoSimd:
344     SetContextDirectiveEnum(OmpDirective::END_DO_SIMD);
345     SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT});
346     break;
347   default:
348     // no clauses are allowed
349     break;
350   }
351 }
352 
Enter(const parser::OpenMPBlockConstruct & x)353 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
354   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
355   const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)};
356   const auto &beginDir{
357       CheckMatching<parser::OmpBlockDirective>(beginBlockDir, endBlockDir)};
358 
359   switch (beginDir.v) {
360   // 2.5 parallel-clause -> if-clause |
361   //                        num-threads-clause |
362   //                        default-clause |
363   //                        private-clause |
364   //                        firstprivate-clause |
365   //                        shared-clause |
366   //                        copyin-clause |
367   //                        reduction-clause |
368   //                        proc-bind-clause
369   case parser::OmpBlockDirective::Directive::Parallel: {
370     // reserve for nesting check
371     PushContext(beginDir.source, OmpDirective::PARALLEL);
372     OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE,
373         OmpClause::FIRSTPRIVATE, OmpClause::SHARED, OmpClause::COPYIN,
374         OmpClause::REDUCTION};
375     SetContextAllowed(allowed);
376     OmpClauseSet allowedOnce{
377         OmpClause::IF, OmpClause::NUM_THREADS, OmpClause::PROC_BIND};
378     SetContextAllowedOnce(allowedOnce);
379   } break;
380   // 2.7.3 single-clause -> private-clause |
381   //                        firstprivate-clause
382   case parser::OmpBlockDirective::Directive::Single:
383     PushContext(beginDir.source, OmpDirective::SINGLE);
384     SetContextAllowed({OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE});
385     break;
386   // 2.7.4 workshare (no clauses are allowed)
387   case parser::OmpBlockDirective::Directive::Workshare:
388     PushContext(beginDir.source, OmpDirective::WORKSHARE);
389     break;
390     // 2.9.1 task-clause -> if-clause |
391     //                      final-clause |
392     //                      untied-clause |
393     //                      default-clause |
394     //                      mergeable-clause |
395     //                      private-clause |
396     //                      firstprivate-clause |
397     //                      shared-clause |
398     //                      depend-clause |
399     //                      priority-clause
400   case parser::OmpBlockDirective::Directive::Task: {
401     PushContext(beginDir.source, OmpDirective::TASK);
402     OmpClauseSet allowed{OmpClause::UNTIED, OmpClause::DEFAULT,
403         OmpClause::MERGEABLE, OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
404         OmpClause::SHARED, OmpClause::DEPEND};
405     SetContextAllowed(allowed);
406     OmpClauseSet allowedOnce{
407         OmpClause::IF, OmpClause::FINAL, OmpClause::PRIORITY};
408     SetContextAllowedOnce(allowedOnce);
409   } break;
410   // 2.10.4 target-clause -> if-clause |
411   //                         device-clause |
412   //                         private-clause |
413   //                         firstprivate-clause |
414   //                         map-clause |
415   //                         is-device-ptr-clause |
416   //                         defaultmap-clause |
417   //                         nowait-clause |
418   //                         depend-clause
419   case parser::OmpBlockDirective::Directive::Target: {
420     PushContext(beginDir.source, OmpDirective::TARGET);
421     OmpClauseSet allowed{OmpClause::IF, OmpClause::PRIVATE,
422         OmpClause::FIRSTPRIVATE, OmpClause::MAP, OmpClause::IS_DEVICE_PTR,
423         OmpClause::DEPEND};
424     SetContextAllowed(allowed);
425     OmpClauseSet allowedOnce{
426         OmpClause::DEVICE, OmpClause::DEFAULTMAP, OmpClause::NOWAIT};
427     SetContextAllowedOnce(allowedOnce);
428   } break;
429   // 2.10.7 teams-clause -> num-teams-clause |
430   //                        thread-limit-clause |
431   //                        default-clause |
432   //                        private-clause |
433   //                        firstprivate-clause |
434   //                        shared-clause |
435   //                        reduction-clause
436   case parser::OmpBlockDirective::Directive::Teams: {
437     PushContext(beginDir.source, OmpDirective::TEAMS);
438     OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
439         OmpClause::SHARED, OmpClause::REDUCTION};
440     SetContextAllowed(allowed);
441     OmpClauseSet allowedOnce{
442         OmpClause::NUM_TEAMS, OmpClause::THREAD_LIMIT, OmpClause::DEFAULT};
443     SetContextAllowedOnce(allowedOnce);
444   } break;
445     // 2.10.1 target-data-clause -> if-clause |
446     //                              device-clause |
447     //                              map-clause |
448     //                              use-device-ptr-clause
449   case parser::OmpBlockDirective::Directive::TargetData: {
450     PushContext(beginDir.source, OmpDirective::TARGET_DATA);
451     OmpClauseSet allowed{
452         OmpClause::IF, OmpClause::MAP, OmpClause::USE_DEVICE_PTR};
453     SetContextAllowed(allowed);
454     SetContextAllowedOnce({OmpClause::DEVICE});
455     SetContextRequired({OmpClause::MAP});
456   } break;
457   default:
458     // TODO others
459     break;
460   }
461 }
462 
Leave(const parser::OpenMPBlockConstruct &)463 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
464   ompContext_.pop_back();
465 }
466 
Enter(const parser::OpenMPSectionsConstruct & x)467 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
468   const auto &beginSectionsDir{
469       std::get<parser::OmpBeginSectionsDirective>(x.t)};
470   const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)};
471   const auto &beginDir{CheckMatching<parser::OmpSectionsDirective>(
472       beginSectionsDir, endSectionsDir)};
473 
474   switch (beginDir.v) {
475   // 2.7.2 sections-clause -> private-clause |
476   //                          firstprivate-clause |
477   //                          lastprivate-clause |
478   //                          reduction-clause
479   case parser::OmpSectionsDirective::Directive::Sections: {
480     PushContext(beginDir.source, OmpDirective::SECTIONS);
481     OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
482         OmpClause::LASTPRIVATE, OmpClause::REDUCTION};
483     SetContextAllowed(allowed);
484   } break;
485   case parser::OmpSectionsDirective::Directive::ParallelSections: {
486     PushContext(beginDir.source, OmpDirective::PARALLEL_SECTIONS);
487     OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE,
488         OmpClause::FIRSTPRIVATE, OmpClause::LASTPRIVATE, OmpClause::SHARED,
489         OmpClause::COPYIN, OmpClause::REDUCTION};
490     SetContextAllowed(allowed);
491     OmpClauseSet allowedOnce{
492         OmpClause::IF, OmpClause::NUM_THREADS, OmpClause::PROC_BIND};
493     SetContextAllowedOnce(allowedOnce);
494   } break;
495   }
496 }
497 
Leave(const parser::OpenMPSectionsConstruct &)498 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
499   ompContext_.pop_back();
500 }
501 
Enter(const parser::OmpEndSectionsDirective & x)502 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
503   const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)};
504   ResetPartialContext(dir.source);
505   switch (dir.v) {
506     // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
507   case parser::OmpSectionsDirective::Directive::Sections:
508     SetContextDirectiveEnum(OmpDirective::END_SECTIONS);
509     SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT});
510     break;
511   default:
512     // no clauses are allowed
513     break;
514   }
515 }
516 
Enter(const parser::OpenMPDeclareSimdConstruct & x)517 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
518   const auto &dir{std::get<parser::Verbatim>(x.t)};
519   PushContext(dir.source, OmpDirective::DECLARE_SIMD);
520   // 2.8.2 declare-simd-clause -> simdlen-clause |
521   //                              linear-clause |
522   //                              aligned-clause |
523   //                              uniform-clause |
524   //                              inbranch-clause |
525   //                              notinbranch-clause
526   OmpClauseSet allowed{
527       OmpClause::LINEAR, OmpClause::ALIGNED, OmpClause::UNIFORM};
528   SetContextAllowed(allowed);
529   SetContextAllowedOnce({OmpClause::SIMDLEN});
530   SetContextAllowedExclusive({OmpClause::INBRANCH, OmpClause::NOTINBRANCH});
531 }
532 
Leave(const parser::OpenMPDeclareSimdConstruct &)533 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
534   ompContext_.pop_back();
535 }
536 
Enter(const parser::OpenMPDeclareTargetConstruct & x)537 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
538   const auto &dir{std::get<parser::Verbatim>(x.t)};
539   PushContext(dir.source, OmpDirective::DECLARE_TARGET);
540   const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
541   if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) {
542     SetContextAllowed(OmpClauseSet{OmpClause::TO, OmpClause::LINK});
543   }
544 }
545 
Leave(const parser::OpenMPDeclareTargetConstruct &)546 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) {
547   ompContext_.pop_back();
548 }
549 
Enter(const parser::OpenMPSimpleStandaloneConstruct & x)550 void OmpStructureChecker::Enter(
551     const parser::OpenMPSimpleStandaloneConstruct &x) {
552   const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
553   switch (dir.v) {
554   case parser::OmpSimpleStandaloneDirective::Directive::Barrier: {
555     // 2.13.3 barrier
556     PushContext(dir.source, OmpDirective::BARRIER);
557   } break;
558   case parser::OmpSimpleStandaloneDirective::Directive::Taskwait: {
559     // 2.13.4 taskwait
560     PushContext(dir.source, OmpDirective::TASKWAIT);
561   } break;
562   case parser::OmpSimpleStandaloneDirective::Directive::Taskyield: {
563     // 2.9.4 taskyield
564     PushContext(dir.source, OmpDirective::TASKYIELD);
565   } break;
566   case parser::OmpSimpleStandaloneDirective::Directive::TargetEnterData: {
567     // 2.10.2 target-enter-data-clause -> if-clause |
568     //                                    device-clause |
569     //                                    map-clause |
570     //                                    depend-clause |
571     //                                    nowait-clause
572     PushContext(dir.source, OmpDirective::TARGET_ENTER_DATA);
573     OmpClauseSet allowed{OmpClause::MAP, OmpClause::DEPEND, OmpClause::NOWAIT};
574     SetContextAllowed(allowed);
575     OmpClauseSet allowedOnce{OmpClause::DEVICE, OmpClause::IF};
576     SetContextAllowedOnce(allowedOnce);
577     SetContextRequired({OmpClause::MAP});
578   } break;
579   case parser::OmpSimpleStandaloneDirective::Directive::TargetExitData: {
580     // 2.10.3  target-enter-data-clause -> if-clause |
581     //                                     device-clause |
582     //                                     map-clause |
583     //                                     depend-clause |
584     //                                     nowait-clause
585     PushContext(dir.source, OmpDirective::TARGET_EXIT_DATA);
586     OmpClauseSet allowed{OmpClause::MAP, OmpClause::DEPEND, OmpClause::NOWAIT};
587     SetContextAllowed(allowed);
588     OmpClauseSet allowedOnce{OmpClause::DEVICE, OmpClause::IF};
589     SetContextAllowedOnce(allowedOnce);
590     SetContextRequired({OmpClause::MAP});
591   } break;
592   case parser::OmpSimpleStandaloneDirective::Directive::TargetUpdate: {
593     // 2.10.5 target-update
594     PushContext(dir.source, OmpDirective::TARGET_UPDATE);
595   } break;
596   case parser::OmpSimpleStandaloneDirective::Directive::Ordered: {
597     // 2.13.8 ordered-construct-clause -> depend-clause
598     PushContext(dir.source, OmpDirective::ORDERED);
599     OmpClauseSet allowed{OmpClause::DEPEND};
600     SetContextAllowed(allowed);
601   } break;
602   }
603 }
604 
Leave(const parser::OpenMPSimpleStandaloneConstruct &)605 void OmpStructureChecker::Leave(
606     const parser::OpenMPSimpleStandaloneConstruct &) {
607   ompContext_.pop_back();
608 }
609 
Enter(const parser::OpenMPFlushConstruct & x)610 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
611   const auto &dir{std::get<parser::Verbatim>(x.t)};
612   PushContext(dir.source, OmpDirective::FLUSH);
613 }
614 
Leave(const parser::OpenMPFlushConstruct &)615 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) {
616   ompContext_.pop_back();
617 }
618 
Enter(const parser::OpenMPCancelConstruct & x)619 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
620   const auto &dir{std::get<parser::Verbatim>(x.t)};
621   PushContext(dir.source, OmpDirective::CANCEL);
622 }
623 
Leave(const parser::OpenMPCancelConstruct &)624 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
625   ompContext_.pop_back();
626 }
627 
Enter(const parser::OpenMPCancellationPointConstruct & x)628 void OmpStructureChecker::Enter(
629     const parser::OpenMPCancellationPointConstruct &x) {
630   const auto &dir{std::get<parser::Verbatim>(x.t)};
631   PushContext(dir.source, OmpDirective::CANCELLATION_POINT);
632 }
633 
Leave(const parser::OpenMPCancellationPointConstruct &)634 void OmpStructureChecker::Leave(
635     const parser::OpenMPCancellationPointConstruct &) {
636   ompContext_.pop_back();
637 }
638 
Enter(const parser::OmpEndBlockDirective & x)639 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
640   const auto &dir{std::get<parser::OmpBlockDirective>(x.t)};
641   ResetPartialContext(dir.source);
642   switch (dir.v) {
643   // 2.7.3 end-single-clause -> copyprivate-clause |
644   //                            nowait-clause
645   case parser::OmpBlockDirective::Directive::Single: {
646     SetContextDirectiveEnum(OmpDirective::END_SINGLE);
647     OmpClauseSet allowed{OmpClause::COPYPRIVATE};
648     SetContextAllowed(allowed);
649     OmpClauseSet allowedOnce{OmpClause::NOWAIT};
650     SetContextAllowedOnce(allowedOnce);
651   } break;
652   // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
653   case parser::OmpBlockDirective::Directive::Workshare:
654     SetContextDirectiveEnum(OmpDirective::END_WORKSHARE);
655     SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT});
656     break;
657   default:
658     // no clauses are allowed
659     break;
660   }
661 }
662 
Leave(const parser::OmpClauseList &)663 void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
664   // 2.7 Loop Construct Restriction
665   if (doSet.test(GetContext().directive)) {
666     if (auto *clause{FindClause(OmpClause::SCHEDULE)}) {
667       // only one schedule clause is allowed
668       const auto &schedClause{std::get<parser::OmpScheduleClause>(clause->u)};
669       if (ScheduleModifierHasType(schedClause,
670               parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
671         if (FindClause(OmpClause::ORDERED)) {
672           context_.Say(clause->source,
673               "The NONMONOTONIC modifier cannot be specified "
674               "if an ORDERED clause is specified"_err_en_US);
675         }
676         if (ScheduleModifierHasType(schedClause,
677                 parser::OmpScheduleModifierType::ModType::Monotonic)) {
678           context_.Say(clause->source,
679               "The MONOTONIC and NONMONOTONIC modifiers "
680               "cannot be both specified"_err_en_US);
681         }
682       }
683     }
684 
685     if (auto *clause{FindClause(OmpClause::ORDERED)}) {
686       // only one ordered clause is allowed
687       const auto &orderedClause{
688           std::get<parser::OmpClause::Ordered>(clause->u)};
689 
690       if (orderedClause.v.has_value()) {
691         if (FindClause(OmpClause::LINEAR)) {
692           context_.Say(clause->source,
693               "A loop directive may not have both a LINEAR clause and "
694               "an ORDERED clause with a parameter"_err_en_US);
695         }
696 
697         if (auto *clause2{FindClause(OmpClause::COLLAPSE)}) {
698           const auto &collapseClause{
699               std::get<parser::OmpClause::Collapse>(clause2->u)};
700           // ordered and collapse both have parameters
701           if (const auto orderedValue{GetIntValue(orderedClause.v)}) {
702             if (const auto collapseValue{GetIntValue(collapseClause.v)}) {
703               if (*orderedValue > 0 && *orderedValue < *collapseValue) {
704                 context_.Say(clause->source,
705                     "The parameter of the ORDERED clause must be "
706                     "greater than or equal to "
707                     "the parameter of the COLLAPSE clause"_err_en_US);
708               }
709             }
710           }
711         }
712       }
713 
714       // TODO: ordered region binding check (requires nesting implementation)
715     }
716   }  // doSet
717 
718   // 2.8.1 Simd Construct Restriction
719   if (simdSet.test(GetContext().directive)) {
720     if (auto *clause{FindClause(OmpClause::SIMDLEN)}) {
721       if (auto *clause2{FindClause(OmpClause::SAFELEN)}) {
722         const auto &simdlenClause{
723             std::get<parser::OmpClause::Simdlen>(clause->u)};
724         const auto &safelenClause{
725             std::get<parser::OmpClause::Safelen>(clause2->u)};
726         // simdlen and safelen both have parameters
727         if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) {
728           if (const auto safelenValue{GetIntValue(safelenClause.v)}) {
729             if (*safelenValue > 0 && *simdlenValue > *safelenValue) {
730               context_.Say(clause->source,
731                   "The parameter of the SIMDLEN clause must be less than or "
732                   "equal to the parameter of the SAFELEN clause"_err_en_US);
733             }
734           }
735         }
736       }
737     }
738 
739     // TODO: A list-item cannot appear in more than one aligned clause
740   }  // SIMD
741 
742   // 2.7.3 Single Construct Restriction
743   if (GetContext().directive == OmpDirective::END_SINGLE) {
744     if (auto *clause{FindClause(OmpClause::COPYPRIVATE)}) {
745       if (FindClause(OmpClause::NOWAIT)) {
746         context_.Say(clause->source,
747             "The COPYPRIVATE clause must not be used with "
748             "the NOWAIT clause"_err_en_US);
749       }
750     }
751   }
752 
753   GetContext().requiredClauses.IterateOverMembers(
754       [this](OmpClause c) { CheckRequired(c); });
755 }
756 
Enter(const parser::OmpClause & x)757 void OmpStructureChecker::Enter(const parser::OmpClause &x) {
758   SetContextClause(x);
759 }
760 
Enter(const parser::OmpNowait &)761 void OmpStructureChecker::Enter(const parser::OmpNowait &) {
762   CheckAllowed(OmpClause::NOWAIT);
763 }
Enter(const parser::OmpClause::Inbranch &)764 void OmpStructureChecker::Enter(const parser::OmpClause::Inbranch &) {
765   CheckAllowed(OmpClause::INBRANCH);
766 }
Enter(const parser::OmpClause::Mergeable &)767 void OmpStructureChecker::Enter(const parser::OmpClause::Mergeable &) {
768   CheckAllowed(OmpClause::MERGEABLE);
769 }
Enter(const parser::OmpClause::Nogroup &)770 void OmpStructureChecker::Enter(const parser::OmpClause::Nogroup &) {
771   CheckAllowed(OmpClause::NOGROUP);
772 }
Enter(const parser::OmpClause::Notinbranch &)773 void OmpStructureChecker::Enter(const parser::OmpClause::Notinbranch &) {
774   CheckAllowed(OmpClause::NOTINBRANCH);
775 }
Enter(const parser::OmpClause::Untied &)776 void OmpStructureChecker::Enter(const parser::OmpClause::Untied &) {
777   CheckAllowed(OmpClause::UNTIED);
778 }
779 
Enter(const parser::OmpClause::Collapse & x)780 void OmpStructureChecker::Enter(const parser::OmpClause::Collapse &x) {
781   CheckAllowed(OmpClause::COLLAPSE);
782   // collapse clause must have a parameter
783   RequiresConstantPositiveParameter(OmpClause::COLLAPSE, x.v);
784 }
785 
Enter(const parser::OmpClause::Copyin &)786 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &) {
787   CheckAllowed(OmpClause::COPYIN);
788 }
Enter(const parser::OmpClause::Copyprivate &)789 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &) {
790   CheckAllowed(OmpClause::COPYPRIVATE);
791 }
Enter(const parser::OmpClause::Device &)792 void OmpStructureChecker::Enter(const parser::OmpClause::Device &) {
793   CheckAllowed(OmpClause::DEVICE);
794 }
Enter(const parser::OmpClause::DistSchedule &)795 void OmpStructureChecker::Enter(const parser::OmpClause::DistSchedule &) {
796   CheckAllowed(OmpClause::DIST_SCHEDULE);
797 }
Enter(const parser::OmpClause::Final &)798 void OmpStructureChecker::Enter(const parser::OmpClause::Final &) {
799   CheckAllowed(OmpClause::FINAL);
800 }
Enter(const parser::OmpClause::Firstprivate &)801 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &) {
802   CheckAllowed(OmpClause::FIRSTPRIVATE);
803 }
Enter(const parser::OmpClause::From &)804 void OmpStructureChecker::Enter(const parser::OmpClause::From &) {
805   CheckAllowed(OmpClause::FROM);
806 }
Enter(const parser::OmpClause::Grainsize & x)807 void OmpStructureChecker::Enter(const parser::OmpClause::Grainsize &x) {
808   CheckAllowed(OmpClause::GRAINSIZE);
809   RequiresPositiveParameter(OmpClause::GRAINSIZE, x.v);
810 }
Enter(const parser::OmpClause::Lastprivate &)811 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &) {
812   CheckAllowed(OmpClause::LASTPRIVATE);
813 }
Enter(const parser::OmpClause::NumTasks & x)814 void OmpStructureChecker::Enter(const parser::OmpClause::NumTasks &x) {
815   CheckAllowed(OmpClause::NUM_TASKS);
816   RequiresPositiveParameter(OmpClause::NUM_TASKS, x.v);
817 }
Enter(const parser::OmpClause::NumTeams & x)818 void OmpStructureChecker::Enter(const parser::OmpClause::NumTeams &x) {
819   CheckAllowed(OmpClause::NUM_TEAMS);
820   RequiresPositiveParameter(OmpClause::NUM_TEAMS, x.v);
821 }
Enter(const parser::OmpClause::NumThreads & x)822 void OmpStructureChecker::Enter(const parser::OmpClause::NumThreads &x) {
823   CheckAllowed(OmpClause::NUM_THREADS);
824   RequiresPositiveParameter(OmpClause::NUM_THREADS, x.v);
825   // if parameter is variable, defer to Expression Analysis
826 }
827 
Enter(const parser::OmpClause::Ordered & x)828 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
829   CheckAllowed(OmpClause::ORDERED);
830   // the parameter of ordered clause is optional
831   if (const auto &expr{x.v}) {
832     RequiresConstantPositiveParameter(OmpClause::ORDERED, *expr);
833 
834     // 2.8.3 Loop SIMD Construct Restriction
835     if (doSimdSet.test(GetContext().directive)) {
836       context_.Say(GetContext().clauseSource,
837           "No ORDERED clause with a parameter can be specified "
838           "on the %s directive"_err_en_US,
839           ContextDirectiveAsFortran());
840     }
841   }
842 }
Enter(const parser::OmpClause::Priority & x)843 void OmpStructureChecker::Enter(const parser::OmpClause::Priority &x) {
844   CheckAllowed(OmpClause::PRIORITY);
845   RequiresPositiveParameter(OmpClause::PRIORITY, x.v);
846 }
Enter(const parser::OmpClause::Private &)847 void OmpStructureChecker::Enter(const parser::OmpClause::Private &) {
848   CheckAllowed(OmpClause::PRIVATE);
849 }
Enter(const parser::OmpClause::Safelen & x)850 void OmpStructureChecker::Enter(const parser::OmpClause::Safelen &x) {
851   CheckAllowed(OmpClause::SAFELEN);
852   RequiresConstantPositiveParameter(OmpClause::SAFELEN, x.v);
853 }
Enter(const parser::OmpClause::Shared &)854 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &) {
855   CheckAllowed(OmpClause::SHARED);
856 }
Enter(const parser::OmpClause::Simdlen & x)857 void OmpStructureChecker::Enter(const parser::OmpClause::Simdlen &x) {
858   CheckAllowed(OmpClause::SIMDLEN);
859   RequiresConstantPositiveParameter(OmpClause::SIMDLEN, x.v);
860 }
Enter(const parser::OmpClause::ThreadLimit & x)861 void OmpStructureChecker::Enter(const parser::OmpClause::ThreadLimit &x) {
862   CheckAllowed(OmpClause::THREAD_LIMIT);
863   RequiresPositiveParameter(OmpClause::THREAD_LIMIT, x.v);
864 }
Enter(const parser::OmpClause::To &)865 void OmpStructureChecker::Enter(const parser::OmpClause::To &) {
866   CheckAllowed(OmpClause::TO);
867 }
Enter(const parser::OmpClause::Link &)868 void OmpStructureChecker::Enter(const parser::OmpClause::Link &) {
869   CheckAllowed(OmpClause::LINK);
870 }
Enter(const parser::OmpClause::Uniform &)871 void OmpStructureChecker::Enter(const parser::OmpClause::Uniform &) {
872   CheckAllowed(OmpClause::UNIFORM);
873 }
Enter(const parser::OmpClause::UseDevicePtr &)874 void OmpStructureChecker::Enter(const parser::OmpClause::UseDevicePtr &) {
875   CheckAllowed(OmpClause::USE_DEVICE_PTR);
876 }
Enter(const parser::OmpClause::IsDevicePtr &)877 void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr &) {
878   CheckAllowed(OmpClause::IS_DEVICE_PTR);
879 }
880 
Enter(const parser::OmpAlignedClause & x)881 void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) {
882   CheckAllowed(OmpClause::ALIGNED);
883 
884   if (const auto &expr{
885           std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) {
886     if (const auto v{GetIntValue(*expr)}) {
887       if (*v <= 0) {
888         context_.Say(GetContext().clauseSource,
889             "The ALIGNMENT parameter of the ALIGNED clause must be "
890             "a constant positive integer expression"_err_en_US);
891       }
892     }
893   }
894   // 2.8.1 TODO: list-item attribute check
895 }
Enter(const parser::OmpDefaultClause &)896 void OmpStructureChecker::Enter(const parser::OmpDefaultClause &) {
897   CheckAllowed(OmpClause::DEFAULT);
898 }
Enter(const parser::OmpDefaultmapClause & x)899 void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) {
900   CheckAllowed(OmpClause::DEFAULTMAP);
901   using VariableCategory = parser::OmpDefaultmapClause::VariableCategory;
902   if (!std::get<std::optional<VariableCategory>>(x.t)) {
903     context_.Say(GetContext().clauseSource,
904         "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP "
905         "clause"_err_en_US);
906   }
907 }
Enter(const parser::OmpDependClause &)908 void OmpStructureChecker::Enter(const parser::OmpDependClause &) {
909   CheckAllowed(OmpClause::DEPEND);
910 }
911 
Enter(const parser::OmpIfClause & x)912 void OmpStructureChecker::Enter(const parser::OmpIfClause &x) {
913   CheckAllowed(OmpClause::IF);
914 
915   using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier;
916   static std::unordered_map<dirNameModifier, OmpDirectiveSet>
917       dirNameModifierMap{{dirNameModifier::Parallel, parallelSet},
918           {dirNameModifier::Target, targetSet},
919           {dirNameModifier::TargetEnterData, {OmpDirective::TARGET_ENTER_DATA}},
920           {dirNameModifier::TargetExitData, {OmpDirective::TARGET_EXIT_DATA}},
921           {dirNameModifier::TargetData, {OmpDirective::TARGET_DATA}},
922           {dirNameModifier::TargetUpdate, {OmpDirective::TARGET_UPDATE}},
923           {dirNameModifier::Task, {OmpDirective::TASK}},
924           {dirNameModifier::Taskloop, taskloopSet}};
925   if (const auto &directiveName{
926           std::get<std::optional<dirNameModifier>>(x.t)}) {
927     auto search{dirNameModifierMap.find(*directiveName)};
928     if (search == dirNameModifierMap.end() ||
929         !search->second.test(GetContext().directive)) {
930       context_
931           .Say(GetContext().clauseSource,
932               "Unmatched directive name modifier %s on the IF clause"_err_en_US,
933               parser::ToUpperCaseLetters(
934                   parser::OmpIfClause::EnumToString(*directiveName)))
935           .Attach(
936               GetContext().directiveSource, "Cannot apply to directive"_en_US);
937     }
938   }
939 }
940 
Enter(const parser::OmpLinearClause & x)941 void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) {
942   CheckAllowed(OmpClause::LINEAR);
943 
944   // 2.7 Loop Construct Restriction
945   if ((doSet | simdSet).test(GetContext().directive)) {
946     if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) {
947       context_.Say(GetContext().clauseSource,
948           "A modifier may not be specified in a LINEAR clause "
949           "on the %s directive"_err_en_US,
950           ContextDirectiveAsFortran());
951     }
952   }
953 }
Enter(const parser::OmpMapClause & x)954 void OmpStructureChecker::Enter(const parser::OmpMapClause &x) {
955   CheckAllowed(OmpClause::MAP);
956   if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) {
957     using Type = parser::OmpMapType::Type;
958     const Type &type{std::get<Type>(maptype->t)};
959     switch (GetContext().directive) {
960     case OmpDirective::TARGET:
961     case OmpDirective::TARGET_DATA: {
962       if (type != Type::To && type != Type::From && type != Type::Tofrom &&
963           type != Type::Alloc) {
964         context_.Say(GetContext().clauseSource,
965             "Only the TO, FROM, TOFROM, or ALLOC map types are permitted "
966             "for MAP clauses on the %s directive"_err_en_US,
967             ContextDirectiveAsFortran());
968       }
969     } break;
970     case OmpDirective::TARGET_ENTER_DATA: {
971       if (type != Type::To && type != Type::Alloc) {
972         context_.Say(GetContext().clauseSource,
973             "Only the TO or ALLOC map types are permitted "
974             "for MAP clauses on the %s directive"_err_en_US,
975             ContextDirectiveAsFortran());
976       }
977     } break;
978     case OmpDirective::TARGET_EXIT_DATA: {
979       if (type != Type::Delete && type != Type::Release && type != Type::From) {
980         context_.Say(GetContext().clauseSource,
981             "Only the FROM, RELEASE, or DELETE map types are permitted "
982             "for MAP clauses on the %s directive"_err_en_US,
983             ContextDirectiveAsFortran());
984       }
985     } break;
986     default: break;
987     }
988   }
989 }
Enter(const parser::OmpProcBindClause &)990 void OmpStructureChecker::Enter(const parser::OmpProcBindClause &) {
991   CheckAllowed(OmpClause::PROC_BIND);
992 }
Enter(const parser::OmpReductionClause &)993 void OmpStructureChecker::Enter(const parser::OmpReductionClause &) {
994   CheckAllowed(OmpClause::REDUCTION);
995 }
996 
ScheduleModifierHasType(const parser::OmpScheduleClause & x,const parser::OmpScheduleModifierType::ModType & type)997 bool OmpStructureChecker::ScheduleModifierHasType(
998     const parser::OmpScheduleClause &x,
999     const parser::OmpScheduleModifierType::ModType &type) {
1000   const auto &modifier{
1001       std::get<std::optional<parser::OmpScheduleModifier>>(x.t)};
1002   if (modifier.has_value()) {
1003     const auto &modType1{
1004         std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)};
1005     const auto &modType2{
1006         std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(
1007             modifier->t)};
1008     if (modType1.v.v == type ||
1009         (modType2.has_value() && modType2->v.v == type)) {
1010       return true;
1011     }
1012   }
1013   return false;
1014 }
Enter(const parser::OmpScheduleClause & x)1015 void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) {
1016   CheckAllowed(OmpClause::SCHEDULE);
1017 
1018   // 2.7 Loop Construct Restriction
1019   if (doSet.test(GetContext().directive)) {
1020     const auto &kind{std::get<1>(x.t)};
1021     const auto &chunk{std::get<2>(x.t)};
1022     if (chunk.has_value()) {
1023       if (kind == parser::OmpScheduleClause::ScheduleType::Runtime ||
1024           kind == parser::OmpScheduleClause::ScheduleType::Auto) {
1025         context_.Say(GetContext().clauseSource,
1026             "When SCHEDULE clause has %s specified, "
1027             "it must not have chunk size specified"_err_en_US,
1028             parser::ToUpperCaseLetters(
1029                 parser::OmpScheduleClause::EnumToString(kind)));
1030       }
1031     }
1032 
1033     if (ScheduleModifierHasType(
1034             x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
1035       if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic &&
1036           kind != parser::OmpScheduleClause::ScheduleType::Guided) {
1037         context_.Say(GetContext().clauseSource,
1038             "The NONMONOTONIC modifier can only be specified with "
1039             "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US);
1040       }
1041     }
1042   }
1043 }
1044 }
1045