1 //===-- lib/Semantics/check-acc-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-acc-structure.h" 10 #include "flang/Parser/parse-tree.h" 11 #include "flang/Semantics/tools.h" 12 13 #define CHECK_SIMPLE_CLAUSE(X, Y) \ 14 void AccStructureChecker::Enter(const parser::AccClause::X &) { \ 15 CheckAllowed(llvm::acc::Clause::Y); \ 16 } 17 18 #define CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(X, Y) \ 19 void AccStructureChecker::Enter(const parser::AccClause::X &c) { \ 20 CheckAllowed(llvm::acc::Clause::Y); \ 21 RequiresConstantPositiveParameter(llvm::acc::Clause::Y, c.v); \ 22 } 23 24 namespace Fortran::semantics { 25 26 static constexpr inline AccClauseSet 27 parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses{ 28 llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait, 29 llvm::acc::Clause::ACCC_num_gangs, llvm::acc::Clause::ACCC_num_workers, 30 llvm::acc::Clause::ACCC_vector_length}; 31 32 static constexpr inline AccClauseSet serialOnlyAllowedAfterDeviceTypeClauses{ 33 llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait}; 34 35 static constexpr inline AccClauseSet loopOnlyAllowedAfterDeviceTypeClauses{ 36 llvm::acc::Clause::ACCC_auto, llvm::acc::Clause::ACCC_collapse, 37 llvm::acc::Clause::ACCC_independent, llvm::acc::Clause::ACCC_gang, 38 llvm::acc::Clause::ACCC_seq, llvm::acc::Clause::ACCC_tile, 39 llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker}; 40 41 static constexpr inline AccClauseSet updateOnlyAllowedAfterDeviceTypeClauses{ 42 llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait}; 43 44 static constexpr inline AccClauseSet routineOnlyAllowedAfterDeviceTypeClauses{ 45 llvm::acc::Clause::ACCC_bind, llvm::acc::Clause::ACCC_gang, 46 llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker}; 47 48 class NoBranchingEnforce { 49 public: 50 NoBranchingEnforce(SemanticsContext &context, 51 parser::CharBlock sourcePosition, llvm::acc::Directive directive) 52 : context_{context}, sourcePosition_{sourcePosition}, currentDirective_{ 53 directive} {} 54 template <typename T> bool Pre(const T &) { return true; } 55 template <typename T> void Post(const T &) {} 56 57 template <typename T> bool Pre(const parser::Statement<T> &statement) { 58 currentStatementSourcePosition_ = statement.source; 59 return true; 60 } 61 62 void Post(const parser::ReturnStmt &) { emitBranchOutError("RETURN"); } 63 void Post(const parser::ExitStmt &) { emitBranchOutError("EXIT"); } 64 void Post(const parser::StopStmt &) { emitBranchOutError("STOP"); } 65 66 private: 67 parser::MessageFixedText GetEnclosingMsg() { 68 return "Enclosing block construct"_en_US; 69 } 70 71 void emitBranchOutError(const char *stmt) { 72 context_ 73 .Say(currentStatementSourcePosition_, 74 "%s statement is not allowed in a %s construct"_err_en_US, stmt, 75 parser::ToUpperCaseLetters( 76 llvm::acc::getOpenACCDirectiveName(currentDirective_).str())) 77 .Attach(sourcePosition_, GetEnclosingMsg()); 78 } 79 80 SemanticsContext &context_; 81 parser::CharBlock currentStatementSourcePosition_; 82 parser::CharBlock sourcePosition_; 83 llvm::acc::Directive currentDirective_; 84 }; 85 86 void AccStructureChecker::Enter(const parser::AccClause &x) { 87 SetContextClause(x); 88 } 89 90 void AccStructureChecker::Leave(const parser::AccClauseList &) {} 91 92 void AccStructureChecker::Enter(const parser::OpenACCBlockConstruct &x) { 93 const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)}; 94 const auto &endBlockDir{std::get<parser::AccEndBlockDirective>(x.t)}; 95 const auto &beginAccBlockDir{ 96 std::get<parser::AccBlockDirective>(beginBlockDir.t)}; 97 98 CheckMatching(beginAccBlockDir, endBlockDir.v); 99 PushContextAndClauseSets(beginAccBlockDir.source, beginAccBlockDir.v); 100 } 101 102 void AccStructureChecker::Leave(const parser::OpenACCBlockConstruct &x) { 103 const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)}; 104 const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)}; 105 const parser::Block &block{std::get<parser::Block>(x.t)}; 106 switch (blockDir.v) { 107 case llvm::acc::Directive::ACCD_kernels: 108 case llvm::acc::Directive::ACCD_parallel: 109 // Restriction - 880-881 (KERNELS) 110 // Restriction - 843-844 (PARALLEL) 111 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 112 parallelAndKernelsOnlyAllowedAfterDeviceTypeClauses); 113 // Restriction - 877 (KERNELS) 114 // Restriction - 840 (PARALLEL) 115 CheckNoBranching(block, GetContext().directive, blockDir.source); 116 break; 117 case llvm::acc::Directive::ACCD_serial: 118 // Restriction - 919 119 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 120 serialOnlyAllowedAfterDeviceTypeClauses); 121 // Restriction - 916 122 CheckNoBranching(block, llvm::acc::Directive::ACCD_serial, blockDir.source); 123 break; 124 case llvm::acc::Directive::ACCD_data: 125 // Restriction - 1117-1118 126 CheckRequireAtLeastOneOf(); 127 break; 128 case llvm::acc::Directive::ACCD_host_data: 129 // Restriction - 1578 130 CheckRequireAtLeastOneOf(); 131 break; 132 default: 133 break; 134 } 135 accContext_.pop_back(); 136 } 137 138 void AccStructureChecker::CheckNoBranching(const parser::Block &block, 139 const llvm::acc::Directive directive, 140 const parser::CharBlock &directiveSource) const { 141 NoBranchingEnforce noBranchingEnforce{context_, directiveSource, directive}; 142 parser::Walk(block, noBranchingEnforce); 143 } 144 145 void AccStructureChecker::Enter( 146 const parser::OpenACCStandaloneDeclarativeConstruct &x) { 147 const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)}; 148 PushContextAndClauseSets(declarativeDir.source, declarativeDir.v); 149 } 150 151 void AccStructureChecker::Leave( 152 const parser::OpenACCStandaloneDeclarativeConstruct &) { 153 // Restriction - 2075 154 CheckAtLeastOneClause(); 155 accContext_.pop_back(); 156 } 157 158 void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) { 159 const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; 160 const auto &combinedDir{ 161 std::get<parser::AccCombinedDirective>(beginBlockDir.t)}; 162 PushContextAndClauseSets(combinedDir.source, combinedDir.v); 163 } 164 165 void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) { 166 const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)}; 167 const auto &combinedDir{ 168 std::get<parser::AccCombinedDirective>(beginBlockDir.t)}; 169 switch (combinedDir.v) { 170 case llvm::acc::Directive::ACCD_kernels_loop: 171 case llvm::acc::Directive::ACCD_parallel_loop: 172 // Restriction - 1962 -> (880-881) (KERNELS LOOP) 173 // Restriction - 1962 -> (843-844) (PARALLEL LOOP) 174 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 175 {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait, 176 llvm::acc::Clause::ACCC_num_gangs, 177 llvm::acc::Clause::ACCC_num_workers, 178 llvm::acc::Clause::ACCC_vector_length}); 179 break; 180 case llvm::acc::Directive::ACCD_serial_loop: 181 // Restriction - 1962 -> (919) (SERIAL LOOP) 182 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 183 {llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait}); 184 break; 185 default: 186 break; 187 } 188 accContext_.pop_back(); 189 } 190 191 std::string AccStructureChecker::ContextDirectiveAsFortran() { 192 return parser::ToUpperCaseLetters( 193 llvm::acc::getOpenACCDirectiveName(GetContext().directive).str()); 194 } 195 196 void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) { 197 const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; 198 const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)}; 199 PushContextAndClauseSets(loopDir.source, loopDir.v); 200 } 201 202 void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) { 203 const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)}; 204 const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)}; 205 if (loopDir.v == llvm::acc::Directive::ACCD_loop) { 206 // Restriction - 1615-1616 207 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 208 loopOnlyAllowedAfterDeviceTypeClauses); 209 // Restriction - 1622 210 CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq, 211 {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector, 212 llvm::acc::Clause::ACCC_worker}); 213 } 214 accContext_.pop_back(); 215 } 216 217 void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) { 218 const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)}; 219 PushContextAndClauseSets(standaloneDir.source, standaloneDir.v); 220 } 221 222 void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) { 223 const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)}; 224 switch (standaloneDir.v) { 225 case llvm::acc::Directive::ACCD_enter_data: 226 case llvm::acc::Directive::ACCD_exit_data: 227 case llvm::acc::Directive::ACCD_set: 228 // Restriction - 1117-1118 (ENTER DATA) 229 // Restriction - 1161-1162 (EXIT DATA) 230 // Restriction - 2254 (SET) 231 CheckRequireAtLeastOneOf(); 232 break; 233 case llvm::acc::Directive::ACCD_update: 234 // Restriction - 2301 235 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 236 updateOnlyAllowedAfterDeviceTypeClauses); 237 break; 238 default: 239 break; 240 } 241 accContext_.pop_back(); 242 } 243 244 void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) { 245 PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine); 246 } 247 void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) { 248 // Restriction - 2409 249 CheckRequireAtLeastOneOf(); 250 // Restriction - 2407-2408 251 CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type, 252 routineOnlyAllowedAfterDeviceTypeClauses); 253 accContext_.pop_back(); 254 } 255 256 // Clause checkers 257 CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse, ACCC_collapse) 258 259 CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto) 260 CHECK_SIMPLE_CLAUSE(Async, ACCC_async) 261 CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach) 262 CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind) 263 CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture) 264 CHECK_SIMPLE_CLAUSE(Copy, ACCC_copy) 265 CHECK_SIMPLE_CLAUSE(Default, ACCC_default) 266 CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async) 267 CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete) 268 CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach) 269 CHECK_SIMPLE_CLAUSE(Device, ACCC_device) 270 CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num) 271 CHECK_SIMPLE_CLAUSE(DevicePtr, ACCC_deviceptr) 272 CHECK_SIMPLE_CLAUSE(DeviceResident, ACCC_device_resident) 273 CHECK_SIMPLE_CLAUSE(DeviceType, ACCC_device_type) 274 CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize) 275 CHECK_SIMPLE_CLAUSE(FirstPrivate, ACCC_firstprivate) 276 CHECK_SIMPLE_CLAUSE(Gang, ACCC_gang) 277 CHECK_SIMPLE_CLAUSE(Host, ACCC_host) 278 CHECK_SIMPLE_CLAUSE(If, ACCC_if) 279 CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present) 280 CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent) 281 CHECK_SIMPLE_CLAUSE(Link, ACCC_link) 282 CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create) 283 CHECK_SIMPLE_CLAUSE(NoHost, ACCC_nohost) 284 CHECK_SIMPLE_CLAUSE(NumGangs, ACCC_num_gangs) 285 CHECK_SIMPLE_CLAUSE(NumWorkers, ACCC_num_workers) 286 CHECK_SIMPLE_CLAUSE(Present, ACCC_present) 287 CHECK_SIMPLE_CLAUSE(Private, ACCC_private) 288 CHECK_SIMPLE_CLAUSE(Read, ACCC_read) 289 CHECK_SIMPLE_CLAUSE(Reduction, ACCC_reduction) 290 CHECK_SIMPLE_CLAUSE(Self, ACCC_self) 291 CHECK_SIMPLE_CLAUSE(Seq, ACCC_seq) 292 CHECK_SIMPLE_CLAUSE(Tile, ACCC_tile) 293 CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device) 294 CHECK_SIMPLE_CLAUSE(Vector, ACCC_vector) 295 CHECK_SIMPLE_CLAUSE(VectorLength, ACCC_vector_length) 296 CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait) 297 CHECK_SIMPLE_CLAUSE(Worker, ACCC_worker) 298 CHECK_SIMPLE_CLAUSE(Write, ACCC_write) 299 300 void AccStructureChecker::Enter(const parser::AccClause::Create &c) { 301 CheckAllowed(llvm::acc::Clause::ACCC_create); 302 const auto &modifierClause{c.v}; 303 if (const auto &modifier{ 304 std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) { 305 if (modifier->v != parser::AccDataModifier::Modifier::Zero) { 306 context_.Say(GetContext().clauseSource, 307 "Only the ZERO modifier is allowed for the %s clause " 308 "on the %s directive"_err_en_US, 309 parser::ToUpperCaseLetters( 310 llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create) 311 .str()), 312 ContextDirectiveAsFortran()); 313 } 314 } 315 } 316 317 void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) { 318 CheckAllowed(llvm::acc::Clause::ACCC_copyin); 319 const auto &modifierClause{c.v}; 320 if (const auto &modifier{ 321 std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) { 322 if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) { 323 context_.Say(GetContext().clauseSource, 324 "Only the READONLY modifier is allowed for the %s clause " 325 "on the %s directive"_err_en_US, 326 parser::ToUpperCaseLetters( 327 llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin) 328 .str()), 329 ContextDirectiveAsFortran()); 330 } 331 } 332 } 333 334 void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) { 335 CheckAllowed(llvm::acc::Clause::ACCC_copyout); 336 const auto &modifierClause{c.v}; 337 if (const auto &modifier{ 338 std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) { 339 if (modifier->v != parser::AccDataModifier::Modifier::Zero) { 340 context_.Say(GetContext().clauseSource, 341 "Only the ZERO modifier is allowed for the %s clause " 342 "on the %s directive"_err_en_US, 343 parser::ToUpperCaseLetters( 344 llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout) 345 .str()), 346 ContextDirectiveAsFortran()); 347 } 348 } 349 } 350 351 void AccStructureChecker::CheckAllowed(llvm::acc::Clause clause) { 352 if (!GetContext().allowedClauses.test(clause) && 353 !GetContext().allowedOnceClauses.test(clause) && 354 !GetContext().allowedExclusiveClauses.test(clause) && 355 !GetContext().requiredClauses.test(clause)) { 356 context_.Say(GetContext().clauseSource, 357 "%s clause is not allowed on the %s directive"_err_en_US, 358 parser::ToUpperCaseLetters( 359 llvm::acc::getOpenACCClauseName(clause).str()), 360 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); 361 return; 362 } 363 if ((GetContext().allowedOnceClauses.test(clause) || 364 GetContext().allowedExclusiveClauses.test(clause)) && 365 FindClause(clause)) { 366 context_.Say(GetContext().clauseSource, 367 "At most one %s clause can appear on the %s directive"_err_en_US, 368 parser::ToUpperCaseLetters( 369 llvm::acc::getOpenACCClauseName(clause).str()), 370 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); 371 return; 372 } 373 if (GetContext().allowedExclusiveClauses.test(clause)) { 374 std::vector<llvm::acc::Clause> others; 375 GetContext().allowedExclusiveClauses.IterateOverMembers( 376 [&](llvm::acc::Clause o) { 377 if (FindClause(o)) { 378 others.emplace_back(o); 379 } 380 }); 381 for (const auto &e : others) { 382 context_.Say(GetContext().clauseSource, 383 "%s and %s clauses are mutually exclusive and may not appear on the " 384 "same %s directive"_err_en_US, 385 parser::ToUpperCaseLetters( 386 llvm::acc::getOpenACCClauseName(clause).str()), 387 parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(e).str()), 388 parser::ToUpperCaseLetters(GetContext().directiveSource.ToString())); 389 } 390 if (!others.empty()) { 391 return; 392 } 393 } 394 SetContextClauseInfo(clause); 395 AddClauseToCrtContext(clause); 396 } 397 398 void AccStructureChecker::CheckOnlyAllowedAfter( 399 llvm::acc::Clause clause, AccClauseSet set) { 400 bool enforceCheck = false; 401 for (auto cl : GetContext().actualClauses) { 402 if (cl == clause) { 403 enforceCheck = true; 404 continue; 405 } else if (enforceCheck && !set.test(cl)) { 406 auto parserClause = GetContext().clauseInfo.find(cl); 407 context_.Say(parserClause->second->source, 408 "Clause %s is not allowed after clause %s on the %s " 409 "directive"_err_en_US, 410 parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()), 411 parser::ToUpperCaseLetters( 412 llvm::acc::getOpenACCClauseName(clause).str()), 413 ContextDirectiveAsFortran()); 414 } 415 } 416 } 417 418 void AccStructureChecker::CheckRequireAtLeastOneOf() { 419 for (auto cl : GetContext().actualClauses) { 420 if (GetContext().requiredClauses.test(cl)) 421 return; 422 } 423 // No clause matched in the actual clauses list 424 context_.Say(GetContext().directiveSource, 425 "At least one of %s clause must appear on the %s directive"_err_en_US, 426 ClauseSetToString(GetContext().requiredClauses), 427 ContextDirectiveAsFortran()); 428 } 429 430 void AccStructureChecker::CheckAtLeastOneClause() { 431 if (GetContext().actualClauses.empty()) { 432 context_.Say(GetContext().directiveSource, 433 "At least one clause is required on the %s directive"_err_en_US, 434 ContextDirectiveAsFortran()); 435 } 436 } 437 438 // Enforce restriction where clauses in the given set are not allowed if the 439 // given clause appears. 440 void AccStructureChecker::CheckNotAllowedIfClause( 441 llvm::acc::Clause clause, AccClauseSet set) { 442 if (std::find(GetContext().actualClauses.begin(), 443 GetContext().actualClauses.end(), 444 clause) == GetContext().actualClauses.end()) { 445 return; // Clause is not present 446 } 447 448 for (auto cl : GetContext().actualClauses) { 449 if (set.test(cl)) { 450 context_.Say(GetContext().directiveSource, 451 "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US, 452 parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()), 453 parser::ToUpperCaseLetters( 454 llvm::acc::getOpenACCClauseName(clause).str()), 455 ContextDirectiveAsFortran()); 456 } 457 } 458 } 459 460 void AccStructureChecker::RequiresConstantPositiveParameter( 461 const llvm::acc::Clause &clause, const parser::ScalarIntConstantExpr &i) { 462 if (const auto v{GetIntValue(i)}) { 463 if (*v <= 0) { 464 context_.Say(GetContext().clauseSource, 465 "The parameter of the %s clause on the %s directive must be " 466 "a constant positive integer expression"_err_en_US, 467 parser::ToUpperCaseLetters( 468 llvm::acc::getOpenACCClauseName(clause).str()), 469 ContextDirectiveAsFortran()); 470 } 471 } 472 } 473 474 void AccStructureChecker::OptionalConstantPositiveParameter( 475 const llvm::acc::Clause &clause, 476 const std::optional<parser::ScalarIntConstantExpr> &o) { 477 if (o != std::nullopt) { 478 RequiresConstantPositiveParameter(clause, o.value()); 479 } 480 } 481 482 std::string AccStructureChecker::ClauseSetToString(const AccClauseSet set) { 483 std::string list; 484 set.IterateOverMembers([&](llvm::acc::Clause o) { 485 if (!list.empty()) 486 list.append(", "); 487 list.append( 488 parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(o).str())); 489 }); 490 return list; 491 } 492 493 void AccStructureChecker::SayNotMatching( 494 const parser::CharBlock &beginSource, const parser::CharBlock &endSource) { 495 context_ 496 .Say(endSource, "Unmatched %s directive"_err_en_US, 497 parser::ToUpperCaseLetters(endSource.ToString())) 498 .Attach(beginSource, "Does not match directive"_en_US); 499 } 500 501 } // namespace Fortran::semantics 502