1 // Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "check-omp-structure.h"
16 #include "tools.h"
17 #include "../parser/parse-tree.h"
18 #include <unordered_map>
19
20 namespace Fortran::semantics {
21
22 static constexpr OmpDirectiveSet parallelSet{
23 OmpDirective::DISTRIBUTE_PARALLEL_DO,
24 OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL,
25 OmpDirective::PARALLEL_DO, OmpDirective::PARALLEL_DO_SIMD,
26 OmpDirective::PARALLEL_SECTIONS, OmpDirective::PARALLEL_WORKSHARE,
27 OmpDirective::TARGET_PARALLEL, OmpDirective::TARGET_PARALLEL_DO,
28 OmpDirective::TARGET_PARALLEL_DO_SIMD,
29 OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO,
30 OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
31 OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO,
32 OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD};
33 static constexpr OmpDirectiveSet doSet{OmpDirective::DISTRIBUTE_PARALLEL_DO,
34 OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL_DO,
35 OmpDirective::PARALLEL_DO_SIMD, OmpDirective::DO, OmpDirective::DO_SIMD,
36 OmpDirective::TARGET_PARALLEL_DO, OmpDirective::TARGET_PARALLEL_DO_SIMD,
37 OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO,
38 OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
39 OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO,
40 OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD};
41 static constexpr OmpDirectiveSet simdSet{
42 OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::DISTRIBUTE_SIMD,
43 OmpDirective::PARALLEL_DO_SIMD, OmpDirective::DO_SIMD, OmpDirective::SIMD,
44 OmpDirective::TARGET_PARALLEL_DO_SIMD,
45 OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
46 OmpDirective::TARGET_TEAMS_DISTRIBUTE_SIMD, OmpDirective::TARGET_SIMD,
47 OmpDirective::TASKLOOP_SIMD,
48 OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
49 OmpDirective::TEAMS_DISTRIBUTE_SIMD};
50 static constexpr OmpDirectiveSet doSimdSet{
51 OmpDirective::DISTRIBUTE_PARALLEL_DO_SIMD, OmpDirective::PARALLEL_DO_SIMD,
52 OmpDirective::DO_SIMD, OmpDirective::TARGET_PARALLEL_DO_SIMD,
53 OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
54 OmpDirective::TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD};
55 static constexpr OmpDirectiveSet taskloopSet{
56 OmpDirective::TASKLOOP, OmpDirective::TASKLOOP_SIMD};
57 static constexpr OmpDirectiveSet targetSet{OmpDirective::TARGET,
58 OmpDirective::TARGET_PARALLEL, OmpDirective::TARGET_PARALLEL_DO,
59 OmpDirective::TARGET_PARALLEL_DO_SIMD, OmpDirective::TARGET_SIMD,
60 OmpDirective::TARGET_TEAMS, OmpDirective::TARGET_TEAMS_DISTRIBUTE,
61 OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO,
62 OmpDirective::TARGET_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD,
63 OmpDirective::TARGET_TEAMS_DISTRIBUTE_SIMD};
64
ContextDirectiveAsFortran()65 std::string OmpStructureChecker::ContextDirectiveAsFortran() {
66 auto dir{EnumToString(GetContext().directive)};
67 std::replace(dir.begin(), dir.end(), '_', ' ');
68 return dir;
69 }
70
SayNotMatching(const parser::CharBlock & beginSource,const parser::CharBlock & endSource)71 void OmpStructureChecker::SayNotMatching(
72 const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
73 context_
74 .Say(endSource, "Unmatched %s directive"_err_en_US,
75 parser::ToUpperCaseLetters(endSource.ToString()))
76 .Attach(beginSource, "Does not match directive"_en_US);
77 }
78
HasInvalidWorksharingNesting(const parser::CharBlock & source,const OmpDirectiveSet & set)79 bool OmpStructureChecker::HasInvalidWorksharingNesting(
80 const parser::CharBlock &source, const OmpDirectiveSet &set) {
81 // set contains all the invalid closely nested directives
82 // for the given directive (`source` here)
83 if (CurrentDirectiveIsNested() && set.test(GetContext().directive)) {
84 context_.Say(source,
85 "A worksharing region may not be closely nested inside a "
86 "worksharing, explicit task, taskloop, critical, ordered, atomic, or "
87 "master region"_err_en_US);
88 return true;
89 }
90 return false;
91 }
92
CheckAllowed(OmpClause type)93 void OmpStructureChecker::CheckAllowed(OmpClause type) {
94 if (!GetContext().allowedClauses.test(type) &&
95 !GetContext().allowedOnceClauses.test(type) &&
96 !GetContext().allowedExclusiveClauses.test(type)) {
97 context_.Say(GetContext().clauseSource,
98 "%s clause is not allowed on the %s directive"_err_en_US,
99 EnumToString(type),
100 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
101 return;
102 }
103 if ((GetContext().allowedOnceClauses.test(type) ||
104 GetContext().allowedExclusiveClauses.test(type)) &&
105 FindClause(type)) {
106 context_.Say(GetContext().clauseSource,
107 "At most one %s clause can appear on the %s directive"_err_en_US,
108 EnumToString(type),
109 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
110 return;
111 }
112 if (GetContext().allowedExclusiveClauses.test(type)) {
113 std::vector<OmpClause> others;
114 GetContext().allowedExclusiveClauses.IterateOverMembers([&](OmpClause o) {
115 if (FindClause(o)) {
116 others.emplace_back(o);
117 }
118 });
119 for (const auto &e : others) {
120 context_.Say(GetContext().clauseSource,
121 "%s and %s are mutually exclusive and may not appear on the "
122 "same %s directive"_err_en_US,
123 EnumToString(type), EnumToString(e),
124 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
125 }
126 if (!others.empty()) {
127 return;
128 }
129 }
130 SetContextClauseInfo(type);
131 }
132
CheckRequired(OmpClause c)133 void OmpStructureChecker::CheckRequired(OmpClause c) {
134 if (!FindClause(c)) {
135 context_.Say(GetContext().directiveSource,
136 "At least one %s clause must appear on the %s directive"_err_en_US,
137 EnumToString(c), ContextDirectiveAsFortran());
138 }
139 }
140
RequiresConstantPositiveParameter(const OmpClause & clause,const parser::ScalarIntConstantExpr & i)141 void OmpStructureChecker::RequiresConstantPositiveParameter(
142 const OmpClause &clause, const parser::ScalarIntConstantExpr &i) {
143 if (const auto v{GetIntValue(i)}) {
144 if (*v <= 0) {
145 context_.Say(GetContext().clauseSource,
146 "The parameter of the %s clause must be "
147 "a constant positive integer expression"_err_en_US,
148 EnumToString(clause));
149 }
150 }
151 }
152
RequiresPositiveParameter(const OmpClause & clause,const parser::ScalarIntExpr & i)153 void OmpStructureChecker::RequiresPositiveParameter(
154 const OmpClause &clause, const parser::ScalarIntExpr &i) {
155 if (const auto v{GetIntValue(i)}) {
156 if (*v <= 0) {
157 context_.Say(GetContext().clauseSource,
158 "The parameter of the %s clause must be "
159 "a positive integer expression"_err_en_US,
160 EnumToString(clause));
161 }
162 }
163 }
164
Enter(const parser::OpenMPConstruct &)165 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) {
166 // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check
167 }
168
Enter(const parser::OpenMPLoopConstruct & x)169 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
170 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
171 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
172
173 // check matching, End directive is optional
174 if (const auto &endLoopDir{
175 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
176 CheckMatching<parser::OmpLoopDirective>(beginLoopDir, *endLoopDir);
177 }
178
179 switch (beginDir.v) {
180 // 2.7.1 do-clause -> private-clause |
181 // firstprivate-clause |
182 // lastprivate-clause |
183 // linear-clause |
184 // reduction-clause |
185 // schedule-clause |
186 // collapse-clause |
187 // ordered-clause
188 case parser::OmpLoopDirective::Directive::Do: {
189 // nesting check
190 HasInvalidWorksharingNesting(beginDir.source,
191 {OmpDirective::DO, OmpDirective::SECTIONS, OmpDirective::SINGLE,
192 OmpDirective::WORKSHARE, OmpDirective::TASK, OmpDirective::TASKLOOP,
193 OmpDirective::CRITICAL, OmpDirective::ORDERED, OmpDirective::ATOMIC,
194 OmpDirective::MASTER});
195
196 PushContext(beginDir.source, OmpDirective::DO);
197 OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
198 OmpClause::LASTPRIVATE, OmpClause::LINEAR, OmpClause::REDUCTION};
199 SetContextAllowed(allowed);
200 OmpClauseSet allowedOnce{
201 OmpClause::SCHEDULE, OmpClause::COLLAPSE, OmpClause::ORDERED};
202 SetContextAllowedOnce(allowedOnce);
203 } break;
204
205 // 2.11.1 parallel-do-clause -> parallel-clause |
206 // do-clause
207 case parser::OmpLoopDirective::Directive::ParallelDo: {
208 PushContext(beginDir.source, OmpDirective::PARALLEL_DO);
209 OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE,
210 OmpClause::FIRSTPRIVATE, OmpClause::SHARED, OmpClause::COPYIN,
211 OmpClause::REDUCTION, OmpClause::LASTPRIVATE, OmpClause::LINEAR};
212 SetContextAllowed(allowed);
213 OmpClauseSet allowedOnce{OmpClause::IF, OmpClause::NUM_THREADS,
214 OmpClause::PROC_BIND, OmpClause::SCHEDULE, OmpClause::COLLAPSE,
215 OmpClause::ORDERED};
216 SetContextAllowedOnce(allowedOnce);
217 } break;
218
219 // 2.8.1 simd-clause -> safelen-clause |
220 // simdlen-clause |
221 // linear-clause |
222 // aligned-clause |
223 // private-clause |
224 // lastprivate-clause |
225 // reduction-clause |
226 // collapse-clause
227 case parser::OmpLoopDirective::Directive::Simd: {
228 PushContext(beginDir.source, OmpDirective::SIMD);
229 OmpClauseSet allowed{OmpClause::LINEAR, OmpClause::ALIGNED,
230 OmpClause::PRIVATE, OmpClause::LASTPRIVATE, OmpClause::REDUCTION};
231 SetContextAllowed(allowed);
232 OmpClauseSet allowedOnce{
233 OmpClause::COLLAPSE, OmpClause::SAFELEN, OmpClause::SIMDLEN};
234 SetContextAllowedOnce(allowedOnce);
235 } break;
236
237 // 2.8.3 do-simd-clause -> do-clause |
238 // simd-clause
239 case parser::OmpLoopDirective::Directive::DoSimd: {
240 PushContext(beginDir.source, OmpDirective::DO_SIMD);
241 OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
242 OmpClause::LASTPRIVATE, OmpClause::LINEAR, OmpClause::REDUCTION,
243 OmpClause::ALIGNED};
244 SetContextAllowed(allowed);
245 OmpClauseSet allowedOnce{OmpClause::SCHEDULE, OmpClause::COLLAPSE,
246 OmpClause::ORDERED, OmpClause::SAFELEN, OmpClause::SIMDLEN};
247 SetContextAllowedOnce(allowedOnce);
248 } break;
249
250 // 2.11.4 parallel-do-simd-clause -> parallel-clause |
251 // do-simd-clause
252 case parser::OmpLoopDirective::Directive::ParallelDoSimd: {
253 PushContext(beginDir.source, OmpDirective::PARALLEL_DO_SIMD);
254 OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE,
255 OmpClause::FIRSTPRIVATE, OmpClause::SHARED, OmpClause::COPYIN,
256 OmpClause::REDUCTION, OmpClause::LASTPRIVATE, OmpClause::LINEAR,
257 OmpClause::ALIGNED};
258 SetContextAllowed(allowed);
259 OmpClauseSet allowedOnce{OmpClause::IF, OmpClause::NUM_THREADS,
260 OmpClause::PROC_BIND, OmpClause::SCHEDULE, OmpClause::COLLAPSE,
261 OmpClause::ORDERED, OmpClause::SAFELEN, OmpClause::SIMDLEN};
262 SetContextAllowedOnce(allowedOnce);
263 } break;
264
265 // 2.9.2 taskloop-clause -> if-clause |
266 // shared-clause |
267 // private-clause |
268 // firstprivate-clause |
269 // lastprivate-clause |
270 // default-clause |
271 // grainsize-clause |
272 // num-tasks-clause |
273 // collapse-clause |
274 // final-clause |
275 // priority-clause |
276 // untied-clause |
277 // mergeable-clause |
278 // nogroup-clause
279 case parser::OmpLoopDirective::Directive::Taskloop: {
280 PushContext(beginDir.source, OmpDirective::TASKLOOP);
281 OmpClauseSet allowed{OmpClause::SHARED, OmpClause::PRIVATE,
282 OmpClause::FIRSTPRIVATE, OmpClause::LASTPRIVATE, OmpClause::DEFAULT,
283 OmpClause::UNTIED, OmpClause::MERGEABLE, OmpClause::NOGROUP};
284 SetContextAllowed(allowed);
285 OmpClauseSet allowedOnce{OmpClause::COLLAPSE, OmpClause::IF,
286 OmpClause::FINAL, OmpClause::PRIORITY};
287 SetContextAllowedOnce(allowedOnce);
288 OmpClauseSet allowedExclusive{OmpClause::GRAINSIZE, OmpClause::NUM_TASKS};
289 SetContextAllowedExclusive(allowedExclusive);
290 } break;
291
292 // 2.9.3 taskloop-simd-clause -> taskloop-clause |
293 // simd-clause
294 case parser::OmpLoopDirective::Directive::TaskloopSimd: {
295 PushContext(beginDir.source, OmpDirective::TASKLOOP_SIMD);
296 OmpClauseSet allowed{OmpClause::LINEAR, OmpClause::ALIGNED,
297 OmpClause::SHARED, OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
298 OmpClause::LASTPRIVATE, OmpClause::DEFAULT, OmpClause::UNTIED,
299 OmpClause::MERGEABLE, OmpClause::NOGROUP};
300 SetContextAllowed(allowed);
301 OmpClauseSet allowedOnce{OmpClause::COLLAPSE, OmpClause::SAFELEN,
302 OmpClause::SIMDLEN, OmpClause::IF, OmpClause::FINAL,
303 OmpClause::PRIORITY};
304 SetContextAllowedOnce(allowedOnce);
305 OmpClauseSet allowedExclusive{OmpClause::GRAINSIZE, OmpClause::NUM_TASKS};
306 SetContextAllowedExclusive(allowedExclusive);
307 } break;
308
309 // 2.10.8 distribute-clause -> private-clause |
310 // firstprivate-clause |
311 // lastprivate-clause |
312 // collapse-clause |
313 // dist-schedule-clause
314 case parser::OmpLoopDirective::Directive::Distribute: {
315 PushContext(beginDir.source, OmpDirective::DISTRIBUTE);
316 OmpClauseSet allowed{
317 OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE, OmpClause::LASTPRIVATE};
318 SetContextAllowed(allowed);
319 OmpClauseSet allowedOnce{OmpClause::COLLAPSE, OmpClause::DIST_SCHEDULE};
320 SetContextAllowedOnce(allowedOnce);
321 } break;
322
323 default:
324 // TODO others
325 break;
326 }
327 }
328
Leave(const parser::OpenMPLoopConstruct &)329 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
330 ompContext_.pop_back();
331 }
332
Enter(const parser::OmpEndLoopDirective & x)333 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
334 const auto &dir{std::get<parser::OmpLoopDirective>(x.t)};
335 ResetPartialContext(dir.source);
336 switch (dir.v) {
337 // 2.7.1 end-do -> END DO [nowait-clause]
338 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause]
339 case parser::OmpLoopDirective::Directive::Do:
340 SetContextDirectiveEnum(OmpDirective::END_DO);
341 SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT});
342 break;
343 case parser::OmpLoopDirective::Directive::DoSimd:
344 SetContextDirectiveEnum(OmpDirective::END_DO_SIMD);
345 SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT});
346 break;
347 default:
348 // no clauses are allowed
349 break;
350 }
351 }
352
Enter(const parser::OpenMPBlockConstruct & x)353 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
354 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
355 const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)};
356 const auto &beginDir{
357 CheckMatching<parser::OmpBlockDirective>(beginBlockDir, endBlockDir)};
358
359 switch (beginDir.v) {
360 // 2.5 parallel-clause -> if-clause |
361 // num-threads-clause |
362 // default-clause |
363 // private-clause |
364 // firstprivate-clause |
365 // shared-clause |
366 // copyin-clause |
367 // reduction-clause |
368 // proc-bind-clause
369 case parser::OmpBlockDirective::Directive::Parallel: {
370 // reserve for nesting check
371 PushContext(beginDir.source, OmpDirective::PARALLEL);
372 OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE,
373 OmpClause::FIRSTPRIVATE, OmpClause::SHARED, OmpClause::COPYIN,
374 OmpClause::REDUCTION};
375 SetContextAllowed(allowed);
376 OmpClauseSet allowedOnce{
377 OmpClause::IF, OmpClause::NUM_THREADS, OmpClause::PROC_BIND};
378 SetContextAllowedOnce(allowedOnce);
379 } break;
380 // 2.7.3 single-clause -> private-clause |
381 // firstprivate-clause
382 case parser::OmpBlockDirective::Directive::Single:
383 PushContext(beginDir.source, OmpDirective::SINGLE);
384 SetContextAllowed({OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE});
385 break;
386 // 2.7.4 workshare (no clauses are allowed)
387 case parser::OmpBlockDirective::Directive::Workshare:
388 PushContext(beginDir.source, OmpDirective::WORKSHARE);
389 break;
390 // 2.9.1 task-clause -> if-clause |
391 // final-clause |
392 // untied-clause |
393 // default-clause |
394 // mergeable-clause |
395 // private-clause |
396 // firstprivate-clause |
397 // shared-clause |
398 // depend-clause |
399 // priority-clause
400 case parser::OmpBlockDirective::Directive::Task: {
401 PushContext(beginDir.source, OmpDirective::TASK);
402 OmpClauseSet allowed{OmpClause::UNTIED, OmpClause::DEFAULT,
403 OmpClause::MERGEABLE, OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
404 OmpClause::SHARED, OmpClause::DEPEND};
405 SetContextAllowed(allowed);
406 OmpClauseSet allowedOnce{
407 OmpClause::IF, OmpClause::FINAL, OmpClause::PRIORITY};
408 SetContextAllowedOnce(allowedOnce);
409 } break;
410 // 2.10.4 target-clause -> if-clause |
411 // device-clause |
412 // private-clause |
413 // firstprivate-clause |
414 // map-clause |
415 // is-device-ptr-clause |
416 // defaultmap-clause |
417 // nowait-clause |
418 // depend-clause
419 case parser::OmpBlockDirective::Directive::Target: {
420 PushContext(beginDir.source, OmpDirective::TARGET);
421 OmpClauseSet allowed{OmpClause::IF, OmpClause::PRIVATE,
422 OmpClause::FIRSTPRIVATE, OmpClause::MAP, OmpClause::IS_DEVICE_PTR,
423 OmpClause::DEPEND};
424 SetContextAllowed(allowed);
425 OmpClauseSet allowedOnce{
426 OmpClause::DEVICE, OmpClause::DEFAULTMAP, OmpClause::NOWAIT};
427 SetContextAllowedOnce(allowedOnce);
428 } break;
429 // 2.10.7 teams-clause -> num-teams-clause |
430 // thread-limit-clause |
431 // default-clause |
432 // private-clause |
433 // firstprivate-clause |
434 // shared-clause |
435 // reduction-clause
436 case parser::OmpBlockDirective::Directive::Teams: {
437 PushContext(beginDir.source, OmpDirective::TEAMS);
438 OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
439 OmpClause::SHARED, OmpClause::REDUCTION};
440 SetContextAllowed(allowed);
441 OmpClauseSet allowedOnce{
442 OmpClause::NUM_TEAMS, OmpClause::THREAD_LIMIT, OmpClause::DEFAULT};
443 SetContextAllowedOnce(allowedOnce);
444 } break;
445 // 2.10.1 target-data-clause -> if-clause |
446 // device-clause |
447 // map-clause |
448 // use-device-ptr-clause
449 case parser::OmpBlockDirective::Directive::TargetData: {
450 PushContext(beginDir.source, OmpDirective::TARGET_DATA);
451 OmpClauseSet allowed{
452 OmpClause::IF, OmpClause::MAP, OmpClause::USE_DEVICE_PTR};
453 SetContextAllowed(allowed);
454 SetContextAllowedOnce({OmpClause::DEVICE});
455 SetContextRequired({OmpClause::MAP});
456 } break;
457 default:
458 // TODO others
459 break;
460 }
461 }
462
Leave(const parser::OpenMPBlockConstruct &)463 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
464 ompContext_.pop_back();
465 }
466
Enter(const parser::OpenMPSectionsConstruct & x)467 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
468 const auto &beginSectionsDir{
469 std::get<parser::OmpBeginSectionsDirective>(x.t)};
470 const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)};
471 const auto &beginDir{CheckMatching<parser::OmpSectionsDirective>(
472 beginSectionsDir, endSectionsDir)};
473
474 switch (beginDir.v) {
475 // 2.7.2 sections-clause -> private-clause |
476 // firstprivate-clause |
477 // lastprivate-clause |
478 // reduction-clause
479 case parser::OmpSectionsDirective::Directive::Sections: {
480 PushContext(beginDir.source, OmpDirective::SECTIONS);
481 OmpClauseSet allowed{OmpClause::PRIVATE, OmpClause::FIRSTPRIVATE,
482 OmpClause::LASTPRIVATE, OmpClause::REDUCTION};
483 SetContextAllowed(allowed);
484 } break;
485 case parser::OmpSectionsDirective::Directive::ParallelSections: {
486 PushContext(beginDir.source, OmpDirective::PARALLEL_SECTIONS);
487 OmpClauseSet allowed{OmpClause::DEFAULT, OmpClause::PRIVATE,
488 OmpClause::FIRSTPRIVATE, OmpClause::LASTPRIVATE, OmpClause::SHARED,
489 OmpClause::COPYIN, OmpClause::REDUCTION};
490 SetContextAllowed(allowed);
491 OmpClauseSet allowedOnce{
492 OmpClause::IF, OmpClause::NUM_THREADS, OmpClause::PROC_BIND};
493 SetContextAllowedOnce(allowedOnce);
494 } break;
495 }
496 }
497
Leave(const parser::OpenMPSectionsConstruct &)498 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
499 ompContext_.pop_back();
500 }
501
Enter(const parser::OmpEndSectionsDirective & x)502 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
503 const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)};
504 ResetPartialContext(dir.source);
505 switch (dir.v) {
506 // 2.7.2 end-sections -> END SECTIONS [nowait-clause]
507 case parser::OmpSectionsDirective::Directive::Sections:
508 SetContextDirectiveEnum(OmpDirective::END_SECTIONS);
509 SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT});
510 break;
511 default:
512 // no clauses are allowed
513 break;
514 }
515 }
516
Enter(const parser::OpenMPDeclareSimdConstruct & x)517 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
518 const auto &dir{std::get<parser::Verbatim>(x.t)};
519 PushContext(dir.source, OmpDirective::DECLARE_SIMD);
520 // 2.8.2 declare-simd-clause -> simdlen-clause |
521 // linear-clause |
522 // aligned-clause |
523 // uniform-clause |
524 // inbranch-clause |
525 // notinbranch-clause
526 OmpClauseSet allowed{
527 OmpClause::LINEAR, OmpClause::ALIGNED, OmpClause::UNIFORM};
528 SetContextAllowed(allowed);
529 SetContextAllowedOnce({OmpClause::SIMDLEN});
530 SetContextAllowedExclusive({OmpClause::INBRANCH, OmpClause::NOTINBRANCH});
531 }
532
Leave(const parser::OpenMPDeclareSimdConstruct &)533 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
534 ompContext_.pop_back();
535 }
536
Enter(const parser::OpenMPDeclareTargetConstruct & x)537 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
538 const auto &dir{std::get<parser::Verbatim>(x.t)};
539 PushContext(dir.source, OmpDirective::DECLARE_TARGET);
540 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)};
541 if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) {
542 SetContextAllowed(OmpClauseSet{OmpClause::TO, OmpClause::LINK});
543 }
544 }
545
Leave(const parser::OpenMPDeclareTargetConstruct &)546 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) {
547 ompContext_.pop_back();
548 }
549
Enter(const parser::OpenMPSimpleStandaloneConstruct & x)550 void OmpStructureChecker::Enter(
551 const parser::OpenMPSimpleStandaloneConstruct &x) {
552 const auto &dir{std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
553 switch (dir.v) {
554 case parser::OmpSimpleStandaloneDirective::Directive::Barrier: {
555 // 2.13.3 barrier
556 PushContext(dir.source, OmpDirective::BARRIER);
557 } break;
558 case parser::OmpSimpleStandaloneDirective::Directive::Taskwait: {
559 // 2.13.4 taskwait
560 PushContext(dir.source, OmpDirective::TASKWAIT);
561 } break;
562 case parser::OmpSimpleStandaloneDirective::Directive::Taskyield: {
563 // 2.9.4 taskyield
564 PushContext(dir.source, OmpDirective::TASKYIELD);
565 } break;
566 case parser::OmpSimpleStandaloneDirective::Directive::TargetEnterData: {
567 // 2.10.2 target-enter-data-clause -> if-clause |
568 // device-clause |
569 // map-clause |
570 // depend-clause |
571 // nowait-clause
572 PushContext(dir.source, OmpDirective::TARGET_ENTER_DATA);
573 OmpClauseSet allowed{OmpClause::MAP, OmpClause::DEPEND, OmpClause::NOWAIT};
574 SetContextAllowed(allowed);
575 OmpClauseSet allowedOnce{OmpClause::DEVICE, OmpClause::IF};
576 SetContextAllowedOnce(allowedOnce);
577 SetContextRequired({OmpClause::MAP});
578 } break;
579 case parser::OmpSimpleStandaloneDirective::Directive::TargetExitData: {
580 // 2.10.3 target-enter-data-clause -> if-clause |
581 // device-clause |
582 // map-clause |
583 // depend-clause |
584 // nowait-clause
585 PushContext(dir.source, OmpDirective::TARGET_EXIT_DATA);
586 OmpClauseSet allowed{OmpClause::MAP, OmpClause::DEPEND, OmpClause::NOWAIT};
587 SetContextAllowed(allowed);
588 OmpClauseSet allowedOnce{OmpClause::DEVICE, OmpClause::IF};
589 SetContextAllowedOnce(allowedOnce);
590 SetContextRequired({OmpClause::MAP});
591 } break;
592 case parser::OmpSimpleStandaloneDirective::Directive::TargetUpdate: {
593 // 2.10.5 target-update
594 PushContext(dir.source, OmpDirective::TARGET_UPDATE);
595 } break;
596 case parser::OmpSimpleStandaloneDirective::Directive::Ordered: {
597 // 2.13.8 ordered-construct-clause -> depend-clause
598 PushContext(dir.source, OmpDirective::ORDERED);
599 OmpClauseSet allowed{OmpClause::DEPEND};
600 SetContextAllowed(allowed);
601 } break;
602 }
603 }
604
Leave(const parser::OpenMPSimpleStandaloneConstruct &)605 void OmpStructureChecker::Leave(
606 const parser::OpenMPSimpleStandaloneConstruct &) {
607 ompContext_.pop_back();
608 }
609
Enter(const parser::OpenMPFlushConstruct & x)610 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
611 const auto &dir{std::get<parser::Verbatim>(x.t)};
612 PushContext(dir.source, OmpDirective::FLUSH);
613 }
614
Leave(const parser::OpenMPFlushConstruct &)615 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) {
616 ompContext_.pop_back();
617 }
618
Enter(const parser::OpenMPCancelConstruct & x)619 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
620 const auto &dir{std::get<parser::Verbatim>(x.t)};
621 PushContext(dir.source, OmpDirective::CANCEL);
622 }
623
Leave(const parser::OpenMPCancelConstruct &)624 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
625 ompContext_.pop_back();
626 }
627
Enter(const parser::OpenMPCancellationPointConstruct & x)628 void OmpStructureChecker::Enter(
629 const parser::OpenMPCancellationPointConstruct &x) {
630 const auto &dir{std::get<parser::Verbatim>(x.t)};
631 PushContext(dir.source, OmpDirective::CANCELLATION_POINT);
632 }
633
Leave(const parser::OpenMPCancellationPointConstruct &)634 void OmpStructureChecker::Leave(
635 const parser::OpenMPCancellationPointConstruct &) {
636 ompContext_.pop_back();
637 }
638
Enter(const parser::OmpEndBlockDirective & x)639 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
640 const auto &dir{std::get<parser::OmpBlockDirective>(x.t)};
641 ResetPartialContext(dir.source);
642 switch (dir.v) {
643 // 2.7.3 end-single-clause -> copyprivate-clause |
644 // nowait-clause
645 case parser::OmpBlockDirective::Directive::Single: {
646 SetContextDirectiveEnum(OmpDirective::END_SINGLE);
647 OmpClauseSet allowed{OmpClause::COPYPRIVATE};
648 SetContextAllowed(allowed);
649 OmpClauseSet allowedOnce{OmpClause::NOWAIT};
650 SetContextAllowedOnce(allowedOnce);
651 } break;
652 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause]
653 case parser::OmpBlockDirective::Directive::Workshare:
654 SetContextDirectiveEnum(OmpDirective::END_WORKSHARE);
655 SetContextAllowed(OmpClauseSet{OmpClause::NOWAIT});
656 break;
657 default:
658 // no clauses are allowed
659 break;
660 }
661 }
662
Leave(const parser::OmpClauseList &)663 void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
664 // 2.7 Loop Construct Restriction
665 if (doSet.test(GetContext().directive)) {
666 if (auto *clause{FindClause(OmpClause::SCHEDULE)}) {
667 // only one schedule clause is allowed
668 const auto &schedClause{std::get<parser::OmpScheduleClause>(clause->u)};
669 if (ScheduleModifierHasType(schedClause,
670 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
671 if (FindClause(OmpClause::ORDERED)) {
672 context_.Say(clause->source,
673 "The NONMONOTONIC modifier cannot be specified "
674 "if an ORDERED clause is specified"_err_en_US);
675 }
676 if (ScheduleModifierHasType(schedClause,
677 parser::OmpScheduleModifierType::ModType::Monotonic)) {
678 context_.Say(clause->source,
679 "The MONOTONIC and NONMONOTONIC modifiers "
680 "cannot be both specified"_err_en_US);
681 }
682 }
683 }
684
685 if (auto *clause{FindClause(OmpClause::ORDERED)}) {
686 // only one ordered clause is allowed
687 const auto &orderedClause{
688 std::get<parser::OmpClause::Ordered>(clause->u)};
689
690 if (orderedClause.v.has_value()) {
691 if (FindClause(OmpClause::LINEAR)) {
692 context_.Say(clause->source,
693 "A loop directive may not have both a LINEAR clause and "
694 "an ORDERED clause with a parameter"_err_en_US);
695 }
696
697 if (auto *clause2{FindClause(OmpClause::COLLAPSE)}) {
698 const auto &collapseClause{
699 std::get<parser::OmpClause::Collapse>(clause2->u)};
700 // ordered and collapse both have parameters
701 if (const auto orderedValue{GetIntValue(orderedClause.v)}) {
702 if (const auto collapseValue{GetIntValue(collapseClause.v)}) {
703 if (*orderedValue > 0 && *orderedValue < *collapseValue) {
704 context_.Say(clause->source,
705 "The parameter of the ORDERED clause must be "
706 "greater than or equal to "
707 "the parameter of the COLLAPSE clause"_err_en_US);
708 }
709 }
710 }
711 }
712 }
713
714 // TODO: ordered region binding check (requires nesting implementation)
715 }
716 } // doSet
717
718 // 2.8.1 Simd Construct Restriction
719 if (simdSet.test(GetContext().directive)) {
720 if (auto *clause{FindClause(OmpClause::SIMDLEN)}) {
721 if (auto *clause2{FindClause(OmpClause::SAFELEN)}) {
722 const auto &simdlenClause{
723 std::get<parser::OmpClause::Simdlen>(clause->u)};
724 const auto &safelenClause{
725 std::get<parser::OmpClause::Safelen>(clause2->u)};
726 // simdlen and safelen both have parameters
727 if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) {
728 if (const auto safelenValue{GetIntValue(safelenClause.v)}) {
729 if (*safelenValue > 0 && *simdlenValue > *safelenValue) {
730 context_.Say(clause->source,
731 "The parameter of the SIMDLEN clause must be less than or "
732 "equal to the parameter of the SAFELEN clause"_err_en_US);
733 }
734 }
735 }
736 }
737 }
738
739 // TODO: A list-item cannot appear in more than one aligned clause
740 } // SIMD
741
742 // 2.7.3 Single Construct Restriction
743 if (GetContext().directive == OmpDirective::END_SINGLE) {
744 if (auto *clause{FindClause(OmpClause::COPYPRIVATE)}) {
745 if (FindClause(OmpClause::NOWAIT)) {
746 context_.Say(clause->source,
747 "The COPYPRIVATE clause must not be used with "
748 "the NOWAIT clause"_err_en_US);
749 }
750 }
751 }
752
753 GetContext().requiredClauses.IterateOverMembers(
754 [this](OmpClause c) { CheckRequired(c); });
755 }
756
Enter(const parser::OmpClause & x)757 void OmpStructureChecker::Enter(const parser::OmpClause &x) {
758 SetContextClause(x);
759 }
760
Enter(const parser::OmpNowait &)761 void OmpStructureChecker::Enter(const parser::OmpNowait &) {
762 CheckAllowed(OmpClause::NOWAIT);
763 }
Enter(const parser::OmpClause::Inbranch &)764 void OmpStructureChecker::Enter(const parser::OmpClause::Inbranch &) {
765 CheckAllowed(OmpClause::INBRANCH);
766 }
Enter(const parser::OmpClause::Mergeable &)767 void OmpStructureChecker::Enter(const parser::OmpClause::Mergeable &) {
768 CheckAllowed(OmpClause::MERGEABLE);
769 }
Enter(const parser::OmpClause::Nogroup &)770 void OmpStructureChecker::Enter(const parser::OmpClause::Nogroup &) {
771 CheckAllowed(OmpClause::NOGROUP);
772 }
Enter(const parser::OmpClause::Notinbranch &)773 void OmpStructureChecker::Enter(const parser::OmpClause::Notinbranch &) {
774 CheckAllowed(OmpClause::NOTINBRANCH);
775 }
Enter(const parser::OmpClause::Untied &)776 void OmpStructureChecker::Enter(const parser::OmpClause::Untied &) {
777 CheckAllowed(OmpClause::UNTIED);
778 }
779
Enter(const parser::OmpClause::Collapse & x)780 void OmpStructureChecker::Enter(const parser::OmpClause::Collapse &x) {
781 CheckAllowed(OmpClause::COLLAPSE);
782 // collapse clause must have a parameter
783 RequiresConstantPositiveParameter(OmpClause::COLLAPSE, x.v);
784 }
785
Enter(const parser::OmpClause::Copyin &)786 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &) {
787 CheckAllowed(OmpClause::COPYIN);
788 }
Enter(const parser::OmpClause::Copyprivate &)789 void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &) {
790 CheckAllowed(OmpClause::COPYPRIVATE);
791 }
Enter(const parser::OmpClause::Device &)792 void OmpStructureChecker::Enter(const parser::OmpClause::Device &) {
793 CheckAllowed(OmpClause::DEVICE);
794 }
Enter(const parser::OmpClause::DistSchedule &)795 void OmpStructureChecker::Enter(const parser::OmpClause::DistSchedule &) {
796 CheckAllowed(OmpClause::DIST_SCHEDULE);
797 }
Enter(const parser::OmpClause::Final &)798 void OmpStructureChecker::Enter(const parser::OmpClause::Final &) {
799 CheckAllowed(OmpClause::FINAL);
800 }
Enter(const parser::OmpClause::Firstprivate &)801 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &) {
802 CheckAllowed(OmpClause::FIRSTPRIVATE);
803 }
Enter(const parser::OmpClause::From &)804 void OmpStructureChecker::Enter(const parser::OmpClause::From &) {
805 CheckAllowed(OmpClause::FROM);
806 }
Enter(const parser::OmpClause::Grainsize & x)807 void OmpStructureChecker::Enter(const parser::OmpClause::Grainsize &x) {
808 CheckAllowed(OmpClause::GRAINSIZE);
809 RequiresPositiveParameter(OmpClause::GRAINSIZE, x.v);
810 }
Enter(const parser::OmpClause::Lastprivate &)811 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &) {
812 CheckAllowed(OmpClause::LASTPRIVATE);
813 }
Enter(const parser::OmpClause::NumTasks & x)814 void OmpStructureChecker::Enter(const parser::OmpClause::NumTasks &x) {
815 CheckAllowed(OmpClause::NUM_TASKS);
816 RequiresPositiveParameter(OmpClause::NUM_TASKS, x.v);
817 }
Enter(const parser::OmpClause::NumTeams & x)818 void OmpStructureChecker::Enter(const parser::OmpClause::NumTeams &x) {
819 CheckAllowed(OmpClause::NUM_TEAMS);
820 RequiresPositiveParameter(OmpClause::NUM_TEAMS, x.v);
821 }
Enter(const parser::OmpClause::NumThreads & x)822 void OmpStructureChecker::Enter(const parser::OmpClause::NumThreads &x) {
823 CheckAllowed(OmpClause::NUM_THREADS);
824 RequiresPositiveParameter(OmpClause::NUM_THREADS, x.v);
825 // if parameter is variable, defer to Expression Analysis
826 }
827
Enter(const parser::OmpClause::Ordered & x)828 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
829 CheckAllowed(OmpClause::ORDERED);
830 // the parameter of ordered clause is optional
831 if (const auto &expr{x.v}) {
832 RequiresConstantPositiveParameter(OmpClause::ORDERED, *expr);
833
834 // 2.8.3 Loop SIMD Construct Restriction
835 if (doSimdSet.test(GetContext().directive)) {
836 context_.Say(GetContext().clauseSource,
837 "No ORDERED clause with a parameter can be specified "
838 "on the %s directive"_err_en_US,
839 ContextDirectiveAsFortran());
840 }
841 }
842 }
Enter(const parser::OmpClause::Priority & x)843 void OmpStructureChecker::Enter(const parser::OmpClause::Priority &x) {
844 CheckAllowed(OmpClause::PRIORITY);
845 RequiresPositiveParameter(OmpClause::PRIORITY, x.v);
846 }
Enter(const parser::OmpClause::Private &)847 void OmpStructureChecker::Enter(const parser::OmpClause::Private &) {
848 CheckAllowed(OmpClause::PRIVATE);
849 }
Enter(const parser::OmpClause::Safelen & x)850 void OmpStructureChecker::Enter(const parser::OmpClause::Safelen &x) {
851 CheckAllowed(OmpClause::SAFELEN);
852 RequiresConstantPositiveParameter(OmpClause::SAFELEN, x.v);
853 }
Enter(const parser::OmpClause::Shared &)854 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &) {
855 CheckAllowed(OmpClause::SHARED);
856 }
Enter(const parser::OmpClause::Simdlen & x)857 void OmpStructureChecker::Enter(const parser::OmpClause::Simdlen &x) {
858 CheckAllowed(OmpClause::SIMDLEN);
859 RequiresConstantPositiveParameter(OmpClause::SIMDLEN, x.v);
860 }
Enter(const parser::OmpClause::ThreadLimit & x)861 void OmpStructureChecker::Enter(const parser::OmpClause::ThreadLimit &x) {
862 CheckAllowed(OmpClause::THREAD_LIMIT);
863 RequiresPositiveParameter(OmpClause::THREAD_LIMIT, x.v);
864 }
Enter(const parser::OmpClause::To &)865 void OmpStructureChecker::Enter(const parser::OmpClause::To &) {
866 CheckAllowed(OmpClause::TO);
867 }
Enter(const parser::OmpClause::Link &)868 void OmpStructureChecker::Enter(const parser::OmpClause::Link &) {
869 CheckAllowed(OmpClause::LINK);
870 }
Enter(const parser::OmpClause::Uniform &)871 void OmpStructureChecker::Enter(const parser::OmpClause::Uniform &) {
872 CheckAllowed(OmpClause::UNIFORM);
873 }
Enter(const parser::OmpClause::UseDevicePtr &)874 void OmpStructureChecker::Enter(const parser::OmpClause::UseDevicePtr &) {
875 CheckAllowed(OmpClause::USE_DEVICE_PTR);
876 }
Enter(const parser::OmpClause::IsDevicePtr &)877 void OmpStructureChecker::Enter(const parser::OmpClause::IsDevicePtr &) {
878 CheckAllowed(OmpClause::IS_DEVICE_PTR);
879 }
880
Enter(const parser::OmpAlignedClause & x)881 void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) {
882 CheckAllowed(OmpClause::ALIGNED);
883
884 if (const auto &expr{
885 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) {
886 if (const auto v{GetIntValue(*expr)}) {
887 if (*v <= 0) {
888 context_.Say(GetContext().clauseSource,
889 "The ALIGNMENT parameter of the ALIGNED clause must be "
890 "a constant positive integer expression"_err_en_US);
891 }
892 }
893 }
894 // 2.8.1 TODO: list-item attribute check
895 }
Enter(const parser::OmpDefaultClause &)896 void OmpStructureChecker::Enter(const parser::OmpDefaultClause &) {
897 CheckAllowed(OmpClause::DEFAULT);
898 }
Enter(const parser::OmpDefaultmapClause & x)899 void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) {
900 CheckAllowed(OmpClause::DEFAULTMAP);
901 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory;
902 if (!std::get<std::optional<VariableCategory>>(x.t)) {
903 context_.Say(GetContext().clauseSource,
904 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP "
905 "clause"_err_en_US);
906 }
907 }
Enter(const parser::OmpDependClause &)908 void OmpStructureChecker::Enter(const parser::OmpDependClause &) {
909 CheckAllowed(OmpClause::DEPEND);
910 }
911
Enter(const parser::OmpIfClause & x)912 void OmpStructureChecker::Enter(const parser::OmpIfClause &x) {
913 CheckAllowed(OmpClause::IF);
914
915 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier;
916 static std::unordered_map<dirNameModifier, OmpDirectiveSet>
917 dirNameModifierMap{{dirNameModifier::Parallel, parallelSet},
918 {dirNameModifier::Target, targetSet},
919 {dirNameModifier::TargetEnterData, {OmpDirective::TARGET_ENTER_DATA}},
920 {dirNameModifier::TargetExitData, {OmpDirective::TARGET_EXIT_DATA}},
921 {dirNameModifier::TargetData, {OmpDirective::TARGET_DATA}},
922 {dirNameModifier::TargetUpdate, {OmpDirective::TARGET_UPDATE}},
923 {dirNameModifier::Task, {OmpDirective::TASK}},
924 {dirNameModifier::Taskloop, taskloopSet}};
925 if (const auto &directiveName{
926 std::get<std::optional<dirNameModifier>>(x.t)}) {
927 auto search{dirNameModifierMap.find(*directiveName)};
928 if (search == dirNameModifierMap.end() ||
929 !search->second.test(GetContext().directive)) {
930 context_
931 .Say(GetContext().clauseSource,
932 "Unmatched directive name modifier %s on the IF clause"_err_en_US,
933 parser::ToUpperCaseLetters(
934 parser::OmpIfClause::EnumToString(*directiveName)))
935 .Attach(
936 GetContext().directiveSource, "Cannot apply to directive"_en_US);
937 }
938 }
939 }
940
Enter(const parser::OmpLinearClause & x)941 void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) {
942 CheckAllowed(OmpClause::LINEAR);
943
944 // 2.7 Loop Construct Restriction
945 if ((doSet | simdSet).test(GetContext().directive)) {
946 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) {
947 context_.Say(GetContext().clauseSource,
948 "A modifier may not be specified in a LINEAR clause "
949 "on the %s directive"_err_en_US,
950 ContextDirectiveAsFortran());
951 }
952 }
953 }
Enter(const parser::OmpMapClause & x)954 void OmpStructureChecker::Enter(const parser::OmpMapClause &x) {
955 CheckAllowed(OmpClause::MAP);
956 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) {
957 using Type = parser::OmpMapType::Type;
958 const Type &type{std::get<Type>(maptype->t)};
959 switch (GetContext().directive) {
960 case OmpDirective::TARGET:
961 case OmpDirective::TARGET_DATA: {
962 if (type != Type::To && type != Type::From && type != Type::Tofrom &&
963 type != Type::Alloc) {
964 context_.Say(GetContext().clauseSource,
965 "Only the TO, FROM, TOFROM, or ALLOC map types are permitted "
966 "for MAP clauses on the %s directive"_err_en_US,
967 ContextDirectiveAsFortran());
968 }
969 } break;
970 case OmpDirective::TARGET_ENTER_DATA: {
971 if (type != Type::To && type != Type::Alloc) {
972 context_.Say(GetContext().clauseSource,
973 "Only the TO or ALLOC map types are permitted "
974 "for MAP clauses on the %s directive"_err_en_US,
975 ContextDirectiveAsFortran());
976 }
977 } break;
978 case OmpDirective::TARGET_EXIT_DATA: {
979 if (type != Type::Delete && type != Type::Release && type != Type::From) {
980 context_.Say(GetContext().clauseSource,
981 "Only the FROM, RELEASE, or DELETE map types are permitted "
982 "for MAP clauses on the %s directive"_err_en_US,
983 ContextDirectiveAsFortran());
984 }
985 } break;
986 default: break;
987 }
988 }
989 }
Enter(const parser::OmpProcBindClause &)990 void OmpStructureChecker::Enter(const parser::OmpProcBindClause &) {
991 CheckAllowed(OmpClause::PROC_BIND);
992 }
Enter(const parser::OmpReductionClause &)993 void OmpStructureChecker::Enter(const parser::OmpReductionClause &) {
994 CheckAllowed(OmpClause::REDUCTION);
995 }
996
ScheduleModifierHasType(const parser::OmpScheduleClause & x,const parser::OmpScheduleModifierType::ModType & type)997 bool OmpStructureChecker::ScheduleModifierHasType(
998 const parser::OmpScheduleClause &x,
999 const parser::OmpScheduleModifierType::ModType &type) {
1000 const auto &modifier{
1001 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)};
1002 if (modifier.has_value()) {
1003 const auto &modType1{
1004 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)};
1005 const auto &modType2{
1006 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>(
1007 modifier->t)};
1008 if (modType1.v.v == type ||
1009 (modType2.has_value() && modType2->v.v == type)) {
1010 return true;
1011 }
1012 }
1013 return false;
1014 }
Enter(const parser::OmpScheduleClause & x)1015 void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) {
1016 CheckAllowed(OmpClause::SCHEDULE);
1017
1018 // 2.7 Loop Construct Restriction
1019 if (doSet.test(GetContext().directive)) {
1020 const auto &kind{std::get<1>(x.t)};
1021 const auto &chunk{std::get<2>(x.t)};
1022 if (chunk.has_value()) {
1023 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime ||
1024 kind == parser::OmpScheduleClause::ScheduleType::Auto) {
1025 context_.Say(GetContext().clauseSource,
1026 "When SCHEDULE clause has %s specified, "
1027 "it must not have chunk size specified"_err_en_US,
1028 parser::ToUpperCaseLetters(
1029 parser::OmpScheduleClause::EnumToString(kind)));
1030 }
1031 }
1032
1033 if (ScheduleModifierHasType(
1034 x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) {
1035 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic &&
1036 kind != parser::OmpScheduleClause::ScheduleType::Guided) {
1037 context_.Say(GetContext().clauseSource,
1038 "The NONMONOTONIC modifier can only be specified with "
1039 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US);
1040 }
1041 }
1042 }
1043 }
1044 }
1045