1 //===-- lib/Semantics/check-omp-structure.cpp -----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "check-omp-structure.h"
10 #include "flang/Parser/parse-tree.h"
11 #include "flang/Semantics/tools.h"
12 #include <algorithm>
13
14 namespace Fortran::semantics {
15
16 // Use when clause falls under 'struct OmpClause' in 'parse-tree.h'.
17 #define CHECK_SIMPLE_CLAUSE(X, Y) \
18 void OmpStructureChecker::Enter(const parser::OmpClause::X &) { \
19 CheckAllowed(llvm::omp::Clause::Y); \
20 }
21
22 #define CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(X, Y) \
23 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
24 CheckAllowed(llvm::omp::Clause::Y); \
25 RequiresConstantPositiveParameter(llvm::omp::Clause::Y, c.v); \
26 }
27
28 #define CHECK_REQ_SCALAR_INT_CLAUSE(X, Y) \
29 void OmpStructureChecker::Enter(const parser::OmpClause::X &c) { \
30 CheckAllowed(llvm::omp::Clause::Y); \
31 RequiresPositiveParameter(llvm::omp::Clause::Y, c.v); \
32 }
33
34 // Use when clause don't falls under 'struct OmpClause' in 'parse-tree.h'.
35 #define CHECK_SIMPLE_PARSER_CLAUSE(X, Y) \
36 void OmpStructureChecker::Enter(const parser::X &) { \
37 CheckAllowed(llvm::omp::Y); \
38 }
39
40 // 'OmpWorkshareBlockChecker' is used to check the validity of the assignment
41 // statements and the expressions enclosed in an OpenMP Workshare construct
42 class OmpWorkshareBlockChecker {
43 public:
OmpWorkshareBlockChecker(SemanticsContext & context,parser::CharBlock source)44 OmpWorkshareBlockChecker(SemanticsContext &context, parser::CharBlock source)
45 : context_{context}, source_{source} {}
46
Pre(const T &)47 template <typename T> bool Pre(const T &) { return true; }
Post(const T &)48 template <typename T> void Post(const T &) {}
49
Pre(const parser::AssignmentStmt & assignment)50 bool Pre(const parser::AssignmentStmt &assignment) {
51 const auto &var{std::get<parser::Variable>(assignment.t)};
52 const auto &expr{std::get<parser::Expr>(assignment.t)};
53 const auto *lhs{GetExpr(var)};
54 const auto *rhs{GetExpr(expr)};
55 Tristate isDefined{semantics::IsDefinedAssignment(
56 lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())};
57 if (isDefined == Tristate::Yes) {
58 context_.Say(expr.source,
59 "Defined assignment statement is not "
60 "allowed in a WORKSHARE construct"_err_en_US);
61 }
62 return true;
63 }
64
Pre(const parser::Expr & expr)65 bool Pre(const parser::Expr &expr) {
66 if (const auto *e{GetExpr(expr)}) {
67 for (const Symbol &symbol : evaluate::CollectSymbols(*e)) {
68 const Symbol &root{GetAssociationRoot(symbol)};
69 if (IsFunction(root) &&
70 !(root.attrs().test(Attr::ELEMENTAL) ||
71 root.attrs().test(Attr::INTRINSIC))) {
72 context_.Say(expr.source,
73 "User defined non-ELEMENTAL function "
74 "'%s' is not allowed in a WORKSHARE construct"_err_en_US,
75 root.name());
76 }
77 }
78 }
79 return false;
80 }
81
82 private:
83 SemanticsContext &context_;
84 parser::CharBlock source_;
85 };
86
HasInvalidWorksharingNesting(const parser::CharBlock & source,const OmpDirectiveSet & set)87 bool OmpStructureChecker::HasInvalidWorksharingNesting(
88 const parser::CharBlock &source, const OmpDirectiveSet &set) {
89 // set contains all the invalid closely nested directives
90 // for the given directive (`source` here)
91 if (CurrentDirectiveIsNested() && set.test(GetContext().directive)) {
92 context_.Say(source,
93 "A worksharing region may not be closely nested inside a "
94 "worksharing, explicit task, taskloop, critical, ordered, atomic, or "
95 "master region"_err_en_US);
96 return true;
97 }
98 return false;
99 }
100
Enter(const parser::OpenMPConstruct &)101 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) {
102 // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check
103 }
104
Enter(const parser::OpenMPLoopConstruct & x)105 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
106 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
107 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
108
109 // check matching, End directive is optional
110 if (const auto &endLoopDir{
111 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
112 const auto &endDir{
113 std::get<parser::OmpLoopDirective>(endLoopDir.value().t)};
114
115 CheckMatching<parser::OmpLoopDirective>(beginDir, endDir);
116 }
117
118 if (beginDir.v != llvm::omp::Directive::OMPD_do) {
119 PushContextAndClauseSets(beginDir.source, beginDir.v);
120 } else {
121 // 2.7.1 do-clause -> private-clause |
122 // firstprivate-clause |
123 // lastprivate-clause |
124 // linear-clause |
125 // reduction-clause |
126 // schedule-clause |
127 // collapse-clause |
128 // ordered-clause
129
130 // nesting check
131 HasInvalidWorksharingNesting(beginDir.source,
132 {llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_sections,
133 llvm::omp::Directive::OMPD_single,
134 llvm::omp::Directive::OMPD_workshare,
135 llvm::omp::Directive::OMPD_task,
136 llvm::omp::Directive::OMPD_taskloop,
137 llvm::omp::Directive::OMPD_critical,
138 llvm::omp::Directive::OMPD_ordered,
139 llvm::omp::Directive::OMPD_atomic,
140 llvm::omp::Directive::OMPD_master});
141 PushContextAndClauseSets(beginDir.source, llvm::omp::Directive::OMPD_do);
142 }
143 SetLoopInfo(x);
144 }
GetLoopIndex(const parser::DoConstruct * x)145 const parser::Name OmpStructureChecker::GetLoopIndex(
146 const parser::DoConstruct *x) {
147 using Bounds = parser::LoopControl::Bounds;
148 return std::get<Bounds>(x->GetLoopControl()->u).name.thing;
149 }
SetLoopInfo(const parser::OpenMPLoopConstruct & x)150 void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) {
151 if (const auto &loopConstruct{
152 std::get<std::optional<parser::DoConstruct>>(x.t)}) {
153 const parser::DoConstruct *loop{&*loopConstruct};
154 if (loop && loop->IsDoNormal()) {
155 const parser::Name &itrVal{GetLoopIndex(loop)};
156 SetLoopIv(itrVal.symbol);
157 }
158 }
159 }
160
Leave(const parser::OpenMPLoopConstruct &)161 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
162 dirContext_.pop_back();
163 }
164
Enter(const parser::OmpEndLoopDirective & x)165 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
166 const auto &dir{std::get<parser::OmpLoopDirective>(x.t)};
167 ResetPartialContext(dir.source);
168 switch (dir.v) {
169 // 2.7.1 end-do -> END DO [nowait-clause]
170 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
171 case llvm::omp::Directive::OMPD_do:
172 case llvm::omp::Directive::OMPD_do_simd:
173 SetClauseSets(dir.v);
174 break;
175 default:
176 // no clauses are allowed
177 break;
178 }
179 }
180
Enter(const parser::OpenMPBlockConstruct & x)181 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
182 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
183 const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)};
184 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
185 const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)};
186 const parser::Block &block{std::get<parser::Block>(x.t)};
187
188 CheckMatching<parser::OmpBlockDirective>(beginDir, endDir);
189
190 // TODO: This check needs to be extended while implementing nesting of regions
191 // checks.
192 if (beginDir.v == llvm::omp::Directive::OMPD_single) {
193 HasInvalidWorksharingNesting(
194 beginDir.source, {llvm::omp::Directive::OMPD_do});
195 }
196
197 PushContextAndClauseSets(beginDir.source, beginDir.v);
198 CheckNoBranching(block, beginDir.v, beginDir.source);
199
200 switch (beginDir.v) {
201 case llvm::omp::OMPD_workshare:
202 case llvm::omp::OMPD_parallel_workshare:
203 CheckWorkshareBlockStmts(block, beginDir.source);
204 break;
205 default:
206 break;
207 }
208 }
209
Leave(const parser::OpenMPBlockConstruct &)210 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
211 dirContext_.pop_back();
212 }
213
Enter(const parser::OpenMPSectionsConstruct & x)214 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
215 const auto &beginSectionsDir{
216 std::get<parser::OmpBeginSectionsDirective>(x.t)};
217 const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)};
218 const auto &beginDir{
219 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
220 const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)};
221 CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir);
222
223 PushContextAndClauseSets(beginDir.source, beginDir.v);
224 }
225
Leave(const parser::OpenMPSectionsConstruct &)226 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
227 dirContext_.pop_back();
228 }
229
Enter(const parser::OmpEndSectionsDirective & x)230 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
231 const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)};
232 ResetPartialContext(dir.source);
233 switch (dir.v) {
234 // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
235 case llvm::omp::Directive::OMPD_sections:
236 PushContextAndClauseSets(
237 dir.source, llvm::omp::Directive::OMPD_end_sections);
238 break;
239 default:
240 // no clauses are allowed
241 break;
242 }
243 }
244
Enter(const parser::OpenMPDeclareSimdConstruct & x)245 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
246 const auto &dir{std::get<parser::Verbatim>(x.t)};
247 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd);
248 }
249
Leave(const parser::OpenMPDeclareSimdConstruct &)250 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
251 dirContext_.pop_back();
252 }
253
Enter(const parser::OpenMPDeclarativeAllocate & x)254 void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeAllocate &x) {
255 const auto &dir{std::get<parser::Verbatim>(x.t)};
256 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
257 }
258
Leave(const parser::OpenMPDeclarativeAllocate &)259 void OmpStructureChecker::Leave(const parser::OpenMPDeclarativeAllocate &) {
260 dirContext_.pop_back();
261 }
262
Enter(const parser::OpenMPDeclareTargetConstruct & x)263 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
264 const auto &dir{std::get<parser::Verbatim>(x.t)};
265 PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target);
266 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
267 if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) {
268 SetClauseSets(llvm::omp::Directive::OMPD_declare_target);
269 }
270 }
271
Leave(const parser::OpenMPDeclareTargetConstruct &)272 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) {
273 dirContext_.pop_back();
274 }
275
Enter(const parser::OpenMPExecutableAllocate & x)276 void OmpStructureChecker::Enter(const parser::OpenMPExecutableAllocate &x) {
277 const auto &dir{std::get<parser::Verbatim>(x.t)};
278 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_allocate);
279 }
280
Leave(const parser::OpenMPExecutableAllocate &)281 void OmpStructureChecker::Leave(const parser::OpenMPExecutableAllocate &) {
282 dirContext_.pop_back();
283 }
284
Enter(const parser::OpenMPSimpleStandaloneConstruct & x)285 void OmpStructureChecker::Enter(
286 const parser::OpenMPSimpleStandaloneConstruct &x) {
287 const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
288 PushContextAndClauseSets(dir.source, dir.v);
289 }
290
Leave(const parser::OpenMPSimpleStandaloneConstruct &)291 void OmpStructureChecker::Leave(
292 const parser::OpenMPSimpleStandaloneConstruct &) {
293 dirContext_.pop_back();
294 }
295
Enter(const parser::OpenMPFlushConstruct & x)296 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
297 const auto &dir{std::get<parser::Verbatim>(x.t)};
298 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_flush);
299 }
300
Leave(const parser::OpenMPFlushConstruct & x)301 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
302 if (FindClause(llvm::omp::Clause::OMPC_acquire) ||
303 FindClause(llvm::omp::Clause::OMPC_release) ||
304 FindClause(llvm::omp::Clause::OMPC_acq_rel)) {
305 if (const auto &flushList{
306 std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
307 context_.Say(parser::FindSourceLocation(flushList),
308 "If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items "
309 "must not be specified on the FLUSH directive"_err_en_US);
310 }
311 }
312 dirContext_.pop_back();
313 }
314
Enter(const parser::OpenMPCancelConstruct & x)315 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
316 const auto &dir{std::get<parser::Verbatim>(x.t)};
317 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel);
318 }
319
Leave(const parser::OpenMPCancelConstruct &)320 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
321 dirContext_.pop_back();
322 }
323
Enter(const parser::OpenMPCriticalConstruct & x)324 void OmpStructureChecker::Enter(const parser::OpenMPCriticalConstruct &x) {
325 const auto &dir{std::get<parser::OmpCriticalDirective>(x.t)};
326 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_critical);
327 }
328
Leave(const parser::OpenMPCriticalConstruct &)329 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) {
330 dirContext_.pop_back();
331 }
332
Enter(const parser::OpenMPCancellationPointConstruct & x)333 void OmpStructureChecker::Enter(
334 const parser::OpenMPCancellationPointConstruct &x) {
335 const auto &dir{std::get<parser::Verbatim>(x.t)};
336 PushContextAndClauseSets(
337 dir.source, llvm::omp::Directive::OMPD_cancellation_point);
338 }
339
Leave(const parser::OpenMPCancellationPointConstruct &)340 void OmpStructureChecker::Leave(
341 const parser::OpenMPCancellationPointConstruct &) {
342 dirContext_.pop_back();
343 }
344
Enter(const parser::OmpEndBlockDirective & x)345 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
346 const auto &dir{std::get<parser::OmpBlockDirective>(x.t)};
347 ResetPartialContext(dir.source);
348 switch (dir.v) {
349 // 2.7.3 end-single-clause -> copyprivate-clause |
350 // nowait-clause
351 case llvm::omp::Directive::OMPD_single:
352 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_single);
353 break;
354 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
355 case llvm::omp::Directive::OMPD_workshare:
356 PushContextAndClauseSets(
357 dir.source, llvm::omp::Directive::OMPD_end_workshare);
358 break;
359 default:
360 // no clauses are allowed
361 break;
362 }
363 }
364
Enter(const parser::OpenMPAtomicConstruct & x)365 void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
366 std::visit(
367 common::visitors{
368 [&](const auto &someAtomicConstruct) {
369 const auto &dir{std::get<parser::Verbatim>(someAtomicConstruct.t)};
370 PushContextAndClauseSets(
371 dir.source, llvm::omp::Directive::OMPD_atomic);
372 },
373 },
374 x.u);
375 }
376
Leave(const parser::OpenMPAtomicConstruct &)377 void OmpStructureChecker::Leave(const parser::OpenMPAtomicConstruct &) {
378 dirContext_.pop_back();
379 }
380
381 // Clauses
382 // Mainly categorized as
383 // 1. Checks on 'OmpClauseList' from 'parse-tree.h'.
384 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h.
385 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h.
386
Leave(const parser::OmpClauseList &)387 void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
388 // 2.7 Loop Construct Restriction
389 if (llvm::omp::doSet.test(GetContext().directive)) {
390 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) {
391 // only one schedule clause is allowed
392 const auto &schedClause{std::get<parser::OmpClause::Schedule>(clause->u)};
393 if (ScheduleModifierHasType(schedClause.v,
394 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
395 if (FindClause(llvm::omp::Clause::OMPC_ordered)) {
396 context_.Say(clause->source,
397 "The NONMONOTONIC modifier cannot be specified "
398 "if an ORDERED clause is specified"_err_en_US);
399 }
400 if (ScheduleModifierHasType(schedClause.v,
401 parser::OmpScheduleModifierType::ModType::Monotonic)) {
402 context_.Say(clause->source,
403 "The MONOTONIC and NONMONOTONIC modifiers "
404 "cannot be both specified"_err_en_US);
405 }
406 }
407 }
408
409 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) {
410 // only one ordered clause is allowed
411 const auto &orderedClause{
412 std::get<parser::OmpClause::Ordered>(clause->u)};
413
414 if (orderedClause.v) {
415 CheckNotAllowedIfClause(
416 llvm::omp::Clause::OMPC_ordered, {llvm::omp::Clause::OMPC_linear});
417
418 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) {
419 const auto &collapseClause{
420 std::get<parser::OmpClause::Collapse>(clause2->u)};
421 // ordered and collapse both have parameters
422 if (const auto orderedValue{GetIntValue(orderedClause.v)}) {
423 if (const auto collapseValue{GetIntValue(collapseClause.v)}) {
424 if (*orderedValue > 0 && *orderedValue < *collapseValue) {
425 context_.Say(clause->source,
426 "The parameter of the ORDERED clause must be "
427 "greater than or equal to "
428 "the parameter of the COLLAPSE clause"_err_en_US);
429 }
430 }
431 }
432 }
433 }
434
435 // TODO: ordered region binding check (requires nesting implementation)
436 }
437 } // doSet
438
439 // 2.8.1 Simd Construct Restriction
440 if (llvm::omp::simdSet.test(GetContext().directive)) {
441 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) {
442 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) {
443 const auto &simdlenClause{
444 std::get<parser::OmpClause::Simdlen>(clause->u)};
445 const auto &safelenClause{
446 std::get<parser::OmpClause::Safelen>(clause2->u)};
447 // simdlen and safelen both have parameters
448 if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) {
449 if (const auto safelenValue{GetIntValue(safelenClause.v)}) {
450 if (*safelenValue > 0 && *simdlenValue > *safelenValue) {
451 context_.Say(clause->source,
452 "The parameter of the SIMDLEN clause must be less than or "
453 "equal to the parameter of the SAFELEN clause"_err_en_US);
454 }
455 }
456 }
457 }
458 }
459 // TODO: A list-item cannot appear in more than one aligned clause
460 } // SIMD
461
462 // 2.7.3 Single Construct Restriction
463 if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) {
464 CheckNotAllowedIfClause(
465 llvm::omp::Clause::OMPC_copyprivate, {llvm::omp::Clause::OMPC_nowait});
466 }
467
468 CheckRequireAtLeastOneOf();
469 }
470
Enter(const parser::OmpClause & x)471 void OmpStructureChecker::Enter(const parser::OmpClause &x) {
472 SetContextClause(x);
473 }
474
475 // Following clauses do not have a seperate node in parse-tree.h.
476 // They fall under 'struct OmpClause' in parse-tree.h.
CHECK_SIMPLE_CLAUSE(Allocate,OMPC_allocate)477 CHECK_SIMPLE_CLAUSE(Allocate, OMPC_allocate)
478 CHECK_SIMPLE_CLAUSE(Copyin, OMPC_copyin)
479 CHECK_SIMPLE_CLAUSE(Copyprivate, OMPC_copyprivate)
480 CHECK_SIMPLE_CLAUSE(Default, OMPC_default)
481 CHECK_SIMPLE_CLAUSE(Device, OMPC_device)
482 CHECK_SIMPLE_CLAUSE(Final, OMPC_final)
483 CHECK_SIMPLE_CLAUSE(From, OMPC_from)
484 CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch)
485 CHECK_SIMPLE_CLAUSE(IsDevicePtr, OMPC_is_device_ptr)
486 CHECK_SIMPLE_CLAUSE(Lastprivate, OMPC_lastprivate)
487 CHECK_SIMPLE_CLAUSE(Link, OMPC_link)
488 CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable)
489 CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup)
490 CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch)
491 CHECK_SIMPLE_CLAUSE(Nowait, OMPC_nowait)
492 CHECK_SIMPLE_CLAUSE(Reduction, OMPC_reduction)
493 CHECK_SIMPLE_CLAUSE(TaskReduction, OMPC_task_reduction)
494 CHECK_SIMPLE_CLAUSE(To, OMPC_to)
495 CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform)
496 CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied)
497 CHECK_SIMPLE_CLAUSE(UseDevicePtr, OMPC_use_device_ptr)
498 CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel)
499 CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire)
500 CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst)
501 CHECK_SIMPLE_CLAUSE(Release, OMPC_release)
502 CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed)
503 CHECK_SIMPLE_CLAUSE(Hint, OMPC_hint)
504 CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind)
505 CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule)
506
507 CHECK_REQ_SCALAR_INT_CLAUSE(Allocator, OMPC_allocator)
508 CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize)
509 CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks)
510 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams)
511 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads)
512 CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority)
513 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit)
514
515 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse)
516 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen)
517 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen)
518
519 // Restrictions specific to each clause are implemented apart from the
520 // generalized restrictions.
521 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
522 CheckAllowed(llvm::omp::Clause::OMPC_ordered);
523 // the parameter of ordered clause is optional
524 if (const auto &expr{x.v}) {
525 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr);
526 // 2.8.3 Loop SIMD Construct Restriction
527 if (llvm::omp::doSimdSet.test(GetContext().directive)) {
528 context_.Say(GetContext().clauseSource,
529 "No ORDERED clause with a parameter can be specified "
530 "on the %s directive"_err_en_US,
531 ContextDirectiveAsFortran());
532 }
533 }
534 }
535
Enter(const parser::OmpClause::Shared & x)536 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) {
537 CheckAllowed(llvm::omp::Clause::OMPC_shared);
538 CheckIsVarPartOfAnotherVar(x.v);
539 }
Enter(const parser::OmpClause::Private & x)540 void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) {
541 CheckAllowed(llvm::omp::Clause::OMPC_private);
542 CheckIsVarPartOfAnotherVar(x.v);
543 CheckIntentInPointer(x.v, llvm::omp::Clause::OMPC_private);
544 }
545
CheckIsVarPartOfAnotherVar(const parser::OmpObjectList & objList)546 void OmpStructureChecker::CheckIsVarPartOfAnotherVar(
547 const parser::OmpObjectList &objList) {
548
549 for (const auto &ompObject : objList.v) {
550 std::visit(
551 common::visitors{
552 [&](const parser::Designator &designator) {
553 if (std::get_if<parser::DataRef>(&designator.u)) {
554 if ((parser::Unwrap<parser::StructureComponent>(ompObject)) ||
555 (parser::Unwrap<parser::ArrayElement>(ompObject))) {
556 context_.Say(GetContext().clauseSource,
557 "A variable that is part of another variable (as an "
558 "array or structure element)"
559 " cannot appear in a PRIVATE or SHARED clause."_err_en_US);
560 }
561 }
562 },
563 [&](const parser::Name &name) {},
564 },
565 ompObject.u);
566 }
567 }
Enter(const parser::OmpClause::Firstprivate & x)568 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) {
569 CheckAllowed(llvm::omp::Clause::OMPC_firstprivate);
570 CheckIsLoopIvPartOfClause(llvmOmpClause::OMPC_firstprivate, x.v);
571 }
CheckIsLoopIvPartOfClause(llvmOmpClause clause,const parser::OmpObjectList & ompObjectList)572 void OmpStructureChecker::CheckIsLoopIvPartOfClause(
573 llvmOmpClause clause, const parser::OmpObjectList &ompObjectList) {
574 for (const auto &ompObject : ompObjectList.v) {
575 if (const parser::Name * name{parser::Unwrap<parser::Name>(ompObject)}) {
576 if (name->symbol == GetContext().loopIV) {
577 context_.Say(name->source,
578 "DO iteration variable %s is not allowed in %s clause."_err_en_US,
579 name->ToString(),
580 parser::ToUpperCaseLetters(getClauseName(clause).str()));
581 }
582 }
583 }
584 }
585 // Following clauses have a seperate node in parse-tree.h.
586 // Atomic-clause
CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead,OMPC_read)587 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicRead, OMPC_read)
588 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicWrite, OMPC_write)
589 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicUpdate, OMPC_update)
590 CHECK_SIMPLE_PARSER_CLAUSE(OmpAtomicCapture, OMPC_capture)
591
592 void OmpStructureChecker::Leave(const parser::OmpAtomicRead &) {
593 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_read,
594 {llvm::omp::Clause::OMPC_release, llvm::omp::Clause::OMPC_acq_rel});
595 }
Leave(const parser::OmpAtomicWrite &)596 void OmpStructureChecker::Leave(const parser::OmpAtomicWrite &) {
597 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_write,
598 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel});
599 }
Leave(const parser::OmpAtomicUpdate &)600 void OmpStructureChecker::Leave(const parser::OmpAtomicUpdate &) {
601 CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_update,
602 {llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_acq_rel});
603 }
604 // OmpAtomic node represents atomic directive without atomic-clause.
605 // atomic-clause - READ,WRITE,UPDATE,CAPTURE.
Leave(const parser::OmpAtomic &)606 void OmpStructureChecker::Leave(const parser::OmpAtomic &) {
607 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acquire)}) {
608 context_.Say(clause->source,
609 "Clause ACQUIRE is not allowed on the ATOMIC directive"_err_en_US);
610 }
611 if (const auto *clause{FindClause(llvm::omp::Clause::OMPC_acq_rel)}) {
612 context_.Say(clause->source,
613 "Clause ACQ_REL is not allowed on the ATOMIC directive"_err_en_US);
614 }
615 }
616 // Restrictions specific to each clause are implemented apart from the
617 // generalized restrictions.
Enter(const parser::OmpAlignedClause & x)618 void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) {
619 CheckAllowed(llvm::omp::Clause::OMPC_aligned);
620
621 if (const auto &expr{
622 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) {
623 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_aligned, *expr);
624 }
625 // 2.8.1 TODO: list-item attribute check
626 }
Enter(const parser::OmpDefaultmapClause & x)627 void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) {
628 CheckAllowed(llvm::omp::Clause::OMPC_defaultmap);
629 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory;
630 if (!std::get<std::optional<VariableCategory>>(x.t)) {
631 context_.Say(GetContext().clauseSource,
632 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP "
633 "clause"_err_en_US);
634 }
635 }
Enter(const parser::OmpIfClause & x)636 void OmpStructureChecker::Enter(const parser::OmpIfClause &x) {
637 CheckAllowed(llvm::omp::Clause::OMPC_if);
638
639 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier;
640 static std::unordered_map<dirNameModifier, OmpDirectiveSet>
641 dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet},
642 {dirNameModifier::Target, llvm::omp::targetSet},
643 {dirNameModifier::TargetEnterData,
644 {llvm::omp::Directive::OMPD_target_enter_data}},
645 {dirNameModifier::TargetExitData,
646 {llvm::omp::Directive::OMPD_target_exit_data}},
647 {dirNameModifier::TargetData,
648 {llvm::omp::Directive::OMPD_target_data}},
649 {dirNameModifier::TargetUpdate,
650 {llvm::omp::Directive::OMPD_target_update}},
651 {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}},
652 {dirNameModifier::Taskloop, llvm::omp::taskloopSet}};
653 if (const auto &directiveName{
654 std::get<std::optional<dirNameModifier>>(x.t)}) {
655 auto search{dirNameModifierMap.find(*directiveName)};
656 if (search == dirNameModifierMap.end() ||
657 !search->second.test(GetContext().directive)) {
658 context_
659 .Say(GetContext().clauseSource,
660 "Unmatched directive name modifier %s on the IF clause"_err_en_US,
661 parser::ToUpperCaseLetters(
662 parser::OmpIfClause::EnumToString(*directiveName)))
663 .Attach(
664 GetContext().directiveSource, "Cannot apply to directive"_en_US);
665 }
666 }
667 }
668
Enter(const parser::OmpLinearClause & x)669 void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) {
670 CheckAllowed(llvm::omp::Clause::OMPC_linear);
671
672 // 2.7 Loop Construct Restriction
673 if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) {
674 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) {
675 context_.Say(GetContext().clauseSource,
676 "A modifier may not be specified in a LINEAR clause "
677 "on the %s directive"_err_en_US,
678 ContextDirectiveAsFortran());
679 }
680 }
681 }
682
CheckAllowedMapTypes(const parser::OmpMapType::Type & type,const std::list<parser::OmpMapType::Type> & allowedMapTypeList)683 void OmpStructureChecker::CheckAllowedMapTypes(
684 const parser::OmpMapType::Type &type,
685 const std::list<parser::OmpMapType::Type> &allowedMapTypeList) {
686 const auto found{std::find(
687 std::begin(allowedMapTypeList), std::end(allowedMapTypeList), type)};
688 if (found == std::end(allowedMapTypeList)) {
689 std::string commaSeperatedMapTypes;
690 llvm::interleave(
691 allowedMapTypeList.begin(), allowedMapTypeList.end(),
692 [&](const parser::OmpMapType::Type &mapType) {
693 commaSeperatedMapTypes.append(parser::ToUpperCaseLetters(
694 parser::OmpMapType::EnumToString(mapType)));
695 },
696 [&] { commaSeperatedMapTypes.append(", "); });
697 context_.Say(GetContext().clauseSource,
698 "Only the %s map types are permitted "
699 "for MAP clauses on the %s directive"_err_en_US,
700 commaSeperatedMapTypes, ContextDirectiveAsFortran());
701 }
702 }
703
Enter(const parser::OmpMapClause & x)704 void OmpStructureChecker::Enter(const parser::OmpMapClause &x) {
705 CheckAllowed(llvm::omp::Clause::OMPC_map);
706 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) {
707 using Type = parser::OmpMapType::Type;
708 const Type &type{std::get<Type>(maptype->t)};
709 switch (GetContext().directive) {
710 case llvm::omp::Directive::OMPD_target:
711 case llvm::omp::Directive::OMPD_target_teams:
712 case llvm::omp::Directive::OMPD_target_teams_distribute:
713 case llvm::omp::Directive::OMPD_target_teams_distribute_simd:
714 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do:
715 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd:
716 case llvm::omp::Directive::OMPD_target_data:
717 CheckAllowedMapTypes(
718 type, {Type::To, Type::From, Type::Tofrom, Type::Alloc});
719 break;
720 case llvm::omp::Directive::OMPD_target_enter_data:
721 CheckAllowedMapTypes(type, {Type::To, Type::Alloc});
722 break;
723 case llvm::omp::Directive::OMPD_target_exit_data:
724 CheckAllowedMapTypes(type, {Type::From, Type::Release, Type::Delete});
725 break;
726 default:
727 break;
728 }
729 }
730 }
731
ScheduleModifierHasType(const parser::OmpScheduleClause & x,const parser::OmpScheduleModifierType::ModType & type)732 bool OmpStructureChecker::ScheduleModifierHasType(
733 const parser::OmpScheduleClause &x,
734 const parser::OmpScheduleModifierType::ModType &type) {
735 const auto &modifier{
736 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)};
737 if (modifier) {
738 const auto &modType1{
739 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)};
740 const auto &modType2{
741 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(
742 modifier->t)};
743 if (modType1.v.v == type || (modType2 && modType2->v.v == type)) {
744 return true;
745 }
746 }
747 return false;
748 }
Enter(const parser::OmpScheduleClause & x)749 void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) {
750 CheckAllowed(llvm::omp::Clause::OMPC_schedule);
751
752 // 2.7 Loop Construct Restriction
753 if (llvm::omp::doSet.test(GetContext().directive)) {
754 const auto &kind{std::get<1>(x.t)};
755 const auto &chunk{std::get<2>(x.t)};
756 if (chunk) {
757 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime ||
758 kind == parser::OmpScheduleClause::ScheduleType::Auto) {
759 context_.Say(GetContext().clauseSource,
760 "When SCHEDULE clause has %s specified, "
761 "it must not have chunk size specified"_err_en_US,
762 parser::ToUpperCaseLetters(
763 parser::OmpScheduleClause::EnumToString(kind)));
764 }
765 if (const auto &chunkExpr{
766 std::get<std::optional<parser::ScalarIntExpr>>(x.t)}) {
767 RequiresPositiveParameter(
768 llvm::omp::Clause::OMPC_schedule, *chunkExpr, "chunk size");
769 }
770 }
771
772 if (ScheduleModifierHasType(
773 x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
774 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic &&
775 kind != parser::OmpScheduleClause::ScheduleType::Guided) {
776 context_.Say(GetContext().clauseSource,
777 "The NONMONOTONIC modifier can only be specified with "
778 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US);
779 }
780 }
781 }
782 }
783
Enter(const parser::OmpDependClause & x)784 void OmpStructureChecker::Enter(const parser::OmpDependClause &x) {
785 CheckAllowed(llvm::omp::Clause::OMPC_depend);
786 if (const auto *inOut{std::get_if<parser::OmpDependClause::InOut>(&x.u)}) {
787 const auto &designators{std::get<std::list<parser::Designator>>(inOut->t)};
788 for (const auto &ele : designators) {
789 if (const auto *dataRef{std::get_if<parser::DataRef>(&ele.u)}) {
790 CheckDependList(*dataRef);
791 if (const auto *arr{
792 std::get_if<common::Indirection<parser::ArrayElement>>(
793 &dataRef->u)}) {
794 CheckDependArraySection(*arr, GetLastName(*dataRef));
795 }
796 }
797 }
798 }
799 }
800
getClauseName(llvm::omp::Clause clause)801 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
802 return llvm::omp::getOpenMPClauseName(clause);
803 }
804
getDirectiveName(llvm::omp::Directive directive)805 llvm::StringRef OmpStructureChecker::getDirectiveName(
806 llvm::omp::Directive directive) {
807 return llvm::omp::getOpenMPDirectiveName(directive);
808 }
809
CheckDependList(const parser::DataRef & d)810 void OmpStructureChecker::CheckDependList(const parser::DataRef &d) {
811 std::visit(
812 common::visitors{
813 [&](const common::Indirection<parser::ArrayElement> &elem) {
814 // Check if the base element is valid on Depend Clause
815 CheckDependList(elem.value().base);
816 },
817 [&](const common::Indirection<parser::StructureComponent> &) {
818 context_.Say(GetContext().clauseSource,
819 "A variable that is part of another variable "
820 "(such as an element of a structure) but is not an array "
821 "element or an array section cannot appear in a DEPEND "
822 "clause"_err_en_US);
823 },
824 [&](const common::Indirection<parser::CoindexedNamedObject> &) {
825 context_.Say(GetContext().clauseSource,
826 "Coarrays are not supported in DEPEND clause"_err_en_US);
827 },
828 [&](const parser::Name &) { return; },
829 },
830 d.u);
831 }
832
CheckDependArraySection(const common::Indirection<parser::ArrayElement> & arr,const parser::Name & name)833 void OmpStructureChecker::CheckDependArraySection(
834 const common::Indirection<parser::ArrayElement> &arr,
835 const parser::Name &name) {
836 for (const auto &subscript : arr.value().subscripts) {
837 if (const auto *triplet{
838 std::get_if<parser::SubscriptTriplet>(&subscript.u)}) {
839 if (std::get<2>(triplet->t)) {
840 context_.Say(GetContext().clauseSource,
841 "Stride should not be specified for array section in DEPEND "
842 "clause"_err_en_US);
843 }
844 const auto &lower{std::get<0>(triplet->t)};
845 const auto &upper{std::get<1>(triplet->t)};
846 if (lower && upper) {
847 const auto lval{GetIntValue(lower)};
848 const auto uval{GetIntValue(upper)};
849 if (lval && uval && *uval < *lval) {
850 context_.Say(GetContext().clauseSource,
851 "'%s' in DEPEND clause is a zero size array section"_err_en_US,
852 name.ToString());
853 break;
854 }
855 }
856 }
857 }
858 }
859
CheckIntentInPointer(const parser::OmpObjectList & objectList,const llvm::omp::Clause clause)860 void OmpStructureChecker::CheckIntentInPointer(
861 const parser::OmpObjectList &objectList, const llvm::omp::Clause clause) {
862 std::vector<const Symbol *> symbols;
863 GetSymbolsInObjectList(objectList, symbols);
864 for (const auto *symbol : symbols) {
865 if (IsPointer(*symbol) && IsIntentIn(*symbol)) {
866 context_.Say(GetContext().clauseSource,
867 "Pointer '%s' with the INTENT(IN) attribute may not appear "
868 "in a %s clause"_err_en_US,
869 symbol->name(),
870 parser::ToUpperCaseLetters(getClauseName(clause).str()));
871 }
872 }
873 }
874
GetSymbolsInObjectList(const parser::OmpObjectList & objectList,std::vector<const Symbol * > & symbols)875 void OmpStructureChecker::GetSymbolsInObjectList(
876 const parser::OmpObjectList &objectList,
877 std::vector<const Symbol *> &symbols) {
878 for (const auto &ompObject : objectList.v) {
879 if (const auto *name{parser::Unwrap<parser::Name>(ompObject)}) {
880 if (const auto *symbol{name->symbol}) {
881 if (const auto *commonBlockDetails{
882 symbol->detailsIf<CommonBlockDetails>()}) {
883 for (const auto &object : commonBlockDetails->objects()) {
884 symbols.emplace_back(&object->GetUltimate());
885 }
886 } else {
887 symbols.emplace_back(&symbol->GetUltimate());
888 }
889 }
890 }
891 }
892 }
893
CheckWorkshareBlockStmts(const parser::Block & block,parser::CharBlock source)894 void OmpStructureChecker::CheckWorkshareBlockStmts(
895 const parser::Block &block, parser::CharBlock source) {
896 OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source};
897
898 for (auto it{block.begin()}; it != block.end(); ++it) {
899 if (parser::Unwrap<parser::AssignmentStmt>(*it) ||
900 parser::Unwrap<parser::ForallStmt>(*it) ||
901 parser::Unwrap<parser::ForallConstruct>(*it) ||
902 parser::Unwrap<parser::WhereStmt>(*it) ||
903 parser::Unwrap<parser::WhereConstruct>(*it)) {
904 parser::Walk(*it, ompWorkshareBlockChecker);
905 } else if (const auto *ompConstruct{
906 parser::Unwrap<parser::OpenMPConstruct>(*it)}) {
907 if (const auto *ompAtomicConstruct{
908 std::get_if<parser::OpenMPAtomicConstruct>(&ompConstruct->u)}) {
909 // Check if assignment statements in the enclosing OpenMP Atomic
910 // construct are allowed in the Workshare construct
911 parser::Walk(*ompAtomicConstruct, ompWorkshareBlockChecker);
912 } else if (const auto *ompCriticalConstruct{
913 std::get_if<parser::OpenMPCriticalConstruct>(
914 &ompConstruct->u)}) {
915 // All the restrictions on the Workshare construct apply to the
916 // statements in the enclosing critical constructs
917 const auto &criticalBlock{
918 std::get<parser::Block>(ompCriticalConstruct->t)};
919 CheckWorkshareBlockStmts(criticalBlock, source);
920 } else {
921 // Check if OpenMP constructs enclosed in the Workshare construct are
922 // 'Parallel' constructs
923 auto currentDir{llvm::omp::Directive::OMPD_unknown};
924 const OmpDirectiveSet parallelDirSet{
925 llvm::omp::Directive::OMPD_parallel,
926 llvm::omp::Directive::OMPD_parallel_do,
927 llvm::omp::Directive::OMPD_parallel_sections,
928 llvm::omp::Directive::OMPD_parallel_workshare,
929 llvm::omp::Directive::OMPD_parallel_do_simd};
930
931 if (const auto *ompBlockConstruct{
932 std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) {
933 const auto &beginBlockDir{
934 std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)};
935 const auto &beginDir{
936 std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
937 currentDir = beginDir.v;
938 } else if (const auto *ompLoopConstruct{
939 std::get_if<parser::OpenMPLoopConstruct>(
940 &ompConstruct->u)}) {
941 const auto &beginLoopDir{
942 std::get<parser::OmpBeginLoopDirective>(ompLoopConstruct->t)};
943 const auto &beginDir{
944 std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
945 currentDir = beginDir.v;
946 } else if (const auto *ompSectionsConstruct{
947 std::get_if<parser::OpenMPSectionsConstruct>(
948 &ompConstruct->u)}) {
949 const auto &beginSectionsDir{
950 std::get<parser::OmpBeginSectionsDirective>(
951 ompSectionsConstruct->t)};
952 const auto &beginDir{
953 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
954 currentDir = beginDir.v;
955 }
956
957 if (!parallelDirSet.test(currentDir)) {
958 context_.Say(source,
959 "OpenMP constructs enclosed in WORKSHARE construct may consist "
960 "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US);
961 }
962 }
963 } else {
964 context_.Say(source,
965 "The structured block in a WORKSHARE construct may consist of only "
966 "SCALAR or ARRAY assignments, FORALL or WHERE statements, "
967 "FORALL, WHERE, ATOMIC, CRITICAL or PARALLEL constructs"_err_en_US);
968 }
969 }
970 }
971
972 } // namespace Fortran::semantics
973