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 
13 namespace Fortran::semantics {
14 
15 bool OmpStructureChecker::HasInvalidWorksharingNesting(
16     const parser::CharBlock &source, const OmpDirectiveSet &set) {
17   // set contains all the invalid closely nested directives
18   // for the given directive (`source` here)
19   if (CurrentDirectiveIsNested() && set.test(GetContext().directive)) {
20     context_.Say(source,
21         "A worksharing region may not be closely nested inside a "
22         "worksharing, explicit task, taskloop, critical, ordered, atomic, or "
23         "master region"_err_en_US);
24     return true;
25   }
26   return false;
27 }
28 
29 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) {
30   // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check
31 }
32 
33 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
34   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
35   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
36 
37   // check matching, End directive is optional
38   if (const auto &endLoopDir{
39           std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
40     const auto &endDir{
41         std::get<parser::OmpLoopDirective>(endLoopDir.value().t)};
42 
43     CheckMatching<parser::OmpLoopDirective>(beginDir, endDir);
44   }
45 
46   if (beginDir.v != llvm::omp::Directive::OMPD_do) {
47     PushContextAndClauseSets(beginDir.source, beginDir.v);
48   } else {
49     // 2.7.1 do-clause -> private-clause |
50     //                    firstprivate-clause |
51     //                    lastprivate-clause |
52     //                    linear-clause |
53     //                    reduction-clause |
54     //                    schedule-clause |
55     //                    collapse-clause |
56     //                    ordered-clause
57 
58     // nesting check
59     HasInvalidWorksharingNesting(beginDir.source,
60         {llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_sections,
61             llvm::omp::Directive::OMPD_single,
62             llvm::omp::Directive::OMPD_workshare,
63             llvm::omp::Directive::OMPD_task,
64             llvm::omp::Directive::OMPD_taskloop,
65             llvm::omp::Directive::OMPD_critical,
66             llvm::omp::Directive::OMPD_ordered,
67             llvm::omp::Directive::OMPD_atomic,
68             llvm::omp::Directive::OMPD_master});
69     PushContextAndClauseSets(beginDir.source, llvm::omp::Directive::OMPD_do);
70   }
71 }
72 
73 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
74   dirContext_.pop_back();
75 }
76 
77 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
78   const auto &dir{std::get<parser::OmpLoopDirective>(x.t)};
79   ResetPartialContext(dir.source);
80   switch (dir.v) {
81   // 2.7.1 end-do -> END DO [nowait-clause]
82   // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
83   case llvm::omp::Directive::OMPD_do:
84   case llvm::omp::Directive::OMPD_do_simd:
85     SetClauseSets(dir.v);
86     break;
87   default:
88     // no clauses are allowed
89     break;
90   }
91 }
92 
93 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
94   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
95   const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)};
96   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
97   const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)};
98   const parser::Block &block{std::get<parser::Block>(x.t)};
99 
100   CheckMatching<parser::OmpBlockDirective>(beginDir, endDir);
101 
102   PushContextAndClauseSets(beginDir.source, beginDir.v);
103 
104   switch (beginDir.v) {
105   case llvm::omp::OMPD_parallel:
106     CheckNoBranching(block, llvm::omp::OMPD_parallel, beginDir.source);
107     break;
108   default:
109     break;
110   }
111 }
112 
113 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
114   dirContext_.pop_back();
115 }
116 
117 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
118   const auto &beginSectionsDir{
119       std::get<parser::OmpBeginSectionsDirective>(x.t)};
120   const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)};
121   const auto &beginDir{
122       std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
123   const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)};
124   CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir);
125 
126   PushContextAndClauseSets(beginDir.source, beginDir.v);
127 }
128 
129 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
130   dirContext_.pop_back();
131 }
132 
133 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
134   const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)};
135   ResetPartialContext(dir.source);
136   switch (dir.v) {
137     // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
138   case llvm::omp::Directive::OMPD_sections:
139     SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_sections);
140     SetContextAllowedOnce(OmpClauseSet{llvm::omp::Clause::OMPC_nowait});
141     break;
142   default:
143     // no clauses are allowed
144     break;
145   }
146 }
147 
148 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
149   const auto &dir{std::get<parser::Verbatim>(x.t)};
150   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd);
151 }
152 
153 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
154   dirContext_.pop_back();
155 }
156 
157 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
158   const auto &dir{std::get<parser::Verbatim>(x.t)};
159   PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target);
160   const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
161   if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) {
162     SetContextAllowed(
163         OmpClauseSet{llvm::omp::Clause::OMPC_to, llvm::omp::Clause::OMPC_link});
164   }
165 }
166 
167 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) {
168   dirContext_.pop_back();
169 }
170 
171 void OmpStructureChecker::Enter(
172     const parser::OpenMPSimpleStandaloneConstruct &x) {
173   const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
174   PushContextAndClauseSets(dir.source, dir.v);
175 }
176 
177 void OmpStructureChecker::Leave(
178     const parser::OpenMPSimpleStandaloneConstruct &) {
179   dirContext_.pop_back();
180 }
181 
182 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
183   const auto &dir{std::get<parser::Verbatim>(x.t)};
184   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_flush);
185 }
186 
187 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) {
188   dirContext_.pop_back();
189 }
190 
191 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
192   const auto &dir{std::get<parser::Verbatim>(x.t)};
193   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel);
194 }
195 
196 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
197   dirContext_.pop_back();
198 }
199 
200 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) {
201   const auto &dir{std::get<parser::OmpCriticalDirective>(x.t)};
202   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_critical);
203 }
204 
205 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) {
206   dirContext_.pop_back();
207 }
208 
209 void OmpStructureChecker::Enter(
210     const parser::OpenMPCancellationPointConstruct &x) {
211   const auto &dir{std::get<parser::Verbatim>(x.t)};
212   PushContextAndClauseSets(
213       dir.source, llvm::omp::Directive::OMPD_cancellation_point);
214 }
215 
216 void OmpStructureChecker::Leave(
217     const parser::OpenMPCancellationPointConstruct &) {
218   dirContext_.pop_back();
219 }
220 
221 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
222   const auto &dir{std::get<parser::OmpBlockDirective>(x.t)};
223   ResetPartialContext(dir.source);
224   switch (dir.v) {
225   // 2.7.3 end-single-clause -> copyprivate-clause |
226   //                            nowait-clause
227   case llvm::omp::Directive::OMPD_single: {
228     SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_single);
229     OmpClauseSet allowed{llvm::omp::Clause::OMPC_copyprivate};
230     SetContextAllowed(allowed);
231     OmpClauseSet allowedOnce{llvm::omp::Clause::OMPC_nowait};
232     SetContextAllowedOnce(allowedOnce);
233   } break;
234   // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
235   case llvm::omp::Directive::OMPD_workshare:
236     SetContextDirectiveEnum(llvm::omp::Directive::OMPD_end_workshare);
237     SetContextAllowed(OmpClauseSet{llvm::omp::Clause::OMPC_nowait});
238     break;
239   default:
240     // no clauses are allowed
241     break;
242   }
243 }
244 
245 void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
246   // 2.7 Loop Construct Restriction
247   if (llvm::omp::doSet.test(GetContext().directive)) {
248     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) {
249       // only one schedule clause is allowed
250       const auto &schedClause{std::get<parser::OmpScheduleClause>(clause->u)};
251       if (ScheduleModifierHasType(schedClause,
252               parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
253         if (FindClause(llvm::omp::Clause::OMPC_ordered)) {
254           context_.Say(clause->source,
255               "The NONMONOTONIC modifier cannot be specified "
256               "if an ORDERED clause is specified"_err_en_US);
257         }
258         if (ScheduleModifierHasType(schedClause,
259                 parser::OmpScheduleModifierType::ModType::Monotonic)) {
260           context_.Say(clause->source,
261               "The MONOTONIC and NONMONOTONIC modifiers "
262               "cannot be both specified"_err_en_US);
263         }
264       }
265     }
266 
267     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) {
268       // only one ordered clause is allowed
269       const auto &orderedClause{
270           std::get<parser::OmpClause::Ordered>(clause->u)};
271 
272       if (orderedClause.v) {
273         if (FindClause(llvm::omp::Clause::OMPC_linear)) {
274           context_.Say(clause->source,
275               "A loop directive may not have both a LINEAR clause and "
276               "an ORDERED clause with a parameter"_err_en_US);
277         }
278 
279         if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) {
280           const auto &collapseClause{
281               std::get<parser::OmpClause::Collapse>(clause2->u)};
282           // ordered and collapse both have parameters
283           if (const auto orderedValue{GetIntValue(orderedClause.v)}) {
284             if (const auto collapseValue{GetIntValue(collapseClause.v)}) {
285               if (*orderedValue > 0 && *orderedValue < *collapseValue) {
286                 context_.Say(clause->source,
287                     "The parameter of the ORDERED clause must be "
288                     "greater than or equal to "
289                     "the parameter of the COLLAPSE clause"_err_en_US);
290               }
291             }
292           }
293         }
294       }
295 
296       // TODO: ordered region binding check (requires nesting implementation)
297     }
298   } // doSet
299 
300   // 2.8.1 Simd Construct Restriction
301   if (llvm::omp::simdSet.test(GetContext().directive)) {
302     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) {
303       if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) {
304         const auto &simdlenClause{
305             std::get<parser::OmpClause::Simdlen>(clause->u)};
306         const auto &safelenClause{
307             std::get<parser::OmpClause::Safelen>(clause2->u)};
308         // simdlen and safelen both have parameters
309         if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) {
310           if (const auto safelenValue{GetIntValue(safelenClause.v)}) {
311             if (*safelenValue > 0 && *simdlenValue > *safelenValue) {
312               context_.Say(clause->source,
313                   "The parameter of the SIMDLEN clause must be less than or "
314                   "equal to the parameter of the SAFELEN clause"_err_en_US);
315             }
316           }
317         }
318       }
319     }
320 
321     // TODO: A list-item cannot appear in more than one aligned clause
322   } // SIMD
323 
324   // 2.7.3 Single Construct Restriction
325   if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) {
326     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_copyprivate)}) {
327       if (FindClause(llvm::omp::Clause::OMPC_nowait)) {
328         context_.Say(clause->source,
329             "The COPYPRIVATE clause must not be used with "
330             "the NOWAIT clause"_err_en_US);
331       }
332     }
333   }
334 
335   GetContext().requiredClauses.IterateOverMembers(
336       [this](llvm::omp::Clause c) { CheckRequired(c); });
337 }
338 
339 void OmpStructureChecker::Enter(const parser::OmpClause &x) {
340   SetContextClause(x);
341 }
342 
343 void OmpStructureChecker::Enter(const parser::OmpNowait &) {
344   CheckAllowed(llvm::omp::Clause::OMPC_nowait);
345 }
346 void OmpStructureChecker::Enter(const parser::OmpClause::Inbranch &) {
347   CheckAllowed(llvm::omp::Clause::OMPC_inbranch);
348 }
349 void OmpStructureChecker::Enter(const parser::OmpClause::Mergeable &) {
350   CheckAllowed(llvm::omp::Clause::OMPC_mergeable);
351 }
352 void OmpStructureChecker::Enter(const parser::OmpClause::Nogroup &) {
353   CheckAllowed(llvm::omp::Clause::OMPC_nogroup);
354 }
355 void OmpStructureChecker::Enter(const parser::OmpClause::Notinbranch &) {
356   CheckAllowed(llvm::omp::Clause::OMPC_notinbranch);
357 }
358 void OmpStructureChecker::Enter(const parser::OmpClause::Untied &) {
359   CheckAllowed(llvm::omp::Clause::OMPC_untied);
360 }
361 
362 void OmpStructureChecker::Enter(const parser::OmpClause::Collapse &x) {
363   CheckAllowed(llvm::omp::Clause::OMPC_collapse);
364   // collapse clause must have a parameter
365   RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_collapse, x.v);
366 }
367 
368 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &) {
369   CheckAllowed(llvm::omp::Clause::OMPC_copyin);
370 }
371 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &) {
372   CheckAllowed(llvm::omp::Clause::OMPC_copyprivate);
373 }
374 void OmpStructureChecker::Enter(const parser::OmpClause::Device &) {
375   CheckAllowed(llvm::omp::Clause::OMPC_device);
376 }
377 void OmpStructureChecker::Enter(const parser::OmpDistScheduleClause &) {
378   CheckAllowed(llvm::omp::Clause::OMPC_dist_schedule);
379 }
380 void OmpStructureChecker::Enter(const parser::OmpClause::Final &) {
381   CheckAllowed(llvm::omp::Clause::OMPC_final);
382 }
383 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &) {
384   CheckAllowed(llvm::omp::Clause::OMPC_firstprivate);
385 }
386 void OmpStructureChecker::Enter(const parser::OmpClause::From &) {
387   CheckAllowed(llvm::omp::Clause::OMPC_from);
388 }
389 void OmpStructureChecker::Enter(const parser::OmpClause::Grainsize &x) {
390   CheckAllowed(llvm::omp::Clause::OMPC_grainsize);
391   RequiresPositiveParameter(llvm::omp::Clause::OMPC_grainsize, x.v);
392 }
393 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &) {
394   CheckAllowed(llvm::omp::Clause::OMPC_lastprivate);
395 }
396 void OmpStructureChecker::Enter(const parser::OmpClause::NumTasks &x) {
397   CheckAllowed(llvm::omp::Clause::OMPC_num_tasks);
398   RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_tasks, x.v);
399 }
400 void OmpStructureChecker::Enter(const parser::OmpClause::NumTeams &x) {
401   CheckAllowed(llvm::omp::Clause::OMPC_num_teams);
402   RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_teams, x.v);
403 }
404 void OmpStructureChecker::Enter(const parser::OmpClause::NumThreads &x) {
405   CheckAllowed(llvm::omp::Clause::OMPC_num_threads);
406   RequiresPositiveParameter(llvm::omp::Clause::OMPC_num_threads, x.v);
407   // if parameter is variable, defer to Expression Analysis
408 }
409 
410 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
411   CheckAllowed(llvm::omp::Clause::OMPC_ordered);
412   // the parameter of ordered clause is optional
413   if (const auto &expr{x.v}) {
414     RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr);
415 
416     // 2.8.3 Loop SIMD Construct Restriction
417     if (llvm::omp::doSimdSet.test(GetContext().directive)) {
418       context_.Say(GetContext().clauseSource,
419           "No ORDERED clause with a parameter can be specified "
420           "on the %s directive"_err_en_US,
421           ContextDirectiveAsFortran());
422     }
423   }
424 }
425 void OmpStructureChecker::Enter(const parser::OmpClause::Priority &x) {
426   CheckAllowed(llvm::omp::Clause::OMPC_priority);
427   RequiresPositiveParameter(llvm::omp::Clause::OMPC_priority, x.v);
428 }
429 void OmpStructureChecker::Enter(const parser::OmpClause::Private &) {
430   CheckAllowed(llvm::omp::Clause::OMPC_private);
431 }
432 void OmpStructureChecker::Enter(const parser::OmpClause::Safelen &x) {
433   CheckAllowed(llvm::omp::Clause::OMPC_safelen);
434   RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_safelen, x.v);
435 }
436 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &) {
437   CheckAllowed(llvm::omp::Clause::OMPC_shared);
438 }
439 void OmpStructureChecker::Enter(const parser::OmpClause::Simdlen &x) {
440   CheckAllowed(llvm::omp::Clause::OMPC_simdlen);
441   RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_simdlen, x.v);
442 }
443 void OmpStructureChecker::Enter(const parser::OmpClause::ThreadLimit &x) {
444   CheckAllowed(llvm::omp::Clause::OMPC_thread_limit);
445   RequiresPositiveParameter(llvm::omp::Clause::OMPC_thread_limit, x.v);
446 }
447 void OmpStructureChecker::Enter(const parser::OmpClause::To &) {
448   CheckAllowed(llvm::omp::Clause::OMPC_to);
449 }
450 void OmpStructureChecker::Enter(const parser::OmpClause::Link &) {
451   CheckAllowed(llvm::omp::Clause::OMPC_link);
452 }
453 void OmpStructureChecker::Enter(const parser::OmpClause::Uniform &) {
454   CheckAllowed(llvm::omp::Clause::OMPC_uniform);
455 }
456 void OmpStructureChecker::Enter(const parser::OmpClause::UseDevicePtr &) {
457   CheckAllowed(llvm::omp::Clause::OMPC_use_device_ptr);
458 }
459 void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr &) {
460   CheckAllowed(llvm::omp::Clause::OMPC_is_device_ptr);
461 }
462 
463 void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) {
464   CheckAllowed(llvm::omp::Clause::OMPC_aligned);
465 
466   if (const auto &expr{
467           std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) {
468     if (const auto v{GetIntValue(*expr)}) {
469       if (*v <= 0) {
470         context_.Say(GetContext().clauseSource,
471             "The ALIGNMENT parameter of the ALIGNED clause must be "
472             "a constant positive integer expression"_err_en_US);
473       }
474     }
475   }
476   // 2.8.1 TODO: list-item attribute check
477 }
478 void OmpStructureChecker::Enter(const parser::OmpAllocateClause &) {
479   CheckAllowed(llvm::omp::Clause::OMPC_allocate);
480 }
481 void OmpStructureChecker::Enter(const parser::OmpDefaultClause &) {
482   CheckAllowed(llvm::omp::Clause::OMPC_default);
483 }
484 void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) {
485   CheckAllowed(llvm::omp::Clause::OMPC_defaultmap);
486   using VariableCategory = parser::OmpDefaultmapClause::VariableCategory;
487   if (!std::get<std::optional<VariableCategory>>(x.t)) {
488     context_.Say(GetContext().clauseSource,
489         "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP "
490         "clause"_err_en_US);
491   }
492 }
493 void OmpStructureChecker::Enter(const parser::OmpDependClause &) {
494   CheckAllowed(llvm::omp::Clause::OMPC_depend);
495 }
496 
497 void OmpStructureChecker::Enter(const parser::OmpIfClause &x) {
498   CheckAllowed(llvm::omp::Clause::OMPC_if);
499 
500   using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier;
501   static std::unordered_map<dirNameModifier, OmpDirectiveSet>
502       dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet},
503           {dirNameModifier::Target, llvm::omp::targetSet},
504           {dirNameModifier::TargetEnterData,
505               {llvm::omp::Directive::OMPD_target_enter_data}},
506           {dirNameModifier::TargetExitData,
507               {llvm::omp::Directive::OMPD_target_exit_data}},
508           {dirNameModifier::TargetData,
509               {llvm::omp::Directive::OMPD_target_data}},
510           {dirNameModifier::TargetUpdate,
511               {llvm::omp::Directive::OMPD_target_update}},
512           {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}},
513           {dirNameModifier::Taskloop, llvm::omp::taskloopSet}};
514   if (const auto &directiveName{
515           std::get<std::optional<dirNameModifier>>(x.t)}) {
516     auto search{dirNameModifierMap.find(*directiveName)};
517     if (search == dirNameModifierMap.end() ||
518         !search->second.test(GetContext().directive)) {
519       context_
520           .Say(GetContext().clauseSource,
521               "Unmatched directive name modifier %s on the IF clause"_err_en_US,
522               parser::ToUpperCaseLetters(
523                   parser::OmpIfClause::EnumToString(*directiveName)))
524           .Attach(
525               GetContext().directiveSource, "Cannot apply to directive"_en_US);
526     }
527   }
528 }
529 
530 void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) {
531   CheckAllowed(llvm::omp::Clause::OMPC_linear);
532 
533   // 2.7 Loop Construct Restriction
534   if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) {
535     if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) {
536       context_.Say(GetContext().clauseSource,
537           "A modifier may not be specified in a LINEAR clause "
538           "on the %s directive"_err_en_US,
539           ContextDirectiveAsFortran());
540     }
541   }
542 }
543 void OmpStructureChecker::Enter(const parser::OmpMapClause &x) {
544   CheckAllowed(llvm::omp::Clause::OMPC_map);
545   if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) {
546     using Type = parser::OmpMapType::Type;
547     const Type &type{std::get<Type>(maptype->t)};
548     switch (GetContext().directive) {
549     case llvm::omp::Directive::OMPD_target:
550     case llvm::omp::Directive::OMPD_target_teams:
551     case llvm::omp::Directive::OMPD_target_teams_distribute:
552     case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
553     case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
554     case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
555     case llvm::omp::Directive::OMPD_target_data: {
556       if (type != Type::To && type != Type::From && type != Type::Tofrom &&
557           type != Type::Alloc) {
558         context_.Say(GetContext().clauseSource,
559             "Only the TO, FROM, TOFROM, or ALLOC map types are permitted "
560             "for MAP clauses on the %s directive"_err_en_US,
561             ContextDirectiveAsFortran());
562       }
563     } break;
564     case llvm::omp::Directive::OMPD_target_enter_data: {
565       if (type != Type::To && type != Type::Alloc) {
566         context_.Say(GetContext().clauseSource,
567             "Only the TO or ALLOC map types are permitted "
568             "for MAP clauses on the %s directive"_err_en_US,
569             ContextDirectiveAsFortran());
570       }
571     } break;
572     case llvm::omp::Directive::OMPD_target_exit_data: {
573       if (type != Type::Delete && type != Type::Release && type != Type::From) {
574         context_.Say(GetContext().clauseSource,
575             "Only the FROM, RELEASE, or DELETE map types are permitted "
576             "for MAP clauses on the %s directive"_err_en_US,
577             ContextDirectiveAsFortran());
578       }
579     } break;
580     default:
581       break;
582     }
583   }
584 }
585 void OmpStructureChecker::Enter(const parser::OmpProcBindClause &) {
586   CheckAllowed(llvm::omp::Clause::OMPC_proc_bind);
587 }
588 void OmpStructureChecker::Enter(const parser::OmpReductionClause &) {
589   CheckAllowed(llvm::omp::Clause::OMPC_reduction);
590 }
591 
592 bool OmpStructureChecker::ScheduleModifierHasType(
593     const parser::OmpScheduleClause &x,
594     const parser::OmpScheduleModifierType::ModType &type) {
595   const auto &modifier{
596       std::get<std::optional<parser::OmpScheduleModifier>>(x.t)};
597   if (modifier) {
598     const auto &modType1{
599         std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)};
600     const auto &modType2{
601         std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(
602             modifier->t)};
603     if (modType1.v.v == type || (modType2 && modType2->v.v == type)) {
604       return true;
605     }
606   }
607   return false;
608 }
609 void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) {
610   CheckAllowed(llvm::omp::Clause::OMPC_schedule);
611 
612   // 2.7 Loop Construct Restriction
613   if (llvm::omp::doSet.test(GetContext().directive)) {
614     const auto &kind{std::get<1>(x.t)};
615     const auto &chunk{std::get<2>(x.t)};
616     if (chunk) {
617       if (kind == parser::OmpScheduleClause::ScheduleType::Runtime ||
618           kind == parser::OmpScheduleClause::ScheduleType::Auto) {
619         context_.Say(GetContext().clauseSource,
620             "When SCHEDULE clause has %s specified, "
621             "it must not have chunk size specified"_err_en_US,
622             parser::ToUpperCaseLetters(
623                 parser::OmpScheduleClause::EnumToString(kind)));
624       }
625     }
626 
627     if (ScheduleModifierHasType(
628             x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
629       if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic &&
630           kind != parser::OmpScheduleClause::ScheduleType::Guided) {
631         context_.Say(GetContext().clauseSource,
632             "The NONMONOTONIC modifier can only be specified with "
633             "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US);
634       }
635     }
636   }
637 }
638 
639 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
640   return llvm::omp::getOpenMPClauseName(clause);
641 }
642 
643 llvm::StringRef OmpStructureChecker::getDirectiveName(
644     llvm::omp::Directive directive) {
645   return llvm::omp::getOpenMPDirectiveName(directive);
646 }
647 
648 } // namespace Fortran::semantics
649