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 bool OmpStructureChecker::HasInvalidWorksharingNesting( 41 const parser::CharBlock &source, const OmpDirectiveSet &set) { 42 // set contains all the invalid closely nested directives 43 // for the given directive (`source` here) 44 if (CurrentDirectiveIsNested() && set.test(GetContext().directive)) { 45 context_.Say(source, 46 "A worksharing region may not be closely nested inside a " 47 "worksharing, explicit task, taskloop, critical, ordered, atomic, or " 48 "master region"_err_en_US); 49 return true; 50 } 51 return false; 52 } 53 54 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) { 55 // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check 56 } 57 58 void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) { 59 const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)}; 60 const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)}; 61 62 // check matching, End directive is optional 63 if (const auto &endLoopDir{ 64 std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) { 65 const auto &endDir{ 66 std::get<parser::OmpLoopDirective>(endLoopDir.value().t)}; 67 68 CheckMatching<parser::OmpLoopDirective>(beginDir, endDir); 69 } 70 71 if (beginDir.v != llvm::omp::Directive::OMPD_do) { 72 PushContextAndClauseSets(beginDir.source, beginDir.v); 73 } else { 74 // 2.7.1 do-clause -> private-clause | 75 // firstprivate-clause | 76 // lastprivate-clause | 77 // linear-clause | 78 // reduction-clause | 79 // schedule-clause | 80 // collapse-clause | 81 // ordered-clause 82 83 // nesting check 84 HasInvalidWorksharingNesting(beginDir.source, 85 {llvm::omp::Directive::OMPD_do, llvm::omp::Directive::OMPD_sections, 86 llvm::omp::Directive::OMPD_single, 87 llvm::omp::Directive::OMPD_workshare, 88 llvm::omp::Directive::OMPD_task, 89 llvm::omp::Directive::OMPD_taskloop, 90 llvm::omp::Directive::OMPD_critical, 91 llvm::omp::Directive::OMPD_ordered, 92 llvm::omp::Directive::OMPD_atomic, 93 llvm::omp::Directive::OMPD_master}); 94 PushContextAndClauseSets(beginDir.source, llvm::omp::Directive::OMPD_do); 95 } 96 } 97 98 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) { 99 dirContext_.pop_back(); 100 } 101 102 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) { 103 const auto &dir{std::get<parser::OmpLoopDirective>(x.t)}; 104 ResetPartialContext(dir.source); 105 switch (dir.v) { 106 // 2.7.1 end-do -> END DO [nowait-clause] 107 // 2.8.3 end-do-simd -> END DO SIMD [nowait-clause] 108 case llvm::omp::Directive::OMPD_do: 109 case llvm::omp::Directive::OMPD_do_simd: 110 SetClauseSets(dir.v); 111 break; 112 default: 113 // no clauses are allowed 114 break; 115 } 116 } 117 118 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { 119 const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)}; 120 const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)}; 121 const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)}; 122 const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)}; 123 const parser::Block &block{std::get<parser::Block>(x.t)}; 124 125 CheckMatching<parser::OmpBlockDirective>(beginDir, endDir); 126 127 PushContextAndClauseSets(beginDir.source, beginDir.v); 128 129 switch (beginDir.v) { 130 case llvm::omp::OMPD_parallel: 131 CheckNoBranching(block, llvm::omp::OMPD_parallel, beginDir.source); 132 break; 133 default: 134 break; 135 } 136 } 137 138 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) { 139 dirContext_.pop_back(); 140 } 141 142 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) { 143 const auto &beginSectionsDir{ 144 std::get<parser::OmpBeginSectionsDirective>(x.t)}; 145 const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)}; 146 const auto &beginDir{ 147 std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)}; 148 const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)}; 149 CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir); 150 151 PushContextAndClauseSets(beginDir.source, beginDir.v); 152 } 153 154 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) { 155 dirContext_.pop_back(); 156 } 157 158 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) { 159 const auto &dir{std::get<parser::OmpSectionsDirective>(x.t)}; 160 ResetPartialContext(dir.source); 161 switch (dir.v) { 162 // 2.7.2 end-sections -> END SECTIONS [nowait-clause] 163 case llvm::omp::Directive::OMPD_sections: 164 PushContextAndClauseSets( 165 dir.source, llvm::omp::Directive::OMPD_end_sections); 166 break; 167 default: 168 // no clauses are allowed 169 break; 170 } 171 } 172 173 void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) { 174 const auto &dir{std::get<parser::Verbatim>(x.t)}; 175 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_declare_simd); 176 } 177 178 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) { 179 dirContext_.pop_back(); 180 } 181 182 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) { 183 const auto &dir{std::get<parser::Verbatim>(x.t)}; 184 PushContext(dir.source, llvm::omp::Directive::OMPD_declare_target); 185 const auto &spec{std::get<parser::OmpDeclareTargetSpecifier>(x.t)}; 186 if (std::holds_alternative<parser::OmpDeclareTargetWithClause>(spec.u)) { 187 SetClauseSets(llvm::omp::Directive::OMPD_declare_target); 188 } 189 } 190 191 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) { 192 dirContext_.pop_back(); 193 } 194 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 201 void OmpStructureChecker::Leave( 202 const parser::OpenMPSimpleStandaloneConstruct &) { 203 dirContext_.pop_back(); 204 } 205 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 211 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) { 212 dirContext_.pop_back(); 213 } 214 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 220 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) { 221 dirContext_.pop_back(); 222 } 223 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 229 void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) { 230 dirContext_.pop_back(); 231 } 232 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 240 void OmpStructureChecker::Leave( 241 const parser::OpenMPCancellationPointConstruct &) { 242 dirContext_.pop_back(); 243 } 244 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 PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_end_single); 253 break; 254 // 2.7.4 end-workshare -> END WORKSHARE [nowait-clause] 255 case llvm::omp::Directive::OMPD_workshare: 256 PushContextAndClauseSets( 257 dir.source, llvm::omp::Directive::OMPD_end_workshare); 258 break; 259 default: 260 // no clauses are allowed 261 break; 262 } 263 } 264 265 // Clauses 266 // Mainly categorized as 267 // 1. Checks on 'OmpClauseList' from 'parse-tree.h'. 268 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h. 269 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h. 270 271 void OmpStructureChecker::Leave(const parser::OmpClauseList &) { 272 // 2.7 Loop Construct Restriction 273 if (llvm::omp::doSet.test(GetContext().directive)) { 274 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_schedule)}) { 275 // only one schedule clause is allowed 276 const auto &schedClause{std::get<parser::OmpScheduleClause>(clause->u)}; 277 if (ScheduleModifierHasType(schedClause, 278 parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 279 if (FindClause(llvm::omp::Clause::OMPC_ordered)) { 280 context_.Say(clause->source, 281 "The NONMONOTONIC modifier cannot be specified " 282 "if an ORDERED clause is specified"_err_en_US); 283 } 284 if (ScheduleModifierHasType(schedClause, 285 parser::OmpScheduleModifierType::ModType::Monotonic)) { 286 context_.Say(clause->source, 287 "The MONOTONIC and NONMONOTONIC modifiers " 288 "cannot be both specified"_err_en_US); 289 } 290 } 291 } 292 293 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_ordered)}) { 294 // only one ordered clause is allowed 295 const auto &orderedClause{ 296 std::get<parser::OmpClause::Ordered>(clause->u)}; 297 298 if (orderedClause.v) { 299 CheckNotAllowedIfClause( 300 llvm::omp::Clause::OMPC_ordered, {llvm::omp::Clause::OMPC_linear}); 301 302 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_collapse)}) { 303 const auto &collapseClause{ 304 std::get<parser::OmpClause::Collapse>(clause2->u)}; 305 // ordered and collapse both have parameters 306 if (const auto orderedValue{GetIntValue(orderedClause.v)}) { 307 if (const auto collapseValue{GetIntValue(collapseClause.v)}) { 308 if (*orderedValue > 0 && *orderedValue < *collapseValue) { 309 context_.Say(clause->source, 310 "The parameter of the ORDERED clause must be " 311 "greater than or equal to " 312 "the parameter of the COLLAPSE clause"_err_en_US); 313 } 314 } 315 } 316 } 317 } 318 319 // TODO: ordered region binding check (requires nesting implementation) 320 } 321 } // doSet 322 323 // 2.8.1 Simd Construct Restriction 324 if (llvm::omp::simdSet.test(GetContext().directive)) { 325 if (auto *clause{FindClause(llvm::omp::Clause::OMPC_simdlen)}) { 326 if (auto *clause2{FindClause(llvm::omp::Clause::OMPC_safelen)}) { 327 const auto &simdlenClause{ 328 std::get<parser::OmpClause::Simdlen>(clause->u)}; 329 const auto &safelenClause{ 330 std::get<parser::OmpClause::Safelen>(clause2->u)}; 331 // simdlen and safelen both have parameters 332 if (const auto simdlenValue{GetIntValue(simdlenClause.v)}) { 333 if (const auto safelenValue{GetIntValue(safelenClause.v)}) { 334 if (*safelenValue > 0 && *simdlenValue > *safelenValue) { 335 context_.Say(clause->source, 336 "The parameter of the SIMDLEN clause must be less than or " 337 "equal to the parameter of the SAFELEN clause"_err_en_US); 338 } 339 } 340 } 341 } 342 } 343 // TODO: A list-item cannot appear in more than one aligned clause 344 } // SIMD 345 346 // 2.7.3 Single Construct Restriction 347 if (GetContext().directive == llvm::omp::Directive::OMPD_end_single) { 348 CheckNotAllowedIfClause( 349 llvm::omp::Clause::OMPC_copyprivate, {llvm::omp::Clause::OMPC_nowait}); 350 } 351 352 GetContext().requiredClauses.IterateOverMembers( 353 [this](llvm::omp::Clause c) { CheckRequired(c); }); 354 } 355 356 void OmpStructureChecker::Enter(const parser::OmpClause &x) { 357 SetContextClause(x); 358 } 359 360 // Following clauses do not have a seperate node in parse-tree.h. 361 // They fall under 'struct OmpClause' in parse-tree.h. 362 CHECK_SIMPLE_CLAUSE(Copyin, OMPC_copyin) 363 CHECK_SIMPLE_CLAUSE(Copyprivate, OMPC_copyprivate) 364 CHECK_SIMPLE_CLAUSE(Device, OMPC_device) 365 CHECK_SIMPLE_CLAUSE(Final, OMPC_final) 366 CHECK_SIMPLE_CLAUSE(Firstprivate, OMPC_firstprivate) 367 CHECK_SIMPLE_CLAUSE(From, OMPC_from) 368 CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) 369 CHECK_SIMPLE_CLAUSE(IsDevicePtr, OMPC_is_device_ptr) 370 CHECK_SIMPLE_CLAUSE(Lastprivate, OMPC_lastprivate) 371 CHECK_SIMPLE_CLAUSE(Link, OMPC_link) 372 CHECK_SIMPLE_CLAUSE(Mergeable, OMPC_mergeable) 373 CHECK_SIMPLE_CLAUSE(Nogroup, OMPC_nogroup) 374 CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch) 375 CHECK_SIMPLE_CLAUSE(To, OMPC_to) 376 CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform) 377 CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied) 378 CHECK_SIMPLE_CLAUSE(UseDevicePtr, OMPC_use_device_ptr) 379 CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel) 380 CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire) 381 CHECK_SIMPLE_CLAUSE(SeqCst, OMPC_seq_cst) 382 CHECK_SIMPLE_CLAUSE(Release, OMPC_release) 383 CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed) 384 385 CHECK_REQ_SCALAR_INT_CLAUSE(Grainsize, OMPC_grainsize) 386 CHECK_REQ_SCALAR_INT_CLAUSE(NumTasks, OMPC_num_tasks) 387 CHECK_REQ_SCALAR_INT_CLAUSE(NumTeams, OMPC_num_teams) 388 CHECK_REQ_SCALAR_INT_CLAUSE(NumThreads, OMPC_num_threads) 389 CHECK_REQ_SCALAR_INT_CLAUSE(Priority, OMPC_priority) 390 CHECK_REQ_SCALAR_INT_CLAUSE(ThreadLimit, OMPC_thread_limit) 391 392 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse) 393 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen) 394 CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen) 395 396 // Restrictions specific to each clause are implemented apart from the 397 // generalized restrictions. 398 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) { 399 CheckAllowed(llvm::omp::Clause::OMPC_ordered); 400 // the parameter of ordered clause is optional 401 if (const auto &expr{x.v}) { 402 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_ordered, *expr); 403 // 2.8.3 Loop SIMD Construct Restriction 404 if (llvm::omp::doSimdSet.test(GetContext().directive)) { 405 context_.Say(GetContext().clauseSource, 406 "No ORDERED clause with a parameter can be specified " 407 "on the %s directive"_err_en_US, 408 ContextDirectiveAsFortran()); 409 } 410 } 411 } 412 413 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) { 414 CheckAllowed(llvm::omp::Clause::OMPC_shared); 415 CheckIsVarPartOfAnotherVar(x.v); 416 } 417 void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) { 418 CheckAllowed(llvm::omp::Clause::OMPC_private); 419 CheckIsVarPartOfAnotherVar(x.v); 420 } 421 422 void OmpStructureChecker::CheckIsVarPartOfAnotherVar( 423 const parser::OmpObjectList &objList) { 424 425 for (const auto &ompObject : objList.v) { 426 std::visit( 427 common::visitors{ 428 [&](const parser::Designator &designator) { 429 if (std::get_if<parser::DataRef>(&designator.u)) { 430 if ((parser::Unwrap<parser::StructureComponent>(ompObject)) || 431 (parser::Unwrap<parser::ArrayElement>(ompObject))) { 432 context_.Say(GetContext().clauseSource, 433 "A variable that is part of another variable (as an " 434 "array or structure element)" 435 " cannot appear in a PRIVATE or SHARED clause."_err_en_US); 436 } 437 } 438 }, 439 [&](const parser::Name &name) {}, 440 }, 441 ompObject.u); 442 } 443 } 444 // Following clauses have a seperate node in parse-tree.h. 445 CHECK_SIMPLE_PARSER_CLAUSE(OmpAllocateClause, OMPC_allocate) 446 CHECK_SIMPLE_PARSER_CLAUSE(OmpDefaultClause, OMPC_default) 447 CHECK_SIMPLE_PARSER_CLAUSE(OmpDistScheduleClause, OMPC_dist_schedule) 448 CHECK_SIMPLE_PARSER_CLAUSE(OmpNowait, OMPC_nowait) 449 CHECK_SIMPLE_PARSER_CLAUSE(OmpProcBindClause, OMPC_proc_bind) 450 CHECK_SIMPLE_PARSER_CLAUSE(OmpReductionClause, OMPC_reduction) 451 452 // Restrictions specific to each clause are implemented apart from the 453 // generalized restrictions. 454 void OmpStructureChecker::Enter(const parser::OmpAlignedClause &x) { 455 CheckAllowed(llvm::omp::Clause::OMPC_aligned); 456 457 if (const auto &expr{ 458 std::get<std::optional<parser::ScalarIntConstantExpr>>(x.t)}) { 459 RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_aligned, *expr); 460 } 461 // 2.8.1 TODO: list-item attribute check 462 } 463 void OmpStructureChecker::Enter(const parser::OmpDefaultmapClause &x) { 464 CheckAllowed(llvm::omp::Clause::OMPC_defaultmap); 465 using VariableCategory = parser::OmpDefaultmapClause::VariableCategory; 466 if (!std::get<std::optional<VariableCategory>>(x.t)) { 467 context_.Say(GetContext().clauseSource, 468 "The argument TOFROM:SCALAR must be specified on the DEFAULTMAP " 469 "clause"_err_en_US); 470 } 471 } 472 void OmpStructureChecker::Enter(const parser::OmpIfClause &x) { 473 CheckAllowed(llvm::omp::Clause::OMPC_if); 474 475 using dirNameModifier = parser::OmpIfClause::DirectiveNameModifier; 476 static std::unordered_map<dirNameModifier, OmpDirectiveSet> 477 dirNameModifierMap{{dirNameModifier::Parallel, llvm::omp::parallelSet}, 478 {dirNameModifier::Target, llvm::omp::targetSet}, 479 {dirNameModifier::TargetEnterData, 480 {llvm::omp::Directive::OMPD_target_enter_data}}, 481 {dirNameModifier::TargetExitData, 482 {llvm::omp::Directive::OMPD_target_exit_data}}, 483 {dirNameModifier::TargetData, 484 {llvm::omp::Directive::OMPD_target_data}}, 485 {dirNameModifier::TargetUpdate, 486 {llvm::omp::Directive::OMPD_target_update}}, 487 {dirNameModifier::Task, {llvm::omp::Directive::OMPD_task}}, 488 {dirNameModifier::Taskloop, llvm::omp::taskloopSet}}; 489 if (const auto &directiveName{ 490 std::get<std::optional<dirNameModifier>>(x.t)}) { 491 auto search{dirNameModifierMap.find(*directiveName)}; 492 if (search == dirNameModifierMap.end() || 493 !search->second.test(GetContext().directive)) { 494 context_ 495 .Say(GetContext().clauseSource, 496 "Unmatched directive name modifier %s on the IF clause"_err_en_US, 497 parser::ToUpperCaseLetters( 498 parser::OmpIfClause::EnumToString(*directiveName))) 499 .Attach( 500 GetContext().directiveSource, "Cannot apply to directive"_en_US); 501 } 502 } 503 } 504 505 void OmpStructureChecker::Enter(const parser::OmpLinearClause &x) { 506 CheckAllowed(llvm::omp::Clause::OMPC_linear); 507 508 // 2.7 Loop Construct Restriction 509 if ((llvm::omp::doSet | llvm::omp::simdSet).test(GetContext().directive)) { 510 if (std::holds_alternative<parser::OmpLinearClause::WithModifier>(x.u)) { 511 context_.Say(GetContext().clauseSource, 512 "A modifier may not be specified in a LINEAR clause " 513 "on the %s directive"_err_en_US, 514 ContextDirectiveAsFortran()); 515 } 516 } 517 } 518 519 void OmpStructureChecker::CheckAllowedMapTypes( 520 const parser::OmpMapType::Type &type, 521 const std::list<parser::OmpMapType::Type> &allowedMapTypeList) { 522 const auto found{std::find( 523 std::begin(allowedMapTypeList), std::end(allowedMapTypeList), type)}; 524 if (found == std::end(allowedMapTypeList)) { 525 std::string commaSeperatedMapTypes; 526 llvm::interleave( 527 allowedMapTypeList.begin(), allowedMapTypeList.end(), 528 [&](const parser::OmpMapType::Type &mapType) { 529 commaSeperatedMapTypes.append(parser::ToUpperCaseLetters( 530 parser::OmpMapType::EnumToString(mapType))); 531 }, 532 [&] { commaSeperatedMapTypes.append(", "); }); 533 context_.Say(GetContext().clauseSource, 534 "Only the %s map types are permitted " 535 "for MAP clauses on the %s directive"_err_en_US, 536 commaSeperatedMapTypes, ContextDirectiveAsFortran()); 537 } 538 } 539 540 void OmpStructureChecker::Enter(const parser::OmpMapClause &x) { 541 CheckAllowed(llvm::omp::Clause::OMPC_map); 542 if (const auto &maptype{std::get<std::optional<parser::OmpMapType>>(x.t)}) { 543 using Type = parser::OmpMapType::Type; 544 const Type &type{std::get<Type>(maptype->t)}; 545 switch (GetContext().directive) { 546 case llvm::omp::Directive::OMPD_target: 547 case llvm::omp::Directive::OMPD_target_teams: 548 case llvm::omp::Directive::OMPD_target_teams_distribute: 549 case llvm::omp::Directive::OMPD_target_teams_distribute_simd: 550 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: 551 case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: 552 case llvm::omp::Directive::OMPD_target_data: 553 CheckAllowedMapTypes( 554 type, {Type::To, Type::From, Type::Tofrom, Type::Alloc}); 555 break; 556 case llvm::omp::Directive::OMPD_target_enter_data: 557 CheckAllowedMapTypes(type, {Type::To, Type::Alloc}); 558 break; 559 case llvm::omp::Directive::OMPD_target_exit_data: 560 CheckAllowedMapTypes(type, {Type::From, Type::Release, Type::Delete}); 561 break; 562 default: 563 break; 564 } 565 } 566 } 567 568 bool OmpStructureChecker::ScheduleModifierHasType( 569 const parser::OmpScheduleClause &x, 570 const parser::OmpScheduleModifierType::ModType &type) { 571 const auto &modifier{ 572 std::get<std::optional<parser::OmpScheduleModifier>>(x.t)}; 573 if (modifier) { 574 const auto &modType1{ 575 std::get<parser::OmpScheduleModifier::Modifier1>(modifier->t)}; 576 const auto &modType2{ 577 std::get<std::optional<parser::OmpScheduleModifier::Modifier2>>( 578 modifier->t)}; 579 if (modType1.v.v == type || (modType2 && modType2->v.v == type)) { 580 return true; 581 } 582 } 583 return false; 584 } 585 void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) { 586 CheckAllowed(llvm::omp::Clause::OMPC_schedule); 587 588 // 2.7 Loop Construct Restriction 589 if (llvm::omp::doSet.test(GetContext().directive)) { 590 const auto &kind{std::get<1>(x.t)}; 591 const auto &chunk{std::get<2>(x.t)}; 592 if (chunk) { 593 if (kind == parser::OmpScheduleClause::ScheduleType::Runtime || 594 kind == parser::OmpScheduleClause::ScheduleType::Auto) { 595 context_.Say(GetContext().clauseSource, 596 "When SCHEDULE clause has %s specified, " 597 "it must not have chunk size specified"_err_en_US, 598 parser::ToUpperCaseLetters( 599 parser::OmpScheduleClause::EnumToString(kind))); 600 } 601 if (const auto &chunkExpr{ 602 std::get<std::optional<parser::ScalarIntExpr>>(x.t)}) { 603 RequiresPositiveParameter( 604 llvm::omp::Clause::OMPC_schedule, *chunkExpr, "chunk size"); 605 } 606 } 607 608 if (ScheduleModifierHasType( 609 x, parser::OmpScheduleModifierType::ModType::Nonmonotonic)) { 610 if (kind != parser::OmpScheduleClause::ScheduleType::Dynamic && 611 kind != parser::OmpScheduleClause::ScheduleType::Guided) { 612 context_.Say(GetContext().clauseSource, 613 "The NONMONOTONIC modifier can only be specified with " 614 "SCHEDULE(DYNAMIC) or SCHEDULE(GUIDED)"_err_en_US); 615 } 616 } 617 } 618 } 619 620 void OmpStructureChecker::Enter(const parser::OmpDependClause &x) { 621 CheckAllowed(llvm::omp::Clause::OMPC_depend); 622 if (const auto *inOut{std::get_if<parser::OmpDependClause::InOut>(&x.u)}) { 623 const auto &designators{std::get<std::list<parser::Designator>>(inOut->t)}; 624 for (const auto &ele : designators) { 625 if (const auto *dataRef{std::get_if<parser::DataRef>(&ele.u)}) { 626 CheckDependList(*dataRef); 627 if (const auto *arr{ 628 std::get_if<common::Indirection<parser::ArrayElement>>( 629 &dataRef->u)}) { 630 CheckDependArraySection(*arr, GetLastName(*dataRef)); 631 } 632 } 633 } 634 } 635 } 636 637 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) { 638 return llvm::omp::getOpenMPClauseName(clause); 639 } 640 641 llvm::StringRef OmpStructureChecker::getDirectiveName( 642 llvm::omp::Directive directive) { 643 return llvm::omp::getOpenMPDirectiveName(directive); 644 } 645 646 void OmpStructureChecker::CheckDependList(const parser::DataRef &d) { 647 std::visit( 648 common::visitors{ 649 [&](const common::Indirection<parser::ArrayElement> &elem) { 650 // Check if the base element is valid on Depend Clause 651 CheckDependList(elem.value().base); 652 }, 653 [&](const common::Indirection<parser::StructureComponent> &) { 654 context_.Say(GetContext().clauseSource, 655 "A variable that is part of another variable " 656 "(such as an element of a structure) but is not an array " 657 "element or an array section cannot appear in a DEPEND " 658 "clause"_err_en_US); 659 }, 660 [&](const common::Indirection<parser::CoindexedNamedObject> &) { 661 context_.Say(GetContext().clauseSource, 662 "Coarrays are not supported in DEPEND clause"_err_en_US); 663 }, 664 [&](const parser::Name &) { return; }, 665 }, 666 d.u); 667 } 668 669 void OmpStructureChecker::CheckDependArraySection( 670 const common::Indirection<parser::ArrayElement> &arr, 671 const parser::Name &name) { 672 for (const auto &subscript : arr.value().subscripts) { 673 if (const auto *triplet{ 674 std::get_if<parser::SubscriptTriplet>(&subscript.u)}) { 675 if (std::get<2>(triplet->t)) { 676 context_.Say(GetContext().clauseSource, 677 "Stride should not be specified for array section in DEPEND " 678 "clause"_err_en_US); 679 } 680 const auto &lower{std::get<0>(triplet->t)}; 681 const auto &upper{std::get<1>(triplet->t)}; 682 if (lower && upper) { 683 const auto lval{GetIntValue(lower)}; 684 const auto uval{GetIntValue(upper)}; 685 if (lval && uval && *uval < *lval) { 686 context_.Say(GetContext().clauseSource, 687 "'%s' in DEPEND clause is a zero size array section"_err_en_US, 688 name.ToString()); 689 break; 690 } 691 } 692 } 693 } 694 } 695 696 } // namespace Fortran::semantics 697