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