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 <algorithm>
13 
14 namespace Fortran::semantics {
15 
16 // Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
17 #define CHECK_SIMPLE_CLAUSE(X, Y) \
18   void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
19     CheckAllowed(llvm::omp::Clause::Y); \
20   }
21 
22 #define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \
23   void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
24     CheckAllowed(llvm::omp::Clause::Y); \
25     RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \
26   }
27 
28 #define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \
29   void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
30     CheckAllowed(llvm::omp::Clause::Y); \
31     RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \
32   }
33 
34 // Use when clause don't falls under 'struct OmpClause' in 'parse-tree.h'.
35 #define CHECK_SIMPLE_PARSER_CLAUSE(X, Y) \
36   void OmpStructureChecker::Enter(const parser::X &) { \
37     CheckAllowed(llvm::omp::Y); \
38   }
39 
40 // 'OmpWorkshareBlockChecker' is used to check the validity of the assignment
41 // statements and the expressions enclosed in an OpenMP Workshare construct
42 class OmpWorkshareBlockChecker {
43 public:
44   OmpWorkshareBlockChecker(SemanticsContext &context, parser::CharBlock source)
45       : context_{context}, source_{source} {}
46 
47   template <typename T> bool Pre(const T &) { return true; }
48   template <typename T> void Post(const T &) {}
49 
50   bool Pre(const parser::AssignmentStmt &assignment) {
51     const auto &var{std::get<parser::Variable>(assignment.t)};
52     const auto &expr{std::get<parser::Expr>(assignment.t)};
53     const auto *lhs{GetExpr(var)};
54     const auto *rhs{GetExpr(expr)};
55     Tristate isDefined{semantics::IsDefinedAssignment(
56         lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())};
57     if (isDefined == Tristate::Yes) {
58       context_.Say(expr.source,
59           "Defined assignment statement is not "
60           "allowed in a WORKSHARE construct"_err_en_US);
61     }
62     return true;
63   }
64 
65   bool Pre(const parser::Expr &expr) {
66     if (const auto *e{GetExpr(expr)}) {
67       for (const Symbol &symbol : evaluate::CollectSymbols(*e)) {
68         const Symbol &root{GetAssociationRoot(symbol)};
69         if (IsFunction(root) &&
70             !(root.attrs().test(Attr::ELEMENTAL) ||
71                 root.attrs().test(Attr::INTRINSIC))) {
72           context_.Say(expr.source,
73               "User defined non-ELEMENTAL function "
74               "'%s' is not allowed in a WORKSHARE construct"_err_en_US,
75               root.name());
76         }
77       }
78     }
79     return false;
80   }
81 
82 private:
83   SemanticsContext &context_;
84   parser::CharBlock source_;
85 };
86 
87 class OmpCycleChecker {
88 public:
89   OmpCycleChecker(SemanticsContext &context, std::int64_t cycleLevel)
90       : context_{context}, cycleLevel_{cycleLevel} {}
91 
92   template <typename T> bool Pre(const T &) { return true; }
93   template <typename T> void Post(const T &) {}
94 
95   bool Pre(const parser::DoConstruct &dc) {
96     cycleLevel_--;
97     const auto &labelName{std::get<0>(std::get<0>(dc.t).statement.t)};
98     if (labelName) {
99       labelNamesandLevels_.emplace(labelName.value().ToString(), cycleLevel_);
100     }
101     return true;
102   }
103 
104   bool Pre(const parser::CycleStmt &cyclestmt) {
105     std::map<std::string, std::int64_t>::iterator it;
106     bool err{false};
107     if (cyclestmt.v) {
108       it = labelNamesandLevels_.find(cyclestmt.v->source.ToString());
109       err = (it != labelNamesandLevels_.end() && it->second > 0);
110     }
111     if (cycleLevel_ > 0 || err) {
112       context_.Say(*cycleSource_,
113           "CYCLE statement to non-innermost associated loop of an OpenMP DO construct"_err_en_US);
114     }
115     return true;
116   }
117 
118   bool Pre(const parser::Statement<parser::ActionStmt> &actionstmt) {
119     cycleSource_ = &actionstmt.source;
120     return true;
121   }
122 
123 private:
124   SemanticsContext &context_;
125   const parser::CharBlock *cycleSource_;
126   std::int64_t cycleLevel_;
127   std::map<std::string, std::int64_t> labelNamesandLevels_;
128 };
129 
130 bool OmpStructureChecker::HasInvalidWorksharingNesting(
131     const parser::CharBlock &source, const OmpDirectiveSet &set) {
132   // set contains all the invalid closely nested directives
133   // for the given directive (`source` here)
134   if (CurrentDirectiveIsNested() && set.test(GetContextParent().directive)) {
135     context_.Say(source,
136         "A worksharing region may not be closely nested inside a "
137         "worksharing, explicit task, taskloop, critical, ordered, atomic, or "
138         "master region"_err_en_US);
139     return true;
140   }
141   return false;
142 }
143 
144 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) {
145   // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check
146 }
147 
148 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
149   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
150   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
151 
152   // check matching, End directive is optional
153   if (const auto &endLoopDir{
154           std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
155     const auto &endDir{
156         std::get<parser::OmpLoopDirective>(endLoopDir.value().t)};
157 
158     CheckMatching<parser::OmpLoopDirective>(beginDir, endDir);
159   }
160 
161   PushContextAndClauseSets(beginDir.source, beginDir.v);
162 
163   if (beginDir.v == llvm::omp::Directive::OMPD_do) {
164     // 2.7.1 do-clause -> private-clause |
165     //                    firstprivate-clause |
166     //                    lastprivate-clause |
167     //                    linear-clause |
168     //                    reduction-clause |
169     //                    schedule-clause |
170     //                    collapse-clause |
171     //                    ordered-clause
172 
173     // nesting check
174     HasInvalidWorksharingNesting(beginDir.source,
175         {llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_sections,
176             llvm::omp::Directive::OMPD_single,
177             llvm::omp::Directive::OMPD_workshare,
178             llvm::omp::Directive::OMPD_task,
179             llvm::omp::Directive::OMPD_taskloop,
180             llvm::omp::Directive::OMPD_critical,
181             llvm::omp::Directive::OMPD_ordered,
182             llvm::omp::Directive::OMPD_atomic,
183             llvm::omp::Directive::OMPD_master});
184   }
185   SetLoopInfo(x);
186 
187   if (const auto &doConstruct{
188           std::get<std::optional<parser::DoConstruct>>(x.t)}) {
189     const auto &doBlock{std::get<parser::Block>(doConstruct->t)};
190     CheckNoBranching(doBlock, beginDir.v, beginDir.source);
191   }
192   CheckDoWhile(x);
193   CheckLoopItrVariableIsInt(x);
194   CheckCycleConstraints(x);
195 }
196 const parser::Name OmpStructureChecker::GetLoopIndex(
197     const parser::DoConstruct *x) {
198   using Bounds = parser::LoopControl::Bounds;
199   return std::get<Bounds>(x->GetLoopControl()->u).name.thing;
200 }
201 void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) {
202   if (const auto &loopConstruct{
203           std::get<std::optional<parser::DoConstruct>>(x.t)}) {
204     const parser::DoConstruct *loop{&*loopConstruct};
205     if (loop && loop->IsDoNormal()) {
206       const parser::Name &itrVal{GetLoopIndex(loop)};
207       SetLoopIv(itrVal.symbol);
208     }
209   }
210 }
211 void OmpStructureChecker::CheckDoWhile(const parser::OpenMPLoopConstruct &x) {
212   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
213   const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
214   if (beginDir.v == llvm::omp::Directive::OMPD_do) {
215     if (const auto &doConstruct{
216             std::get<std::optional<parser::DoConstruct>>(x.t)}) {
217       if (doConstruct.value().IsDoWhile()) {
218         const auto &doStmt{std::get<parser::Statement<parser::NonLabelDoStmt>>(
219             doConstruct.value().t)};
220         context_.Say(doStmt.source,
221             "The DO loop cannot be a DO WHILE with DO directive."_err_en_US);
222       }
223     }
224   }
225 }
226 
227 void OmpStructureChecker::CheckLoopItrVariableIsInt(
228     const parser::OpenMPLoopConstruct &x) {
229   if (const auto &loopConstruct{
230           std::get<std::optional<parser::DoConstruct>>(x.t)}) {
231 
232     for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
233       if (loop->IsDoNormal()) {
234         const parser::Name &itrVal{GetLoopIndex(loop)};
235         if (itrVal.symbol) {
236           const auto *type{itrVal.symbol->GetType()};
237           if (!type->IsNumeric(TypeCategory::Integer)) {
238             context_.Say(itrVal.source,
239                 "The DO loop iteration"
240                 " variable must be of the type integer."_err_en_US,
241                 itrVal.ToString());
242           }
243         }
244       }
245       // Get the next DoConstruct if block is not empty.
246       const auto &block{std::get<parser::Block>(loop->t)};
247       const auto it{block.begin()};
248       loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
249                                : nullptr;
250     }
251   }
252 }
253 
254 std::int64_t OmpStructureChecker::GetOrdCollapseLevel(
255     const parser::OpenMPLoopConstruct &x) {
256   const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
257   const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
258   std::int64_t orderedCollapseLevel{1};
259   std::int64_t orderedLevel{0};
260   std::int64_t collapseLevel{0};
261 
262   for (const auto &clause : clauseList.v) {
263     if (const auto *collapseClause{
264             std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
265       if (const auto v{GetIntValue(collapseClause->v)}) {
266         collapseLevel = *v;
267       }
268     }
269     if (const auto *orderedClause{
270             std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
271       if (const auto v{GetIntValue(orderedClause->v)}) {
272         orderedLevel = *v;
273       }
274     }
275   }
276   if (orderedLevel >= collapseLevel) {
277     orderedCollapseLevel = orderedLevel;
278   } else {
279     orderedCollapseLevel = collapseLevel;
280   }
281   return orderedCollapseLevel;
282 }
283 
284 void OmpStructureChecker::CheckCycleConstraints(
285     const parser::OpenMPLoopConstruct &x) {
286   std::int64_t ordCollapseLevel{GetOrdCollapseLevel(x)};
287   OmpCycleChecker ompCycleChecker{context_, ordCollapseLevel};
288   parser::Walk(x, ompCycleChecker);
289 }
290 
291 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
292   dirContext_.pop_back();
293 }
294 
295 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
296   const auto &dir{std::get<parser::OmpLoopDirective>(x.t)};
297   ResetPartialContext(dir.source);
298   switch (dir.v) {
299   // 2.7.1 end-do -> END DO [nowait-clause]
300   // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
301   case llvm::omp::Directive::OMPD_do:
302   case llvm::omp::Directive::OMPD_do_simd:
303     SetClauseSets(dir.v);
304     break;
305   default:
306     // no clauses are allowed
307     break;
308   }
309 }
310 
311 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
312   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
313   const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)};
314   const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
315   const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)};
316   const parser::Block &block{std::get<parser::Block>(x.t)};
317 
318   CheckMatching<parser::OmpBlockDirective>(beginDir, endDir);
319 
320   PushContextAndClauseSets(beginDir.source, beginDir.v);
321 
322   // TODO: This check needs to be extended while implementing nesting of regions
323   // checks.
324   if (beginDir.v == llvm::omp::Directive::OMPD_single) {
325     HasInvalidWorksharingNesting(
326         beginDir.source, {llvm::omp::Directive::OMPD_do});
327   }
328   if (CurrentDirectiveIsNested())
329     CheckIfDoOrderedClause(beginDir);
330 
331   CheckNoBranching(block, beginDir.v, beginDir.source);
332 
333   switch (beginDir.v) {
334   case llvm::omp::OMPD_workshare:
335   case llvm::omp::OMPD_parallel_workshare:
336     CheckWorkshareBlockStmts(block, beginDir.source);
337     break;
338   default:
339     break;
340   }
341 }
342 
343 void OmpStructureChecker::CheckIfDoOrderedClause(
344     const parser::OmpBlockDirective &blkDirective) {
345   if (blkDirective.v == llvm::omp::OMPD_ordered) {
346     if (!FindClauseParent(llvm::omp::Clause::OMPC_ordered)) {
347       context_.Say(blkDirective.source,
348           "The ORDERED clause must be present on the loop"
349           " construct if any ORDERED region ever binds"
350           " to a loop region arising from the loop construct."_err_en_US);
351     }
352   }
353 }
354 
355 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
356   dirContext_.pop_back();
357 }
358 
359 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
360   const auto &beginSectionsDir{
361       std::get<parser::OmpBeginSectionsDirective>(x.t)};
362   const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)};
363   const auto &beginDir{
364       std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
365   const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)};
366   CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir);
367 
368   PushContextAndClauseSets(beginDir.source, beginDir.v);
369   const auto &sectionBlocks{std::get<parser::OmpSectionBlocks>(x.t)};
370   for (const auto &block : sectionBlocks.v) {
371     CheckNoBranching(block, beginDir.v, beginDir.source);
372   }
373 }
374 
375 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
376   dirContext_.pop_back();
377 }
378 
379 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
380   const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)};
381   ResetPartialContext(dir.source);
382   switch (dir.v) {
383     // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
384   case llvm::omp::Directive::OMPD_sections:
385     PushContextAndClauseSets(
386         dir.source, llvm::omp::Directive::OMPD_end_sections);
387     break;
388   default:
389     // no clauses are allowed
390     break;
391   }
392 }
393 
394 // TODO: Verify the popping of dirContext requirement after nowait
395 // implementation, as there is an implicit barrier at the end of the worksharing
396 // constructs unless a nowait clause is specified. Only OMPD_end_sections is
397 // popped becuase it is pushed while entering the EndSectionsDirective.
398 void OmpStructureChecker::Leave(const parser::OmpEndSectionsDirective &x) {
399   if (GetContext().directive == llvm::omp::Directive::OMPD_end_sections) {
400     dirContext_.pop_back();
401   }
402 }
403 
404 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
405   const auto &dir{std::get<parser::Verbatim>(x.t)};
406   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd);
407 }
408 
409 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
410   dirContext_.pop_back();
411 }
412 
413 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
414   const auto &dir{std::get<parser::Verbatim>(x.t)};
415   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
416 }
417 
418 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &) {
419   dirContext_.pop_back();
420 }
421 
422 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
423   const auto &dir{std::get<parser::Verbatim>(x.t)};
424   PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target);
425   const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
426   if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) {
427     SetClauseSets(llvm::omp::Directive::OMPD_declare_target);
428   }
429 }
430 
431 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) {
432   dirContext_.pop_back();
433 }
434 
435 void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
436   const auto &dir{std::get<parser::Verbatim>(x.t)};
437   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
438 }
439 
440 void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &) {
441   dirContext_.pop_back();
442 }
443 
444 void OmpStructureChecker::Enter(
445     const parser::OpenMPSimpleStandaloneConstruct &x) {
446   const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
447   PushContextAndClauseSets(dir.source, dir.v);
448 }
449 
450 void OmpStructureChecker::Leave(
451     const parser::OpenMPSimpleStandaloneConstruct &) {
452   dirContext_.pop_back();
453 }
454 
455 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
456   const auto &dir{std::get<parser::Verbatim>(x.t)};
457   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_flush);
458 }
459 
460 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
461   if (FindClause(llvm::omp::Clause::OMPC_acquire) ||
462       FindClause(llvm::omp::Clause::OMPC_release) ||
463       FindClause(llvm::omp::Clause::OMPC_acq_rel)) {
464     if (const auto &flushList{
465             std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
466       context_.Say(parser::FindSourceLocation(flushList),
467           "If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items "
468           "must not be specified on the FLUSH directive"_err_en_US);
469     }
470   }
471   dirContext_.pop_back();
472 }
473 
474 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
475   const auto &dir{std::get<parser::Verbatim>(x.t)};
476   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel);
477 }
478 
479 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
480   dirContext_.pop_back();
481 }
482 
483 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) {
484   const auto &dir{std::get<parser::OmpCriticalDirective>(x.t)};
485   PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_critical);
486   const auto &block{std::get<parser::Block>(x.t)};
487   CheckNoBranching(block, llvm::omp::Directive::OMPD_critical, dir.source);
488 }
489 
490 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) {
491   dirContext_.pop_back();
492 }
493 
494 void OmpStructureChecker::Enter(
495     const parser::OpenMPCancellationPointConstruct &x) {
496   const auto &dir{std::get<parser::Verbatim>(x.t)};
497   PushContextAndClauseSets(
498       dir.source, llvm::omp::Directive::OMPD_cancellation_point);
499 }
500 
501 void OmpStructureChecker::Leave(
502     const parser::OpenMPCancellationPointConstruct &) {
503   dirContext_.pop_back();
504 }
505 
506 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
507   const auto &dir{std::get<parser::OmpBlockDirective>(x.t)};
508   ResetPartialContext(dir.source);
509   switch (dir.v) {
510   // 2.7.3 end-single-clause -> copyprivate-clause |
511   //                            nowait-clause
512   case llvm::omp::Directive::OMPD_single:
513     PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_single);
514     break;
515   // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
516   case llvm::omp::Directive::OMPD_workshare:
517     PushContextAndClauseSets(
518         dir.source, llvm::omp::Directive::OMPD_end_workshare);
519     break;
520   default:
521     // no clauses are allowed
522     break;
523   }
524 }
525 
526 // TODO: Verify the popping of dirContext requirement after nowait
527 // implementation, as there is an implicit barrier at the end of the worksharing
528 // constructs unless a nowait clause is specified. Only OMPD_end_single and
529 // end_workshareare popped as they are pushed while entering the
530 // EndBlockDirective.
531 void OmpStructureChecker::Leave(const parser::OmpEndBlockDirective &x) {
532   if ((GetContext().directive == llvm::omp::Directive::OMPD_end_single) ||
533       (GetContext().directive == llvm::omp::Directive::OMPD_end_workshare)) {
534     dirContext_.pop_back();
535   }
536 }
537 
538 void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
539   std::visit(
540       common::visitors{
541           [&](const auto &someAtomicConstruct) {
542             const auto &dir{std::get<parser::Verbatim>(someAtomicConstruct.t)};
543             PushContextAndClauseSets(
544                 dir.source, llvm::omp::Directive::OMPD_atomic);
545           },
546       },
547       x.u);
548 }
549 
550 void OmpStructureChecker::Leave(const parser::OpenMPAtomicConstruct &) {
551   dirContext_.pop_back();
552 }
553 
554 // Clauses
555 // Mainly categorized as
556 // 1. Checks on 'OmpClauseList' from 'parse-tree.h'.
557 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h.
558 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h.
559 
560 void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
561   // 2.7 Loop Construct Restriction
562   if (llvm::omp::doSet.test(GetContext().directive)) {
563     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) {
564       // only one schedule clause is allowed
565       const auto &schedClause{std::get<parser::OmpClause::Schedule>(clause->u)};
566       if (ScheduleModifierHasType(schedClause.v,
567               parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
568         if (FindClause(llvm::omp::Clause::OMPC_ordered)) {
569           context_.Say(clause->source,
570               "The NONMONOTONIC modifier cannot be specified "
571               "if an ORDERED clause is specified"_err_en_US);
572         }
573         if (ScheduleModifierHasType(schedClause.v,
574                 parser::OmpScheduleModifierType::ModType::Monotonic)) {
575           context_.Say(clause->source,
576               "The MONOTONIC and NONMONOTONIC modifiers "
577               "cannot be both specified"_err_en_US);
578         }
579       }
580     }
581 
582     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) {
583       // only one ordered clause is allowed
584       const auto &orderedClause{
585           std::get<parser::OmpClause::Ordered>(clause->u)};
586 
587       if (orderedClause.v) {
588         CheckNotAllowedIfClause(
589             llvm::omp::Clause::OMPC_ordered, {llvm::omp::Clause::OMPC_linear});
590 
591         if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) {
592           const auto &collapseClause{
593               std::get<parser::OmpClause::Collapse>(clause2->u)};
594           // ordered and collapse both have parameters
595           if (const auto orderedValue{GetIntValue(orderedClause.v)}) {
596             if (const auto collapseValue{GetIntValue(collapseClause.v)}) {
597               if (*orderedValue > 0 && *orderedValue < *collapseValue) {
598                 context_.Say(clause->source,
599                     "The parameter of the ORDERED clause must be "
600                     "greater than or equal to "
601                     "the parameter of the COLLAPSE clause"_err_en_US);
602               }
603             }
604           }
605         }
606       }
607 
608       // TODO: ordered region binding check (requires nesting implementation)
609     }
610   } // doSet
611 
612   // 2.8.1 Simd Construct Restriction
613   if (llvm::omp::simdSet.test(GetContext().directive)) {
614     if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) {
615       if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) {
616         const auto &simdlenClause{
617             std::get<parser::OmpClause::Simdlen>(clause->u)};
618         const auto &safelenClause{
619             std::get<parser::OmpClause::Safelen>(clause2->u)};
620         // simdlen and safelen both have parameters
621         if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) {
622           if (const auto safelenValue{GetIntValue(safelenClause.v)}) {
623             if (*safelenValue > 0 && *simdlenValue > *safelenValue) {
624               context_.Say(clause->source,
625                   "The parameter of the SIMDLEN clause must be less than or "
626                   "equal to the parameter of the SAFELEN clause"_err_en_US);
627             }
628           }
629         }
630       }
631     }
632     // A list-item cannot appear in more than one aligned clause
633     semantics::SymbolSet alignedVars;
634     auto clauseAll = FindClauses(llvm::omp::Clause::OMPC_aligned);
635     for (auto itr = clauseAll.first; itr != clauseAll.second; ++itr) {
636       const auto &alignedClause{
637           std::get<parser::OmpClause::Aligned>(itr->second->u)};
638       const auto &alignedNameList{
639           std::get<std::list<parser::Name>>(alignedClause.v.t)};
640       for (auto const &var : alignedNameList) {
641         if (alignedVars.count(*(var.symbol)) == 1) {
642           context_.Say(itr->second->source,
643               "List item '%s' present at multiple ALIGNED clauses"_err_en_US,
644               var.ToString());
645           break;
646         }
647         alignedVars.insert(*(var.symbol));
648       }
649     }
650   } // SIMD
651 
652   // 2.7.3 Single Construct Restriction
653   if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) {
654     CheckNotAllowedIfClause(
655         llvm::omp::Clause::OMPC_copyprivate, {llvm::omp::Clause::OMPC_nowait});
656   }
657 
658   CheckRequireAtLeastOneOf();
659 }
660 
661 void OmpStructureChecker::Enter(const parser::OmpClause &x) {
662   SetContextClause(x);
663 }
664 
665 // Following clauses do not have a separate node in parse-tree.h.
666 CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel)
667 CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire)
668 CHECK_SIMPLE_CLAUSE(AtomicDefaultMemOrder, OMPC_atomic_default_mem_order)
669 CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity)
670 CHECK_SIMPLE_CLAUSE(Allocate, OMPC_allocate)
671 CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture)
672 CHECK_SIMPLE_CLAUSE(Copyin, OMPC_copyin)
673 CHECK_SIMPLE_CLAUSE(Default, OMPC_default)
674 CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj)
675 CHECK_SIMPLE_CLAUSE(Destroy, OMPC_destroy)
676 CHECK_SIMPLE_CLAUSE(Detach, OMPC_detach)
677 CHECK_SIMPLE_CLAUSE(Device, OMPC_device)
678 CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type)
679 CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule)
680 CHECK_SIMPLE_CLAUSE(DynamicAllocators, OMPC_dynamic_allocators)
681 CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive)
682 CHECK_SIMPLE_CLAUSE(Final, OMPC_final)
683 CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush)
684 CHECK_SIMPLE_CLAUSE(From, OMPC_from)
685 CHECK_SIMPLE_CLAUSE(Hint, OMPC_hint)
686 CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction)
687 CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive)
688 CHECK_SIMPLE_CLAUSE(Match, OMPC_match)
689 CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal)
690 CHECK_SIMPLE_CLAUSE(Order, OMPC_order)
691 CHECK_SIMPLE_CLAUSE(Read, OMPC_read)
692 CHECK_SIMPLE_CLAUSE(ReverseOffload, OMPC_reverse_offload)
693 CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate)
694 CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads)
695 CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch)
696 CHECK_SIMPLE_CLAUSE(IsDevicePtr, OMPC_is_device_ptr)
697 CHECK_SIMPLE_CLAUSE(Link, OMPC_link)
698 CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable)
699 CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup)
700 CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch)
701 CHECK_SIMPLE_CLAUSE(Nowait, OMPC_nowait)
702 CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind)
703 CHECK_SIMPLE_CLAUSE(Release, OMPC_release)
704 CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed)
705 CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst)
706 CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd)
707 CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes)
708 CHECK_SIMPLE_CLAUSE(TaskReduction, OMPC_task_reduction)
709 CHECK_SIMPLE_CLAUSE(To, OMPC_to)
710 CHECK_SIMPLE_CLAUSE(UnifiedAddress, OMPC_unified_address)
711 CHECK_SIMPLE_CLAUSE(UnifiedSharedMemory, OMPC_unified_shared_memory)
712 CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform)
713 CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown)
714 CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied)
715 CHECK_SIMPLE_CLAUSE(UseDevicePtr, OMPC_use_device_ptr)
716 CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators)
717 CHECK_SIMPLE_CLAUSE(Update, OMPC_update)
718 CHECK_SIMPLE_CLAUSE(UseDeviceAddr, OMPC_use_device_addr)
719 CHECK_SIMPLE_CLAUSE(Write, OMPC_write)
720 
721 CHECK_REQ_SCALAR_INT_CLAUSE(Allocator, OMPC_allocator)
722 CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize)
723 CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks)
724 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams)
725 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads)
726 CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority)
727 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit)
728 
729 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse)
730 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen)
731 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen)
732 
733 // Restrictions specific to each clause are implemented apart from the
734 // generalized restrictions.
735 void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) {
736   CheckAllowed(llvm::omp::Clause::OMPC_reduction);
737   if (CheckReductionOperators(x)) {
738     CheckReductionTypeList(x);
739   }
740 }
741 bool OmpStructureChecker::CheckReductionOperators(
742     const parser::OmpClause::Reduction &x) {
743 
744   const auto &definedOp{std::get<0>(x.v.t)};
745   bool ok = false;
746   std::visit(
747       common::visitors{
748           [&](const parser::DefinedOperator &dOpr) {
749             const auto &intrinsicOp{
750                 std::get<parser::DefinedOperator::IntrinsicOperator>(dOpr.u)};
751             ok = CheckIntrinsicOperator(intrinsicOp);
752           },
753           [&](const parser::ProcedureDesignator &procD) {
754             const parser::Name *name{std::get_if<parser::Name>(&procD.u)};
755             if (name) {
756               if (name->source == "max" || name->source == "min" ||
757                   name->source == "iand" || name->source == "ior" ||
758                   name->source == "ieor") {
759                 ok = true;
760               } else {
761                 context_.Say(GetContext().clauseSource,
762                     "Invalid reduction identifier in REDUCTION clause."_err_en_US,
763                     ContextDirectiveAsFortran());
764               }
765             }
766           },
767       },
768       definedOp.u);
769 
770   return ok;
771 }
772 bool OmpStructureChecker::CheckIntrinsicOperator(
773     const parser::DefinedOperator::IntrinsicOperator &op) {
774 
775   switch (op) {
776   case parser::DefinedOperator::IntrinsicOperator::Add:
777   case parser::DefinedOperator::IntrinsicOperator::Subtract:
778   case parser::DefinedOperator::IntrinsicOperator::Multiply:
779   case parser::DefinedOperator::IntrinsicOperator::AND:
780   case parser::DefinedOperator::IntrinsicOperator::OR:
781   case parser::DefinedOperator::IntrinsicOperator::EQV:
782   case parser::DefinedOperator::IntrinsicOperator::NEQV:
783     return true;
784   default:
785     context_.Say(GetContext().clauseSource,
786         "Invalid reduction operator in REDUCTION clause."_err_en_US,
787         ContextDirectiveAsFortran());
788   }
789   return false;
790 }
791 
792 void OmpStructureChecker::CheckReductionTypeList(
793     const parser::OmpClause::Reduction &x) {
794   const auto &ompObjectList{std::get<parser::OmpObjectList>(x.v.t)};
795   CheckIntentInPointerAndDefinable(
796       ompObjectList, llvm::omp::Clause::OMPC_reduction);
797   CheckReductionArraySection(ompObjectList);
798   CheckMultipleAppearanceAcrossContext(ompObjectList);
799 }
800 
801 void OmpStructureChecker::CheckIntentInPointerAndDefinable(
802     const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) {
803   for (const auto &ompObject : objectList.v) {
804     if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) {
805       if (const auto *symbol{name->symbol}) {
806         if (IsPointer(symbol->GetUltimate()) &&
807             IsIntentIn(symbol->GetUltimate())) {
808           context_.Say(GetContext().clauseSource,
809               "Pointer '%s' with the INTENT(IN) attribute may not appear "
810               "in a %s clause"_err_en_US,
811               symbol->name(),
812               parser::ToUpperCaseLetters(getClauseName(clause).str()));
813         }
814         if (auto msg{
815                 WhyNotModifiable(*symbol, context_.FindScope(name->source))}) {
816           context_.Say(GetContext().clauseSource,
817               "Variable '%s' on the %s clause is not definable"_err_en_US,
818               symbol->name(),
819               parser::ToUpperCaseLetters(getClauseName(clause).str()));
820         }
821       }
822     }
823   }
824 }
825 
826 void OmpStructureChecker::CheckReductionArraySection(
827     const parser::OmpObjectList &ompObjectList) {
828   for (const auto &ompObject : ompObjectList.v) {
829     if (const auto *dataRef{parser::Unwrap<parser::DataRef>(ompObject)}) {
830       if (const auto *arrayElement{
831               parser::Unwrap<parser::ArrayElement>(ompObject)}) {
832         if (arrayElement) {
833           CheckArraySection(*arrayElement, GetLastName(*dataRef),
834               llvm::omp::Clause::OMPC_reduction);
835         }
836       }
837     }
838   }
839 }
840 
841 void OmpStructureChecker::CheckMultipleAppearanceAcrossContext(
842     const parser::OmpObjectList &redObjectList) {
843   const parser::OmpObjectList *objList{nullptr};
844   //  TODO: Verify the assumption here that the immediately enclosing region is
845   //  the parallel region to which the worksharing construct having reduction
846   //  binds to.
847   if (auto *enclosingContext{GetEnclosingDirContext()}) {
848     for (auto it : enclosingContext->clauseInfo) {
849       llvmOmpClause type = it.first;
850       const auto *clause = it.second;
851       if (type == llvm::omp::Clause::OMPC_private) {
852         const auto &pClause{std::get<parser::OmpClause::Private>(clause->u)};
853         objList = &pClause.v;
854       } else if (type == llvm::omp::Clause::OMPC_firstprivate) {
855         const auto &fpClause{
856             std::get<parser::OmpClause::Firstprivate>(clause->u)};
857         objList = &fpClause.v;
858       } else if (type == llvm::omp::Clause::OMPC_lastprivate) {
859         const auto &lpClause{
860             std::get<parser::OmpClause::Lastprivate>(clause->u)};
861         objList = &lpClause.v;
862       } else if (type == llvm::omp::Clause::OMPC_reduction) {
863         const auto &rClause{std::get<parser::OmpClause::Reduction>(clause->u)};
864         const auto &olist{std::get<1>(rClause.v.t)};
865         objList = &olist;
866       }
867       if (objList) {
868         for (const auto &ompObject : objList->v) {
869           if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) {
870             if (const auto *symbol{name->symbol}) {
871               for (const auto &redOmpObject : redObjectList.v) {
872                 if (const auto *rname{
873                         parser::Unwrap<parser::Name>(redOmpObject)}) {
874                   if (const auto *rsymbol{rname->symbol}) {
875                     if (rsymbol->name() == symbol->name()) {
876                       context_.Say(GetContext().clauseSource,
877                           "%s variable '%s' is %s in outer context must"
878                           " be shared in the parallel regions to which any"
879                           " of the worksharing regions arising from the "
880                           "worksharing"
881                           " construct bind."_err_en_US,
882                           parser::ToUpperCaseLetters(
883                               getClauseName(llvm::omp::Clause::OMPC_reduction)
884                                   .str()),
885                           symbol->name(),
886                           parser::ToUpperCaseLetters(
887                               getClauseName(type).str()));
888                     }
889                   }
890                 }
891               }
892             }
893           }
894         }
895       }
896     }
897   }
898 }
899 
900 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
901   CheckAllowed(llvm::omp::Clause::OMPC_ordered);
902   // the parameter of ordered clause is optional
903   if (const auto &expr{x.v}) {
904     RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr);
905     // 2.8.3 Loop SIMD Construct Restriction
906     if (llvm::omp::doSimdSet.test(GetContext().directive)) {
907       context_.Say(GetContext().clauseSource,
908           "No ORDERED clause with a parameter can be specified "
909           "on the %s directive"_err_en_US,
910           ContextDirectiveAsFortran());
911     }
912   }
913 }
914 
915 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) {
916   CheckAllowed(llvm::omp::Clause::OMPC_shared);
917   CheckIsVarPartOfAnotherVar(x.v);
918 }
919 void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) {
920   CheckAllowed(llvm::omp::Clause::OMPC_private);
921   CheckIsVarPartOfAnotherVar(x.v);
922   CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_private);
923 }
924 
925 void OmpStructureChecker::CheckIsVarPartOfAnotherVar(
926     const parser::OmpObjectList &objList) {
927   for (const auto &ompObject : objList.v) {
928     if ((parser::Unwrap<parser::StructureComponent>(ompObject)) ||
929         (parser::Unwrap<parser::ArrayElement>(ompObject))) {
930       context_.Say(GetContext().clauseSource,
931           "A variable that is part of another variable (as an "
932           "array or structure element)"
933           " cannot appear in a PRIVATE or SHARED clause."_err_en_US);
934     }
935   }
936 }
937 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) {
938   CheckAllowed(llvm::omp::Clause::OMPC_firstprivate);
939   CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate, x.v);
940 
941   SymbolSourceMap currSymbols;
942   GetSymbolsInObjectList(x.v, currSymbols);
943 
944   DirectivesClauseTriple dirClauseTriple;
945   // Check firstprivate variables in worksharing constructs
946   dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do,
947       std::make_pair(
948           llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
949   dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections,
950       std::make_pair(
951           llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
952   dirClauseTriple.emplace(llvm::omp::Directive::OMPD_single,
953       std::make_pair(
954           llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
955   // Check firstprivate variables in distribute construct
956   dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute,
957       std::make_pair(
958           llvm::omp::Directive::OMPD_teams, llvm::omp::privateReductionSet));
959   dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute,
960       std::make_pair(llvm::omp::Directive::OMPD_target_teams,
961           llvm::omp::privateReductionSet));
962   // Check firstprivate variables in task and taskloop constructs
963   dirClauseTriple.emplace(llvm::omp::Directive::OMPD_task,
964       std::make_pair(llvm::omp::Directive::OMPD_parallel,
965           OmpClauseSet{llvm::omp::Clause::OMPC_reduction}));
966   dirClauseTriple.emplace(llvm::omp::Directive::OMPD_taskloop,
967       std::make_pair(llvm::omp::Directive::OMPD_parallel,
968           OmpClauseSet{llvm::omp::Clause::OMPC_reduction}));
969 
970   CheckPrivateSymbolsInOuterCxt(
971       currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_firstprivate);
972 }
973 
974 void OmpStructureChecker::CheckIsLoopIvPartOfClause(
975     llvmOmpClause clause, const parser::OmpObjectList &ompObjectList) {
976   for (const auto &ompObject : ompObjectList.v) {
977     if (const parser::Name * name{parser::Unwrap<parser::Name>(ompObject)}) {
978       if (name->symbol == GetContext().loopIV) {
979         context_.Say(name->source,
980             "DO iteration variable %s is not allowed in %s clause."_err_en_US,
981             name->ToString(),
982             parser::ToUpperCaseLetters(getClauseName(clause).str()));
983       }
984     }
985   }
986 }
987 // Following clauses have a seperate node in parse-tree.h.
988 // Atomic-clause
989 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead, OMPC_read)
990 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicWrite, OMPC_write)
991 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicUpdate, OMPC_update)
992 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicCapture, OMPC_capture)
993 
994 void OmpStructureChecker::Leave(const parser::OmpAtomicRead &) {
995   CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_read,
996       {llvm::omp::Clause::OMPC_release, llvm::omp::Clause::OMPC_acq_rel});
997 }
998 void OmpStructureChecker::Leave(const parser::OmpAtomicWrite &) {
999   CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_write,
1000       {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel});
1001 }
1002 void OmpStructureChecker::Leave(const parser::OmpAtomicUpdate &) {
1003   CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_update,
1004       {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel});
1005 }
1006 // OmpAtomic node represents atomic directive without atomic-clause.
1007 // atomic-clause - READ,WRITE,UPDATE,CAPTURE.
1008 void OmpStructureChecker::Leave(const parser::OmpAtomic &) {
1009   if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acquire)}) {
1010     context_.Say(clause->source,
1011         "Clause ACQUIRE is not allowed on the ATOMIC directive"_err_en_US);
1012   }
1013   if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acq_rel)}) {
1014     context_.Say(clause->source,
1015         "Clause ACQ_REL is not allowed on the ATOMIC directive"_err_en_US);
1016   }
1017 }
1018 // Restrictions specific to each clause are implemented apart from the
1019 // generalized restrictions.
1020 void OmpStructureChecker::Enter(const parser::OmpClause::Aligned &x) {
1021   CheckAllowed(llvm::omp::Clause::OMPC_aligned);
1022 
1023   if (const auto &expr{
1024           std::get<std::optional<parser::ScalarIntConstantExpr>>(x.v.t)}) {
1025     RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_aligned, *expr);
1026   }
1027   // 2.8.1 TODO: list-item attribute check
1028 }
1029 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) {
1030   CheckAllowed(llvm::omp::Clause::OMPC_defaultmap);
1031   using VariableCategory = parser::OmpDefaultmapClause::VariableCategory;
1032   if (!std::get<std::optional<VariableCategory>>(x.v.t)) {
1033     context_.Say(GetContext().clauseSource,
1034         "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP "
1035         "clause"_err_en_US);
1036   }
1037 }
1038 void OmpStructureChecker::Enter(const parser::OmpClause::If &x) {
1039   CheckAllowed(llvm::omp::Clause::OMPC_if);
1040   using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier;
1041   static std::unordered_map<dirNameModifier, OmpDirectiveSet>
1042       dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet},
1043           {dirNameModifier::Target, llvm::omp::targetSet},
1044           {dirNameModifier::TargetEnterData,
1045               {llvm::omp::Directive::OMPD_target_enter_data}},
1046           {dirNameModifier::TargetExitData,
1047               {llvm::omp::Directive::OMPD_target_exit_data}},
1048           {dirNameModifier::TargetData,
1049               {llvm::omp::Directive::OMPD_target_data}},
1050           {dirNameModifier::TargetUpdate,
1051               {llvm::omp::Directive::OMPD_target_update}},
1052           {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}},
1053           {dirNameModifier::Taskloop, llvm::omp::taskloopSet}};
1054   if (const auto &directiveName{
1055           std::get<std::optional<dirNameModifier>>(x.v.t)}) {
1056     auto search{dirNameModifierMap.find(*directiveName)};
1057     if (search == dirNameModifierMap.end() ||
1058         !search->second.test(GetContext().directive)) {
1059       context_
1060           .Say(GetContext().clauseSource,
1061               "Unmatched directive name modifier %s on the IF clause"_err_en_US,
1062               parser::ToUpperCaseLetters(
1063                   parser::OmpIfClause::EnumToString(*directiveName)))
1064           .Attach(
1065               GetContext().directiveSource, "Cannot apply to directive"_en_US);
1066     }
1067   }
1068 }
1069 
1070 void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
1071   CheckAllowed(llvm::omp::Clause::OMPC_linear);
1072 
1073   // 2.7 Loop Construct Restriction
1074   if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) {
1075     if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.v.u)) {
1076       context_.Say(GetContext().clauseSource,
1077           "A modifier may not be specified in a LINEAR clause "
1078           "on the %s directive"_err_en_US,
1079           ContextDirectiveAsFortran());
1080     }
1081   }
1082 }
1083 
1084 void OmpStructureChecker::CheckAllowedMapTypes(
1085     const parser::OmpMapType::Type &type,
1086     const std::list<parser::OmpMapType::Type> &allowedMapTypeList) {
1087   const auto found{std::find(
1088       std::begin(allowedMapTypeList), std::end(allowedMapTypeList), type)};
1089   if (found == std::end(allowedMapTypeList)) {
1090     std::string commaSeperatedMapTypes;
1091     llvm::interleave(
1092         allowedMapTypeList.begin(), allowedMapTypeList.end(),
1093         [&](const parser::OmpMapType::Type &mapType) {
1094           commaSeperatedMapTypes.append(parser::ToUpperCaseLetters(
1095               parser::OmpMapType::EnumToString(mapType)));
1096         },
1097         [&] { commaSeperatedMapTypes.append(", "); });
1098     context_.Say(GetContext().clauseSource,
1099         "Only the %s map types are permitted "
1100         "for MAP clauses on the %s directive"_err_en_US,
1101         commaSeperatedMapTypes, ContextDirectiveAsFortran());
1102   }
1103 }
1104 
1105 void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
1106   CheckAllowed(llvm::omp::Clause::OMPC_map);
1107 
1108   if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.v.t)}) {
1109     using Type = parser::OmpMapType::Type;
1110     const Type &type{std::get<Type>(maptype->t)};
1111     switch (GetContext().directive) {
1112     case llvm::omp::Directive::OMPD_target:
1113     case llvm::omp::Directive::OMPD_target_teams:
1114     case llvm::omp::Directive::OMPD_target_teams_distribute:
1115     case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
1116     case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
1117     case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
1118     case llvm::omp::Directive::OMPD_target_data:
1119       CheckAllowedMapTypes(
1120           type, {Type::To, Type::From, Type::Tofrom, Type::Alloc});
1121       break;
1122     case llvm::omp::Directive::OMPD_target_enter_data:
1123       CheckAllowedMapTypes(type, {Type::To, Type::Alloc});
1124       break;
1125     case llvm::omp::Directive::OMPD_target_exit_data:
1126       CheckAllowedMapTypes(type, {Type::From, Type::Release, Type::Delete});
1127       break;
1128     default:
1129       break;
1130     }
1131   }
1132 }
1133 
1134 bool OmpStructureChecker::ScheduleModifierHasType(
1135     const parser::OmpScheduleClause &x,
1136     const parser::OmpScheduleModifierType::ModType &type) {
1137   const auto &modifier{
1138       std::get<std::optional<parser::OmpScheduleModifier>>(x.t)};
1139   if (modifier) {
1140     const auto &modType1{
1141         std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)};
1142     const auto &modType2{
1143         std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(
1144             modifier->t)};
1145     if (modType1.v.v == type || (modType2 && modType2->v.v == type)) {
1146       return true;
1147     }
1148   }
1149   return false;
1150 }
1151 void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) {
1152   CheckAllowed(llvm::omp::Clause::OMPC_schedule);
1153   const parser::OmpScheduleClause &scheduleClause = x.v;
1154 
1155   // 2.7 Loop Construct Restriction
1156   if (llvm::omp::doSet.test(GetContext().directive)) {
1157     const auto &kind{std::get<1>(scheduleClause.t)};
1158     const auto &chunk{std::get<2>(scheduleClause.t)};
1159     if (chunk) {
1160       if (kind == parser::OmpScheduleClause::ScheduleType::Runtime ||
1161           kind == parser::OmpScheduleClause::ScheduleType::Auto) {
1162         context_.Say(GetContext().clauseSource,
1163             "When SCHEDULE clause has %s specified, "
1164             "it must not have chunk size specified"_err_en_US,
1165             parser::ToUpperCaseLetters(
1166                 parser::OmpScheduleClause::EnumToString(kind)));
1167       }
1168       if (const auto &chunkExpr{std::get<std::optional<parser::ScalarIntExpr>>(
1169               scheduleClause.t)}) {
1170         RequiresPositiveParameter(
1171             llvm::omp::Clause::OMPC_schedule, *chunkExpr, "chunk size");
1172       }
1173     }
1174 
1175     if (ScheduleModifierHasType(scheduleClause,
1176             parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
1177       if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic &&
1178           kind != parser::OmpScheduleClause::ScheduleType::Guided) {
1179         context_.Say(GetContext().clauseSource,
1180             "The NONMONOTONIC modifier can only be specified with "
1181             "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US);
1182       }
1183     }
1184   }
1185 }
1186 
1187 void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) {
1188   CheckAllowed(llvm::omp::Clause::OMPC_depend);
1189   if (const auto *inOut{std::get_if<parser::OmpDependClause::InOut>(&x.v.u)}) {
1190     const auto &designators{std::get<std::list<parser::Designator>>(inOut->t)};
1191     for (const auto &ele : designators) {
1192       if (const auto *dataRef{std::get_if<parser::DataRef>(&ele.u)}) {
1193         CheckDependList(*dataRef);
1194         if (const auto *arr{
1195                 std::get_if<common::Indirection<parser::ArrayElement>>(
1196                     &dataRef->u)}) {
1197           CheckArraySection(arr->value(), GetLastName(*dataRef),
1198               llvm::omp::Clause::OMPC_depend);
1199         }
1200       }
1201     }
1202   }
1203 }
1204 
1205 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &x) {
1206   CheckAllowed(llvm::omp::Clause::OMPC_copyprivate);
1207   CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_copyprivate);
1208 }
1209 
1210 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) {
1211   CheckAllowed(llvm::omp::Clause::OMPC_lastprivate);
1212 
1213   DirectivesClauseTriple dirClauseTriple;
1214   SymbolSourceMap currSymbols;
1215   GetSymbolsInObjectList(x.v, currSymbols);
1216   CheckDefinableObjects(currSymbols, llvm::omp::Clause::OMPC_lastprivate);
1217 
1218   // Check lastprivate variables in worksharing constructs
1219   dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do,
1220       std::make_pair(
1221           llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
1222   dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections,
1223       std::make_pair(
1224           llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
1225 
1226   CheckPrivateSymbolsInOuterCxt(
1227       currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_lastprivate);
1228 }
1229 
1230 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
1231   return llvm::omp::getOpenMPClauseName(clause);
1232 }
1233 
1234 llvm::StringRef OmpStructureChecker::getDirectiveName(
1235     llvm::omp::Directive directive) {
1236   return llvm::omp::getOpenMPDirectiveName(directive);
1237 }
1238 
1239 void OmpStructureChecker::CheckDependList(const parser::DataRef &d) {
1240   std::visit(
1241       common::visitors{
1242           [&](const common::Indirection<parser::ArrayElement> &elem) {
1243             // Check if the base element is valid on Depend Clause
1244             CheckDependList(elem.value().base);
1245           },
1246           [&](const common::Indirection<parser::StructureComponent> &) {
1247             context_.Say(GetContext().clauseSource,
1248                 "A variable that is part of another variable "
1249                 "(such as an element of a structure) but is not an array "
1250                 "element or an array section cannot appear in a DEPEND "
1251                 "clause"_err_en_US);
1252           },
1253           [&](const common::Indirection<parser::CoindexedNamedObject> &) {
1254             context_.Say(GetContext().clauseSource,
1255                 "Coarrays are not supported in DEPEND clause"_err_en_US);
1256           },
1257           [&](const parser::Name &) { return; },
1258       },
1259       d.u);
1260 }
1261 
1262 // Called from both Reduction and Depend clause.
1263 void OmpStructureChecker::CheckArraySection(
1264     const parser::ArrayElement &arrayElement, const parser::Name &name,
1265     const llvm::omp::Clause clause) {
1266   if (!arrayElement.subscripts.empty()) {
1267     for (const auto &subscript : arrayElement.subscripts) {
1268       if (const auto *triplet{
1269               std::get_if<parser::SubscriptTriplet>(&subscript.u)}) {
1270         if (std::get<0>(triplet->t) && std::get<1>(triplet->t)) {
1271           const auto &lower{std::get<0>(triplet->t)};
1272           const auto &upper{std::get<1>(triplet->t)};
1273           if (lower && upper) {
1274             const auto lval{GetIntValue(lower)};
1275             const auto uval{GetIntValue(upper)};
1276             if (lval && uval && *uval < *lval) {
1277               context_.Say(GetContext().clauseSource,
1278                   "'%s' in %s clause"
1279                   " is a zero size array section"_err_en_US,
1280                   name.ToString(),
1281                   parser::ToUpperCaseLetters(getClauseName(clause).str()));
1282               break;
1283             } else if (std::get<2>(triplet->t)) {
1284               const auto &strideExpr{std::get<2>(triplet->t)};
1285               if (strideExpr) {
1286                 if (clause == llvm::omp::Clause::OMPC_depend) {
1287                   context_.Say(GetContext().clauseSource,
1288                       "Stride should not be specified for array section in "
1289                       "DEPEND "
1290                       "clause"_err_en_US);
1291                 }
1292                 const auto stride{GetIntValue(strideExpr)};
1293                 if ((stride && stride != 1)) {
1294                   context_.Say(GetContext().clauseSource,
1295                       "A list item that appears in a REDUCTION clause"
1296                       " should have a contiguous storage array section."_err_en_US,
1297                       ContextDirectiveAsFortran());
1298                   break;
1299                 }
1300               }
1301             }
1302           }
1303         }
1304       }
1305     }
1306   }
1307 }
1308 
1309 void OmpStructureChecker::CheckIntentInPointer(
1310     const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) {
1311   SymbolSourceMap symbols;
1312   GetSymbolsInObjectList(objectList, symbols);
1313   for (auto it{symbols.begin()}; it != symbols.end(); ++it) {
1314     const auto *symbol{it->first};
1315     const auto source{it->second};
1316     if (IsPointer(*symbol) && IsIntentIn(*symbol)) {
1317       context_.Say(source,
1318           "Pointer '%s' with the INTENT(IN) attribute may not appear "
1319           "in a %s clause"_err_en_US,
1320           symbol->name(),
1321           parser::ToUpperCaseLetters(getClauseName(clause).str()));
1322     }
1323   }
1324 }
1325 
1326 void OmpStructureChecker::GetSymbolsInObjectList(
1327     const parser::OmpObjectList &objectList, SymbolSourceMap &symbols) {
1328   for (const auto &ompObject : objectList.v) {
1329     if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) {
1330       if (const auto *symbol{name->symbol}) {
1331         if (const auto *commonBlockDetails{
1332                 symbol->detailsIf<CommonBlockDetails>()}) {
1333           for (const auto &object : commonBlockDetails->objects()) {
1334             symbols.emplace(&object->GetUltimate(), name->source);
1335           }
1336         } else {
1337           symbols.emplace(&symbol->GetUltimate(), name->source);
1338         }
1339       }
1340     }
1341   }
1342 }
1343 
1344 void OmpStructureChecker::CheckDefinableObjects(
1345     SymbolSourceMap &symbols, const llvm::omp::Clause clause) {
1346   for (auto it{symbols.begin()}; it != symbols.end(); ++it) {
1347     const auto *symbol{it->first};
1348     const auto source{it->second};
1349     if (auto msg{WhyNotModifiable(*symbol, context_.FindScope(source))}) {
1350       context_
1351           .Say(source,
1352               "Variable '%s' on the %s clause is not definable"_err_en_US,
1353               symbol->name(),
1354               parser::ToUpperCaseLetters(getClauseName(clause).str()))
1355           .Attach(source, std::move(*msg), symbol->name());
1356     }
1357   }
1358 }
1359 
1360 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt(
1361     SymbolSourceMap &currSymbols, DirectivesClauseTriple &dirClauseTriple,
1362     const llvm::omp::Clause currClause) {
1363   SymbolSourceMap enclosingSymbols;
1364   auto range{dirClauseTriple.equal_range(GetContext().directive)};
1365   for (auto dirIter{range.first}; dirIter != range.second; ++dirIter) {
1366     auto enclosingDir{dirIter->second.first};
1367     auto enclosingClauseSet{dirIter->second.second};
1368     if (auto *enclosingContext{GetEnclosingContextWithDir(enclosingDir)}) {
1369       for (auto it{enclosingContext->clauseInfo.begin()};
1370            it != enclosingContext->clauseInfo.end(); ++it) {
1371         // TODO: Replace the hard-coded clause names by using autogen checks or
1372         // a function which maps parser::OmpClause::<name> to the corresponding
1373         // llvm::omp::Clause::OMPC_<name>
1374         std::visit(common::visitors{
1375                        [&](const parser::OmpClause::Private &x) {
1376                          if (enclosingClauseSet.test(
1377                                  llvm::omp::Clause::OMPC_private)) {
1378                            GetSymbolsInObjectList(x.v, enclosingSymbols);
1379                          }
1380                        },
1381                        [&](const parser::OmpClause::Firstprivate &x) {
1382                          if (enclosingClauseSet.test(
1383                                  llvm::omp::Clause::OMPC_firstprivate)) {
1384                            GetSymbolsInObjectList(x.v, enclosingSymbols);
1385                          }
1386                        },
1387                        [&](const parser::OmpClause::Lastprivate &x) {
1388                          if (enclosingClauseSet.test(
1389                                  llvm::omp::Clause::OMPC_lastprivate)) {
1390                            GetSymbolsInObjectList(x.v, enclosingSymbols);
1391                          }
1392                        },
1393                        [&](const parser::OmpClause::Reduction &x) {
1394                          if (enclosingClauseSet.test(
1395                                  llvm::omp::Clause::OMPC_reduction)) {
1396                            const auto &ompObjectList{
1397                                std::get<parser::OmpObjectList>(x.v.t)};
1398                            GetSymbolsInObjectList(
1399                                ompObjectList, enclosingSymbols);
1400                          }
1401                        },
1402                        [&](const auto &) {},
1403                    },
1404             it->second->u);
1405       }
1406 
1407       // Check if the symbols in current context are private in outer context
1408       for (auto iter{currSymbols.begin()}; iter != currSymbols.end(); ++iter) {
1409         const auto *symbol{iter->first};
1410         const auto source{iter->second};
1411         if (enclosingSymbols.find(symbol) != enclosingSymbols.end()) {
1412           context_.Say(source,
1413               "%s variable '%s' is PRIVATE in outer context"_err_en_US,
1414               parser::ToUpperCaseLetters(getClauseName(currClause).str()),
1415               symbol->name());
1416         }
1417       }
1418     }
1419   }
1420 }
1421 
1422 void OmpStructureChecker::CheckWorkshareBlockStmts(
1423     const parser::Block &block, parser::CharBlock source) {
1424   OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source};
1425 
1426   for (auto it{block.begin()}; it != block.end(); ++it) {
1427     if (parser::Unwrap<parser::AssignmentStmt>(*it) ||
1428         parser::Unwrap<parser::ForallStmt>(*it) ||
1429         parser::Unwrap<parser::ForallConstruct>(*it) ||
1430         parser::Unwrap<parser::WhereStmt>(*it) ||
1431         parser::Unwrap<parser::WhereConstruct>(*it)) {
1432       parser::Walk(*it, ompWorkshareBlockChecker);
1433     } else if (const auto *ompConstruct{
1434                    parser::Unwrap<parser::OpenMPConstruct>(*it)}) {
1435       if (const auto *ompAtomicConstruct{
1436               std::get_if<parser::OpenMPAtomicConstruct>(&ompConstruct->u)}) {
1437         // Check if assignment statements in the enclosing OpenMP Atomic
1438         // construct are allowed in the Workshare construct
1439         parser::Walk(*ompAtomicConstruct, ompWorkshareBlockChecker);
1440       } else if (const auto *ompCriticalConstruct{
1441                      std::get_if<parser::OpenMPCriticalConstruct>(
1442                          &ompConstruct->u)}) {
1443         // All the restrictions on the Workshare construct apply to the
1444         // statements in the enclosing critical constructs
1445         const auto &criticalBlock{
1446             std::get<parser::Block>(ompCriticalConstruct->t)};
1447         CheckWorkshareBlockStmts(criticalBlock, source);
1448       } else {
1449         // Check if OpenMP constructs enclosed in the Workshare construct are
1450         // 'Parallel' constructs
1451         auto currentDir{llvm::omp::Directive::OMPD_unknown};
1452         const OmpDirectiveSet parallelDirSet{
1453             llvm::omp::Directive::OMPD_parallel,
1454             llvm::omp::Directive::OMPD_parallel_do,
1455             llvm::omp::Directive::OMPD_parallel_sections,
1456             llvm::omp::Directive::OMPD_parallel_workshare,
1457             llvm::omp::Directive::OMPD_parallel_do_simd};
1458 
1459         if (const auto *ompBlockConstruct{
1460                 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) {
1461           const auto &beginBlockDir{
1462               std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)};
1463           const auto &beginDir{
1464               std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
1465           currentDir = beginDir.v;
1466         } else if (const auto *ompLoopConstruct{
1467                        std::get_if<parser::OpenMPLoopConstruct>(
1468                            &ompConstruct->u)}) {
1469           const auto &beginLoopDir{
1470               std::get<parser::OmpBeginLoopDirective>(ompLoopConstruct->t)};
1471           const auto &beginDir{
1472               std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
1473           currentDir = beginDir.v;
1474         } else if (const auto *ompSectionsConstruct{
1475                        std::get_if<parser::OpenMPSectionsConstruct>(
1476                            &ompConstruct->u)}) {
1477           const auto &beginSectionsDir{
1478               std::get<parser::OmpBeginSectionsDirective>(
1479                   ompSectionsConstruct->t)};
1480           const auto &beginDir{
1481               std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
1482           currentDir = beginDir.v;
1483         }
1484 
1485         if (!parallelDirSet.test(currentDir)) {
1486           context_.Say(source,
1487               "OpenMP constructs enclosed in WORKSHARE construct may consist "
1488               "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US);
1489         }
1490       }
1491     } else {
1492       context_.Say(source,
1493           "The structured block in a WORKSHARE construct may consist of only "
1494           "SCALAR or ARRAY assignments, FORALL or WHERE statements, "
1495           "FORALL, WHERE, ATOMIC, CRITICAL or PARALLEL constructs"_err_en_US);
1496     }
1497   }
1498 }
1499 
1500 } // namespace Fortran::semantics
1501