1 //===-- lib/Semantics/check-omp-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-omp-structure.h"
10 #include "flang/Parser/parse-tree.h"
11 #include "flang/Semantics/tools.h"
12 #include <unordered_map>
13 
14 namespace Fortran::semantics {
15 
16 static OmpClauseSet doAllowedClauses{llvm::omp::Clause::OMPC_private,
17     llvm::omp::Clause::OMPC_firstprivate, llvm::omp::Clause::OMPC_lastprivate,
18     llvm::omp::Clause::OMPC_linear, llvm::omp::Clause::OMPC_reduction};
19 static OmpClauseSet doAllowedOnceClauses{llvm::omp::Clause::OMPC_schedule,
20     llvm::omp::Clause::OMPC_collapse, llvm::omp::Clause::OMPC_ordered};
21 
22 static OmpClauseSet simdAllowedClauses{llvm::omp::Clause::OMPC_linear,
23     llvm::omp::Clause::OMPC_aligned, llvm::omp::Clause::OMPC_private,
24     llvm::omp::Clause::OMPC_lastprivate, llvm::omp::Clause::OMPC_reduction};
25 static OmpClauseSet simdAllowedOnceClauses{llvm::omp::Clause::OMPC_collapse,
26     llvm::omp::Clause::OMPC_safelen, llvm::omp::Clause::OMPC_simdlen};
27 
28 static OmpClauseSet parallelAllowedClauses{llvm::omp::Clause::OMPC_default,
29     llvm::omp::Clause::OMPC_private, llvm::omp::Clause::OMPC_firstprivate,
30     llvm::omp::Clause::OMPC_shared, llvm::omp::Clause::OMPC_copyin,
31     llvm::omp::Clause::OMPC_reduction};
32 static OmpClauseSet parallelAllowedOnceClauses{llvm::omp::Clause::OMPC_if,
33     llvm::omp::Clause::OMPC_num_threads, llvm::omp::Clause::OMPC_proc_bind};
34 
35 static OmpClauseSet taskloopAllowedClauses{llvm::omp::Clause::OMPC_shared,
36     llvm::omp::Clause::OMPC_private, llvm::omp::Clause::OMPC_firstprivate,
37     llvm::omp::Clause::OMPC_lastprivate, llvm::omp::Clause::OMPC_default,
38     llvm::omp::Clause::OMPC_untied, llvm::omp::Clause::OMPC_mergeable,
39     llvm::omp::Clause::OMPC_nogroup};
40 static OmpClauseSet taskloopAllowedOnceClauses{llvm::omp::Clause::OMPC_collapse,
41     llvm::omp::Clause::OMPC_if, llvm::omp::Clause::OMPC_final,
42     llvm::omp::Clause::OMPC_priority};
43 static OmpClauseSet taskloopAllowedExclusiveClauses{
44     llvm::omp::Clause::OMPC_grainsize, llvm::omp::Clause::OMPC_num_tasks};
45 
46 static OmpClauseSet distributeAllowedClauses{llvm::omp::Clause::OMPC_private,
47     llvm::omp::Clause::OMPC_firstprivate, llvm::omp::Clause::OMPC_lastprivate};
48 static OmpClauseSet distributeAllowedOnceClauses{
49     llvm::omp::Clause::OMPC_collapse, llvm::omp::Clause::OMPC_dist_schedule};
50 
51 static OmpClauseSet targetAllowedClauses{llvm::omp::Clause::OMPC_if,
52     llvm::omp::Clause::OMPC_private, llvm::omp::Clause::OMPC_firstprivate,
53     llvm::omp::Clause::OMPC_map, llvm::omp::Clause::OMPC_is_device_ptr,
54     llvm::omp::Clause::OMPC_depend};
55 static OmpClauseSet targetAllowedOnceClauses{llvm::omp::Clause::OMPC_device,
56     llvm::omp::Clause::OMPC_defaultmap, llvm::omp::Clause::OMPC_nowait};
57 
58 static OmpClauseSet teamsAllowedClauses{llvm::omp::Clause::OMPC_private,
59     llvm::omp::Clause::OMPC_firstprivate, llvm::omp::Clause::OMPC_shared,
60     llvm::omp::Clause::OMPC_reduction};
61 static OmpClauseSet teamsAllowedOnceClauses{llvm::omp::Clause::OMPC_num_teams,
62     llvm::omp::Clause::OMPC_thread_limit, llvm::omp::Clause::OMPC_default};
63 
64 static OmpClauseSet sectionsAllowedClauses{llvm::omp::Clause::OMPC_private,
65     llvm::omp::Clause::OMPC_firstprivate, llvm::omp::Clause::OMPC_lastprivate,
66     llvm::omp::Clause::OMPC_reduction};
67 
68 std::string OmpStructureChecker::ContextDirectiveAsFortran() {
69   auto dir = llvm::omp::getOpenMPDirectiveName(GetContext().directive).str();
70   std::transform(dir.begin(), dir.end(), dir.begin(),
71       [](unsigned char c) { return std::toupper(c); });
72   return dir;
73 }
74 
75 void OmpStructureChecker::SayNotMatching(
76     const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
77   context_
78       .Say(endSource, "Unmatched %s directive"_err_en_US,
79           parser::ToUpperCaseLetters(endSource.ToString()))
80       .Attach(beginSource, "Does not match directive"_en_US);
81 }
82 
83 bool OmpStructureChecker::HasInvalidWorksharingNesting(
84     const parser::CharBlock &source, const OmpDirectiveSet &set) {
85   // set contains all the invalid closely nested directives
86   // for the given directive (`source` here)
87   if (CurrentDirectiveIsNested() && set.test(GetContext().directive)) {
88     context_.Say(source,
89         "A worksharing region may not be closely nested inside a "
90         "worksharing, explicit task, taskloop, critical, ordered, atomic, or "
91         "master region"_err_en_US);
92     return true;
93   }
94   return false;
95 }
96 
97 void OmpStructureChecker::CheckAllowed(llvm::omp::Clause type) {
98   if (!GetContext().allowedClauses.test(type) &&
99       !GetContext().allowedOnceClauses.test(type) &&
100       !GetContext().allowedExclusiveClauses.test(type)) {
101     context_.Say(GetContext().clauseSource,
102         "%s clause is not allowed on the %s directive"_err_en_US,
103         parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(type).str()),
104         parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
105     return;
106   }
107   if ((GetContext().allowedOnceClauses.test(type) ||
108           GetContext().allowedExclusiveClauses.test(type)) &&
109       FindClause(type)) {
110     context_.Say(GetContext().clauseSource,
111         "At most one %s clause can appear on the %s directive"_err_en_US,
112         parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(type).str()),
113         parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
114     return;
115   }
116   if (GetContext().allowedExclusiveClauses.test(type)) {
117     std::vector<llvm::omp::Clause> others;
118     GetContext().allowedExclusiveClauses.IterateOverMembers(
119         [&](llvm::omp::Clause o) {
120           if (FindClause(o)) {
121             others.emplace_back(o);
122           }
123         });
124     for (const auto &e : others) {
125       context_.Say(GetContext().clauseSource,
126           "%s and %s are mutually exclusive and may not appear on the "
127           "same %s directive"_err_en_US,
128           parser::ToUpperCaseLetters(
129               llvm::omp::getOpenMPClauseName(type).str()),
130           parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(e).str()),
131           parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
132     }
133     if (!others.empty()) {
134       return;
135     }
136   }
137   SetContextClauseInfo(type);
138 }
139 
140 void OmpStructureChecker::CheckRequired(llvm::omp::Clause c) {
141   if (!FindClause(c)) {
142     context_.Say(GetContext().directiveSource,
143         "At least one %s clause must appear on the %s directive"_err_en_US,
144         parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(c).str()),
145         ContextDirectiveAsFortran());
146   }
147 }
148 
149 void OmpStructureChecker::RequiresConstantPositiveParameter(
150     const llvm::omp::Clause &clause, const parser::ScalarIntConstantExpr &i) {
151   if (const auto v{GetIntValue(i)}) {
152     if (*v <= 0) {
153       context_.Say(GetContext().clauseSource,
154           "The parameter of the %s clause must be "
155           "a constant positive integer expression"_err_en_US,
156           parser::ToUpperCaseLetters(
157               llvm::omp::getOpenMPClauseName(clause).str()));
158     }
159   }
160 }
161 
162 void OmpStructureChecker::RequiresPositiveParameter(
163     const llvm::omp::Clause &clause, const parser::ScalarIntExpr &i) {
164   if (const auto v{GetIntValue(i)}) {
165     if (*v <= 0) {
166       context_.Say(GetContext().clauseSource,
167           "The parameter of the %s clause must be "
168           "a positive integer expression"_err_en_US,
169           parser::ToUpperCaseLetters(
170               llvm::omp::getOpenMPClauseName(clause).str()));
171     }
172   }
173 }
174 
175 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) {
176   // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check
177 }
178 
179 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
180   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
181   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
182 
183   // check matching, End directive is optional
184   if (const auto &endLoopDir{
185           std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
186     CheckMatching<parser::OmpLoopDirective>(beginLoopDir, *endLoopDir);
187   }
188 
189   if (beginDir.v != llvm::omp::Directive::OMPD_do)
190     PushContext(beginDir.source, beginDir.v);
191 
192   switch (beginDir.v) {
193   // 2.7.1 do-clause -> private-clause |
194   //                    firstprivate-clause |
195   //                    lastprivate-clause |
196   //                    linear-clause |
197   //                    reduction-clause |
198   //                    schedule-clause |
199   //                    collapse-clause |
200   //                    ordered-clause
201   case llvm::omp::Directive::OMPD_do: {
202     // nesting check
203     HasInvalidWorksharingNesting(beginDir.source,
204         {llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_sections,
205             llvm::omp::Directive::OMPD_single,
206             llvm::omp::Directive::OMPD_workshare,
207             llvm::omp::Directive::OMPD_task,
208             llvm::omp::Directive::OMPD_taskloop,
209             llvm::omp::Directive::OMPD_critical,
210             llvm::omp::Directive::OMPD_ordered,
211             llvm::omp::Directive::OMPD_atomic,
212             llvm::omp::Directive::OMPD_master});
213     PushContext(beginDir.source, llvm::omp::Directive::OMPD_do);
214     SetContextAllowed(doAllowedClauses);
215     SetContextAllowedOnce(doAllowedOnceClauses);
216   } break;
217 
218   // 2.11.1 parallel-do-clause -> parallel-clause |
219   //                              do-clause
220   case llvm::omp::Directive::OMPD_parallel_do: {
221     SetContextAllowed(parallelAllowedClauses | doAllowedClauses);
222     SetContextAllowedOnce(parallelAllowedOnceClauses | doAllowedOnceClauses);
223   } break;
224 
225   // 2.8.1 simd-clause -> safelen-clause |
226   //                      simdlen-clause |
227   //                      linear-clause |
228   //                      aligned-clause |
229   //                      private-clause |
230   //                      lastprivate-clause |
231   //                      reduction-clause |
232   //                      collapse-clause
233   case llvm::omp::Directive::OMPD_simd: {
234     SetContextAllowed(simdAllowedClauses);
235     SetContextAllowedOnce(simdAllowedOnceClauses);
236   } break;
237 
238   // 2.8.3 do-simd-clause -> do-clause |
239   //                         simd-clause
240   case llvm::omp::Directive::OMPD_do_simd: {
241     SetContextAllowed(doAllowedClauses | simdAllowedClauses);
242     SetContextAllowedOnce(doAllowedOnceClauses | simdAllowedOnceClauses);
243   } break;
244 
245   // 2.11.4 parallel-do-simd-clause -> parallel-clause |
246   //                                   do-simd-clause
247   case llvm::omp::Directive::OMPD_parallel_do_simd: {
248     SetContextAllowed(
249         parallelAllowedClauses | doAllowedClauses | simdAllowedClauses);
250     SetContextAllowedOnce(parallelAllowedOnceClauses | doAllowedOnceClauses |
251         simdAllowedOnceClauses);
252   } break;
253 
254   // 2.9.2 taskloop-clause -> if-clause |
255   //                          shared-clause |
256   //                          private-clause |
257   //                          firstprivate-clause |
258   //                          lastprivate-clause |
259   //                          default-clause |
260   //                          grainsize-clause |
261   //                          num-tasks-clause |
262   //                          collapse-clause |
263   //                          final-clause |
264   //                          priority-clause |
265   //                          untied-clause |
266   //                          mergeable-clause |
267   //                          nogroup-clause
268   case llvm::omp::Directive::OMPD_taskloop: {
269     SetContextAllowed(taskloopAllowedClauses);
270     SetContextAllowedOnce(taskloopAllowedOnceClauses);
271     SetContextAllowedExclusive(taskloopAllowedExclusiveClauses);
272   } break;
273 
274   // 2.9.3 taskloop-simd-clause -> taskloop-clause |
275   //                               simd-clause
276   case llvm::omp::Directive::OMPD_taskloop_simd: {
277     SetContextAllowed((taskloopAllowedClauses | simdAllowedClauses) -
278         llvm::omp::Clause::OMPC_reduction);
279     SetContextAllowedOnce(taskloopAllowedOnceClauses | simdAllowedOnceClauses);
280     SetContextAllowedExclusive(taskloopAllowedExclusiveClauses);
281   } break;
282 
283   // 2.10.8 distribute-clause -> private-clause |
284   //                             firstprivate-clause |
285   //                             lastprivate-clause |
286   //                             collapse-clause |
287   //                             dist-schedule-clause
288   case llvm::omp::Directive::OMPD_distribute: {
289     SetContextAllowed(distributeAllowedClauses);
290     SetContextAllowedOnce(distributeAllowedOnceClauses);
291   } break;
292 
293   // 2.10.9 distribute-simd-clause -> distribute-clause |
294   //                                  simd-clause
295   case llvm::omp::Directive::OMPD_distribute_simd: {
296     SetContextAllowed(distributeAllowedClauses | simdAllowedClauses);
297     SetContextAllowedOnce(
298         distributeAllowedOnceClauses | simdAllowedOnceClauses);
299   } break;
300 
301   // 2.10.10 distribute-parallel-do-clause -> distribute-clause |
302   //                                          parallel-do-clause
303   case llvm::omp::Directive::OMPD_distribute_parallel_do: {
304     SetContextAllowed(
305         distributeAllowedClauses | parallelAllowedClauses | doAllowedClauses);
306     SetContextAllowedOnce(distributeAllowedOnceClauses |
307         parallelAllowedOnceClauses | doAllowedOnceClauses);
308   } break;
309 
310   // 2.10.11 distribute-parallel-do-simd-clause -> distribute-clause |
311   //                                               parallel-do-simd-clause
312   case llvm::omp::Directive::OMPD_distribute_parallel_do_simd: {
313     SetContextAllowed(distributeAllowedClauses | parallelAllowedClauses |
314         doAllowedClauses | simdAllowedClauses);
315     SetContextAllowedOnce(distributeAllowedOnceClauses |
316         parallelAllowedOnceClauses | doAllowedOnceClauses | simdAllowedClauses);
317   } break;
318 
319   // 2.11.6 target-parallel-do-clause -> target-clause |
320   //                                     parallel-do-clause
321   case llvm::omp::Directive::OMPD_target_parallel_do: {
322     SetContextAllowed(
323         targetAllowedClauses | parallelAllowedClauses | doAllowedClauses);
324     SetContextAllowedOnce(
325         (targetAllowedOnceClauses | parallelAllowedOnceClauses |
326             doAllowedOnceClauses) -
327         llvm::omp::Clause::OMPC_nowait);
328   } break;
329 
330   // 2.11.7 target-parallel-do-simd-clause -> target-clause |
331   //                                          parallel-do-simd-clause
332   case llvm::omp::Directive::OMPD_target_parallel_do_simd: {
333     SetContextAllowed(targetAllowedClauses | parallelAllowedClauses |
334         doAllowedClauses | simdAllowedClauses);
335     SetContextAllowedOnce(
336         (targetAllowedOnceClauses | parallelAllowedOnceClauses |
337             doAllowedOnceClauses | simdAllowedOnceClauses) -
338         llvm::omp::Clause::OMPC_nowait);
339   } break;
340 
341   // 2.11.8 target-simd-clause -> target-clause |
342   //                              simd-clause
343   case llvm::omp::Directive::OMPD_target_simd: {
344     SetContextAllowed(targetAllowedClauses | simdAllowedClauses);
345     SetContextAllowedOnce(targetAllowedOnceClauses | simdAllowedOnceClauses);
346   } break;
347 
348   // 2.11.10 teams-distribute-clause -> teams-clause |
349   //                                    distribute-clause
350   case llvm::omp::Directive::OMPD_teams_distribute: {
351     SetContextAllowed(teamsAllowedClauses | distributeAllowedClauses);
352     SetContextAllowedOnce(
353         teamsAllowedOnceClauses | distributeAllowedOnceClauses);
354   } break;
355 
356   // 2.11.11 teams-distribute-simd-clause -> teams-clause |
357   //                                         distribute-simd-clause
358   case llvm::omp::Directive::OMPD_teams_distribute_simd: {
359     SetContextAllowed(
360         teamsAllowedClauses | distributeAllowedClauses | simdAllowedClauses);
361     SetContextAllowedOnce(teamsAllowedOnceClauses |
362         distributeAllowedOnceClauses | simdAllowedOnceClauses);
363   } break;
364 
365   // 2.11.12 target-teams-distribute-clause -> target-clause |
366   //                                           teams-distribute-clause
367   case llvm::omp::Directive::OMPD_target_teams_distribute: {
368     SetContextAllowed(
369         targetAllowedClauses | teamsAllowedClauses | distributeAllowedClauses);
370     SetContextAllowedOnce(targetAllowedOnceClauses | teamsAllowedOnceClauses |
371         distributeAllowedOnceClauses);
372   } break;
373 
374   // 2.11.13 target-teams-distribute-simd-clause -> target-clause |
375   //                                                teams-distribute-simd-clause
376   case llvm::omp::Directive::OMPD_target_teams_distribute_simd: {
377     SetContextAllowed(targetAllowedClauses | teamsAllowedClauses |
378         distributeAllowedClauses | simdAllowedClauses);
379     SetContextAllowed(targetAllowedOnceClauses | teamsAllowedOnceClauses |
380         distributeAllowedOnceClauses | simdAllowedOnceClauses);
381   } break;
382 
383   // 2.11.14 teams-distribute-parallel-do-clause -> teams-clause |
384   //                                                distribute-parallel-do-clause
385   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do: {
386     SetContextAllowed(teamsAllowedClauses | distributeAllowedClauses |
387         parallelAllowedClauses | doAllowedClauses);
388     SetContextAllowedOnce(teamsAllowedOnceClauses |
389         distributeAllowedOnceClauses | parallelAllowedOnceClauses |
390         doAllowedOnceClauses);
391   } break;
392 
393   // 2.11.15 target-teams-distribute-parallel-do-clause -> target-clause |
394   //                                                       teams-distribute-parallel-do-clause
395   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: {
396     SetContextAllowed(targetAllowedClauses | teamsAllowedClauses |
397         distributeAllowedClauses | parallelAllowedClauses | doAllowedClauses);
398     SetContextAllowedOnce(targetAllowedOnceClauses | teamsAllowedOnceClauses |
399         distributeAllowedOnceClauses | parallelAllowedOnceClauses |
400         doAllowedOnceClauses);
401   } break;
402 
403   // 2.11.16 teams-distribute-parallel-do-clause -> teams-clause |
404   //                                                distribute-parallel-do-simd-clause
405   case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd: {
406     SetContextAllowed(teamsAllowedClauses | distributeAllowedClauses |
407         parallelAllowedClauses | doAllowedClauses | simdAllowedClauses);
408     SetContextAllowedOnce(teamsAllowedOnceClauses |
409         distributeAllowedOnceClauses | parallelAllowedOnceClauses |
410         doAllowedOnceClauses | simdAllowedOnceClauses);
411   } break;
412 
413   case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: {
414     SetContextAllowed(targetAllowedClauses | teamsAllowedClauses |
415         distributeAllowedClauses | parallelAllowedClauses | doAllowedClauses |
416         simdAllowedClauses);
417     SetContextAllowedOnce(targetAllowedOnceClauses | teamsAllowedOnceClauses |
418         distributeAllowedOnceClauses | parallelAllowedOnceClauses |
419         doAllowedOnceClauses | simdAllowedOnceClauses);
420   } break;
421 
422   default:
423     // TODO others
424     break;
425   }
426 }
427 
428 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
429   ompContext_.pop_back();
430 }
431 
432 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
433   const auto &dir{std::get<parser::OmpLoopDirective>(x.t)};
434   ResetPartialContext(dir.source);
435   switch (dir.v) {
436   // 2.7.1 end-do -> END DO [nowait-clause]
437   // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
438   case llvm::omp::Directive::OMPD_do:
439     SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_do);
440     SetContextAllowed(OmpClauseSet{llvm::omp::Clause::OMPC_nowait});
441     break;
442   case llvm::omp::Directive::OMPD_do_simd:
443     SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_do_simd);
444     SetContextAllowed(OmpClauseSet{llvm::omp::Clause::OMPC_nowait});
445     break;
446   default:
447     // no clauses are allowed
448     break;
449   }
450 }
451 
452 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
453   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
454   const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)};
455   const auto &beginDir{
456       CheckMatching<parser::OmpBlockDirective>(beginBlockDir, endBlockDir)};
457 
458   PushContext(beginDir.source, beginDir.v);
459   switch (beginDir.v) {
460   // 2.5 parallel-clause -> if-clause |
461   //                        num-threads-clause |
462   //                        default-clause |
463   //                        private-clause |
464   //                        firstprivate-clause |
465   //                        shared-clause |
466   //                        copyin-clause |
467   //                        reduction-clause |
468   //                        proc-bind-clause
469   case llvm::omp::Directive::OMPD_parallel: {
470     // reserve for nesting check
471     SetContextAllowed(parallelAllowedClauses);
472     SetContextAllowedOnce(parallelAllowedOnceClauses);
473   } break;
474   // 2.7.3 single-clause -> private-clause |
475   //                        firstprivate-clause
476   case llvm::omp::Directive::OMPD_single:
477     SetContextAllowed({llvm::omp::Clause::OMPC_private,
478         llvm::omp::Clause::OMPC_firstprivate});
479     break;
480   // 2.7.4 workshare (no clauses are allowed)
481   case llvm::omp::Directive::OMPD_workshare:
482     break;
483   // 2.11.3 parallel-workshare-clause -> parallel-clause
484   case llvm::omp::Directive::OMPD_parallel_workshare: {
485     SetContextAllowed(parallelAllowedClauses);
486     SetContextAllowedOnce(parallelAllowedOnceClauses);
487   } break;
488     // 2.9.1 task-clause -> if-clause |
489     //                      final-clause |
490     //                      untied-clause |
491     //                      default-clause |
492     //                      mergeable-clause |
493     //                      private-clause |
494     //                      firstprivate-clause |
495     //                      shared-clause |
496     //                      depend-clause |
497     //                      priority-clause
498   case llvm::omp::Directive::OMPD_task: {
499     OmpClauseSet allowed{llvm::omp::Clause::OMPC_untied,
500         llvm::omp::Clause::OMPC_default, llvm::omp::Clause::OMPC_mergeable,
501         llvm::omp::Clause::OMPC_private, llvm::omp::Clause::OMPC_firstprivate,
502         llvm::omp::Clause::OMPC_shared, llvm::omp::Clause::OMPC_depend};
503     SetContextAllowed(allowed);
504     OmpClauseSet allowedOnce{llvm::omp::Clause::OMPC_if,
505         llvm::omp::Clause::OMPC_final, llvm::omp::Clause::OMPC_priority};
506     SetContextAllowedOnce(allowedOnce);
507   } break;
508   // 2.10.4 target-clause -> if-clause |
509   //                         device-clause |
510   //                         private-clause |
511   //                         firstprivate-clause |
512   //                         map-clause |
513   //                         is-device-ptr-clause |
514   //                         defaultmap-clause |
515   //                         nowait-clause |
516   //                         depend-clause
517   case llvm::omp::Directive::OMPD_target: {
518     SetContextAllowed(targetAllowedClauses);
519     SetContextAllowedOnce(targetAllowedOnceClauses);
520   } break;
521   // 2.10.7 teams-clause -> num-teams-clause |
522   //                        thread-limit-clause |
523   //                        default-clause |
524   //                        private-clause |
525   //                        firstprivate-clause |
526   //                        shared-clause |
527   //                        reduction-clause
528   case llvm::omp::Directive::OMPD_teams: {
529     SetContextAllowed(teamsAllowedClauses);
530     SetContextAllowedOnce(teamsAllowedOnceClauses);
531   } break;
532   // 2.11.9 target-teams -> target-clause |
533   //                        teams-clause
534   case llvm::omp::Directive::OMPD_target_teams: {
535     SetContextAllowed(targetAllowedClauses | teamsAllowedClauses);
536     SetContextAllowedOnce(targetAllowedOnceClauses | teamsAllowedOnceClauses);
537   } break;
538   // 2.10.1 target-data-clause -> if-clause |
539   //                              device-clause |
540   //                              map-clause |
541   //                              use-device-ptr-clause
542   case llvm::omp::Directive::OMPD_target_data: {
543     OmpClauseSet allowed{llvm::omp::Clause::OMPC_if,
544         llvm::omp::Clause::OMPC_map, llvm::omp::Clause::OMPC_use_device_ptr};
545     SetContextAllowed(allowed);
546     SetContextAllowedOnce({llvm::omp::Clause::OMPC_device});
547     SetContextRequired({llvm::omp::Clause::OMPC_map});
548   } break;
549   // 2.13.1 master (no clauses are allowed)
550   case llvm::omp::Directive::OMPD_master:
551     break;
552   // 2.11.5 target-parallel-clause -> target-clause |
553   //                                  parallel-clause
554   case llvm::omp::Directive::OMPD_target_parallel: {
555     SetContextAllowed((targetAllowedClauses | parallelAllowedClauses) -
556         llvm::omp::Clause::OMPC_copyin);
557     SetContextAllowedOnce(
558         targetAllowedOnceClauses | parallelAllowedOnceClauses);
559   } break;
560   default:
561     // TODO others
562     break;
563   }
564 }
565 
566 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
567   ompContext_.pop_back();
568 }
569 
570 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
571   const auto &beginSectionsDir{
572       std::get<parser::OmpBeginSectionsDirective>(x.t)};
573   const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)};
574   const auto &beginDir{CheckMatching<parser::OmpSectionsDirective>(
575       beginSectionsDir, endSectionsDir)};
576 
577   PushContext(beginDir.source, beginDir.v);
578   switch (beginDir.v) {
579   // 2.7.2 sections-clause -> private-clause |
580   //                          firstprivate-clause |
581   //                          lastprivate-clause |
582   //                          reduction-clause
583   case llvm::omp::Directive::OMPD_sections: {
584     SetContextAllowed(sectionsAllowedClauses);
585   } break;
586     // 2.11.2 -> parallel-sections-clause -> parallel-clause |
587     //                                       sections-clause
588   case llvm::omp::Directive::OMPD_parallel_sections: {
589     SetContextAllowed(parallelAllowedClauses | sectionsAllowedClauses);
590     SetContextAllowedOnce(parallelAllowedOnceClauses);
591   } break;
592   default:
593     // TODO others
594     break;
595   }
596 }
597 
598 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
599   ompContext_.pop_back();
600 }
601 
602 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
603   const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)};
604   ResetPartialContext(dir.source);
605   switch (dir.v) {
606     // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
607   case llvm::omp::Directive::OMPD_sections:
608     SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_sections);
609     SetContextAllowed(OmpClauseSet{llvm::omp::Clause::OMPC_nowait});
610     break;
611   default:
612     // no clauses are allowed
613     break;
614   }
615 }
616 
617 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
618   const auto &dir{std::get<parser::Verbatim>(x.t)};
619   PushContext(dir.source, llvm::omp::Directive::OMPD_declare_simd);
620   // 2.8.2 declare-simd-clause -> simdlen-clause |
621   //                              linear-clause |
622   //                              aligned-clause |
623   //                              uniform-clause |
624   //                              inbranch-clause |
625   //                              notinbranch-clause
626   OmpClauseSet allowed{llvm::omp::Clause::OMPC_linear,
627       llvm::omp::Clause::OMPC_aligned, llvm::omp::Clause::OMPC_uniform};
628   SetContextAllowed(allowed);
629   SetContextAllowedOnce({llvm::omp::Clause::OMPC_simdlen});
630   SetContextAllowedExclusive(
631       {llvm::omp::Clause::OMPC_inbranch, llvm::omp::Clause::OMPC_notinbranch});
632 }
633 
634 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
635   ompContext_.pop_back();
636 }
637 
638 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
639   const auto &dir{std::get<parser::Verbatim>(x.t)};
640   PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target);
641   const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
642   if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) {
643     SetContextAllowed(
644         OmpClauseSet{llvm::omp::Clause::OMPC_to, llvm::omp::Clause::OMPC_link});
645   }
646 }
647 
648 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) {
649   ompContext_.pop_back();
650 }
651 
652 void OmpStructureChecker::Enter(
653     const parser::OpenMPSimpleStandaloneConstruct &x) {
654   const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
655   PushContext(dir.source, dir.v);
656   switch (dir.v) {
657   case llvm::omp::Directive::OMPD_barrier: {
658     // 2.13.3 barrier
659   } break;
660   case llvm::omp::Directive::OMPD_taskwait: {
661     // 2.13.4 taskwait
662   } break;
663   case llvm::omp::Directive::OMPD_taskyield: {
664     // 2.9.4 taskyield
665   } break;
666   case llvm::omp::Directive::OMPD_target_enter_data: {
667     // 2.10.2 target-enter-data-clause -> if-clause |
668     //                                    device-clause |
669     //                                    map-clause |
670     //                                    depend-clause |
671     //                                    nowait-clause
672     OmpClauseSet allowed{llvm::omp::Clause::OMPC_map,
673         llvm::omp::Clause::OMPC_depend, llvm::omp::Clause::OMPC_nowait};
674     SetContextAllowed(allowed);
675     OmpClauseSet allowedOnce{
676         llvm::omp::Clause::OMPC_device, llvm::omp::Clause::OMPC_if};
677     SetContextAllowedOnce(allowedOnce);
678     SetContextRequired({llvm::omp::Clause::OMPC_map});
679   } break;
680   case llvm::omp::Directive::OMPD_target_exit_data: {
681     // 2.10.3  target-enter-data-clause -> if-clause |
682     //                                     device-clause |
683     //                                     map-clause |
684     //                                     depend-clause |
685     //                                     nowait-clause
686     OmpClauseSet allowed{llvm::omp::Clause::OMPC_map,
687         llvm::omp::Clause::OMPC_depend, llvm::omp::Clause::OMPC_nowait};
688     SetContextAllowed(allowed);
689     OmpClauseSet allowedOnce{
690         llvm::omp::Clause::OMPC_device, llvm::omp::Clause::OMPC_if};
691     SetContextAllowedOnce(allowedOnce);
692     SetContextRequired({llvm::omp::Clause::OMPC_map});
693   } break;
694   case llvm::omp::Directive::OMPD_target_update: {
695     // 2.10.5 target-update
696   } break;
697   case llvm::omp::Directive::OMPD_ordered: {
698     // 2.13.8 ordered-construct-clause -> depend-clause
699     OmpClauseSet allowed{llvm::omp::Clause::OMPC_depend};
700     SetContextAllowed(allowed);
701   } break;
702   default:
703     // TODO others
704     break;
705   }
706 }
707 
708 void OmpStructureChecker::Leave(
709     const parser::OpenMPSimpleStandaloneConstruct &) {
710   ompContext_.pop_back();
711 }
712 
713 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
714   const auto &dir{std::get<parser::Verbatim>(x.t)};
715   PushContext(dir.source, llvm::omp::Directive::OMPD_flush);
716 }
717 
718 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) {
719   ompContext_.pop_back();
720 }
721 
722 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
723   const auto &dir{std::get<parser::Verbatim>(x.t)};
724   PushContext(dir.source, llvm::omp::Directive::OMPD_cancel);
725 }
726 
727 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
728   ompContext_.pop_back();
729 }
730 
731 void OmpStructureChecker::Enter(
732     const parser::OpenMPCancellationPointConstruct &x) {
733   const auto &dir{std::get<parser::Verbatim>(x.t)};
734   PushContext(dir.source, llvm::omp::Directive::OMPD_cancellation_point);
735 }
736 
737 void OmpStructureChecker::Leave(
738     const parser::OpenMPCancellationPointConstruct &) {
739   ompContext_.pop_back();
740 }
741 
742 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
743   const auto &dir{std::get<parser::OmpBlockDirective>(x.t)};
744   ResetPartialContext(dir.source);
745   switch (dir.v) {
746   // 2.7.3 end-single-clause -> copyprivate-clause |
747   //                            nowait-clause
748   case llvm::omp::Directive::OMPD_single: {
749     SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_single);
750     OmpClauseSet allowed{llvm::omp::Clause::OMPC_copyprivate};
751     SetContextAllowed(allowed);
752     OmpClauseSet allowedOnce{llvm::omp::Clause::OMPC_nowait};
753     SetContextAllowedOnce(allowedOnce);
754   } break;
755   // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
756   case llvm::omp::Directive::OMPD_workshare:
757     SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_workshare);
758     SetContextAllowed(OmpClauseSet{llvm::omp::Clause::OMPC_nowait});
759     break;
760   default:
761     // no clauses are allowed
762     break;
763   }
764 }
765 
766 void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
767   // 2.7 Loop Construct Restriction
768   if (llvm::omp::doSet.test(GetContext().directive)) {
769     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) {
770       // only one schedule clause is allowed
771       const auto &schedClause{std::get<parser::OmpScheduleClause>(clause->u)};
772       if (ScheduleModifierHasType(schedClause,
773               parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
774         if (FindClause(llvm::omp::Clause::OMPC_ordered)) {
775           context_.Say(clause->source,
776               "The NONMONOTONIC modifier cannot be specified "
777               "if an ORDERED clause is specified"_err_en_US);
778         }
779         if (ScheduleModifierHasType(schedClause,
780                 parser::OmpScheduleModifierType::ModType::Monotonic)) {
781           context_.Say(clause->source,
782               "The MONOTONIC and NONMONOTONIC modifiers "
783               "cannot be both specified"_err_en_US);
784         }
785       }
786     }
787 
788     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) {
789       // only one ordered clause is allowed
790       const auto &orderedClause{
791           std::get<parser::OmpClause::Ordered>(clause->u)};
792 
793       if (orderedClause.v) {
794         if (FindClause(llvm::omp::Clause::OMPC_linear)) {
795           context_.Say(clause->source,
796               "A loop directive may not have both a LINEAR clause and "
797               "an ORDERED clause with a parameter"_err_en_US);
798         }
799 
800         if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) {
801           const auto &collapseClause{
802               std::get<parser::OmpClause::Collapse>(clause2->u)};
803           // ordered and collapse both have parameters
804           if (const auto orderedValue{GetIntValue(orderedClause.v)}) {
805             if (const auto collapseValue{GetIntValue(collapseClause.v)}) {
806               if (*orderedValue > 0 && *orderedValue < *collapseValue) {
807                 context_.Say(clause->source,
808                     "The parameter of the ORDERED clause must be "
809                     "greater than or equal to "
810                     "the parameter of the COLLAPSE clause"_err_en_US);
811               }
812             }
813           }
814         }
815       }
816 
817       // TODO: ordered region binding check (requires nesting implementation)
818     }
819   } // doSet
820 
821   // 2.8.1 Simd Construct Restriction
822   if (llvm::omp::simdSet.test(GetContext().directive)) {
823     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) {
824       if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) {
825         const auto &simdlenClause{
826             std::get<parser::OmpClause::Simdlen>(clause->u)};
827         const auto &safelenClause{
828             std::get<parser::OmpClause::Safelen>(clause2->u)};
829         // simdlen and safelen both have parameters
830         if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) {
831           if (const auto safelenValue{GetIntValue(safelenClause.v)}) {
832             if (*safelenValue > 0 && *simdlenValue > *safelenValue) {
833               context_.Say(clause->source,
834                   "The parameter of the SIMDLEN clause must be less than or "
835                   "equal to the parameter of the SAFELEN clause"_err_en_US);
836             }
837           }
838         }
839       }
840     }
841 
842     // TODO: A list-item cannot appear in more than one aligned clause
843   } // SIMD
844 
845   // 2.7.3 Single Construct Restriction
846   if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) {
847     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_copyprivate)}) {
848       if (FindClause(llvm::omp::Clause::OMPC_nowait)) {
849         context_.Say(clause->source,
850             "The COPYPRIVATE clause must not be used with "
851             "the NOWAIT clause"_err_en_US);
852       }
853     }
854   }
855 
856   GetContext().requiredClauses.IterateOverMembers(
857       [this](llvm::omp::Clause c) { CheckRequired(c); });
858 }
859 
860 void OmpStructureChecker::Enter(const parser::OmpClause &x) {
861   SetContextClause(x);
862 }
863 
864 void OmpStructureChecker::Enter(const parser::OmpNowait &) {
865   CheckAllowed(llvm::omp::Clause::OMPC_nowait);
866 }
867 void OmpStructureChecker::Enter(const parser::OmpClause::Inbranch &) {
868   CheckAllowed(llvm::omp::Clause::OMPC_inbranch);
869 }
870 void OmpStructureChecker::Enter(const parser::OmpClause::Mergeable &) {
871   CheckAllowed(llvm::omp::Clause::OMPC_mergeable);
872 }
873 void OmpStructureChecker::Enter(const parser::OmpClause::Nogroup &) {
874   CheckAllowed(llvm::omp::Clause::OMPC_nogroup);
875 }
876 void OmpStructureChecker::Enter(const parser::OmpClause::Notinbranch &) {
877   CheckAllowed(llvm::omp::Clause::OMPC_notinbranch);
878 }
879 void OmpStructureChecker::Enter(const parser::OmpClause::Untied &) {
880   CheckAllowed(llvm::omp::Clause::OMPC_untied);
881 }
882 
883 void OmpStructureChecker::Enter(const parser::OmpClause::Collapse &x) {
884   CheckAllowed(llvm::omp::Clause::OMPC_collapse);
885   // collapse clause must have a parameter
886   RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_collapse, x.v);
887 }
888 
889 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &) {
890   CheckAllowed(llvm::omp::Clause::OMPC_copyin);
891 }
892 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &) {
893   CheckAllowed(llvm::omp::Clause::OMPC_copyprivate);
894 }
895 void OmpStructureChecker::Enter(const parser::OmpClause::Device &) {
896   CheckAllowed(llvm::omp::Clause::OMPC_device);
897 }
898 void OmpStructureChecker::Enter(const parser::OmpClause::DistSchedule &) {
899   CheckAllowed(llvm::omp::Clause::OMPC_dist_schedule);
900 }
901 void OmpStructureChecker::Enter(const parser::OmpClause::Final &) {
902   CheckAllowed(llvm::omp::Clause::OMPC_final);
903 }
904 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &) {
905   CheckAllowed(llvm::omp::Clause::OMPC_firstprivate);
906 }
907 void OmpStructureChecker::Enter(const parser::OmpClause::From &) {
908   CheckAllowed(llvm::omp::Clause::OMPC_from);
909 }
910 void OmpStructureChecker::Enter(const parser::OmpClause::Grainsize &x) {
911   CheckAllowed(llvm::omp::Clause::OMPC_grainsize);
912   RequiresPositiveParameter(llvm::omp::Clause::OMPC_grainsize, x.v);
913 }
914 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &) {
915   CheckAllowed(llvm::omp::Clause::OMPC_lastprivate);
916 }
917 void OmpStructureChecker::Enter(const parser::OmpClause::NumTasks &x) {
918   CheckAllowed(llvm::omp::Clause::OMPC_num_tasks);
919   RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_tasks, x.v);
920 }
921 void OmpStructureChecker::Enter(const parser::OmpClause::NumTeams &x) {
922   CheckAllowed(llvm::omp::Clause::OMPC_num_teams);
923   RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_teams, x.v);
924 }
925 void OmpStructureChecker::Enter(const parser::OmpClause::NumThreads &x) {
926   CheckAllowed(llvm::omp::Clause::OMPC_num_threads);
927   RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_threads, x.v);
928   // if parameter is variable, defer to Expression Analysis
929 }
930 
931 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
932   CheckAllowed(llvm::omp::Clause::OMPC_ordered);
933   // the parameter of ordered clause is optional
934   if (const auto &expr{x.v}) {
935     RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr);
936 
937     // 2.8.3 Loop SIMD Construct Restriction
938     if (llvm::omp::doSimdSet.test(GetContext().directive)) {
939       context_.Say(GetContext().clauseSource,
940           "No ORDERED clause with a parameter can be specified "
941           "on the %s directive"_err_en_US,
942           ContextDirectiveAsFortran());
943     }
944   }
945 }
946 void OmpStructureChecker::Enter(const parser::OmpClause::Priority &x) {
947   CheckAllowed(llvm::omp::Clause::OMPC_priority);
948   RequiresPositiveParameter(llvm::omp::Clause::OMPC_priority, x.v);
949 }
950 void OmpStructureChecker::Enter(const parser::OmpClause::Private &) {
951   CheckAllowed(llvm::omp::Clause::OMPC_private);
952 }
953 void OmpStructureChecker::Enter(const parser::OmpClause::Safelen &x) {
954   CheckAllowed(llvm::omp::Clause::OMPC_safelen);
955   RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_safelen, x.v);
956 }
957 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &) {
958   CheckAllowed(llvm::omp::Clause::OMPC_shared);
959 }
960 void OmpStructureChecker::Enter(const parser::OmpClause::Simdlen &x) {
961   CheckAllowed(llvm::omp::Clause::OMPC_simdlen);
962   RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_simdlen, x.v);
963 }
964 void OmpStructureChecker::Enter(const parser::OmpClause::ThreadLimit &x) {
965   CheckAllowed(llvm::omp::Clause::OMPC_thread_limit);
966   RequiresPositiveParameter(llvm::omp::Clause::OMPC_thread_limit, x.v);
967 }
968 void OmpStructureChecker::Enter(const parser::OmpClause::To &) {
969   CheckAllowed(llvm::omp::Clause::OMPC_to);
970 }
971 void OmpStructureChecker::Enter(const parser::OmpClause::Link &) {
972   CheckAllowed(llvm::omp::Clause::OMPC_link);
973 }
974 void OmpStructureChecker::Enter(const parser::OmpClause::Uniform &) {
975   CheckAllowed(llvm::omp::Clause::OMPC_uniform);
976 }
977 void OmpStructureChecker::Enter(const parser::OmpClause::UseDevicePtr &) {
978   CheckAllowed(llvm::omp::Clause::OMPC_use_device_ptr);
979 }
980 void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr &) {
981   CheckAllowed(llvm::omp::Clause::OMPC_is_device_ptr);
982 }
983 
984 void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) {
985   CheckAllowed(llvm::omp::Clause::OMPC_aligned);
986 
987   if (const auto &expr{
988           std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) {
989     if (const auto v{GetIntValue(*expr)}) {
990       if (*v <= 0) {
991         context_.Say(GetContext().clauseSource,
992             "The ALIGNMENT parameter of the ALIGNED clause must be "
993             "a constant positive integer expression"_err_en_US);
994       }
995     }
996   }
997   // 2.8.1 TODO: list-item attribute check
998 }
999 void OmpStructureChecker::Enter(const parser::OmpDefaultClause &) {
1000   CheckAllowed(llvm::omp::Clause::OMPC_default);
1001 }
1002 void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) {
1003   CheckAllowed(llvm::omp::Clause::OMPC_defaultmap);
1004   using VariableCategory = parser::OmpDefaultmapClause::VariableCategory;
1005   if (!std::get<std::optional<VariableCategory>>(x.t)) {
1006     context_.Say(GetContext().clauseSource,
1007         "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP "
1008         "clause"_err_en_US);
1009   }
1010 }
1011 void OmpStructureChecker::Enter(const parser::OmpDependClause &) {
1012   CheckAllowed(llvm::omp::Clause::OMPC_depend);
1013 }
1014 
1015 void OmpStructureChecker::Enter(const parser::OmpIfClause &x) {
1016   CheckAllowed(llvm::omp::Clause::OMPC_if);
1017 
1018   using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier;
1019   static std::unordered_map<dirNameModifier, OmpDirectiveSet>
1020       dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet},
1021           {dirNameModifier::Target, llvm::omp::targetSet},
1022           {dirNameModifier::TargetEnterData,
1023               {llvm::omp::Directive::OMPD_target_enter_data}},
1024           {dirNameModifier::TargetExitData,
1025               {llvm::omp::Directive::OMPD_target_exit_data}},
1026           {dirNameModifier::TargetData,
1027               {llvm::omp::Directive::OMPD_target_data}},
1028           {dirNameModifier::TargetUpdate,
1029               {llvm::omp::Directive::OMPD_target_update}},
1030           {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}},
1031           {dirNameModifier::Taskloop, llvm::omp::taskloopSet}};
1032   if (const auto &directiveName{
1033           std::get<std::optional<dirNameModifier>>(x.t)}) {
1034     auto search{dirNameModifierMap.find(*directiveName)};
1035     if (search == dirNameModifierMap.end() ||
1036         !search->second.test(GetContext().directive)) {
1037       context_
1038           .Say(GetContext().clauseSource,
1039               "Unmatched directive name modifier %s on the IF clause"_err_en_US,
1040               parser::ToUpperCaseLetters(
1041                   parser::OmpIfClause::EnumToString(*directiveName)))
1042           .Attach(
1043               GetContext().directiveSource, "Cannot apply to directive"_en_US);
1044     }
1045   }
1046 }
1047 
1048 void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) {
1049   CheckAllowed(llvm::omp::Clause::OMPC_linear);
1050 
1051   // 2.7 Loop Construct Restriction
1052   if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) {
1053     if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) {
1054       context_.Say(GetContext().clauseSource,
1055           "A modifier may not be specified in a LINEAR clause "
1056           "on the %s directive"_err_en_US,
1057           ContextDirectiveAsFortran());
1058     }
1059   }
1060 }
1061 void OmpStructureChecker::Enter(const parser::OmpMapClause &x) {
1062   CheckAllowed(llvm::omp::Clause::OMPC_map);
1063   if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) {
1064     using Type = parser::OmpMapType::Type;
1065     const Type &type{std::get<Type>(maptype->t)};
1066     switch (GetContext().directive) {
1067     case llvm::omp::Directive::OMPD_target:
1068     case llvm::omp::Directive::OMPD_target_teams:
1069     case llvm::omp::Directive::OMPD_target_teams_distribute:
1070     case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
1071     case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
1072     case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
1073     case llvm::omp::Directive::OMPD_target_data: {
1074       if (type != Type::To && type != Type::From && type != Type::Tofrom &&
1075           type != Type::Alloc) {
1076         context_.Say(GetContext().clauseSource,
1077             "Only the TO, FROM, TOFROM, or ALLOC map types are permitted "
1078             "for MAP clauses on the %s directive"_err_en_US,
1079             ContextDirectiveAsFortran());
1080       }
1081     } break;
1082     case llvm::omp::Directive::OMPD_target_enter_data: {
1083       if (type != Type::To && type != Type::Alloc) {
1084         context_.Say(GetContext().clauseSource,
1085             "Only the TO or ALLOC map types are permitted "
1086             "for MAP clauses on the %s directive"_err_en_US,
1087             ContextDirectiveAsFortran());
1088       }
1089     } break;
1090     case llvm::omp::Directive::OMPD_target_exit_data: {
1091       if (type != Type::Delete && type != Type::Release && type != Type::From) {
1092         context_.Say(GetContext().clauseSource,
1093             "Only the FROM, RELEASE, or DELETE map types are permitted "
1094             "for MAP clauses on the %s directive"_err_en_US,
1095             ContextDirectiveAsFortran());
1096       }
1097     } break;
1098     default:
1099       break;
1100     }
1101   }
1102 }
1103 void OmpStructureChecker::Enter(const parser::OmpProcBindClause &) {
1104   CheckAllowed(llvm::omp::Clause::OMPC_proc_bind);
1105 }
1106 void OmpStructureChecker::Enter(const parser::OmpReductionClause &) {
1107   CheckAllowed(llvm::omp::Clause::OMPC_reduction);
1108 }
1109 
1110 bool OmpStructureChecker::ScheduleModifierHasType(
1111     const parser::OmpScheduleClause &x,
1112     const parser::OmpScheduleModifierType::ModType &type) {
1113   const auto &modifier{
1114       std::get<std::optional<parser::OmpScheduleModifier>>(x.t)};
1115   if (modifier) {
1116     const auto &modType1{
1117         std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)};
1118     const auto &modType2{
1119         std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(
1120             modifier->t)};
1121     if (modType1.v.v == type || (modType2 && modType2->v.v == type)) {
1122       return true;
1123     }
1124   }
1125   return false;
1126 }
1127 void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) {
1128   CheckAllowed(llvm::omp::Clause::OMPC_schedule);
1129 
1130   // 2.7 Loop Construct Restriction
1131   if (llvm::omp::doSet.test(GetContext().directive)) {
1132     const auto &kind{std::get<1>(x.t)};
1133     const auto &chunk{std::get<2>(x.t)};
1134     if (chunk) {
1135       if (kind == parser::OmpScheduleClause::ScheduleType::Runtime ||
1136           kind == parser::OmpScheduleClause::ScheduleType::Auto) {
1137         context_.Say(GetContext().clauseSource,
1138             "When SCHEDULE clause has %s specified, "
1139             "it must not have chunk size specified"_err_en_US,
1140             parser::ToUpperCaseLetters(
1141                 parser::OmpScheduleClause::EnumToString(kind)));
1142       }
1143     }
1144 
1145     if (ScheduleModifierHasType(
1146             x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
1147       if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic &&
1148           kind != parser::OmpScheduleClause::ScheduleType::Guided) {
1149         context_.Say(GetContext().clauseSource,
1150             "The NONMONOTONIC modifier can only be specified with "
1151             "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US);
1152       }
1153     }
1154   }
1155 }
1156 } // namespace Fortran::semantics
1157