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