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