[lldb] Add ability to hide the root name of a value
[llvm-project.git] / flang / lib / Semantics / check-omp-structure.cpp
blobcffdabf8441d047a8a9d14697f7a2981ef4bab5f
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 //===----------------------------------------------------------------------===//
9 #include "check-omp-structure.h"
10 #include "definable.h"
11 #include "flang/Parser/parse-tree.h"
12 #include "flang/Semantics/tools.h"
13 #include <algorithm>
15 namespace Fortran::semantics {
17 // Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
18 #define CHECK_SIMPLE_CLAUSE(X, Y) \
19 void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
20 CheckAllowed(llvm::omp::Clause::Y); \
23 #define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \
24 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
25 CheckAllowed(llvm::omp::Clause::Y); \
26 RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \
29 #define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \
30 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
31 CheckAllowed(llvm::omp::Clause::Y); \
32 RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \
35 // Use when clause don't falls under 'struct OmpClause' in 'parse-tree.h'.
36 #define CHECK_SIMPLE_PARSER_CLAUSE(X, Y) \
37 void OmpStructureChecker::Enter(const parser::X &) { \
38 CheckAllowed(llvm::omp::Y); \
41 // 'OmpWorkshareBlockChecker' is used to check the validity of the assignment
42 // statements and the expressions enclosed in an OpenMP Workshare construct
43 class OmpWorkshareBlockChecker {
44 public:
45 OmpWorkshareBlockChecker(SemanticsContext &context, parser::CharBlock source)
46 : context_{context}, source_{source} {}
48 template <typename T> bool Pre(const T &) { return true; }
49 template <typename T> void Post(const T &) {}
51 bool Pre(const parser::AssignmentStmt &assignment) {
52 const auto &var{std::get<parser::Variable>(assignment.t)};
53 const auto &expr{std::get<parser::Expr>(assignment.t)};
54 const auto *lhs{GetExpr(context_, var)};
55 const auto *rhs{GetExpr(context_, expr)};
56 if (lhs && rhs) {
57 Tristate isDefined{semantics::IsDefinedAssignment(
58 lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())};
59 if (isDefined == Tristate::Yes) {
60 context_.Say(expr.source,
61 "Defined assignment statement is not "
62 "allowed in a WORKSHARE construct"_err_en_US);
65 return true;
68 bool Pre(const parser::Expr &expr) {
69 if (const auto *e{GetExpr(context_, expr)}) {
70 for (const Symbol &symbol : evaluate::CollectSymbols(*e)) {
71 const Symbol &root{GetAssociationRoot(symbol)};
72 if (IsFunction(root) && !IsElementalProcedure(root)) {
73 context_.Say(expr.source,
74 "User defined non-ELEMENTAL function "
75 "'%s' is not allowed in a WORKSHARE construct"_err_en_US,
76 root.name());
80 return false;
83 private:
84 SemanticsContext &context_;
85 parser::CharBlock source_;
88 class OmpCycleChecker {
89 public:
90 OmpCycleChecker(SemanticsContext &context, std::int64_t cycleLevel)
91 : context_{context}, cycleLevel_{cycleLevel} {}
93 template <typename T> bool Pre(const T &) { return true; }
94 template <typename T> void Post(const T &) {}
96 bool Pre(const parser::DoConstruct &dc) {
97 cycleLevel_--;
98 const auto &labelName{std::get<0>(std::get<0>(dc.t).statement.t)};
99 if (labelName) {
100 labelNamesandLevels_.emplace(labelName.value().ToString(), cycleLevel_);
102 return true;
105 bool Pre(const parser::CycleStmt &cyclestmt) {
106 std::map<std::string, std::int64_t>::iterator it;
107 bool err{false};
108 if (cyclestmt.v) {
109 it = labelNamesandLevels_.find(cyclestmt.v->source.ToString());
110 err = (it != labelNamesandLevels_.end() && it->second > 0);
112 if (cycleLevel_ > 0 || err) {
113 context_.Say(*cycleSource_,
114 "CYCLE statement to non-innermost associated loop of an OpenMP DO "
115 "construct"_err_en_US);
117 return true;
120 bool Pre(const parser::Statement<parser::ActionStmt> &actionstmt) {
121 cycleSource_ = &actionstmt.source;
122 return true;
125 private:
126 SemanticsContext &context_;
127 const parser::CharBlock *cycleSource_;
128 std::int64_t cycleLevel_;
129 std::map<std::string, std::int64_t> labelNamesandLevels_;
132 bool OmpStructureChecker::IsCloselyNestedRegion(const OmpDirectiveSet &set) {
133 // Definition of close nesting:
135 // `A region nested inside another region with no parallel region nested
136 // between them`
138 // Examples:
139 // non-parallel construct 1
140 // non-parallel construct 2
141 // parallel construct
142 // construct 3
143 // In the above example, construct 3 is NOT closely nested inside construct 1
144 // or 2
146 // non-parallel construct 1
147 // non-parallel construct 2
148 // construct 3
149 // In the above example, construct 3 is closely nested inside BOTH construct 1
150 // and 2
152 // Algorithm:
153 // Starting from the parent context, Check in a bottom-up fashion, each level
154 // of the context stack. If we have a match for one of the (supplied)
155 // violating directives, `close nesting` is satisfied. If no match is there in
156 // the entire stack, `close nesting` is not satisfied. If at any level, a
157 // `parallel` region is found, `close nesting` is not satisfied.
159 if (CurrentDirectiveIsNested()) {
160 int index = dirContext_.size() - 2;
161 while (index != -1) {
162 if (set.test(dirContext_[index].directive)) {
163 return true;
164 } else if (llvm::omp::parallelSet.test(dirContext_[index].directive)) {
165 return false;
167 index--;
170 return false;
173 void OmpStructureChecker::CheckMultListItems() {
174 semantics::UnorderedSymbolSet listVars;
175 auto checkMultipleOcurrence = [&](const std::list<parser::Name> &nameList,
176 const parser::CharBlock &item,
177 const std::string &clauseName) {
178 for (auto const &var : nameList) {
179 if (llvm::is_contained(listVars, *(var.symbol))) {
180 context_.Say(item,
181 "List item '%s' present at multiple %s clauses"_err_en_US,
182 var.ToString(), clauseName);
184 listVars.insert(*(var.symbol));
188 // Aligned clause
189 auto alignedClauses{FindClauses(llvm::omp::Clause::OMPC_aligned)};
190 for (auto itr = alignedClauses.first; itr != alignedClauses.second; ++itr) {
191 const auto &alignedClause{
192 std::get<parser::OmpClause::Aligned>(itr->second->u)};
193 const auto &alignedNameList{
194 std::get<std::list<parser::Name>>(alignedClause.v.t)};
195 checkMultipleOcurrence(alignedNameList, itr->second->source, "ALIGNED");
198 // Nontemporal clause
199 auto nonTemporalClauses{FindClauses(llvm::omp::Clause::OMPC_nontemporal)};
200 for (auto itr = nonTemporalClauses.first; itr != nonTemporalClauses.second;
201 ++itr) {
202 const auto &nontempClause{
203 std::get<parser::OmpClause::Nontemporal>(itr->second->u)};
204 const auto &nontempNameList{nontempClause.v};
205 checkMultipleOcurrence(nontempNameList, itr->second->source, "NONTEMPORAL");
209 bool OmpStructureChecker::HasInvalidWorksharingNesting(
210 const parser::CharBlock &source, const OmpDirectiveSet &set) {
211 // set contains all the invalid closely nested directives
212 // for the given directive (`source` here)
213 if (IsCloselyNestedRegion(set)) {
214 context_.Say(source,
215 "A worksharing region may not be closely nested inside a "
216 "worksharing, explicit task, taskloop, critical, ordered, atomic, or "
217 "master region"_err_en_US);
218 return true;
220 return false;
223 void OmpStructureChecker::HasInvalidDistributeNesting(
224 const parser::OpenMPLoopConstruct &x) {
225 bool violation{false};
227 OmpDirectiveSet distributeSet{llvm::omp::Directive::OMPD_distribute,
228 llvm::omp::Directive::OMPD_distribute_parallel_do,
229 llvm::omp::Directive::OMPD_distribute_parallel_do_simd,
230 llvm::omp::Directive::OMPD_distribute_parallel_for,
231 llvm::omp::Directive::OMPD_distribute_parallel_for_simd,
232 llvm::omp::Directive::OMPD_distribute_simd};
234 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
235 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
236 if (distributeSet.test(beginDir.v)) {
237 // `distribute` region has to be nested
238 if (!CurrentDirectiveIsNested()) {
239 violation = true;
240 } else {
241 // `distribute` region has to be strictly nested inside `teams`
242 if (!llvm::omp::teamSet.test(GetContextParent().directive)) {
243 violation = true;
247 if (violation) {
248 context_.Say(beginDir.source,
249 "`DISTRIBUTE` region has to be strictly nested inside `TEAMS` "
250 "region."_err_en_US);
254 void OmpStructureChecker::HasInvalidTeamsNesting(
255 const llvm::omp::Directive &dir, const parser::CharBlock &source) {
256 OmpDirectiveSet allowedSet{llvm::omp::Directive::OMPD_parallel,
257 llvm::omp::Directive::OMPD_parallel_do,
258 llvm::omp::Directive::OMPD_parallel_do_simd,
259 llvm::omp::Directive::OMPD_parallel_for,
260 llvm::omp::Directive::OMPD_parallel_for_simd,
261 llvm::omp::Directive::OMPD_parallel_master,
262 llvm::omp::Directive::OMPD_parallel_master_taskloop,
263 llvm::omp::Directive::OMPD_parallel_master_taskloop_simd,
264 llvm::omp::Directive::OMPD_parallel_sections,
265 llvm::omp::Directive::OMPD_parallel_workshare,
266 llvm::omp::Directive::OMPD_distribute,
267 llvm::omp::Directive::OMPD_distribute_parallel_do,
268 llvm::omp::Directive::OMPD_distribute_parallel_do_simd,
269 llvm::omp::Directive::OMPD_distribute_parallel_for,
270 llvm::omp::Directive::OMPD_distribute_parallel_for_simd,
271 llvm::omp::Directive::OMPD_distribute_simd};
273 if (!allowedSet.test(dir)) {
274 context_.Say(source,
275 "Only `DISTRIBUTE` or `PARALLEL` regions are allowed to be strictly "
276 "nested inside `TEAMS` region."_err_en_US);
280 void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
281 const parser::CharBlock &source, const parser::Name &name) {
282 if (const auto *symbol{name.symbol}) {
283 const auto *commonBlock{FindCommonBlockContaining(*symbol)};
284 const auto &scope{context_.FindScope(symbol->name())};
285 const Scope &containingScope{GetProgramUnitContaining(scope)};
286 if (!isPredefinedAllocator &&
287 (IsSave(*symbol) || commonBlock ||
288 containingScope.kind() == Scope::Kind::Module)) {
289 context_.Say(source,
290 "If list items within the ALLOCATE directive have the "
291 "SAVE attribute, are a common block name, or are "
292 "declared in the scope of a module, then only "
293 "predefined memory allocator parameters can be used "
294 "in the allocator clause"_err_en_US);
299 void OmpStructureChecker::CheckPredefinedAllocatorRestriction(
300 const parser::CharBlock &source,
301 const parser::OmpObjectList &ompObjectList) {
302 for (const auto &ompObject : ompObjectList.v) {
303 common::visit(
304 common::visitors{
305 [&](const parser::Designator &designator) {
306 if (const auto *dataRef{
307 std::get_if<parser::DataRef>(&designator.u)}) {
308 if (const auto *name{std::get_if<parser::Name>(&dataRef->u)}) {
309 CheckPredefinedAllocatorRestriction(source, *name);
313 [&](const parser::Name &name) {
314 CheckPredefinedAllocatorRestriction(source, name);
317 ompObject.u);
321 template <class D>
322 void OmpStructureChecker::CheckHintClause(
323 D *leftOmpClauseList, D *rightOmpClauseList) {
324 auto checkForValidHintClause = [&](const D *clauseList) {
325 for (const auto &clause : clauseList->v) {
326 const Fortran::parser::OmpClause *ompClause = nullptr;
327 if constexpr (std::is_same_v<D,
328 const Fortran::parser::OmpAtomicClauseList>) {
329 ompClause = std::get_if<Fortran::parser::OmpClause>(&clause.u);
330 if (!ompClause)
331 continue;
332 } else if constexpr (std::is_same_v<D,
333 const Fortran::parser::OmpClauseList>) {
334 ompClause = &clause;
336 if (const Fortran::parser::OmpClause::Hint *
337 hintClause{
338 std::get_if<Fortran::parser::OmpClause::Hint>(&ompClause->u)}) {
339 std::optional<std::int64_t> hintValue = GetIntValue(hintClause->v);
340 if (hintValue && *hintValue >= 0) {
341 /*`omp_sync_hint_nonspeculative` and `omp_lock_hint_speculative`*/
342 if ((*hintValue & 0xC) == 0xC
343 /*`omp_sync_hint_uncontended` and omp_sync_hint_contended*/
344 || (*hintValue & 0x3) == 0x3)
345 context_.Say(clause.source,
346 "Hint clause value "
347 "is not a valid OpenMP synchronization value"_err_en_US);
348 } else {
349 context_.Say(clause.source,
350 "Hint clause must have non-negative constant "
351 "integer expression"_err_en_US);
357 if (leftOmpClauseList) {
358 checkForValidHintClause(leftOmpClauseList);
360 if (rightOmpClauseList) {
361 checkForValidHintClause(rightOmpClauseList);
365 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
366 // Simd Construct with Ordered Construct Nesting check
367 // We cannot use CurrentDirectiveIsNested() here because
368 // PushContextAndClauseSets() has not been called yet, it is
369 // called individually for each construct. Therefore a
370 // dirContext_ size `1` means the current construct is nested
371 if (dirContext_.size() >= 1) {
372 if (GetDirectiveNest(SIMDNest) > 0) {
373 CheckSIMDNest(x);
375 if (GetDirectiveNest(TargetNest) > 0) {
376 CheckTargetNest(x);
381 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
382 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
383 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
385 // check matching, End directive is optional
386 if (const auto &endLoopDir{
387 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
388 const auto &endDir{
389 std::get<parser::OmpLoopDirective>(endLoopDir.value().t)};
391 CheckMatching<parser::OmpLoopDirective>(beginDir, endDir);
394 PushContextAndClauseSets(beginDir.source, beginDir.v);
395 if (llvm::omp::simdSet.test(GetContext().directive)) {
396 EnterDirectiveNest(SIMDNest);
399 if (beginDir.v == llvm::omp::Directive::OMPD_do) {
400 // 2.7.1 do-clause -> private-clause |
401 // firstprivate-clause |
402 // lastprivate-clause |
403 // linear-clause |
404 // reduction-clause |
405 // schedule-clause |
406 // collapse-clause |
407 // ordered-clause
409 // nesting check
410 HasInvalidWorksharingNesting(
411 beginDir.source, llvm::omp::nestedWorkshareErrSet);
413 SetLoopInfo(x);
415 if (const auto &doConstruct{
416 std::get<std::optional<parser::DoConstruct>>(x.t)}) {
417 const auto &doBlock{std::get<parser::Block>(doConstruct->t)};
418 CheckNoBranching(doBlock, beginDir.v, beginDir.source);
420 CheckDoWhile(x);
421 CheckLoopItrVariableIsInt(x);
422 CheckCycleConstraints(x);
423 HasInvalidDistributeNesting(x);
424 if (CurrentDirectiveIsNested() &&
425 llvm::omp::teamSet.test(GetContextParent().directive)) {
426 HasInvalidTeamsNesting(beginDir.v, beginDir.source);
428 if ((beginDir.v == llvm::omp::Directive::OMPD_distribute_parallel_do_simd) ||
429 (beginDir.v == llvm::omp::Directive::OMPD_distribute_simd)) {
430 CheckDistLinear(x);
433 const parser::Name OmpStructureChecker::GetLoopIndex(
434 const parser::DoConstruct *x) {
435 using Bounds = parser::LoopControl::Bounds;
436 return std::get<Bounds>(x->GetLoopControl()->u).name.thing;
438 void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) {
439 if (const auto &loopConstruct{
440 std::get<std::optional<parser::DoConstruct>>(x.t)}) {
441 const parser::DoConstruct *loop{&*loopConstruct};
442 if (loop && loop->IsDoNormal()) {
443 const parser::Name &itrVal{GetLoopIndex(loop)};
444 SetLoopIv(itrVal.symbol);
448 void OmpStructureChecker::CheckDoWhile(const parser::OpenMPLoopConstruct &x) {
449 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
450 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
451 if (beginDir.v == llvm::omp::Directive::OMPD_do) {
452 if (const auto &doConstruct{
453 std::get<std::optional<parser::DoConstruct>>(x.t)}) {
454 if (doConstruct.value().IsDoWhile()) {
455 const auto &doStmt{std::get<parser::Statement<parser::NonLabelDoStmt>>(
456 doConstruct.value().t)};
457 context_.Say(doStmt.source,
458 "The DO loop cannot be a DO WHILE with DO directive."_err_en_US);
464 void OmpStructureChecker::CheckLoopItrVariableIsInt(
465 const parser::OpenMPLoopConstruct &x) {
466 if (const auto &loopConstruct{
467 std::get<std::optional<parser::DoConstruct>>(x.t)}) {
469 for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
470 if (loop->IsDoNormal()) {
471 const parser::Name &itrVal{GetLoopIndex(loop)};
472 if (itrVal.symbol) {
473 const auto *type{itrVal.symbol->GetType()};
474 if (!type->IsNumeric(TypeCategory::Integer)) {
475 context_.Say(itrVal.source,
476 "The DO loop iteration"
477 " variable must be of the type integer."_err_en_US,
478 itrVal.ToString());
482 // Get the next DoConstruct if block is not empty.
483 const auto &block{std::get<parser::Block>(loop->t)};
484 const auto it{block.begin()};
485 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
486 : nullptr;
491 void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
492 // Check the following:
493 // The only OpenMP constructs that can be encountered during execution of
494 // a simd region are the `atomic` construct, the `loop` construct, the `simd`
495 // construct and the `ordered` construct with the `simd` clause.
496 // TODO: Expand the check to include `LOOP` construct as well when it is
497 // supported.
499 // Check if the parent context has the SIMD clause
500 // Please note that we use GetContext() instead of GetContextParent()
501 // because PushContextAndClauseSets() has not been called on the
502 // current context yet.
503 // TODO: Check for declare simd regions.
504 bool eligibleSIMD{false};
505 common::visit(Fortran::common::visitors{
506 // Allow `!$OMP ORDERED SIMD`
507 [&](const parser::OpenMPBlockConstruct &c) {
508 const auto &beginBlockDir{
509 std::get<parser::OmpBeginBlockDirective>(c.t)};
510 const auto &beginDir{
511 std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
512 if (beginDir.v == llvm::omp::Directive::OMPD_ordered) {
513 const auto &clauses{
514 std::get<parser::OmpClauseList>(beginBlockDir.t)};
515 for (const auto &clause : clauses.v) {
516 if (std::get_if<parser::OmpClause::Simd>(&clause.u)) {
517 eligibleSIMD = true;
518 break;
523 [&](const parser::OpenMPSimpleStandaloneConstruct &c) {
524 const auto &dir{
525 std::get<parser::OmpSimpleStandaloneDirective>(c.t)};
526 if (dir.v == llvm::omp::Directive::OMPD_ordered) {
527 const auto &clauses{
528 std::get<parser::OmpClauseList>(c.t)};
529 for (const auto &clause : clauses.v) {
530 if (std::get_if<parser::OmpClause::Simd>(&clause.u)) {
531 eligibleSIMD = true;
532 break;
537 // Allowing SIMD construct
538 [&](const parser::OpenMPLoopConstruct &c) {
539 const auto &beginLoopDir{
540 std::get<parser::OmpBeginLoopDirective>(c.t)};
541 const auto &beginDir{
542 std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
543 if ((beginDir.v == llvm::omp::Directive::OMPD_simd) ||
544 (beginDir.v == llvm::omp::Directive::OMPD_do_simd)) {
545 eligibleSIMD = true;
548 [&](const parser::OpenMPAtomicConstruct &c) {
549 // Allow `!$OMP ATOMIC`
550 eligibleSIMD = true;
552 [&](const auto &c) {},
554 c.u);
555 if (!eligibleSIMD) {
556 context_.Say(parser::FindSourceLocation(c),
557 "The only OpenMP constructs that can be encountered during execution "
558 "of a 'SIMD' region are the `ATOMIC` construct, the `LOOP` construct, "
559 "the `SIMD` construct and the `ORDERED` construct with the `SIMD` "
560 "clause."_err_en_US);
564 void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) {
565 // 2.12.5 Target Construct Restriction
566 bool eligibleTarget{true};
567 llvm::omp::Directive ineligibleTargetDir;
568 common::visit(
569 common::visitors{
570 [&](const parser::OpenMPBlockConstruct &c) {
571 const auto &beginBlockDir{
572 std::get<parser::OmpBeginBlockDirective>(c.t)};
573 const auto &beginDir{
574 std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
575 if (beginDir.v == llvm::omp::Directive::OMPD_target_data) {
576 eligibleTarget = false;
577 ineligibleTargetDir = beginDir.v;
580 [&](const parser::OpenMPStandaloneConstruct &c) {
581 common::visit(
582 common::visitors{
583 [&](const parser::OpenMPSimpleStandaloneConstruct &c) {
584 const auto &dir{
585 std::get<parser::OmpSimpleStandaloneDirective>(c.t)};
586 if (dir.v == llvm::omp::Directive::OMPD_target_update ||
587 dir.v ==
588 llvm::omp::Directive::OMPD_target_enter_data ||
589 dir.v ==
590 llvm::omp::Directive::OMPD_target_exit_data) {
591 eligibleTarget = false;
592 ineligibleTargetDir = dir.v;
595 [&](const auto &c) {},
597 c.u);
599 [&](const auto &c) {},
601 c.u);
602 if (!eligibleTarget) {
603 context_.Say(parser::FindSourceLocation(c),
604 "If %s directive is nested inside TARGET region, the behaviour "
605 "is unspecified"_port_en_US,
606 parser::ToUpperCaseLetters(
607 getDirectiveName(ineligibleTargetDir).str()));
611 std::int64_t OmpStructureChecker::GetOrdCollapseLevel(
612 const parser::OpenMPLoopConstruct &x) {
613 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
614 const auto &clauseList{std::get<parser::OmpClauseList>(beginLoopDir.t)};
615 std::int64_t orderedCollapseLevel{1};
616 std::int64_t orderedLevel{0};
617 std::int64_t collapseLevel{0};
619 for (const auto &clause : clauseList.v) {
620 if (const auto *collapseClause{
621 std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
622 if (const auto v{GetIntValue(collapseClause->v)}) {
623 collapseLevel = *v;
626 if (const auto *orderedClause{
627 std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
628 if (const auto v{GetIntValue(orderedClause->v)}) {
629 orderedLevel = *v;
633 if (orderedLevel >= collapseLevel) {
634 orderedCollapseLevel = orderedLevel;
635 } else {
636 orderedCollapseLevel = collapseLevel;
638 return orderedCollapseLevel;
641 void OmpStructureChecker::CheckCycleConstraints(
642 const parser::OpenMPLoopConstruct &x) {
643 std::int64_t ordCollapseLevel{GetOrdCollapseLevel(x)};
644 OmpCycleChecker ompCycleChecker{context_, ordCollapseLevel};
645 parser::Walk(x, ompCycleChecker);
648 void OmpStructureChecker::CheckDistLinear(
649 const parser::OpenMPLoopConstruct &x) {
651 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
652 const auto &clauses{std::get<parser::OmpClauseList>(beginLoopDir.t)};
654 semantics::UnorderedSymbolSet indexVars;
656 // Collect symbols of all the variables from linear clauses
657 for (const auto &clause : clauses.v) {
658 if (const auto *linearClause{
659 std::get_if<parser::OmpClause::Linear>(&clause.u)}) {
661 std::list<parser::Name> values;
662 // Get the variant type
663 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(
664 linearClause->v.u)) {
665 const auto &withM{
666 std::get<parser::OmpLinearClause::WithModifier>(linearClause->v.u)};
667 values = withM.names;
668 } else {
669 const auto &withOutM{std::get<parser::OmpLinearClause::WithoutModifier>(
670 linearClause->v.u)};
671 values = withOutM.names;
673 for (auto const &v : values) {
674 indexVars.insert(*(v.symbol));
679 if (!indexVars.empty()) {
680 // Get collapse level, if given, to find which loops are "associated."
681 std::int64_t collapseVal{GetOrdCollapseLevel(x)};
682 // Include the top loop if no collapse is specified
683 if (collapseVal == 0) {
684 collapseVal = 1;
687 // Match the loop index variables with the collected symbols from linear
688 // clauses.
689 if (const auto &loopConstruct{
690 std::get<std::optional<parser::DoConstruct>>(x.t)}) {
691 for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
692 if (loop->IsDoNormal()) {
693 const parser::Name &itrVal{GetLoopIndex(loop)};
694 if (itrVal.symbol) {
695 // Remove the symbol from the collcted set
696 indexVars.erase(*(itrVal.symbol));
698 collapseVal--;
699 if (collapseVal == 0) {
700 break;
703 // Get the next DoConstruct if block is not empty.
704 const auto &block{std::get<parser::Block>(loop->t)};
705 const auto it{block.begin()};
706 loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
707 : nullptr;
711 // Show error for the remaining variables
712 for (auto var : indexVars) {
713 const Symbol &root{GetAssociationRoot(var)};
714 context_.Say(parser::FindSourceLocation(x),
715 "Variable '%s' not allowed in `LINEAR` clause, only loop iterator "
716 "can be specified in `LINEAR` clause of a construct combined with "
717 "`DISTRIBUTE`"_err_en_US,
718 root.name());
723 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
724 if (llvm::omp::simdSet.test(GetContext().directive)) {
725 ExitDirectiveNest(SIMDNest);
727 dirContext_.pop_back();
730 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
731 const auto &dir{std::get<parser::OmpLoopDirective>(x.t)};
732 ResetPartialContext(dir.source);
733 switch (dir.v) {
734 // 2.7.1 end-do -> END DO [nowait-clause]
735 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
736 case llvm::omp::Directive::OMPD_do:
737 case llvm::omp::Directive::OMPD_do_simd:
738 SetClauseSets(dir.v);
739 break;
740 default:
741 // no clauses are allowed
742 break;
746 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
747 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
748 const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)};
749 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
750 const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)};
751 const parser::Block &block{std::get<parser::Block>(x.t)};
753 CheckMatching<parser::OmpBlockDirective>(beginDir, endDir);
755 PushContextAndClauseSets(beginDir.source, beginDir.v);
756 if (GetContext().directive == llvm::omp::Directive::OMPD_target) {
757 EnterDirectiveNest(TargetNest);
760 if (CurrentDirectiveIsNested()) {
761 if (llvm::omp::teamSet.test(GetContextParent().directive)) {
762 HasInvalidTeamsNesting(beginDir.v, beginDir.source);
764 if (GetContext().directive == llvm::omp::Directive::OMPD_master) {
765 CheckMasterNesting(x);
767 // A teams region can only be strictly nested within the implicit parallel
768 // region or a target region.
769 if (GetContext().directive == llvm::omp::Directive::OMPD_teams &&
770 GetContextParent().directive != llvm::omp::Directive::OMPD_target) {
771 context_.Say(parser::FindSourceLocation(x),
772 "%s region can only be strictly nested within the implicit parallel "
773 "region or TARGET region"_err_en_US,
774 ContextDirectiveAsFortran());
776 // If a teams construct is nested within a target construct, that target
777 // construct must contain no statements, declarations or directives outside
778 // of the teams construct.
779 if (GetContext().directive == llvm::omp::Directive::OMPD_teams &&
780 GetContextParent().directive == llvm::omp::Directive::OMPD_target &&
781 !GetDirectiveNest(TargetBlockOnlyTeams)) {
782 context_.Say(GetContextParent().directiveSource,
783 "TARGET construct with nested TEAMS region contains statements or "
784 "directives outside of the TEAMS construct"_err_en_US);
788 CheckNoBranching(block, beginDir.v, beginDir.source);
790 switch (beginDir.v) {
791 case llvm::omp::Directive::OMPD_target:
792 if (CheckTargetBlockOnlyTeams(block)) {
793 EnterDirectiveNest(TargetBlockOnlyTeams);
795 break;
796 case llvm::omp::OMPD_workshare:
797 case llvm::omp::OMPD_parallel_workshare:
798 CheckWorkshareBlockStmts(block, beginDir.source);
799 HasInvalidWorksharingNesting(
800 beginDir.source, llvm::omp::nestedWorkshareErrSet);
801 break;
802 case llvm::omp::Directive::OMPD_single:
803 // TODO: This check needs to be extended while implementing nesting of
804 // regions checks.
805 HasInvalidWorksharingNesting(
806 beginDir.source, llvm::omp::nestedWorkshareErrSet);
807 break;
808 default:
809 break;
813 void OmpStructureChecker::CheckMasterNesting(
814 const parser::OpenMPBlockConstruct &x) {
815 // A MASTER region may not be `closely nested` inside a worksharing, loop,
816 // task, taskloop, or atomic region.
817 // TODO: Expand the check to include `LOOP` construct as well when it is
818 // supported.
819 if (IsCloselyNestedRegion(llvm::omp::nestedMasterErrSet)) {
820 context_.Say(parser::FindSourceLocation(x),
821 "`MASTER` region may not be closely nested inside of `WORKSHARING`, "
822 "`LOOP`, `TASK`, `TASKLOOP`,"
823 " or `ATOMIC` region."_err_en_US);
827 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
828 if (GetDirectiveNest(TargetBlockOnlyTeams)) {
829 ExitDirectiveNest(TargetBlockOnlyTeams);
831 if (GetContext().directive == llvm::omp::Directive::OMPD_target) {
832 ExitDirectiveNest(TargetNest);
834 dirContext_.pop_back();
837 void OmpStructureChecker::ChecksOnOrderedAsBlock() {
838 if (FindClause(llvm::omp::Clause::OMPC_depend)) {
839 context_.Say(GetContext().clauseSource,
840 "DEPEND(*) clauses are not allowed when ORDERED construct is a block"
841 " construct with an ORDERED region"_err_en_US);
842 return;
845 OmpDirectiveSet notAllowedParallelSet{llvm::omp::Directive::OMPD_parallel,
846 llvm::omp::Directive::OMPD_target_parallel,
847 llvm::omp::Directive::OMPD_parallel_sections,
848 llvm::omp::Directive::OMPD_parallel_workshare};
849 bool isNestedInDo{false};
850 bool isNestedInDoSIMD{false};
851 bool isNestedInSIMD{false};
852 bool noOrderedClause{false};
853 bool isOrderedClauseWithPara{false};
854 bool isCloselyNestedRegion{true};
855 if (CurrentDirectiveIsNested()) {
856 for (int i = (int)dirContext_.size() - 2; i >= 0; i--) {
857 if (llvm::omp::nestedOrderedErrSet.test(dirContext_[i].directive)) {
858 context_.Say(GetContext().directiveSource,
859 "`ORDERED` region may not be closely nested inside of `CRITICAL`, "
860 "`ORDERED`, explicit `TASK` or `TASKLOOP` region."_err_en_US);
861 break;
862 } else if (llvm::omp::doSet.test(dirContext_[i].directive)) {
863 isNestedInDo = true;
864 isNestedInDoSIMD = llvm::omp::doSimdSet.test(dirContext_[i].directive);
865 if (const auto *clause{
866 FindClause(dirContext_[i], llvm::omp::Clause::OMPC_ordered)}) {
867 const auto &orderedClause{
868 std::get<parser::OmpClause::Ordered>(clause->u)};
869 const auto orderedValue{GetIntValue(orderedClause.v)};
870 isOrderedClauseWithPara = orderedValue > 0;
871 } else {
872 noOrderedClause = true;
874 break;
875 } else if (llvm::omp::simdSet.test(dirContext_[i].directive)) {
876 isNestedInSIMD = true;
877 break;
878 } else if (notAllowedParallelSet.test(dirContext_[i].directive)) {
879 isCloselyNestedRegion = false;
880 break;
885 if (!isCloselyNestedRegion) {
886 context_.Say(GetContext().directiveSource,
887 "An ORDERED directive without the DEPEND clause must be closely nested "
888 "in a SIMD, worksharing-loop, or worksharing-loop SIMD "
889 "region"_err_en_US);
890 } else {
891 if (CurrentDirectiveIsNested() &&
892 FindClause(llvm::omp::Clause::OMPC_simd) &&
893 (!isNestedInDoSIMD && !isNestedInSIMD)) {
894 context_.Say(GetContext().directiveSource,
895 "An ORDERED directive with SIMD clause must be closely nested in a "
896 "SIMD or worksharing-loop SIMD region"_err_en_US);
898 if (isNestedInDo && (noOrderedClause || isOrderedClauseWithPara)) {
899 context_.Say(GetContext().directiveSource,
900 "An ORDERED directive without the DEPEND clause must be closely "
901 "nested in a worksharing-loop (or worksharing-loop SIMD) region with "
902 "ORDERED clause without the parameter"_err_en_US);
907 void OmpStructureChecker::Leave(const parser::OmpBeginBlockDirective &) {
908 switch (GetContext().directive) {
909 case llvm::omp::Directive::OMPD_ordered:
910 // [5.1] 2.19.9 Ordered Construct Restriction
911 ChecksOnOrderedAsBlock();
912 break;
913 default:
914 break;
918 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
919 const auto &beginSectionsDir{
920 std::get<parser::OmpBeginSectionsDirective>(x.t)};
921 const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)};
922 const auto &beginDir{
923 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
924 const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)};
925 CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir);
927 PushContextAndClauseSets(beginDir.source, beginDir.v);
928 const auto &sectionBlocks{std::get<parser::OmpSectionBlocks>(x.t)};
929 for (const parser::OpenMPConstruct &block : sectionBlocks.v) {
930 CheckNoBranching(std::get<parser::OpenMPSectionConstruct>(block.u).v,
931 beginDir.v, beginDir.source);
933 HasInvalidWorksharingNesting(
934 beginDir.source, llvm::omp::nestedWorkshareErrSet);
937 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
938 dirContext_.pop_back();
941 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
942 const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)};
943 ResetPartialContext(dir.source);
944 switch (dir.v) {
945 // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
946 case llvm::omp::Directive::OMPD_sections:
947 PushContextAndClauseSets(
948 dir.source, llvm::omp::Directive::OMPD_end_sections);
949 break;
950 default:
951 // no clauses are allowed
952 break;
956 // TODO: Verify the popping of dirContext requirement after nowait
957 // implementation, as there is an implicit barrier at the end of the worksharing
958 // constructs unless a nowait clause is specified. Only OMPD_end_sections is
959 // popped becuase it is pushed while entering the EndSectionsDirective.
960 void OmpStructureChecker::Leave(const parser::OmpEndSectionsDirective &x) {
961 if (GetContext().directive == llvm::omp::Directive::OMPD_end_sections) {
962 dirContext_.pop_back();
966 void OmpStructureChecker::CheckThreadprivateOrDeclareTargetVar(
967 const parser::OmpObjectList &objList) {
968 for (const auto &ompObject : objList.v) {
969 common::visit(
970 common::visitors{
971 [&](const parser::Designator &) {
972 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) {
973 if (name->symbol->GetUltimate().IsSubprogram()) {
974 if (GetContext().directive ==
975 llvm::omp::Directive::OMPD_threadprivate)
976 context_.Say(name->source,
977 "The procedure name cannot be in a %s "
978 "directive"_err_en_US,
979 ContextDirectiveAsFortran());
980 // TODO: Check for procedure name in declare target directive.
981 } else if (name->symbol->attrs().test(Attr::PARAMETER)) {
982 if (GetContext().directive ==
983 llvm::omp::Directive::OMPD_threadprivate)
984 context_.Say(name->source,
985 "The entity with PARAMETER attribute cannot be in a %s "
986 "directive"_err_en_US,
987 ContextDirectiveAsFortran());
988 else if (GetContext().directive ==
989 llvm::omp::Directive::OMPD_declare_target)
990 context_.Say(name->source,
991 "The entity with PARAMETER attribute is used in a %s "
992 "directive"_warn_en_US,
993 ContextDirectiveAsFortran());
994 } else if (FindCommonBlockContaining(*name->symbol)) {
995 context_.Say(name->source,
996 "A variable in a %s directive cannot be an element of a "
997 "common block"_err_en_US,
998 ContextDirectiveAsFortran());
999 } else if (FindEquivalenceSet(*name->symbol)) {
1000 context_.Say(name->source,
1001 "A variable in a %s directive cannot appear in an "
1002 "EQUIVALENCE statement"_err_en_US,
1003 ContextDirectiveAsFortran());
1004 } else if (name->symbol->test(Symbol::Flag::OmpThreadprivate) &&
1005 GetContext().directive ==
1006 llvm::omp::Directive::OMPD_declare_target) {
1007 context_.Say(name->source,
1008 "A THREADPRIVATE variable cannot appear in a %s "
1009 "directive"_err_en_US,
1010 ContextDirectiveAsFortran());
1011 } else {
1012 const semantics::Scope &useScope{
1013 context_.FindScope(GetContext().directiveSource)};
1014 const semantics::Scope &curScope =
1015 name->symbol->GetUltimate().owner();
1016 if (!curScope.IsTopLevel()) {
1017 const semantics::Scope &declScope =
1018 GetProgramUnitContaining(curScope);
1019 const semantics::Symbol *sym{
1020 declScope.parent().FindSymbol(name->symbol->name())};
1021 if (sym &&
1022 (sym->has<MainProgramDetails>() ||
1023 sym->has<ModuleDetails>())) {
1024 context_.Say(name->source,
1025 "The module name or main program name cannot be in a "
1026 "%s "
1027 "directive"_err_en_US,
1028 ContextDirectiveAsFortran());
1029 } else if (!IsSave(*name->symbol) &&
1030 declScope.kind() != Scope::Kind::MainProgram &&
1031 declScope.kind() != Scope::Kind::Module) {
1032 context_.Say(name->source,
1033 "A variable that appears in a %s directive must be "
1034 "declared in the scope of a module or have the SAVE "
1035 "attribute, either explicitly or "
1036 "implicitly"_err_en_US,
1037 ContextDirectiveAsFortran());
1038 } else if (useScope != declScope) {
1039 context_.Say(name->source,
1040 "The %s directive and the common block or variable "
1041 "in it must appear in the same declaration section "
1042 "of a scoping unit"_err_en_US,
1043 ContextDirectiveAsFortran());
1049 [&](const parser::Name &) {}, // common block
1051 ompObject.u);
1055 void OmpStructureChecker::Enter(const parser::OpenMPThreadprivate &c) {
1056 const auto &dir{std::get<parser::Verbatim>(c.t)};
1057 PushContextAndClauseSets(
1058 dir.source, llvm::omp::Directive::OMPD_threadprivate);
1061 void OmpStructureChecker::Leave(const parser::OpenMPThreadprivate &c) {
1062 const auto &dir{std::get<parser::Verbatim>(c.t)};
1063 const auto &objectList{std::get<parser::OmpObjectList>(c.t)};
1064 CheckIsVarPartOfAnotherVar(dir.source, objectList);
1065 CheckThreadprivateOrDeclareTargetVar(objectList);
1066 dirContext_.pop_back();
1069 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
1070 const auto &dir{std::get<parser::Verbatim>(x.t)};
1071 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd);
1074 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
1075 dirContext_.pop_back();
1078 void OmpStructureChecker::Enter(const parser::OpenMPRequiresConstruct &x) {
1079 const auto &dir{std::get<parser::Verbatim>(x.t)};
1080 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_requires);
1083 void OmpStructureChecker::Leave(const parser::OpenMPRequiresConstruct &) {
1084 dirContext_.pop_back();
1087 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
1088 isPredefinedAllocator = true;
1089 const auto &dir{std::get<parser::Verbatim>(x.t)};
1090 const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
1091 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
1092 CheckIsVarPartOfAnotherVar(dir.source, objectList);
1095 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &x) {
1096 const auto &dir{std::get<parser::Verbatim>(x.t)};
1097 const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
1098 CheckPredefinedAllocatorRestriction(dir.source, objectList);
1099 dirContext_.pop_back();
1102 void OmpStructureChecker::Enter(const parser::OmpClause::Allocator &x) {
1103 CheckAllowed(llvm::omp::Clause::OMPC_allocator);
1104 // Note: Predefined allocators are stored in ScalarExpr as numbers
1105 // whereas custom allocators are stored as strings, so if the ScalarExpr
1106 // actually has an int value, then it must be a predefined allocator
1107 isPredefinedAllocator = GetIntValue(x.v).has_value();
1108 RequiresPositiveParameter(llvm::omp::Clause::OMPC_allocator, x.v);
1111 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
1112 const auto &dir{std::get<parser::Verbatim>(x.t)};
1113 PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target);
1114 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
1115 if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) {
1116 SetClauseSets(llvm::omp::Directive::OMPD_declare_target);
1120 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) {
1121 const auto &dir{std::get<parser::Verbatim>(x.t)};
1122 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
1123 if (const auto *objectList{parser::Unwrap<parser::OmpObjectList>(spec.u)}) {
1124 CheckIsVarPartOfAnotherVar(dir.source, *objectList);
1125 CheckThreadprivateOrDeclareTargetVar(*objectList);
1126 } else if (const auto *clauseList{
1127 parser::Unwrap<parser::OmpClauseList>(spec.u)}) {
1128 for (const auto &clause : clauseList->v) {
1129 if (const auto *toClause{std::get_if<parser::OmpClause::To>(&clause.u)}) {
1130 CheckIsVarPartOfAnotherVar(dir.source, toClause->v);
1131 CheckThreadprivateOrDeclareTargetVar(toClause->v);
1132 } else if (const auto *linkClause{
1133 std::get_if<parser::OmpClause::Link>(&clause.u)}) {
1134 CheckIsVarPartOfAnotherVar(dir.source, linkClause->v);
1135 CheckThreadprivateOrDeclareTargetVar(linkClause->v);
1139 dirContext_.pop_back();
1142 void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
1143 isPredefinedAllocator = true;
1144 const auto &dir{std::get<parser::Verbatim>(x.t)};
1145 const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)};
1146 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
1147 if (objectList) {
1148 CheckIsVarPartOfAnotherVar(dir.source, *objectList);
1152 void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &x) {
1153 const auto &dir{std::get<parser::Verbatim>(x.t)};
1154 const auto &objectList{std::get<std::optional<parser::OmpObjectList>>(x.t)};
1155 if (objectList)
1156 CheckPredefinedAllocatorRestriction(dir.source, *objectList);
1157 dirContext_.pop_back();
1160 void OmpStructureChecker::CheckBarrierNesting(
1161 const parser::OpenMPSimpleStandaloneConstruct &x) {
1162 // A barrier region may not be `closely nested` inside a worksharing, loop,
1163 // task, taskloop, critical, ordered, atomic, or master region.
1164 // TODO: Expand the check to include `LOOP` construct as well when it is
1165 // supported.
1166 if (GetContext().directive == llvm::omp::Directive::OMPD_barrier) {
1167 if (IsCloselyNestedRegion(llvm::omp::nestedBarrierErrSet)) {
1168 context_.Say(parser::FindSourceLocation(x),
1169 "`BARRIER` region may not be closely nested inside of `WORKSHARING`, "
1170 "`LOOP`, `TASK`, `TASKLOOP`,"
1171 "`CRITICAL`, `ORDERED`, `ATOMIC` or `MASTER` region."_err_en_US);
1176 void OmpStructureChecker::ChecksOnOrderedAsStandalone() {
1177 if (FindClause(llvm::omp::Clause::OMPC_threads) ||
1178 FindClause(llvm::omp::Clause::OMPC_simd)) {
1179 context_.Say(GetContext().clauseSource,
1180 "THREADS, SIMD clauses are not allowed when ORDERED construct is a "
1181 "standalone construct with no ORDERED region"_err_en_US);
1184 bool isSinkPresent{false};
1185 int dependSourceCount{0};
1186 auto clauseAll = FindClauses(llvm::omp::Clause::OMPC_depend);
1187 for (auto itr = clauseAll.first; itr != clauseAll.second; ++itr) {
1188 const auto &dependClause{
1189 std::get<parser::OmpClause::Depend>(itr->second->u)};
1190 if (std::get_if<parser::OmpDependClause::Source>(&dependClause.v.u)) {
1191 dependSourceCount++;
1192 if (isSinkPresent) {
1193 context_.Say(itr->second->source,
1194 "DEPEND(SOURCE) is not allowed when DEPEND(SINK: vec) is present "
1195 "on ORDERED directive"_err_en_US);
1197 if (dependSourceCount > 1) {
1198 context_.Say(itr->second->source,
1199 "At most one DEPEND(SOURCE) clause can appear on the ORDERED "
1200 "directive"_err_en_US);
1202 } else if (std::get_if<parser::OmpDependClause::Sink>(&dependClause.v.u)) {
1203 isSinkPresent = true;
1204 if (dependSourceCount > 0) {
1205 context_.Say(itr->second->source,
1206 "DEPEND(SINK: vec) is not allowed when DEPEND(SOURCE) is present "
1207 "on ORDERED directive"_err_en_US);
1209 } else {
1210 context_.Say(itr->second->source,
1211 "Only DEPEND(SOURCE) or DEPEND(SINK: vec) are allowed when ORDERED "
1212 "construct is a standalone construct with no ORDERED "
1213 "region"_err_en_US);
1217 OmpDirectiveSet allowedDoSet{llvm::omp::Directive::OMPD_do,
1218 llvm::omp::Directive::OMPD_parallel_do,
1219 llvm::omp::Directive::OMPD_target_parallel_do};
1220 bool isNestedInDoOrderedWithPara{false};
1221 if (CurrentDirectiveIsNested() &&
1222 allowedDoSet.test(GetContextParent().directive)) {
1223 if (const auto *clause{
1224 FindClause(GetContextParent(), llvm::omp::Clause::OMPC_ordered)}) {
1225 const auto &orderedClause{
1226 std::get<parser::OmpClause::Ordered>(clause->u)};
1227 const auto orderedValue{GetIntValue(orderedClause.v)};
1228 if (orderedValue > 0) {
1229 isNestedInDoOrderedWithPara = true;
1230 CheckOrderedDependClause(orderedValue);
1235 if (FindClause(llvm::omp::Clause::OMPC_depend) &&
1236 !isNestedInDoOrderedWithPara) {
1237 context_.Say(GetContext().clauseSource,
1238 "An ORDERED construct with the DEPEND clause must be closely nested "
1239 "in a worksharing-loop (or parallel worksharing-loop) construct with "
1240 "ORDERED clause with a parameter"_err_en_US);
1244 void OmpStructureChecker::CheckOrderedDependClause(
1245 std::optional<std::int64_t> orderedValue) {
1246 auto clauseAll{FindClauses(llvm::omp::Clause::OMPC_depend)};
1247 for (auto itr = clauseAll.first; itr != clauseAll.second; ++itr) {
1248 const auto &dependClause{
1249 std::get<parser::OmpClause::Depend>(itr->second->u)};
1250 if (const auto *sinkVectors{
1251 std::get_if<parser::OmpDependClause::Sink>(&dependClause.v.u)}) {
1252 std::int64_t numVar = sinkVectors->v.size();
1253 if (orderedValue != numVar) {
1254 context_.Say(itr->second->source,
1255 "The number of variables in DEPEND(SINK: vec) clause does not "
1256 "match the parameter specified in ORDERED clause"_err_en_US);
1262 void OmpStructureChecker::Enter(
1263 const parser::OpenMPSimpleStandaloneConstruct &x) {
1264 const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
1265 PushContextAndClauseSets(dir.source, dir.v);
1266 CheckBarrierNesting(x);
1269 void OmpStructureChecker::Leave(
1270 const parser::OpenMPSimpleStandaloneConstruct &) {
1271 switch (GetContext().directive) {
1272 case llvm::omp::Directive::OMPD_ordered:
1273 // [5.1] 2.19.9 Ordered Construct Restriction
1274 ChecksOnOrderedAsStandalone();
1275 break;
1276 default:
1277 break;
1279 dirContext_.pop_back();
1282 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
1283 const auto &dir{std::get<parser::Verbatim>(x.t)};
1284 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_flush);
1287 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
1288 if (FindClause(llvm::omp::Clause::OMPC_acquire) ||
1289 FindClause(llvm::omp::Clause::OMPC_release) ||
1290 FindClause(llvm::omp::Clause::OMPC_acq_rel)) {
1291 if (const auto &flushList{
1292 std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
1293 context_.Say(parser::FindSourceLocation(flushList),
1294 "If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items "
1295 "must not be specified on the FLUSH directive"_err_en_US);
1298 dirContext_.pop_back();
1301 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
1302 const auto &dir{std::get<parser::Verbatim>(x.t)};
1303 const auto &type{std::get<parser::OmpCancelType>(x.t)};
1304 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel);
1305 CheckCancellationNest(dir.source, type.v);
1308 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
1309 dirContext_.pop_back();
1312 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) {
1313 const auto &dir{std::get<parser::OmpCriticalDirective>(x.t)};
1314 const auto &endDir{std::get<parser::OmpEndCriticalDirective>(x.t)};
1315 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_critical);
1316 const auto &block{std::get<parser::Block>(x.t)};
1317 CheckNoBranching(block, llvm::omp::Directive::OMPD_critical, dir.source);
1318 const auto &dirName{std::get<std::optional<parser::Name>>(dir.t)};
1319 const auto &endDirName{std::get<std::optional<parser::Name>>(endDir.t)};
1320 const auto &ompClause{std::get<parser::OmpClauseList>(dir.t)};
1321 if (dirName && endDirName &&
1322 dirName->ToString().compare(endDirName->ToString())) {
1323 context_
1324 .Say(endDirName->source,
1325 parser::MessageFormattedText{
1326 "CRITICAL directive names do not match"_err_en_US})
1327 .Attach(dirName->source, "should be "_en_US);
1328 } else if (dirName && !endDirName) {
1329 context_
1330 .Say(dirName->source,
1331 parser::MessageFormattedText{
1332 "CRITICAL directive names do not match"_err_en_US})
1333 .Attach(dirName->source, "should be NULL"_en_US);
1334 } else if (!dirName && endDirName) {
1335 context_
1336 .Say(endDirName->source,
1337 parser::MessageFormattedText{
1338 "CRITICAL directive names do not match"_err_en_US})
1339 .Attach(endDirName->source, "should be NULL"_en_US);
1341 if (!dirName && !ompClause.source.empty() &&
1342 ompClause.source.NULTerminatedToString() != "hint(omp_sync_hint_none)") {
1343 context_.Say(dir.source,
1344 parser::MessageFormattedText{
1345 "Hint clause other than omp_sync_hint_none cannot be specified for "
1346 "an unnamed CRITICAL directive"_err_en_US});
1348 CheckHintClause<const parser::OmpClauseList>(&ompClause, nullptr);
1351 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) {
1352 dirContext_.pop_back();
1355 void OmpStructureChecker::Enter(
1356 const parser::OpenMPCancellationPointConstruct &x) {
1357 const auto &dir{std::get<parser::Verbatim>(x.t)};
1358 const auto &type{std::get<parser::OmpCancelType>(x.t)};
1359 PushContextAndClauseSets(
1360 dir.source, llvm::omp::Directive::OMPD_cancellation_point);
1361 CheckCancellationNest(dir.source, type.v);
1364 void OmpStructureChecker::Leave(
1365 const parser::OpenMPCancellationPointConstruct &) {
1366 dirContext_.pop_back();
1369 void OmpStructureChecker::CheckCancellationNest(
1370 const parser::CharBlock &source, const parser::OmpCancelType::Type &type) {
1371 if (CurrentDirectiveIsNested()) {
1372 // If construct-type-clause is taskgroup, the cancellation construct must be
1373 // closely nested inside a task or a taskloop construct and the cancellation
1374 // region must be closely nested inside a taskgroup region. If
1375 // construct-type-clause is sections, the cancellation construct must be
1376 // closely nested inside a sections or section construct. Otherwise, the
1377 // cancellation construct must be closely nested inside an OpenMP construct
1378 // that matches the type specified in construct-type-clause of the
1379 // cancellation construct.
1381 OmpDirectiveSet allowedTaskgroupSet{
1382 llvm::omp::Directive::OMPD_task, llvm::omp::Directive::OMPD_taskloop};
1383 OmpDirectiveSet allowedSectionsSet{llvm::omp::Directive::OMPD_sections,
1384 llvm::omp::Directive::OMPD_parallel_sections};
1385 OmpDirectiveSet allowedDoSet{llvm::omp::Directive::OMPD_do,
1386 llvm::omp::Directive::OMPD_distribute_parallel_do,
1387 llvm::omp::Directive::OMPD_parallel_do,
1388 llvm::omp::Directive::OMPD_target_parallel_do,
1389 llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do,
1390 llvm::omp::Directive::OMPD_teams_distribute_parallel_do};
1391 OmpDirectiveSet allowedParallelSet{llvm::omp::Directive::OMPD_parallel,
1392 llvm::omp::Directive::OMPD_target_parallel};
1394 bool eligibleCancellation{false};
1395 switch (type) {
1396 case parser::OmpCancelType::Type::Taskgroup:
1397 if (allowedTaskgroupSet.test(GetContextParent().directive)) {
1398 eligibleCancellation = true;
1399 if (dirContext_.size() >= 3) {
1400 // Check if the cancellation region is closely nested inside a
1401 // taskgroup region when there are more than two levels of directives
1402 // in the directive context stack.
1403 if (GetContextParent().directive == llvm::omp::Directive::OMPD_task ||
1404 FindClauseParent(llvm::omp::Clause::OMPC_nogroup)) {
1405 for (int i = dirContext_.size() - 3; i >= 0; i--) {
1406 if (dirContext_[i].directive ==
1407 llvm::omp::Directive::OMPD_taskgroup) {
1408 break;
1410 if (allowedParallelSet.test(dirContext_[i].directive)) {
1411 eligibleCancellation = false;
1412 break;
1418 if (!eligibleCancellation) {
1419 context_.Say(source,
1420 "With %s clause, %s construct must be closely nested inside TASK "
1421 "or TASKLOOP construct and %s region must be closely nested inside "
1422 "TASKGROUP region"_err_en_US,
1423 parser::ToUpperCaseLetters(
1424 parser::OmpCancelType::EnumToString(type)),
1425 ContextDirectiveAsFortran(), ContextDirectiveAsFortran());
1427 return;
1428 case parser::OmpCancelType::Type::Sections:
1429 if (allowedSectionsSet.test(GetContextParent().directive)) {
1430 eligibleCancellation = true;
1432 break;
1433 case Fortran::parser::OmpCancelType::Type::Do:
1434 if (allowedDoSet.test(GetContextParent().directive)) {
1435 eligibleCancellation = true;
1437 break;
1438 case parser::OmpCancelType::Type::Parallel:
1439 if (allowedParallelSet.test(GetContextParent().directive)) {
1440 eligibleCancellation = true;
1442 break;
1444 if (!eligibleCancellation) {
1445 context_.Say(source,
1446 "With %s clause, %s construct cannot be closely nested inside %s "
1447 "construct"_err_en_US,
1448 parser::ToUpperCaseLetters(parser::OmpCancelType::EnumToString(type)),
1449 ContextDirectiveAsFortran(),
1450 parser::ToUpperCaseLetters(
1451 getDirectiveName(GetContextParent().directive).str()));
1453 } else {
1454 // The cancellation directive cannot be orphaned.
1455 switch (type) {
1456 case parser::OmpCancelType::Type::Taskgroup:
1457 context_.Say(source,
1458 "%s %s directive is not closely nested inside "
1459 "TASK or TASKLOOP"_err_en_US,
1460 ContextDirectiveAsFortran(),
1461 parser::ToUpperCaseLetters(
1462 parser::OmpCancelType::EnumToString(type)));
1463 break;
1464 case parser::OmpCancelType::Type::Sections:
1465 context_.Say(source,
1466 "%s %s directive is not closely nested inside "
1467 "SECTION or SECTIONS"_err_en_US,
1468 ContextDirectiveAsFortran(),
1469 parser::ToUpperCaseLetters(
1470 parser::OmpCancelType::EnumToString(type)));
1471 break;
1472 case Fortran::parser::OmpCancelType::Type::Do:
1473 context_.Say(source,
1474 "%s %s directive is not closely nested inside "
1475 "the construct that matches the DO clause type"_err_en_US,
1476 ContextDirectiveAsFortran(),
1477 parser::ToUpperCaseLetters(
1478 parser::OmpCancelType::EnumToString(type)));
1479 break;
1480 case parser::OmpCancelType::Type::Parallel:
1481 context_.Say(source,
1482 "%s %s directive is not closely nested inside "
1483 "the construct that matches the PARALLEL clause type"_err_en_US,
1484 ContextDirectiveAsFortran(),
1485 parser::ToUpperCaseLetters(
1486 parser::OmpCancelType::EnumToString(type)));
1487 break;
1492 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
1493 const auto &dir{std::get<parser::OmpBlockDirective>(x.t)};
1494 ResetPartialContext(dir.source);
1495 switch (dir.v) {
1496 // 2.7.3 end-single-clause -> copyprivate-clause |
1497 // nowait-clause
1498 case llvm::omp::Directive::OMPD_single:
1499 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_single);
1500 break;
1501 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
1502 case llvm::omp::Directive::OMPD_workshare:
1503 PushContextAndClauseSets(
1504 dir.source, llvm::omp::Directive::OMPD_end_workshare);
1505 break;
1506 default:
1507 // no clauses are allowed
1508 break;
1512 // TODO: Verify the popping of dirContext requirement after nowait
1513 // implementation, as there is an implicit barrier at the end of the worksharing
1514 // constructs unless a nowait clause is specified. Only OMPD_end_single and
1515 // end_workshareare popped as they are pushed while entering the
1516 // EndBlockDirective.
1517 void OmpStructureChecker::Leave(const parser::OmpEndBlockDirective &x) {
1518 if ((GetContext().directive == llvm::omp::Directive::OMPD_end_single) ||
1519 (GetContext().directive == llvm::omp::Directive::OMPD_end_workshare)) {
1520 dirContext_.pop_back();
1524 template <typename T, typename D>
1525 bool OmpStructureChecker::IsOperatorValid(const T &node, const D &variable) {
1526 using AllowedBinaryOperators =
1527 std::variant<parser::Expr::Add, parser::Expr::Multiply,
1528 parser::Expr::Subtract, parser::Expr::Divide, parser::Expr::AND,
1529 parser::Expr::OR, parser::Expr::EQV, parser::Expr::NEQV>;
1530 using BinaryOperators = std::variant<parser::Expr::Add,
1531 parser::Expr::Multiply, parser::Expr::Subtract, parser::Expr::Divide,
1532 parser::Expr::AND, parser::Expr::OR, parser::Expr::EQV,
1533 parser::Expr::NEQV, parser::Expr::Power, parser::Expr::Concat,
1534 parser::Expr::LT, parser::Expr::LE, parser::Expr::EQ, parser::Expr::NE,
1535 parser::Expr::GE, parser::Expr::GT>;
1537 if constexpr (common::HasMember<T, BinaryOperators>) {
1538 const auto &variableName{variable.GetSource().ToString()};
1539 const auto &exprLeft{std::get<0>(node.t)};
1540 const auto &exprRight{std::get<1>(node.t)};
1541 if ((exprLeft.value().source.ToString() != variableName) &&
1542 (exprRight.value().source.ToString() != variableName)) {
1543 context_.Say(variable.GetSource(),
1544 "Atomic update variable '%s' not found in the RHS of the "
1545 "assignment statement in an ATOMIC (UPDATE) construct"_err_en_US,
1546 variableName);
1548 return common::HasMember<T, AllowedBinaryOperators>;
1550 return true;
1553 void OmpStructureChecker::CheckAtomicUpdateAssignmentStmt(
1554 const parser::AssignmentStmt &assignment) {
1555 const auto &expr{std::get<parser::Expr>(assignment.t)};
1556 const auto &var{std::get<parser::Variable>(assignment.t)};
1557 common::visit(
1558 common::visitors{
1559 [&](const common::Indirection<parser::FunctionReference> &x) {
1560 const auto &procedureDesignator{
1561 std::get<parser::ProcedureDesignator>(x.value().v.t)};
1562 const parser::Name *name{
1563 std::get_if<parser::Name>(&procedureDesignator.u)};
1564 if (name &&
1565 !(name->source == "max" || name->source == "min" ||
1566 name->source == "iand" || name->source == "ior" ||
1567 name->source == "ieor")) {
1568 context_.Say(expr.source,
1569 "Invalid intrinsic procedure name in "
1570 "OpenMP ATOMIC (UPDATE) statement"_err_en_US);
1571 } else if (name) {
1572 bool foundMatch{false};
1573 if (auto varDesignatorIndirection =
1574 std::get_if<Fortran::common::Indirection<
1575 Fortran::parser::Designator>>(&var.u)) {
1576 const auto &varDesignator = varDesignatorIndirection->value();
1577 if (const auto *dataRef = std::get_if<Fortran::parser::DataRef>(
1578 &varDesignator.u)) {
1579 if (const auto *name =
1580 std::get_if<Fortran::parser::Name>(&dataRef->u)) {
1581 const auto &varSymbol = *name->symbol;
1582 if (const auto *e{GetExpr(context_, expr)}) {
1583 for (const Symbol &symbol :
1584 evaluate::CollectSymbols(*e)) {
1585 if (symbol == varSymbol) {
1586 foundMatch = true;
1587 break;
1594 if (!foundMatch) {
1595 context_.Say(expr.source,
1596 "Atomic update variable '%s' not found in the "
1597 "argument list of intrinsic procedure"_err_en_US,
1598 var.GetSource().ToString());
1602 [&](const auto &x) {
1603 if (!IsOperatorValid(x, var)) {
1604 context_.Say(expr.source,
1605 "Invalid operator in OpenMP ATOMIC (UPDATE) "
1606 "statement"_err_en_US);
1610 expr.u);
1613 void OmpStructureChecker::CheckAtomicMemoryOrderClause(
1614 const parser::OmpAtomicClauseList *leftHandClauseList,
1615 const parser::OmpAtomicClauseList *rightHandClauseList) {
1616 int numMemoryOrderClause = 0;
1617 auto checkForValidMemoryOrderClause =
1618 [&](const parser::OmpAtomicClauseList *clauseList) {
1619 for (const auto &clause : clauseList->v) {
1620 if (std::get_if<Fortran::parser::OmpMemoryOrderClause>(&clause.u)) {
1621 numMemoryOrderClause++;
1622 if (numMemoryOrderClause > 1) {
1623 context_.Say(clause.source,
1624 "More than one memory order clause not allowed on "
1625 "OpenMP Atomic construct"_err_en_US);
1626 return;
1631 if (leftHandClauseList) {
1632 checkForValidMemoryOrderClause(leftHandClauseList);
1634 if (rightHandClauseList) {
1635 checkForValidMemoryOrderClause(rightHandClauseList);
1639 void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
1640 common::visit(
1641 common::visitors{
1642 [&](const parser::OmpAtomic &atomicConstruct) {
1643 const auto &dir{std::get<parser::Verbatim>(atomicConstruct.t)};
1644 PushContextAndClauseSets(
1645 dir.source, llvm::omp::Directive::OMPD_atomic);
1646 CheckAtomicUpdateAssignmentStmt(
1647 std::get<parser::Statement<parser::AssignmentStmt>>(
1648 atomicConstruct.t)
1649 .statement);
1650 CheckAtomicMemoryOrderClause(
1651 &std::get<parser::OmpAtomicClauseList>(atomicConstruct.t),
1652 nullptr);
1653 CheckHintClause<const parser::OmpAtomicClauseList>(
1654 &std::get<parser::OmpAtomicClauseList>(atomicConstruct.t),
1655 nullptr);
1657 [&](const parser::OmpAtomicUpdate &atomicUpdate) {
1658 const auto &dir{std::get<parser::Verbatim>(atomicUpdate.t)};
1659 PushContextAndClauseSets(
1660 dir.source, llvm::omp::Directive::OMPD_atomic);
1661 CheckAtomicUpdateAssignmentStmt(
1662 std::get<parser::Statement<parser::AssignmentStmt>>(
1663 atomicUpdate.t)
1664 .statement);
1665 CheckAtomicMemoryOrderClause(
1666 &std::get<0>(atomicUpdate.t), &std::get<2>(atomicUpdate.t));
1667 CheckHintClause<const parser::OmpAtomicClauseList>(
1668 &std::get<0>(atomicUpdate.t), &std::get<2>(atomicUpdate.t));
1670 [&](const auto &atomicConstruct) {
1671 const auto &dir{std::get<parser::Verbatim>(atomicConstruct.t)};
1672 PushContextAndClauseSets(
1673 dir.source, llvm::omp::Directive::OMPD_atomic);
1674 CheckAtomicMemoryOrderClause(&std::get<0>(atomicConstruct.t),
1675 &std::get<2>(atomicConstruct.t));
1676 CheckHintClause<const parser::OmpAtomicClauseList>(
1677 &std::get<0>(atomicConstruct.t),
1678 &std::get<2>(atomicConstruct.t));
1681 x.u);
1684 void OmpStructureChecker::Leave(const parser::OpenMPAtomicConstruct &) {
1685 dirContext_.pop_back();
1688 // Clauses
1689 // Mainly categorized as
1690 // 1. Checks on 'OmpClauseList' from 'parse-tree.h'.
1691 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h.
1692 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h.
1694 void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
1695 // 2.7.1 Loop Construct Restriction
1696 if (llvm::omp::doSet.test(GetContext().directive)) {
1697 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) {
1698 // only one schedule clause is allowed
1699 const auto &schedClause{std::get<parser::OmpClause::Schedule>(clause->u)};
1700 if (ScheduleModifierHasType(schedClause.v,
1701 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
1702 if (FindClause(llvm::omp::Clause::OMPC_ordered)) {
1703 context_.Say(clause->source,
1704 "The NONMONOTONIC modifier cannot be specified "
1705 "if an ORDERED clause is specified"_err_en_US);
1707 if (ScheduleModifierHasType(schedClause.v,
1708 parser::OmpScheduleModifierType::ModType::Monotonic)) {
1709 context_.Say(clause->source,
1710 "The MONOTONIC and NONMONOTONIC modifiers "
1711 "cannot be both specified"_err_en_US);
1716 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) {
1717 // only one ordered clause is allowed
1718 const auto &orderedClause{
1719 std::get<parser::OmpClause::Ordered>(clause->u)};
1721 if (orderedClause.v) {
1722 CheckNotAllowedIfClause(
1723 llvm::omp::Clause::OMPC_ordered, {llvm::omp::Clause::OMPC_linear});
1725 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) {
1726 const auto &collapseClause{
1727 std::get<parser::OmpClause::Collapse>(clause2->u)};
1728 // ordered and collapse both have parameters
1729 if (const auto orderedValue{GetIntValue(orderedClause.v)}) {
1730 if (const auto collapseValue{GetIntValue(collapseClause.v)}) {
1731 if (*orderedValue > 0 && *orderedValue < *collapseValue) {
1732 context_.Say(clause->source,
1733 "The parameter of the ORDERED clause must be "
1734 "greater than or equal to "
1735 "the parameter of the COLLAPSE clause"_err_en_US);
1742 // TODO: ordered region binding check (requires nesting implementation)
1744 } // doSet
1746 // 2.8.1 Simd Construct Restriction
1747 if (llvm::omp::simdSet.test(GetContext().directive)) {
1748 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) {
1749 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) {
1750 const auto &simdlenClause{
1751 std::get<parser::OmpClause::Simdlen>(clause->u)};
1752 const auto &safelenClause{
1753 std::get<parser::OmpClause::Safelen>(clause2->u)};
1754 // simdlen and safelen both have parameters
1755 if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) {
1756 if (const auto safelenValue{GetIntValue(safelenClause.v)}) {
1757 if (*safelenValue > 0 && *simdlenValue > *safelenValue) {
1758 context_.Say(clause->source,
1759 "The parameter of the SIMDLEN clause must be less than or "
1760 "equal to the parameter of the SAFELEN clause"_err_en_US);
1766 // Sema checks related to presence of multiple list items within the same
1767 // clause
1768 CheckMultListItems();
1769 } // SIMD
1771 // 2.7.3 Single Construct Restriction
1772 if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) {
1773 CheckNotAllowedIfClause(
1774 llvm::omp::Clause::OMPC_copyprivate, {llvm::omp::Clause::OMPC_nowait});
1777 auto testThreadprivateVarErr = [&](Symbol sym, parser::Name name,
1778 llvmOmpClause clauseTy) {
1779 if (sym.test(Symbol::Flag::OmpThreadprivate))
1780 context_.Say(name.source,
1781 "A THREADPRIVATE variable cannot be in %s clause"_err_en_US,
1782 parser::ToUpperCaseLetters(getClauseName(clauseTy).str()));
1785 // [5.1] 2.21.2 Threadprivate Directive Restriction
1786 OmpClauseSet threadprivateAllowedSet{llvm::omp::Clause::OMPC_copyin,
1787 llvm::omp::Clause::OMPC_copyprivate, llvm::omp::Clause::OMPC_schedule,
1788 llvm::omp::Clause::OMPC_num_threads, llvm::omp::Clause::OMPC_thread_limit,
1789 llvm::omp::Clause::OMPC_if};
1790 for (auto it : GetContext().clauseInfo) {
1791 llvmOmpClause type = it.first;
1792 const auto *clause = it.second;
1793 if (!threadprivateAllowedSet.test(type)) {
1794 if (const auto *objList{GetOmpObjectList(*clause)}) {
1795 for (const auto &ompObject : objList->v) {
1796 common::visit(
1797 common::visitors{
1798 [&](const parser::Designator &) {
1799 if (const auto *name{
1800 parser::Unwrap<parser::Name>(ompObject)})
1801 testThreadprivateVarErr(
1802 name->symbol->GetUltimate(), *name, type);
1804 [&](const parser::Name &name) {
1805 if (name.symbol) {
1806 for (const auto &mem :
1807 name.symbol->get<CommonBlockDetails>().objects()) {
1808 testThreadprivateVarErr(mem->GetUltimate(), name, type);
1809 break;
1814 ompObject.u);
1820 CheckRequireAtLeastOneOf();
1823 void OmpStructureChecker::Enter(const parser::OmpClause &x) {
1824 SetContextClause(x);
1827 // Following clauses do not have a separate node in parse-tree.h.
1828 CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel)
1829 CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire)
1830 CHECK_SIMPLE_CLAUSE(AtomicDefaultMemOrder, OMPC_atomic_default_mem_order)
1831 CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity)
1832 CHECK_SIMPLE_CLAUSE(Allocate, OMPC_allocate)
1833 CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture)
1834 CHECK_SIMPLE_CLAUSE(Default, OMPC_default)
1835 CHECK_SIMPLE_CLAUSE(Depobj, OMPC_depobj)
1836 CHECK_SIMPLE_CLAUSE(Destroy, OMPC_destroy)
1837 CHECK_SIMPLE_CLAUSE(Detach, OMPC_detach)
1838 CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type)
1839 CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule)
1840 CHECK_SIMPLE_CLAUSE(DynamicAllocators, OMPC_dynamic_allocators)
1841 CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive)
1842 CHECK_SIMPLE_CLAUSE(Final, OMPC_final)
1843 CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush)
1844 CHECK_SIMPLE_CLAUSE(From, OMPC_from)
1845 CHECK_SIMPLE_CLAUSE(Full, OMPC_full)
1846 CHECK_SIMPLE_CLAUSE(Hint, OMPC_hint)
1847 CHECK_SIMPLE_CLAUSE(InReduction, OMPC_in_reduction)
1848 CHECK_SIMPLE_CLAUSE(Inclusive, OMPC_inclusive)
1849 CHECK_SIMPLE_CLAUSE(Match, OMPC_match)
1850 CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal)
1851 CHECK_SIMPLE_CLAUSE(Order, OMPC_order)
1852 CHECK_SIMPLE_CLAUSE(Read, OMPC_read)
1853 CHECK_SIMPLE_CLAUSE(ReverseOffload, OMPC_reverse_offload)
1854 CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate)
1855 CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads)
1856 CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch)
1857 CHECK_SIMPLE_CLAUSE(IsDevicePtr, OMPC_is_device_ptr)
1858 CHECK_SIMPLE_CLAUSE(HasDeviceAddr, OMPC_has_device_addr)
1859 CHECK_SIMPLE_CLAUSE(Link, OMPC_link)
1860 CHECK_SIMPLE_CLAUSE(Indirect, OMPC_indirect)
1861 CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable)
1862 CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup)
1863 CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch)
1864 CHECK_SIMPLE_CLAUSE(Nowait, OMPC_nowait)
1865 CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial)
1866 CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind)
1867 CHECK_SIMPLE_CLAUSE(Release, OMPC_release)
1868 CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed)
1869 CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst)
1870 CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd)
1871 CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes)
1872 CHECK_SIMPLE_CLAUSE(TaskReduction, OMPC_task_reduction)
1873 CHECK_SIMPLE_CLAUSE(To, OMPC_to)
1874 CHECK_SIMPLE_CLAUSE(UnifiedAddress, OMPC_unified_address)
1875 CHECK_SIMPLE_CLAUSE(UnifiedSharedMemory, OMPC_unified_shared_memory)
1876 CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform)
1877 CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown)
1878 CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied)
1879 CHECK_SIMPLE_CLAUSE(UseDevicePtr, OMPC_use_device_ptr)
1880 CHECK_SIMPLE_CLAUSE(UsesAllocators, OMPC_uses_allocators)
1881 CHECK_SIMPLE_CLAUSE(Update, OMPC_update)
1882 CHECK_SIMPLE_CLAUSE(UseDeviceAddr, OMPC_use_device_addr)
1883 CHECK_SIMPLE_CLAUSE(Write, OMPC_write)
1884 CHECK_SIMPLE_CLAUSE(Init, OMPC_init)
1885 CHECK_SIMPLE_CLAUSE(Use, OMPC_use)
1886 CHECK_SIMPLE_CLAUSE(Novariants, OMPC_novariants)
1887 CHECK_SIMPLE_CLAUSE(Nocontext, OMPC_nocontext)
1888 CHECK_SIMPLE_CLAUSE(At, OMPC_at)
1889 CHECK_SIMPLE_CLAUSE(Severity, OMPC_severity)
1890 CHECK_SIMPLE_CLAUSE(Message, OMPC_message)
1891 CHECK_SIMPLE_CLAUSE(Filter, OMPC_filter)
1892 CHECK_SIMPLE_CLAUSE(When, OMPC_when)
1893 CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args)
1894 CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args)
1895 CHECK_SIMPLE_CLAUSE(MemoryOrder, OMPC_memory_order)
1896 CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind)
1897 CHECK_SIMPLE_CLAUSE(Align, OMPC_align)
1898 CHECK_SIMPLE_CLAUSE(Compare, OMPC_compare)
1899 CHECK_SIMPLE_CLAUSE(CancellationConstructType, OMPC_cancellation_construct_type)
1901 CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize)
1902 CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks)
1903 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams)
1904 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads)
1905 CHECK_REQ_SCALAR_INT_CLAUSE(OmpxDynCgroupMem, OMPC_ompx_dyn_cgroup_mem)
1906 CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority)
1907 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit)
1909 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse)
1910 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen)
1911 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen)
1913 // Restrictions specific to each clause are implemented apart from the
1914 // generalized restrictions.
1915 void OmpStructureChecker::Enter(const parser::OmpClause::Reduction &x) {
1916 CheckAllowed(llvm::omp::Clause::OMPC_reduction);
1917 if (CheckReductionOperators(x)) {
1918 CheckReductionTypeList(x);
1921 bool OmpStructureChecker::CheckReductionOperators(
1922 const parser::OmpClause::Reduction &x) {
1924 const auto &definedOp{std::get<0>(x.v.t)};
1925 bool ok = false;
1926 common::visit(
1927 common::visitors{
1928 [&](const parser::DefinedOperator &dOpr) {
1929 const auto &intrinsicOp{
1930 std::get<parser::DefinedOperator::IntrinsicOperator>(dOpr.u)};
1931 ok = CheckIntrinsicOperator(intrinsicOp);
1933 [&](const parser::ProcedureDesignator &procD) {
1934 const parser::Name *name{std::get_if<parser::Name>(&procD.u)};
1935 if (name) {
1936 if (name->source == "max" || name->source == "min" ||
1937 name->source == "iand" || name->source == "ior" ||
1938 name->source == "ieor") {
1939 ok = true;
1940 } else {
1941 context_.Say(GetContext().clauseSource,
1942 "Invalid reduction identifier in REDUCTION "
1943 "clause."_err_en_US,
1944 ContextDirectiveAsFortran());
1949 definedOp.u);
1951 return ok;
1953 bool OmpStructureChecker::CheckIntrinsicOperator(
1954 const parser::DefinedOperator::IntrinsicOperator &op) {
1956 switch (op) {
1957 case parser::DefinedOperator::IntrinsicOperator::Add:
1958 case parser::DefinedOperator::IntrinsicOperator::Multiply:
1959 case parser::DefinedOperator::IntrinsicOperator::AND:
1960 case parser::DefinedOperator::IntrinsicOperator::OR:
1961 case parser::DefinedOperator::IntrinsicOperator::EQV:
1962 case parser::DefinedOperator::IntrinsicOperator::NEQV:
1963 return true;
1964 case parser::DefinedOperator::IntrinsicOperator::Subtract:
1965 context_.Say(GetContext().clauseSource,
1966 "The minus reduction operator is deprecated since OpenMP 5.2 and is "
1967 "not supported in the REDUCTION clause."_err_en_US,
1968 ContextDirectiveAsFortran());
1969 break;
1970 default:
1971 context_.Say(GetContext().clauseSource,
1972 "Invalid reduction operator in REDUCTION clause."_err_en_US,
1973 ContextDirectiveAsFortran());
1975 return false;
1978 void OmpStructureChecker::CheckReductionTypeList(
1979 const parser::OmpClause::Reduction &x) {
1980 const auto &ompObjectList{std::get<parser::OmpObjectList>(x.v.t)};
1981 CheckIntentInPointerAndDefinable(
1982 ompObjectList, llvm::omp::Clause::OMPC_reduction);
1983 CheckReductionArraySection(ompObjectList);
1984 // If this is a worksharing construct then ensure the reduction variable
1985 // is not private in the parallel region that it binds to.
1986 OmpDirectiveSet workshareSet{llvm::omp::Directive::OMPD_do,
1987 llvm::omp::Directive::OMPD_sections, llvm::omp::Directive::OMPD_do_simd};
1988 if (workshareSet.test(GetContext().directive)) {
1989 CheckSharedBindingInOuterContext(ompObjectList);
1993 void OmpStructureChecker::CheckIntentInPointerAndDefinable(
1994 const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) {
1995 for (const auto &ompObject : objectList.v) {
1996 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) {
1997 if (const auto *symbol{name->symbol}) {
1998 if (IsPointer(symbol->GetUltimate()) &&
1999 IsIntentIn(symbol->GetUltimate())) {
2000 context_.Say(GetContext().clauseSource,
2001 "Pointer '%s' with the INTENT(IN) attribute may not appear "
2002 "in a %s clause"_err_en_US,
2003 symbol->name(),
2004 parser::ToUpperCaseLetters(getClauseName(clause).str()));
2005 } else if (auto msg{WhyNotDefinable(name->source,
2006 context_.FindScope(name->source), DefinabilityFlags{},
2007 *symbol)}) {
2008 context_
2009 .Say(GetContext().clauseSource,
2010 "Variable '%s' on the %s clause is not definable"_err_en_US,
2011 symbol->name(),
2012 parser::ToUpperCaseLetters(getClauseName(clause).str()))
2013 .Attach(std::move(*msg));
2020 void OmpStructureChecker::CheckReductionArraySection(
2021 const parser::OmpObjectList &ompObjectList) {
2022 for (const auto &ompObject : ompObjectList.v) {
2023 if (const auto *dataRef{parser::Unwrap<parser::DataRef>(ompObject)}) {
2024 if (const auto *arrayElement{
2025 parser::Unwrap<parser::ArrayElement>(ompObject)}) {
2026 if (arrayElement) {
2027 CheckArraySection(*arrayElement, GetLastName(*dataRef),
2028 llvm::omp::Clause::OMPC_reduction);
2035 void OmpStructureChecker::CheckSharedBindingInOuterContext(
2036 const parser::OmpObjectList &redObjectList) {
2037 // TODO: Verify the assumption here that the immediately enclosing region is
2038 // the parallel region to which the worksharing construct having reduction
2039 // binds to.
2040 if (auto *enclosingContext{GetEnclosingDirContext()}) {
2041 for (auto it : enclosingContext->clauseInfo) {
2042 llvmOmpClause type = it.first;
2043 const auto *clause = it.second;
2044 if (llvm::omp::privateReductionSet.test(type)) {
2045 if (const auto *objList{GetOmpObjectList(*clause)}) {
2046 for (const auto &ompObject : objList->v) {
2047 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) {
2048 if (const auto *symbol{name->symbol}) {
2049 for (const auto &redOmpObject : redObjectList.v) {
2050 if (const auto *rname{
2051 parser::Unwrap<parser::Name>(redOmpObject)}) {
2052 if (const auto *rsymbol{rname->symbol}) {
2053 if (rsymbol->name() == symbol->name()) {
2054 context_.Say(GetContext().clauseSource,
2055 "%s variable '%s' is %s in outer context must"
2056 " be shared in the parallel regions to which any"
2057 " of the worksharing regions arising from the "
2058 "worksharing construct bind."_err_en_US,
2059 parser::ToUpperCaseLetters(
2060 getClauseName(llvm::omp::Clause::OMPC_reduction)
2061 .str()),
2062 symbol->name(),
2063 parser::ToUpperCaseLetters(
2064 getClauseName(type).str()));
2078 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
2079 CheckAllowed(llvm::omp::Clause::OMPC_ordered);
2080 // the parameter of ordered clause is optional
2081 if (const auto &expr{x.v}) {
2082 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr);
2083 // 2.8.3 Loop SIMD Construct Restriction
2084 if (llvm::omp::doSimdSet.test(GetContext().directive)) {
2085 context_.Say(GetContext().clauseSource,
2086 "No ORDERED clause with a parameter can be specified "
2087 "on the %s directive"_err_en_US,
2088 ContextDirectiveAsFortran());
2093 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) {
2094 CheckAllowed(llvm::omp::Clause::OMPC_shared);
2095 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v);
2097 void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) {
2098 CheckAllowed(llvm::omp::Clause::OMPC_private);
2099 CheckIsVarPartOfAnotherVar(GetContext().clauseSource, x.v);
2100 CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_private);
2103 bool OmpStructureChecker::IsDataRefTypeParamInquiry(
2104 const parser::DataRef *dataRef) {
2105 bool dataRefIsTypeParamInquiry{false};
2106 if (const auto *structComp{
2107 parser::Unwrap<parser::StructureComponent>(dataRef)}) {
2108 if (const auto *compSymbol{structComp->component.symbol}) {
2109 if (const auto *compSymbolMiscDetails{
2110 std::get_if<MiscDetails>(&compSymbol->details())}) {
2111 const auto detailsKind = compSymbolMiscDetails->kind();
2112 dataRefIsTypeParamInquiry =
2113 (detailsKind == MiscDetails::Kind::KindParamInquiry ||
2114 detailsKind == MiscDetails::Kind::LenParamInquiry);
2115 } else if (compSymbol->has<TypeParamDetails>()) {
2116 dataRefIsTypeParamInquiry = true;
2120 return dataRefIsTypeParamInquiry;
2123 void OmpStructureChecker::CheckIsVarPartOfAnotherVar(
2124 const parser::CharBlock &source, const parser::OmpObjectList &objList) {
2125 OmpDirectiveSet nonPartialVarSet{llvm::omp::Directive::OMPD_allocate,
2126 llvm::omp::Directive::OMPD_threadprivate,
2127 llvm::omp::Directive::OMPD_declare_target};
2128 for (const auto &ompObject : objList.v) {
2129 common::visit(
2130 common::visitors{
2131 [&](const parser::Designator &designator) {
2132 if (const auto *dataRef{
2133 std::get_if<parser::DataRef>(&designator.u)}) {
2134 if (IsDataRefTypeParamInquiry(dataRef)) {
2135 context_.Say(source,
2136 "A type parameter inquiry cannot appear on the %s "
2137 "directive"_err_en_US,
2138 ContextDirectiveAsFortran());
2139 } else if (parser::Unwrap<parser::StructureComponent>(
2140 ompObject) ||
2141 parser::Unwrap<parser::ArrayElement>(ompObject)) {
2142 if (nonPartialVarSet.test(GetContext().directive)) {
2143 context_.Say(source,
2144 "A variable that is part of another variable (as an "
2145 "array or structure element) cannot appear on the %s "
2146 "directive"_err_en_US,
2147 ContextDirectiveAsFortran());
2148 } else {
2149 context_.Say(source,
2150 "A variable that is part of another variable (as an "
2151 "array or structure element) cannot appear in a "
2152 "PRIVATE or SHARED clause"_err_en_US);
2157 [&](const parser::Name &name) {},
2159 ompObject.u);
2163 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) {
2164 CheckAllowed(llvm::omp::Clause::OMPC_firstprivate);
2165 CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate, x.v);
2167 SymbolSourceMap currSymbols;
2168 GetSymbolsInObjectList(x.v, currSymbols);
2169 CheckCopyingPolymorphicAllocatable(
2170 currSymbols, llvm::omp::Clause::OMPC_firstprivate);
2172 DirectivesClauseTriple dirClauseTriple;
2173 // Check firstprivate variables in worksharing constructs
2174 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do,
2175 std::make_pair(
2176 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
2177 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections,
2178 std::make_pair(
2179 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
2180 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_single,
2181 std::make_pair(
2182 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
2183 // Check firstprivate variables in distribute construct
2184 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute,
2185 std::make_pair(
2186 llvm::omp::Directive::OMPD_teams, llvm::omp::privateReductionSet));
2187 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_distribute,
2188 std::make_pair(llvm::omp::Directive::OMPD_target_teams,
2189 llvm::omp::privateReductionSet));
2190 // Check firstprivate variables in task and taskloop constructs
2191 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_task,
2192 std::make_pair(llvm::omp::Directive::OMPD_parallel,
2193 OmpClauseSet{llvm::omp::Clause::OMPC_reduction}));
2194 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_taskloop,
2195 std::make_pair(llvm::omp::Directive::OMPD_parallel,
2196 OmpClauseSet{llvm::omp::Clause::OMPC_reduction}));
2198 CheckPrivateSymbolsInOuterCxt(
2199 currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_firstprivate);
2202 void OmpStructureChecker::CheckIsLoopIvPartOfClause(
2203 llvmOmpClause clause, const parser::OmpObjectList &ompObjectList) {
2204 for (const auto &ompObject : ompObjectList.v) {
2205 if (const parser::Name * name{parser::Unwrap<parser::Name>(ompObject)}) {
2206 if (name->symbol == GetContext().loopIV) {
2207 context_.Say(name->source,
2208 "DO iteration variable %s is not allowed in %s clause."_err_en_US,
2209 name->ToString(),
2210 parser::ToUpperCaseLetters(getClauseName(clause).str()));
2215 // Following clauses have a seperate node in parse-tree.h.
2216 // Atomic-clause
2217 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead, OMPC_read)
2218 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicWrite, OMPC_write)
2219 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicUpdate, OMPC_update)
2220 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicCapture, OMPC_capture)
2222 void OmpStructureChecker::Leave(const parser::OmpAtomicRead &) {
2223 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_read,
2224 {llvm::omp::Clause::OMPC_release, llvm::omp::Clause::OMPC_acq_rel});
2226 void OmpStructureChecker::Leave(const parser::OmpAtomicWrite &) {
2227 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_write,
2228 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel});
2230 void OmpStructureChecker::Leave(const parser::OmpAtomicUpdate &) {
2231 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_update,
2232 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel});
2234 // OmpAtomic node represents atomic directive without atomic-clause.
2235 // atomic-clause - READ,WRITE,UPDATE,CAPTURE.
2236 void OmpStructureChecker::Leave(const parser::OmpAtomic &) {
2237 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acquire)}) {
2238 context_.Say(clause->source,
2239 "Clause ACQUIRE is not allowed on the ATOMIC directive"_err_en_US);
2241 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acq_rel)}) {
2242 context_.Say(clause->source,
2243 "Clause ACQ_REL is not allowed on the ATOMIC directive"_err_en_US);
2246 // Restrictions specific to each clause are implemented apart from the
2247 // generalized restrictions.
2248 void OmpStructureChecker::Enter(const parser::OmpClause::Aligned &x) {
2249 CheckAllowed(llvm::omp::Clause::OMPC_aligned);
2251 if (const auto &expr{
2252 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.v.t)}) {
2253 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_aligned, *expr);
2255 // 2.8.1 TODO: list-item attribute check
2257 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) {
2258 CheckAllowed(llvm::omp::Clause::OMPC_defaultmap);
2259 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory;
2260 if (!std::get<std::optional<VariableCategory>>(x.v.t)) {
2261 context_.Say(GetContext().clauseSource,
2262 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP "
2263 "clause"_err_en_US);
2266 void OmpStructureChecker::Enter(const parser::OmpClause::If &x) {
2267 CheckAllowed(llvm::omp::Clause::OMPC_if);
2268 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier;
2269 static std::unordered_map<dirNameModifier, OmpDirectiveSet>
2270 dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet},
2271 {dirNameModifier::Target, llvm::omp::targetSet},
2272 {dirNameModifier::TargetEnterData,
2273 {llvm::omp::Directive::OMPD_target_enter_data}},
2274 {dirNameModifier::TargetExitData,
2275 {llvm::omp::Directive::OMPD_target_exit_data}},
2276 {dirNameModifier::TargetData,
2277 {llvm::omp::Directive::OMPD_target_data}},
2278 {dirNameModifier::TargetUpdate,
2279 {llvm::omp::Directive::OMPD_target_update}},
2280 {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}},
2281 {dirNameModifier::Taskloop, llvm::omp::taskloopSet}};
2282 if (const auto &directiveName{
2283 std::get<std::optional<dirNameModifier>>(x.v.t)}) {
2284 auto search{dirNameModifierMap.find(*directiveName)};
2285 if (search == dirNameModifierMap.end() ||
2286 !search->second.test(GetContext().directive)) {
2287 context_
2288 .Say(GetContext().clauseSource,
2289 "Unmatched directive name modifier %s on the IF clause"_err_en_US,
2290 parser::ToUpperCaseLetters(
2291 parser::OmpIfClause::EnumToString(*directiveName)))
2292 .Attach(
2293 GetContext().directiveSource, "Cannot apply to directive"_en_US);
2298 void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
2299 CheckAllowed(llvm::omp::Clause::OMPC_linear);
2301 // 2.7 Loop Construct Restriction
2302 if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) {
2303 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.v.u)) {
2304 context_.Say(GetContext().clauseSource,
2305 "A modifier may not be specified in a LINEAR clause "
2306 "on the %s directive"_err_en_US,
2307 ContextDirectiveAsFortran());
2312 void OmpStructureChecker::CheckAllowedMapTypes(
2313 const parser::OmpMapType::Type &type,
2314 const std::list<parser::OmpMapType::Type> &allowedMapTypeList) {
2315 if (!llvm::is_contained(allowedMapTypeList, type)) {
2316 std::string commaSeperatedMapTypes;
2317 llvm::interleave(
2318 allowedMapTypeList.begin(), allowedMapTypeList.end(),
2319 [&](const parser::OmpMapType::Type &mapType) {
2320 commaSeperatedMapTypes.append(parser::ToUpperCaseLetters(
2321 parser::OmpMapType::EnumToString(mapType)));
2323 [&] { commaSeperatedMapTypes.append(", "); });
2324 context_.Say(GetContext().clauseSource,
2325 "Only the %s map types are permitted "
2326 "for MAP clauses on the %s directive"_err_en_US,
2327 commaSeperatedMapTypes, ContextDirectiveAsFortran());
2331 void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
2332 CheckAllowed(llvm::omp::Clause::OMPC_map);
2334 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.v.t)}) {
2335 using Type = parser::OmpMapType::Type;
2336 const Type &type{std::get<Type>(maptype->t)};
2337 switch (GetContext().directive) {
2338 case llvm::omp::Directive::OMPD_target:
2339 case llvm::omp::Directive::OMPD_target_teams:
2340 case llvm::omp::Directive::OMPD_target_teams_distribute:
2341 case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
2342 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
2343 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
2344 case llvm::omp::Directive::OMPD_target_data:
2345 CheckAllowedMapTypes(
2346 type, {Type::To, Type::From, Type::Tofrom, Type::Alloc});
2347 break;
2348 case llvm::omp::Directive::OMPD_target_enter_data:
2349 CheckAllowedMapTypes(type, {Type::To, Type::Alloc});
2350 break;
2351 case llvm::omp::Directive::OMPD_target_exit_data:
2352 CheckAllowedMapTypes(type, {Type::From, Type::Release, Type::Delete});
2353 break;
2354 default:
2355 break;
2360 bool OmpStructureChecker::ScheduleModifierHasType(
2361 const parser::OmpScheduleClause &x,
2362 const parser::OmpScheduleModifierType::ModType &type) {
2363 const auto &modifier{
2364 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)};
2365 if (modifier) {
2366 const auto &modType1{
2367 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)};
2368 const auto &modType2{
2369 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(
2370 modifier->t)};
2371 if (modType1.v.v == type || (modType2 && modType2->v.v == type)) {
2372 return true;
2375 return false;
2377 void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) {
2378 CheckAllowed(llvm::omp::Clause::OMPC_schedule);
2379 const parser::OmpScheduleClause &scheduleClause = x.v;
2381 // 2.7 Loop Construct Restriction
2382 if (llvm::omp::doSet.test(GetContext().directive)) {
2383 const auto &kind{std::get<1>(scheduleClause.t)};
2384 const auto &chunk{std::get<2>(scheduleClause.t)};
2385 if (chunk) {
2386 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime ||
2387 kind == parser::OmpScheduleClause::ScheduleType::Auto) {
2388 context_.Say(GetContext().clauseSource,
2389 "When SCHEDULE clause has %s specified, "
2390 "it must not have chunk size specified"_err_en_US,
2391 parser::ToUpperCaseLetters(
2392 parser::OmpScheduleClause::EnumToString(kind)));
2394 if (const auto &chunkExpr{std::get<std::optional<parser::ScalarIntExpr>>(
2395 scheduleClause.t)}) {
2396 RequiresPositiveParameter(
2397 llvm::omp::Clause::OMPC_schedule, *chunkExpr, "chunk size");
2401 if (ScheduleModifierHasType(scheduleClause,
2402 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
2403 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic &&
2404 kind != parser::OmpScheduleClause::ScheduleType::Guided) {
2405 context_.Say(GetContext().clauseSource,
2406 "The NONMONOTONIC modifier can only be specified with "
2407 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US);
2413 void OmpStructureChecker::Enter(const parser::OmpClause::Device &x) {
2414 CheckAllowed(llvm::omp::Clause::OMPC_device);
2415 const parser::OmpDeviceClause &deviceClause = x.v;
2416 const auto &device{std::get<1>(deviceClause.t)};
2417 RequiresPositiveParameter(
2418 llvm::omp::Clause::OMPC_device, device, "device expression");
2421 void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) {
2422 CheckAllowed(llvm::omp::Clause::OMPC_depend);
2423 if (const auto *inOut{std::get_if<parser::OmpDependClause::InOut>(&x.v.u)}) {
2424 const auto &designators{std::get<std::list<parser::Designator>>(inOut->t)};
2425 for (const auto &ele : designators) {
2426 if (const auto *dataRef{std::get_if<parser::DataRef>(&ele.u)}) {
2427 CheckDependList(*dataRef);
2428 if (const auto *arr{
2429 std::get_if<common::Indirection<parser::ArrayElement>>(
2430 &dataRef->u)}) {
2431 CheckArraySection(arr->value(), GetLastName(*dataRef),
2432 llvm::omp::Clause::OMPC_depend);
2439 void OmpStructureChecker::CheckCopyingPolymorphicAllocatable(
2440 SymbolSourceMap &symbols, const llvm::omp::Clause clause) {
2441 for (auto it{symbols.begin()}; it != symbols.end(); ++it) {
2442 const auto *symbol{it->first};
2443 const auto source{it->second};
2444 if (IsPolymorphicAllocatable(*symbol)) {
2445 context_.Say(source,
2446 "If a polymorphic variable with allocatable attribute '%s' is in "
2447 "%s clause, the behavior is unspecified"_port_en_US,
2448 symbol->name(),
2449 parser::ToUpperCaseLetters(getClauseName(clause).str()));
2454 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &x) {
2455 CheckAllowed(llvm::omp::Clause::OMPC_copyprivate);
2456 CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_copyprivate);
2457 SymbolSourceMap currSymbols;
2458 GetSymbolsInObjectList(x.v, currSymbols);
2459 CheckCopyingPolymorphicAllocatable(
2460 currSymbols, llvm::omp::Clause::OMPC_copyprivate);
2463 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) {
2464 CheckAllowed(llvm::omp::Clause::OMPC_lastprivate);
2466 DirectivesClauseTriple dirClauseTriple;
2467 SymbolSourceMap currSymbols;
2468 GetSymbolsInObjectList(x.v, currSymbols);
2469 CheckDefinableObjects(currSymbols, GetClauseKindForParserClass(x));
2470 CheckCopyingPolymorphicAllocatable(
2471 currSymbols, llvm::omp::Clause::OMPC_lastprivate);
2473 // Check lastprivate variables in worksharing constructs
2474 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_do,
2475 std::make_pair(
2476 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
2477 dirClauseTriple.emplace(llvm::omp::Directive::OMPD_sections,
2478 std::make_pair(
2479 llvm::omp::Directive::OMPD_parallel, llvm::omp::privateReductionSet));
2481 CheckPrivateSymbolsInOuterCxt(
2482 currSymbols, dirClauseTriple, GetClauseKindForParserClass(x));
2485 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &x) {
2486 CheckAllowed(llvm::omp::Clause::OMPC_copyin);
2488 SymbolSourceMap currSymbols;
2489 GetSymbolsInObjectList(x.v, currSymbols);
2490 CheckCopyingPolymorphicAllocatable(
2491 currSymbols, llvm::omp::Clause::OMPC_copyin);
2494 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
2495 return llvm::omp::getOpenMPClauseName(clause);
2498 llvm::StringRef OmpStructureChecker::getDirectiveName(
2499 llvm::omp::Directive directive) {
2500 return llvm::omp::getOpenMPDirectiveName(directive);
2503 void OmpStructureChecker::CheckDependList(const parser::DataRef &d) {
2504 common::visit(
2505 common::visitors{
2506 [&](const common::Indirection<parser::ArrayElement> &elem) {
2507 // Check if the base element is valid on Depend Clause
2508 CheckDependList(elem.value().base);
2510 [&](const common::Indirection<parser::StructureComponent> &) {
2511 context_.Say(GetContext().clauseSource,
2512 "A variable that is part of another variable "
2513 "(such as an element of a structure) but is not an array "
2514 "element or an array section cannot appear in a DEPEND "
2515 "clause"_err_en_US);
2517 [&](const common::Indirection<parser::CoindexedNamedObject> &) {
2518 context_.Say(GetContext().clauseSource,
2519 "Coarrays are not supported in DEPEND clause"_err_en_US);
2521 [&](const parser::Name &) { return; },
2523 d.u);
2526 // Called from both Reduction and Depend clause.
2527 void OmpStructureChecker::CheckArraySection(
2528 const parser::ArrayElement &arrayElement, const parser::Name &name,
2529 const llvm::omp::Clause clause) {
2530 if (!arrayElement.subscripts.empty()) {
2531 for (const auto &subscript : arrayElement.subscripts) {
2532 if (const auto *triplet{
2533 std::get_if<parser::SubscriptTriplet>(&subscript.u)}) {
2534 if (std::get<0>(triplet->t) && std::get<1>(triplet->t)) {
2535 const auto &lower{std::get<0>(triplet->t)};
2536 const auto &upper{std::get<1>(triplet->t)};
2537 if (lower && upper) {
2538 const auto lval{GetIntValue(lower)};
2539 const auto uval{GetIntValue(upper)};
2540 if (lval && uval && *uval < *lval) {
2541 context_.Say(GetContext().clauseSource,
2542 "'%s' in %s clause"
2543 " is a zero size array section"_err_en_US,
2544 name.ToString(),
2545 parser::ToUpperCaseLetters(getClauseName(clause).str()));
2546 break;
2547 } else if (std::get<2>(triplet->t)) {
2548 const auto &strideExpr{std::get<2>(triplet->t)};
2549 if (strideExpr) {
2550 if (clause == llvm::omp::Clause::OMPC_depend) {
2551 context_.Say(GetContext().clauseSource,
2552 "Stride should not be specified for array section in "
2553 "DEPEND "
2554 "clause"_err_en_US);
2556 const auto stride{GetIntValue(strideExpr)};
2557 if ((stride && stride != 1)) {
2558 context_.Say(GetContext().clauseSource,
2559 "A list item that appears in a REDUCTION clause"
2560 " should have a contiguous storage array "
2561 "section."_err_en_US,
2562 ContextDirectiveAsFortran());
2563 break;
2574 void OmpStructureChecker::CheckIntentInPointer(
2575 const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) {
2576 SymbolSourceMap symbols;
2577 GetSymbolsInObjectList(objectList, symbols);
2578 for (auto it{symbols.begin()}; it != symbols.end(); ++it) {
2579 const auto *symbol{it->first};
2580 const auto source{it->second};
2581 if (IsPointer(*symbol) && IsIntentIn(*symbol)) {
2582 context_.Say(source,
2583 "Pointer '%s' with the INTENT(IN) attribute may not appear "
2584 "in a %s clause"_err_en_US,
2585 symbol->name(),
2586 parser::ToUpperCaseLetters(getClauseName(clause).str()));
2591 void OmpStructureChecker::GetSymbolsInObjectList(
2592 const parser::OmpObjectList &objectList, SymbolSourceMap &symbols) {
2593 for (const auto &ompObject : objectList.v) {
2594 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) {
2595 if (const auto *symbol{name->symbol}) {
2596 if (const auto *commonBlockDetails{
2597 symbol->detailsIf<CommonBlockDetails>()}) {
2598 for (const auto &object : commonBlockDetails->objects()) {
2599 symbols.emplace(&object->GetUltimate(), name->source);
2601 } else {
2602 symbols.emplace(&symbol->GetUltimate(), name->source);
2609 void OmpStructureChecker::CheckDefinableObjects(
2610 SymbolSourceMap &symbols, const llvm::omp::Clause clause) {
2611 for (auto it{symbols.begin()}; it != symbols.end(); ++it) {
2612 const auto *symbol{it->first};
2613 const auto source{it->second};
2614 if (auto msg{WhyNotDefinable(source, context_.FindScope(source),
2615 DefinabilityFlags{}, *symbol)}) {
2616 context_
2617 .Say(source,
2618 "Variable '%s' on the %s clause is not definable"_err_en_US,
2619 symbol->name(),
2620 parser::ToUpperCaseLetters(getClauseName(clause).str()))
2621 .Attach(std::move(*msg));
2626 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt(
2627 SymbolSourceMap &currSymbols, DirectivesClauseTriple &dirClauseTriple,
2628 const llvm::omp::Clause currClause) {
2629 SymbolSourceMap enclosingSymbols;
2630 auto range{dirClauseTriple.equal_range(GetContext().directive)};
2631 for (auto dirIter{range.first}; dirIter != range.second; ++dirIter) {
2632 auto enclosingDir{dirIter->second.first};
2633 auto enclosingClauseSet{dirIter->second.second};
2634 if (auto *enclosingContext{GetEnclosingContextWithDir(enclosingDir)}) {
2635 for (auto it{enclosingContext->clauseInfo.begin()};
2636 it != enclosingContext->clauseInfo.end(); ++it) {
2637 if (enclosingClauseSet.test(it->first)) {
2638 if (const auto *ompObjectList{GetOmpObjectList(*it->second)}) {
2639 GetSymbolsInObjectList(*ompObjectList, enclosingSymbols);
2644 // Check if the symbols in current context are private in outer context
2645 for (auto iter{currSymbols.begin()}; iter != currSymbols.end(); ++iter) {
2646 const auto *symbol{iter->first};
2647 const auto source{iter->second};
2648 if (enclosingSymbols.find(symbol) != enclosingSymbols.end()) {
2649 context_.Say(source,
2650 "%s variable '%s' is PRIVATE in outer context"_err_en_US,
2651 parser::ToUpperCaseLetters(getClauseName(currClause).str()),
2652 symbol->name());
2659 bool OmpStructureChecker::CheckTargetBlockOnlyTeams(
2660 const parser::Block &block) {
2661 bool nestedTeams{false};
2663 if (!block.empty()) {
2664 auto it{block.begin()};
2665 if (const auto *ompConstruct{
2666 parser::Unwrap<parser::OpenMPConstruct>(*it)}) {
2667 if (const auto *ompBlockConstruct{
2668 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) {
2669 const auto &beginBlockDir{
2670 std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)};
2671 const auto &beginDir{
2672 std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
2673 if (beginDir.v == llvm::omp::Directive::OMPD_teams) {
2674 nestedTeams = true;
2679 if (nestedTeams && ++it == block.end()) {
2680 return true;
2684 return false;
2687 void OmpStructureChecker::CheckWorkshareBlockStmts(
2688 const parser::Block &block, parser::CharBlock source) {
2689 OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source};
2691 for (auto it{block.begin()}; it != block.end(); ++it) {
2692 if (parser::Unwrap<parser::AssignmentStmt>(*it) ||
2693 parser::Unwrap<parser::ForallStmt>(*it) ||
2694 parser::Unwrap<parser::ForallConstruct>(*it) ||
2695 parser::Unwrap<parser::WhereStmt>(*it) ||
2696 parser::Unwrap<parser::WhereConstruct>(*it)) {
2697 parser::Walk(*it, ompWorkshareBlockChecker);
2698 } else if (const auto *ompConstruct{
2699 parser::Unwrap<parser::OpenMPConstruct>(*it)}) {
2700 if (const auto *ompAtomicConstruct{
2701 std::get_if<parser::OpenMPAtomicConstruct>(&ompConstruct->u)}) {
2702 // Check if assignment statements in the enclosing OpenMP Atomic
2703 // construct are allowed in the Workshare construct
2704 parser::Walk(*ompAtomicConstruct, ompWorkshareBlockChecker);
2705 } else if (const auto *ompCriticalConstruct{
2706 std::get_if<parser::OpenMPCriticalConstruct>(
2707 &ompConstruct->u)}) {
2708 // All the restrictions on the Workshare construct apply to the
2709 // statements in the enclosing critical constructs
2710 const auto &criticalBlock{
2711 std::get<parser::Block>(ompCriticalConstruct->t)};
2712 CheckWorkshareBlockStmts(criticalBlock, source);
2713 } else {
2714 // Check if OpenMP constructs enclosed in the Workshare construct are
2715 // 'Parallel' constructs
2716 auto currentDir{llvm::omp::Directive::OMPD_unknown};
2717 const OmpDirectiveSet parallelDirSet{
2718 llvm::omp::Directive::OMPD_parallel,
2719 llvm::omp::Directive::OMPD_parallel_do,
2720 llvm::omp::Directive::OMPD_parallel_sections,
2721 llvm::omp::Directive::OMPD_parallel_workshare,
2722 llvm::omp::Directive::OMPD_parallel_do_simd};
2724 if (const auto *ompBlockConstruct{
2725 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) {
2726 const auto &beginBlockDir{
2727 std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)};
2728 const auto &beginDir{
2729 std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
2730 currentDir = beginDir.v;
2731 } else if (const auto *ompLoopConstruct{
2732 std::get_if<parser::OpenMPLoopConstruct>(
2733 &ompConstruct->u)}) {
2734 const auto &beginLoopDir{
2735 std::get<parser::OmpBeginLoopDirective>(ompLoopConstruct->t)};
2736 const auto &beginDir{
2737 std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
2738 currentDir = beginDir.v;
2739 } else if (const auto *ompSectionsConstruct{
2740 std::get_if<parser::OpenMPSectionsConstruct>(
2741 &ompConstruct->u)}) {
2742 const auto &beginSectionsDir{
2743 std::get<parser::OmpBeginSectionsDirective>(
2744 ompSectionsConstruct->t)};
2745 const auto &beginDir{
2746 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
2747 currentDir = beginDir.v;
2750 if (!parallelDirSet.test(currentDir)) {
2751 context_.Say(source,
2752 "OpenMP constructs enclosed in WORKSHARE construct may consist "
2753 "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US);
2756 } else {
2757 context_.Say(source,
2758 "The structured block in a WORKSHARE construct may consist of only "
2759 "SCALAR or ARRAY assignments, FORALL or WHERE statements, "
2760 "FORALL, WHERE, ATOMIC, CRITICAL or PARALLEL constructs"_err_en_US);
2765 const parser::OmpObjectList *OmpStructureChecker::GetOmpObjectList(
2766 const parser::OmpClause &clause) {
2768 // Clauses with OmpObjectList as its data member
2769 using MemberObjectListClauses = std::tuple<parser::OmpClause::Copyprivate,
2770 parser::OmpClause::Copyin, parser::OmpClause::Firstprivate,
2771 parser::OmpClause::From, parser::OmpClause::Lastprivate,
2772 parser::OmpClause::Link, parser::OmpClause::Private,
2773 parser::OmpClause::Shared, parser::OmpClause::To>;
2775 // Clauses with OmpObjectList in the tuple
2776 using TupleObjectListClauses = std::tuple<parser::OmpClause::Allocate,
2777 parser::OmpClause::Map, parser::OmpClause::Reduction>;
2779 // TODO:: Generate the tuples using TableGen.
2780 // Handle other constructs with OmpObjectList such as OpenMPThreadprivate.
2781 return common::visit(
2782 common::visitors{
2783 [&](const auto &x) -> const parser::OmpObjectList * {
2784 using Ty = std::decay_t<decltype(x)>;
2785 if constexpr (common::HasMember<Ty, MemberObjectListClauses>) {
2786 return &x.v;
2787 } else if constexpr (common::HasMember<Ty,
2788 TupleObjectListClauses>) {
2789 return &(std::get<parser::OmpObjectList>(x.v.t));
2790 } else {
2791 return nullptr;
2795 clause.u);
2798 } // namespace Fortran::semantics