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