1 //===-- lib/Semantics/resolve-labels.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 "resolve-labels.h" 10 #include "flang/Common/enum-set.h" 11 #include "flang/Common/template.h" 12 #include "flang/Parser/parse-tree-visitor.h" 13 #include "flang/Semantics/semantics.h" 14 #include <cstdarg> 15 #include <type_traits> 16 17 namespace Fortran::semantics { 18 19 using namespace parser::literals; 20 21 ENUM_CLASS( 22 TargetStatementEnum, Do, Branch, Format, CompatibleDo, CompatibleBranch) 23 using LabeledStmtClassificationSet = 24 common::EnumSet<TargetStatementEnum, TargetStatementEnum_enumSize>; 25 26 using IndexList = std::vector<std::pair<parser::CharBlock, parser::CharBlock>>; 27 // A ProxyForScope is an integral proxy for a Fortran scope. This is required 28 // because the parse tree does not actually have the scopes required. 29 using ProxyForScope = unsigned; 30 // Minimal scope information 31 struct ScopeInfo { 32 ProxyForScope parent{}; 33 bool isExteriorGotoFatal{false}; 34 int depth{0}; 35 }; 36 struct LabeledStatementInfoTuplePOD { 37 ProxyForScope proxyForScope; 38 parser::CharBlock parserCharBlock; 39 LabeledStmtClassificationSet labeledStmtClassificationSet; 40 bool isExecutableConstructEndStmt; 41 }; 42 using TargetStmtMap = std::map<parser::Label, LabeledStatementInfoTuplePOD>; 43 struct SourceStatementInfoTuplePOD { 44 SourceStatementInfoTuplePOD(const parser::Label &parserLabel, 45 const ProxyForScope &proxyForScope, 46 const parser::CharBlock &parserCharBlock) 47 : parserLabel{parserLabel}, proxyForScope{proxyForScope}, 48 parserCharBlock{parserCharBlock} {} 49 parser::Label parserLabel; 50 ProxyForScope proxyForScope; 51 parser::CharBlock parserCharBlock; 52 }; 53 using SourceStmtList = std::vector<SourceStatementInfoTuplePOD>; 54 enum class Legality { never, always, formerly }; 55 56 bool HasScope(ProxyForScope scope) { return scope != ProxyForScope{0u}; } 57 58 // F18:R1131 59 template <typename A> 60 constexpr Legality IsLegalDoTerm(const parser::Statement<A> &) { 61 if (std::is_same_v<A, common::Indirection<parser::EndDoStmt>> || 62 std::is_same_v<A, parser::EndDoStmt>) { 63 return Legality::always; 64 } else if (std::is_same_v<A, parser::EndForallStmt> || 65 std::is_same_v<A, parser::EndWhereStmt>) { 66 // Executable construct end statements are also supported as 67 // an extension but they need special care because the associated 68 // construct create their own scope. 69 return Legality::formerly; 70 } else { 71 return Legality::never; 72 } 73 } 74 75 constexpr Legality IsLegalDoTerm( 76 const parser::Statement<parser::ActionStmt> &actionStmt) { 77 if (std::holds_alternative<parser::ContinueStmt>(actionStmt.statement.u)) { 78 // See F08:C816 79 return Legality::always; 80 } else if (!(std::holds_alternative< 81 common::Indirection<parser::ArithmeticIfStmt>>( 82 actionStmt.statement.u) || 83 std::holds_alternative<common::Indirection<parser::CycleStmt>>( 84 actionStmt.statement.u) || 85 std::holds_alternative<common::Indirection<parser::ExitStmt>>( 86 actionStmt.statement.u) || 87 std::holds_alternative<common::Indirection<parser::StopStmt>>( 88 actionStmt.statement.u) || 89 std::holds_alternative<common::Indirection<parser::GotoStmt>>( 90 actionStmt.statement.u) || 91 std::holds_alternative< 92 common::Indirection<parser::ReturnStmt>>( 93 actionStmt.statement.u))) { 94 return Legality::formerly; 95 } else { 96 return Legality::never; 97 } 98 } 99 100 template <typename A> constexpr bool IsFormat(const parser::Statement<A> &) { 101 return std::is_same_v<A, common::Indirection<parser::FormatStmt>>; 102 } 103 104 template <typename A> 105 constexpr Legality IsLegalBranchTarget(const parser::Statement<A> &) { 106 if (std::is_same_v<A, parser::ActionStmt> || 107 std::is_same_v<A, parser::AssociateStmt> || 108 std::is_same_v<A, parser::EndAssociateStmt> || 109 std::is_same_v<A, parser::IfThenStmt> || 110 std::is_same_v<A, parser::EndIfStmt> || 111 std::is_same_v<A, parser::SelectCaseStmt> || 112 std::is_same_v<A, parser::EndSelectStmt> || 113 std::is_same_v<A, parser::SelectRankStmt> || 114 std::is_same_v<A, parser::SelectTypeStmt> || 115 std::is_same_v<A, common::Indirection<parser::LabelDoStmt>> || 116 std::is_same_v<A, parser::NonLabelDoStmt> || 117 std::is_same_v<A, parser::EndDoStmt> || 118 std::is_same_v<A, common::Indirection<parser::EndDoStmt>> || 119 std::is_same_v<A, parser::BlockStmt> || 120 std::is_same_v<A, parser::EndBlockStmt> || 121 std::is_same_v<A, parser::CriticalStmt> || 122 std::is_same_v<A, parser::EndCriticalStmt> || 123 std::is_same_v<A, parser::ForallConstructStmt> || 124 std::is_same_v<A, parser::WhereConstructStmt> || 125 std::is_same_v<A, parser::EndFunctionStmt> || 126 std::is_same_v<A, parser::EndMpSubprogramStmt> || 127 std::is_same_v<A, parser::EndProgramStmt> || 128 std::is_same_v<A, parser::EndSubroutineStmt>) { 129 return Legality::always; 130 } else { 131 return Legality::never; 132 } 133 } 134 135 template <typename A> 136 constexpr LabeledStmtClassificationSet ConstructBranchTargetFlags( 137 const parser::Statement<A> &statement) { 138 LabeledStmtClassificationSet labeledStmtClassificationSet{}; 139 if (IsLegalDoTerm(statement) == Legality::always) { 140 labeledStmtClassificationSet.set(TargetStatementEnum::Do); 141 } else if (IsLegalDoTerm(statement) == Legality::formerly) { 142 labeledStmtClassificationSet.set(TargetStatementEnum::CompatibleDo); 143 } 144 if (IsLegalBranchTarget(statement) == Legality::always) { 145 labeledStmtClassificationSet.set(TargetStatementEnum::Branch); 146 } else if (IsLegalBranchTarget(statement) == Legality::formerly) { 147 labeledStmtClassificationSet.set(TargetStatementEnum::CompatibleBranch); 148 } 149 if (IsFormat(statement)) { 150 labeledStmtClassificationSet.set(TargetStatementEnum::Format); 151 } 152 return labeledStmtClassificationSet; 153 } 154 155 static unsigned SayLabel(parser::Label label) { 156 return static_cast<unsigned>(label); 157 } 158 159 struct UnitAnalysis { 160 UnitAnalysis() { scopeModel.emplace_back(); } 161 162 SourceStmtList doStmtSources; 163 SourceStmtList formatStmtSources; 164 SourceStmtList otherStmtSources; 165 SourceStmtList assignStmtSources; 166 TargetStmtMap targetStmts; 167 std::vector<ScopeInfo> scopeModel; 168 }; 169 170 // Some parse tree record for statements simply wrap construct names; 171 // others include them as tuple components. Given a statement, 172 // return a pointer to its name if it has one. 173 template <typename A> 174 const parser::CharBlock *GetStmtName(const parser::Statement<A> &stmt) { 175 const std::optional<parser::Name> *name{nullptr}; 176 if constexpr (WrapperTrait<A>) { 177 if constexpr (std::is_same_v<decltype(A::v), parser::Name>) { 178 return &stmt.statement.v.source; 179 } else { 180 name = &stmt.statement.v; 181 } 182 } else if constexpr (std::is_same_v<A, parser::SelectRankStmt> || 183 std::is_same_v<A, parser::SelectTypeStmt>) { 184 name = &std::get<0>(stmt.statement.t); 185 } else if constexpr (common::HasMember<parser::Name, 186 decltype(stmt.statement.t)>) { 187 return &std::get<parser::Name>(stmt.statement.t).source; 188 } else { 189 name = &std::get<std::optional<parser::Name>>(stmt.statement.t); 190 } 191 if (name && *name) { 192 return &(*name)->source; 193 } 194 return nullptr; 195 } 196 197 class ParseTreeAnalyzer { 198 public: 199 ParseTreeAnalyzer(ParseTreeAnalyzer &&that) = default; 200 ParseTreeAnalyzer(SemanticsContext &context) : context_{context} {} 201 202 template <typename A> constexpr bool Pre(const A &x) { 203 using LabeledProgramUnitStmts = 204 std::tuple<parser::MainProgram, parser::FunctionSubprogram, 205 parser::SubroutineSubprogram, parser::SeparateModuleSubprogram>; 206 if constexpr (common::HasMember<A, LabeledProgramUnitStmts>) { 207 const auto &endStmt{std::get<std::tuple_size_v<decltype(x.t)> - 1>(x.t)}; 208 if (endStmt.label) { 209 // The END statement for a subprogram appears after any internal 210 // subprograms. Visit that statement in advance so that results 211 // are placed in the correct programUnits_ slot. 212 auto targetFlags{ConstructBranchTargetFlags(endStmt)}; 213 AddTargetLabelDefinition( 214 endStmt.label.value(), targetFlags, currentScope_); 215 } 216 } 217 return true; 218 } 219 template <typename A> constexpr void Post(const A &) {} 220 221 template <typename A> bool Pre(const parser::Statement<A> &statement) { 222 currentPosition_ = statement.source; 223 const auto &label = statement.label; 224 if (!label) { 225 return true; 226 } 227 using LabeledConstructStmts = std::tuple<parser::AssociateStmt, 228 parser::BlockStmt, parser::ChangeTeamStmt, parser::CriticalStmt, 229 parser::IfThenStmt, parser::NonLabelDoStmt, parser::SelectCaseStmt, 230 parser::SelectRankStmt, parser::SelectTypeStmt, 231 parser::ForallConstructStmt, parser::WhereConstructStmt>; 232 using LabeledConstructEndStmts = std::tuple<parser::EndAssociateStmt, 233 parser::EndBlockStmt, parser::EndChangeTeamStmt, 234 parser::EndCriticalStmt, parser::EndDoStmt, parser::EndForallStmt, 235 parser::EndIfStmt, parser::EndWhereStmt>; 236 using LabeledProgramUnitEndStmts = 237 std::tuple<parser::EndFunctionStmt, parser::EndMpSubprogramStmt, 238 parser::EndProgramStmt, parser::EndSubroutineStmt>; 239 auto targetFlags{ConstructBranchTargetFlags(statement)}; 240 if constexpr (common::HasMember<A, LabeledConstructStmts>) { 241 AddTargetLabelDefinition(label.value(), targetFlags, ParentScope()); 242 } else if constexpr (std::is_same_v<A, parser::EndSelectStmt>) { 243 // the label on an END SELECT is not in the last case 244 AddTargetLabelDefinition(label.value(), targetFlags, ParentScope(), true); 245 } else if constexpr (common::HasMember<A, LabeledConstructEndStmts>) { 246 constexpr bool isExecutableConstructEndStmt{true}; 247 AddTargetLabelDefinition(label.value(), targetFlags, currentScope_, 248 isExecutableConstructEndStmt); 249 } else if constexpr (!common::HasMember<A, LabeledProgramUnitEndStmts>) { 250 // Program unit END statements have already been processed. 251 AddTargetLabelDefinition(label.value(), targetFlags, currentScope_); 252 } 253 return true; 254 } 255 256 // see 11.1.1 257 bool Pre(const parser::ProgramUnit &) { return InitializeNewScopeContext(); } 258 bool Pre(const parser::InternalSubprogram &) { 259 return InitializeNewScopeContext(); 260 } 261 bool Pre(const parser::ModuleSubprogram &) { 262 return InitializeNewScopeContext(); 263 } 264 bool Pre(const parser::AssociateConstruct &associateConstruct) { 265 return PushConstructName(associateConstruct); 266 } 267 bool Pre(const parser::BlockConstruct &blockConstruct) { 268 return PushConstructName(blockConstruct); 269 } 270 bool Pre(const parser::ChangeTeamConstruct &changeTeamConstruct) { 271 return PushConstructName(changeTeamConstruct); 272 } 273 bool Pre(const parser::CriticalConstruct &criticalConstruct) { 274 return PushConstructName(criticalConstruct); 275 } 276 bool Pre(const parser::DoConstruct &doConstruct) { 277 return PushConstructName(doConstruct); 278 } 279 bool Pre(const parser::IfConstruct &ifConstruct) { 280 return PushConstructName(ifConstruct); 281 } 282 bool Pre(const parser::IfConstruct::ElseIfBlock &) { 283 return SwitchToNewScope(); 284 } 285 bool Pre(const parser::IfConstruct::ElseBlock &) { 286 return SwitchToNewScope(); 287 } 288 bool Pre(const parser::CaseConstruct &caseConstruct) { 289 return PushConstructName(caseConstruct); 290 } 291 void Post(const parser::SelectCaseStmt &) { PushScope(); } 292 bool Pre(const parser::CaseConstruct::Case &) { return SwitchToNewScope(); } 293 bool Pre(const parser::SelectRankConstruct &selectRankConstruct) { 294 return PushConstructName(selectRankConstruct); 295 } 296 void Post(const parser::SelectRankStmt &) { PushScope(); } 297 bool Pre(const parser::SelectRankConstruct::RankCase &) { 298 return SwitchToNewScope(); 299 } 300 bool Pre(const parser::SelectTypeConstruct &selectTypeConstruct) { 301 return PushConstructName(selectTypeConstruct); 302 } 303 void Post(const parser::SelectTypeStmt &) { PushScope(); } 304 bool Pre(const parser::SelectTypeConstruct::TypeCase &) { 305 return SwitchToNewScope(); 306 } 307 void Post(const parser::EndSelectStmt &) { PopScope(); } 308 bool Pre(const parser::WhereConstruct &whereConstruct) { 309 return PushConstructName(whereConstruct); 310 } 311 bool Pre(const parser::ForallConstruct &forallConstruct) { 312 return PushConstructName(forallConstruct); 313 } 314 315 void Post(const parser::AssociateConstruct &associateConstruct) { 316 PopConstructName(associateConstruct); 317 } 318 void Post(const parser::BlockConstruct &blockConstruct) { 319 PopConstructName(blockConstruct); 320 } 321 void Post(const parser::ChangeTeamConstruct &changeTeamConstruct) { 322 PopConstructName(changeTeamConstruct); 323 } 324 void Post(const parser::CriticalConstruct &criticalConstruct) { 325 PopConstructName(criticalConstruct); 326 } 327 void Post(const parser::DoConstruct &doConstruct) { 328 PopConstructName(doConstruct); 329 } 330 void Post(const parser::IfConstruct &ifConstruct) { 331 PopConstructName(ifConstruct); 332 } 333 void Post(const parser::CaseConstruct &caseConstruct) { 334 PopConstructName(caseConstruct); 335 } 336 void Post(const parser::SelectRankConstruct &selectRankConstruct) { 337 PopConstructName(selectRankConstruct); 338 } 339 void Post(const parser::SelectTypeConstruct &selectTypeConstruct) { 340 PopConstructName(selectTypeConstruct); 341 } 342 void Post(const parser::WhereConstruct &whereConstruct) { 343 PopConstructName(whereConstruct); 344 } 345 void Post(const parser::ForallConstruct &forallConstruct) { 346 PopConstructName(forallConstruct); 347 } 348 349 // Checks for missing or mismatching names on various constructs (e.g., IF) 350 // and their intermediate or terminal statements that allow optional 351 // construct names(e.g., ELSE). When an optional construct name is present, 352 // the construct as a whole must have a name that matches. 353 template <typename FIRST, typename CONSTRUCT, typename STMT> 354 void CheckOptionalName(const char *constructTag, const CONSTRUCT &a, 355 const parser::Statement<STMT> &stmt) { 356 if (const parser::CharBlock * name{GetStmtName(stmt)}) { 357 const auto &firstStmt{std::get<parser::Statement<FIRST>>(a.t)}; 358 if (const parser::CharBlock * firstName{GetStmtName(firstStmt)}) { 359 if (*firstName != *name) { 360 context_.Say(*name, "%s name mismatch"_err_en_US, constructTag) 361 .Attach(*firstName, "should be"_en_US); 362 } 363 } else { 364 context_.Say(*name, "%s name not allowed"_err_en_US, constructTag) 365 .Attach(firstStmt.source, "in unnamed %s"_en_US, constructTag); 366 } 367 } 368 } 369 370 // C1414 371 void Post(const parser::BlockData &blockData) { 372 CheckOptionalName<parser::BlockDataStmt>("BLOCK DATA subprogram", blockData, 373 std::get<parser::Statement<parser::EndBlockDataStmt>>(blockData.t)); 374 } 375 376 bool Pre(const parser::InterfaceBody &) { 377 PushDisposableMap(); 378 return true; 379 } 380 void Post(const parser::InterfaceBody &) { PopDisposableMap(); } 381 382 // C1564 383 void Post(const parser::InterfaceBody::Function &func) { 384 CheckOptionalName<parser::FunctionStmt>("FUNCTION", func, 385 std::get<parser::Statement<parser::EndFunctionStmt>>(func.t)); 386 } 387 388 // C1564 389 void Post(const parser::FunctionSubprogram &functionSubprogram) { 390 CheckOptionalName<parser::FunctionStmt>("FUNCTION", functionSubprogram, 391 std::get<parser::Statement<parser::EndFunctionStmt>>( 392 functionSubprogram.t)); 393 } 394 395 // C1502 396 void Post(const parser::InterfaceBlock &interfaceBlock) { 397 if (const auto &endGenericSpec{ 398 std::get<parser::Statement<parser::EndInterfaceStmt>>( 399 interfaceBlock.t) 400 .statement.v}) { 401 const auto &interfaceStmt{ 402 std::get<parser::Statement<parser::InterfaceStmt>>(interfaceBlock.t)}; 403 if (std::holds_alternative<parser::Abstract>(interfaceStmt.statement.u)) { 404 context_ 405 .Say(endGenericSpec->source, 406 "END INTERFACE generic name (%s) may not appear for ABSTRACT INTERFACE"_err_en_US, 407 endGenericSpec->source) 408 .Attach( 409 interfaceStmt.source, "corresponding ABSTRACT INTERFACE"_en_US); 410 } else if (const auto &genericSpec{ 411 std::get<std::optional<parser::GenericSpec>>( 412 interfaceStmt.statement.u)}) { 413 bool ok{genericSpec->source == endGenericSpec->source}; 414 if (!ok) { 415 // Accept variant spellings of .LT. &c. 416 const auto *endOp{ 417 std::get_if<parser::DefinedOperator>(&endGenericSpec->u)}; 418 const auto *op{std::get_if<parser::DefinedOperator>(&genericSpec->u)}; 419 if (endOp && op) { 420 const auto *endIntrin{ 421 std::get_if<parser::DefinedOperator::IntrinsicOperator>( 422 &endOp->u)}; 423 const auto *intrin{ 424 std::get_if<parser::DefinedOperator::IntrinsicOperator>( 425 &op->u)}; 426 ok = endIntrin && intrin && *endIntrin == *intrin; 427 } 428 } 429 if (!ok) { 430 context_ 431 .Say(endGenericSpec->source, 432 "END INTERFACE generic name (%s) does not match generic INTERFACE (%s)"_err_en_US, 433 endGenericSpec->source, genericSpec->source) 434 .Attach(genericSpec->source, "corresponding INTERFACE"_en_US); 435 } 436 } else { 437 context_ 438 .Say(endGenericSpec->source, 439 "END INTERFACE generic name (%s) may not appear for non-generic INTERFACE"_err_en_US, 440 endGenericSpec->source) 441 .Attach(interfaceStmt.source, "corresponding INTERFACE"_en_US); 442 } 443 } 444 } 445 446 // C1402 447 void Post(const parser::Module &module) { 448 CheckOptionalName<parser::ModuleStmt>("MODULE", module, 449 std::get<parser::Statement<parser::EndModuleStmt>>(module.t)); 450 } 451 452 // C1569 453 void Post(const parser::SeparateModuleSubprogram &separateModuleSubprogram) { 454 CheckOptionalName<parser::MpSubprogramStmt>("MODULE PROCEDURE", 455 separateModuleSubprogram, 456 std::get<parser::Statement<parser::EndMpSubprogramStmt>>( 457 separateModuleSubprogram.t)); 458 } 459 460 // C1401 461 void Post(const parser::MainProgram &mainProgram) { 462 if (const parser::CharBlock * 463 endName{GetStmtName(std::get<parser::Statement<parser::EndProgramStmt>>( 464 mainProgram.t))}) { 465 if (const auto &program{ 466 std::get<std::optional<parser::Statement<parser::ProgramStmt>>>( 467 mainProgram.t)}) { 468 if (*endName != program->statement.v.source) { 469 context_.Say(*endName, "END PROGRAM name mismatch"_err_en_US) 470 .Attach(program->statement.v.source, "should be"_en_US); 471 } 472 } else { 473 context_.Say(*endName, 474 "END PROGRAM has name without PROGRAM statement"_err_en_US); 475 } 476 } 477 } 478 479 // C1413 480 void Post(const parser::Submodule &submodule) { 481 CheckOptionalName<parser::SubmoduleStmt>("SUBMODULE", submodule, 482 std::get<parser::Statement<parser::EndSubmoduleStmt>>(submodule.t)); 483 } 484 485 // C1567 486 void Post(const parser::InterfaceBody::Subroutine &sub) { 487 CheckOptionalName<parser::SubroutineStmt>("SUBROUTINE", sub, 488 std::get<parser::Statement<parser::EndSubroutineStmt>>(sub.t)); 489 } 490 491 // C1567 492 void Post(const parser::SubroutineSubprogram &subroutineSubprogram) { 493 CheckOptionalName<parser::SubroutineStmt>("SUBROUTINE", 494 subroutineSubprogram, 495 std::get<parser::Statement<parser::EndSubroutineStmt>>( 496 subroutineSubprogram.t)); 497 } 498 499 // C739 500 bool Pre(const parser::DerivedTypeDef &) { 501 PushDisposableMap(); 502 return true; 503 } 504 void Post(const parser::DerivedTypeDef &derivedTypeDef) { 505 CheckOptionalName<parser::DerivedTypeStmt>("derived type definition", 506 derivedTypeDef, 507 std::get<parser::Statement<parser::EndTypeStmt>>(derivedTypeDef.t)); 508 PopDisposableMap(); 509 } 510 511 void Post(const parser::LabelDoStmt &labelDoStmt) { 512 AddLabelReferenceFromDoStmt(std::get<parser::Label>(labelDoStmt.t)); 513 } 514 void Post(const parser::GotoStmt &gotoStmt) { AddLabelReference(gotoStmt.v); } 515 void Post(const parser::ComputedGotoStmt &computedGotoStmt) { 516 AddLabelReference(std::get<std::list<parser::Label>>(computedGotoStmt.t)); 517 } 518 void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) { 519 AddLabelReference(std::get<1>(arithmeticIfStmt.t)); 520 AddLabelReference(std::get<2>(arithmeticIfStmt.t)); 521 AddLabelReference(std::get<3>(arithmeticIfStmt.t)); 522 } 523 void Post(const parser::AssignStmt &assignStmt) { 524 AddLabelReferenceFromAssignStmt(std::get<parser::Label>(assignStmt.t)); 525 } 526 void Post(const parser::AssignedGotoStmt &assignedGotoStmt) { 527 AddLabelReference(std::get<std::list<parser::Label>>(assignedGotoStmt.t)); 528 } 529 void Post(const parser::AltReturnSpec &altReturnSpec) { 530 AddLabelReference(altReturnSpec.v); 531 } 532 533 void Post(const parser::ErrLabel &errLabel) { AddLabelReference(errLabel.v); } 534 void Post(const parser::EndLabel &endLabel) { AddLabelReference(endLabel.v); } 535 void Post(const parser::EorLabel &eorLabel) { AddLabelReference(eorLabel.v); } 536 void Post(const parser::Format &format) { 537 if (const auto *labelPointer{std::get_if<parser::Label>(&format.u)}) { 538 AddLabelReferenceToFormatStmt(*labelPointer); 539 } 540 } 541 void Post(const parser::CycleStmt &cycleStmt) { 542 if (cycleStmt.v) { 543 CheckLabelContext("CYCLE", cycleStmt.v->source); 544 } 545 } 546 void Post(const parser::ExitStmt &exitStmt) { 547 if (exitStmt.v) { 548 CheckLabelContext("EXIT", exitStmt.v->source); 549 } 550 } 551 552 const std::vector<UnitAnalysis> &ProgramUnits() const { 553 return programUnits_; 554 } 555 SemanticsContext &ErrorHandler() { return context_; } 556 557 private: 558 ScopeInfo &PushScope() { 559 auto &model{programUnits_.back().scopeModel}; 560 int newDepth{model.empty() ? 1 : model[currentScope_].depth + 1}; 561 ScopeInfo &result{model.emplace_back()}; 562 result.parent = currentScope_; 563 result.depth = newDepth; 564 currentScope_ = model.size() - 1; 565 return result; 566 } 567 bool InitializeNewScopeContext() { 568 programUnits_.emplace_back(UnitAnalysis{}); 569 currentScope_ = 0u; 570 PushScope(); 571 return true; 572 } 573 ScopeInfo &PopScope() { 574 ScopeInfo &result{programUnits_.back().scopeModel[currentScope_]}; 575 currentScope_ = result.parent; 576 return result; 577 } 578 ProxyForScope ParentScope() { 579 return programUnits_.back().scopeModel[currentScope_].parent; 580 } 581 bool SwitchToNewScope() { 582 ScopeInfo &oldScope{PopScope()}; 583 bool isExteriorGotoFatal{oldScope.isExteriorGotoFatal}; 584 PushScope().isExteriorGotoFatal = isExteriorGotoFatal; 585 return true; 586 } 587 588 template <typename A> bool PushConstructName(const A &a) { 589 const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)}; 590 if (optionalName) { 591 constructNames_.emplace_back(optionalName->ToString()); 592 } 593 // Gotos into this construct from outside it are diagnosed, and 594 // are fatal unless the construct is a DO, IF, or SELECT CASE. 595 PushScope().isExteriorGotoFatal = 596 !(std::is_same_v<A, parser::DoConstruct> || 597 std::is_same_v<A, parser::IfConstruct> || 598 std::is_same_v<A, parser::CaseConstruct>); 599 return true; 600 } 601 bool PushConstructName(const parser::BlockConstruct &blockConstruct) { 602 const auto &optionalName{ 603 std::get<parser::Statement<parser::BlockStmt>>(blockConstruct.t) 604 .statement.v}; 605 if (optionalName) { 606 constructNames_.emplace_back(optionalName->ToString()); 607 } 608 PushScope().isExteriorGotoFatal = true; 609 return true; 610 } 611 template <typename A> void PopConstructNameIfPresent(const A &a) { 612 const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)}; 613 if (optionalName) { 614 constructNames_.pop_back(); 615 } 616 } 617 void PopConstructNameIfPresent(const parser::BlockConstruct &blockConstruct) { 618 const auto &optionalName{ 619 std::get<parser::Statement<parser::BlockStmt>>(blockConstruct.t) 620 .statement.v}; 621 if (optionalName) { 622 constructNames_.pop_back(); 623 } 624 } 625 626 template <typename A> void PopConstructName(const A &a) { 627 CheckName(a); 628 PopScope(); 629 PopConstructNameIfPresent(a); 630 } 631 632 template <typename FIRST, typename CASEBLOCK, typename CASE, 633 typename CONSTRUCT> 634 void CheckSelectNames(const char *tag, const CONSTRUCT &construct) { 635 CheckEndName<FIRST, parser::EndSelectStmt>(tag, construct); 636 for (const auto &inner : std::get<std::list<CASEBLOCK>>(construct.t)) { 637 CheckOptionalName<FIRST>( 638 tag, construct, std::get<parser::Statement<CASE>>(inner.t)); 639 } 640 } 641 642 // C1144 643 void PopConstructName(const parser::CaseConstruct &caseConstruct) { 644 CheckSelectNames<parser::SelectCaseStmt, parser::CaseConstruct::Case, 645 parser::CaseStmt>("SELECT CASE", caseConstruct); 646 PopScope(); 647 PopConstructNameIfPresent(caseConstruct); 648 } 649 650 // C1154, C1156 651 void PopConstructName( 652 const parser::SelectRankConstruct &selectRankConstruct) { 653 CheckSelectNames<parser::SelectRankStmt, 654 parser::SelectRankConstruct::RankCase, parser::SelectRankCaseStmt>( 655 "SELECT RANK", selectRankConstruct); 656 PopScope(); 657 PopConstructNameIfPresent(selectRankConstruct); 658 } 659 660 // C1165 661 void PopConstructName( 662 const parser::SelectTypeConstruct &selectTypeConstruct) { 663 CheckSelectNames<parser::SelectTypeStmt, 664 parser::SelectTypeConstruct::TypeCase, parser::TypeGuardStmt>( 665 "SELECT TYPE", selectTypeConstruct); 666 PopScope(); 667 PopConstructNameIfPresent(selectTypeConstruct); 668 } 669 670 // Checks for missing or mismatching names on various constructs (e.g., BLOCK) 671 // and their END statements. Both names must be present if either one is. 672 template <typename FIRST, typename END, typename CONSTRUCT> 673 void CheckEndName(const char *constructTag, const CONSTRUCT &a) { 674 const auto &constructStmt{std::get<parser::Statement<FIRST>>(a.t)}; 675 const auto &endStmt{std::get<parser::Statement<END>>(a.t)}; 676 const parser::CharBlock *endName{GetStmtName(endStmt)}; 677 if (const parser::CharBlock * constructName{GetStmtName(constructStmt)}) { 678 if (endName) { 679 if (*constructName != *endName) { 680 context_ 681 .Say(*endName, "%s construct name mismatch"_err_en_US, 682 constructTag) 683 .Attach(*constructName, "should be"_en_US); 684 } 685 } else { 686 context_ 687 .Say(endStmt.source, 688 "%s construct name required but missing"_err_en_US, 689 constructTag) 690 .Attach(*constructName, "should be"_en_US); 691 } 692 } else if (endName) { 693 context_ 694 .Say(*endName, "%s construct name unexpected"_err_en_US, constructTag) 695 .Attach( 696 constructStmt.source, "unnamed %s statement"_en_US, constructTag); 697 } 698 } 699 700 // C1106 701 void CheckName(const parser::AssociateConstruct &associateConstruct) { 702 CheckEndName<parser::AssociateStmt, parser::EndAssociateStmt>( 703 "ASSOCIATE", associateConstruct); 704 } 705 // C1117 706 void CheckName(const parser::CriticalConstruct &criticalConstruct) { 707 CheckEndName<parser::CriticalStmt, parser::EndCriticalStmt>( 708 "CRITICAL", criticalConstruct); 709 } 710 // C1131 711 void CheckName(const parser::DoConstruct &doConstruct) { 712 CheckEndName<parser::NonLabelDoStmt, parser::EndDoStmt>("DO", doConstruct); 713 } 714 // C1035 715 void CheckName(const parser::ForallConstruct &forallConstruct) { 716 CheckEndName<parser::ForallConstructStmt, parser::EndForallStmt>( 717 "FORALL", forallConstruct); 718 } 719 720 // C1109 721 void CheckName(const parser::BlockConstruct &blockConstruct) { 722 CheckEndName<parser::BlockStmt, parser::EndBlockStmt>( 723 "BLOCK", blockConstruct); 724 } 725 // C1112 726 void CheckName(const parser::ChangeTeamConstruct &changeTeamConstruct) { 727 CheckEndName<parser::ChangeTeamStmt, parser::EndChangeTeamStmt>( 728 "CHANGE TEAM", changeTeamConstruct); 729 } 730 731 // C1142 732 void CheckName(const parser::IfConstruct &ifConstruct) { 733 CheckEndName<parser::IfThenStmt, parser::EndIfStmt>("IF", ifConstruct); 734 for (const auto &elseIfBlock : 735 std::get<std::list<parser::IfConstruct::ElseIfBlock>>(ifConstruct.t)) { 736 CheckOptionalName<parser::IfThenStmt>("IF construct", ifConstruct, 737 std::get<parser::Statement<parser::ElseIfStmt>>(elseIfBlock.t)); 738 } 739 if (const auto &elseBlock{ 740 std::get<std::optional<parser::IfConstruct::ElseBlock>>( 741 ifConstruct.t)}) { 742 CheckOptionalName<parser::IfThenStmt>("IF construct", ifConstruct, 743 std::get<parser::Statement<parser::ElseStmt>>(elseBlock->t)); 744 } 745 } 746 747 // C1033 748 void CheckName(const parser::WhereConstruct &whereConstruct) { 749 CheckEndName<parser::WhereConstructStmt, parser::EndWhereStmt>( 750 "WHERE", whereConstruct); 751 for (const auto &maskedElsewhere : 752 std::get<std::list<parser::WhereConstruct::MaskedElsewhere>>( 753 whereConstruct.t)) { 754 CheckOptionalName<parser::WhereConstructStmt>("WHERE construct", 755 whereConstruct, 756 std::get<parser::Statement<parser::MaskedElsewhereStmt>>( 757 maskedElsewhere.t)); 758 } 759 if (const auto &elsewhere{ 760 std::get<std::optional<parser::WhereConstruct::Elsewhere>>( 761 whereConstruct.t)}) { 762 CheckOptionalName<parser::WhereConstructStmt>("WHERE construct", 763 whereConstruct, 764 std::get<parser::Statement<parser::ElsewhereStmt>>(elsewhere->t)); 765 } 766 } 767 768 // C1134, C1166 769 void CheckLabelContext( 770 const char *const stmtString, const parser::CharBlock &constructName) { 771 const auto iter{std::find(constructNames_.crbegin(), 772 constructNames_.crend(), constructName.ToString())}; 773 if (iter == constructNames_.crend()) { 774 context_.Say(constructName, "%s construct-name is not in scope"_err_en_US, 775 stmtString); 776 } 777 } 778 779 // 6.2.5, paragraph 2 780 void CheckLabelInRange(parser::Label label) { 781 if (label < 1 || label > 99999) { 782 context_.Say(currentPosition_, "Label '%u' is out of range"_err_en_US, 783 SayLabel(label)); 784 } 785 } 786 787 // 6.2.5., paragraph 2 788 void AddTargetLabelDefinition(parser::Label label, 789 LabeledStmtClassificationSet labeledStmtClassificationSet, 790 ProxyForScope scope, bool isExecutableConstructEndStmt = false) { 791 CheckLabelInRange(label); 792 TargetStmtMap &targetStmtMap{disposableMaps_.empty() 793 ? programUnits_.back().targetStmts 794 : disposableMaps_.back()}; 795 const auto pair{targetStmtMap.emplace(label, 796 LabeledStatementInfoTuplePOD{scope, currentPosition_, 797 labeledStmtClassificationSet, isExecutableConstructEndStmt})}; 798 if (!pair.second) { 799 context_.Say(currentPosition_, "Label '%u' is not distinct"_err_en_US, 800 SayLabel(label)); 801 } 802 } 803 804 void AddLabelReferenceFromDoStmt(parser::Label label) { 805 CheckLabelInRange(label); 806 programUnits_.back().doStmtSources.emplace_back( 807 label, currentScope_, currentPosition_); 808 } 809 810 void AddLabelReferenceToFormatStmt(parser::Label label) { 811 CheckLabelInRange(label); 812 programUnits_.back().formatStmtSources.emplace_back( 813 label, currentScope_, currentPosition_); 814 } 815 816 void AddLabelReferenceFromAssignStmt(parser::Label label) { 817 CheckLabelInRange(label); 818 programUnits_.back().assignStmtSources.emplace_back( 819 label, currentScope_, currentPosition_); 820 } 821 822 void AddLabelReference(parser::Label label) { 823 CheckLabelInRange(label); 824 programUnits_.back().otherStmtSources.emplace_back( 825 label, currentScope_, currentPosition_); 826 } 827 828 void AddLabelReference(const std::list<parser::Label> &labels) { 829 for (const parser::Label &label : labels) { 830 AddLabelReference(label); 831 } 832 } 833 834 void PushDisposableMap() { disposableMaps_.emplace_back(); } 835 void PopDisposableMap() { disposableMaps_.pop_back(); } 836 837 std::vector<UnitAnalysis> programUnits_; 838 SemanticsContext &context_; 839 parser::CharBlock currentPosition_; 840 ProxyForScope currentScope_; 841 std::vector<std::string> constructNames_; 842 // For labels in derived type definitions and procedure 843 // interfaces, which are their own inclusive scopes. None 844 // of these labels can be used as a branch target, but they 845 // should be pairwise distinct. 846 std::vector<TargetStmtMap> disposableMaps_; 847 }; 848 849 bool InInclusiveScope(const std::vector<ScopeInfo> &scopes, ProxyForScope tail, 850 ProxyForScope head) { 851 for (; tail != head; tail = scopes[tail].parent) { 852 if (!HasScope(tail)) { 853 return false; 854 } 855 } 856 return true; 857 } 858 859 ParseTreeAnalyzer LabelAnalysis( 860 SemanticsContext &context, const parser::Program &program) { 861 ParseTreeAnalyzer analysis{context}; 862 Walk(program, analysis); 863 return analysis; 864 } 865 866 bool InBody(const parser::CharBlock &position, 867 const std::pair<parser::CharBlock, parser::CharBlock> &pair) { 868 if (position.begin() >= pair.first.begin()) { 869 if (position.begin() < pair.second.end()) { 870 return true; 871 } 872 } 873 return false; 874 } 875 876 LabeledStatementInfoTuplePOD GetLabel( 877 const TargetStmtMap &labels, const parser::Label &label) { 878 const auto iter{labels.find(label)}; 879 if (iter == labels.cend()) { 880 return {0u, nullptr, LabeledStmtClassificationSet{}, false}; 881 } else { 882 return iter->second; 883 } 884 } 885 886 // 11.1.7.3 887 void CheckBranchesIntoDoBody(const SourceStmtList &branches, 888 const TargetStmtMap &labels, const IndexList &loopBodies, 889 SemanticsContext &context) { 890 for (const auto &branch : branches) { 891 const auto &label{branch.parserLabel}; 892 auto branchTarget{GetLabel(labels, label)}; 893 if (HasScope(branchTarget.proxyForScope)) { 894 const auto &fromPosition{branch.parserCharBlock}; 895 const auto &toPosition{branchTarget.parserCharBlock}; 896 for (const auto &body : loopBodies) { 897 if (!InBody(fromPosition, body) && InBody(toPosition, body)) { 898 context 899 .Say( 900 fromPosition, "branch into loop body from outside"_warn_en_US) 901 .Attach(body.first, "the loop branched into"_en_US); 902 } 903 } 904 } 905 } 906 } 907 908 void CheckDoNesting(const IndexList &loopBodies, SemanticsContext &context) { 909 for (auto i1{loopBodies.cbegin()}; i1 != loopBodies.cend(); ++i1) { 910 const auto &v1{*i1}; 911 for (auto i2{i1 + 1}; i2 != loopBodies.cend(); ++i2) { 912 const auto &v2{*i2}; 913 if (v2.first.begin() < v1.second.end() && 914 v1.second.begin() < v2.second.begin()) { 915 context.Say(v1.first, "DO loop doesn't properly nest"_err_en_US) 916 .Attach(v2.first, "DO loop conflicts"_en_US); 917 } 918 } 919 } 920 } 921 922 parser::CharBlock SkipLabel(const parser::CharBlock &position) { 923 const std::size_t maxPosition{position.size()}; 924 if (maxPosition && parser::IsDecimalDigit(position[0])) { 925 std::size_t i{1l}; 926 for (; (i < maxPosition) && parser::IsDecimalDigit(position[i]); ++i) { 927 } 928 for (; (i < maxPosition) && parser::IsWhiteSpace(position[i]); ++i) { 929 } 930 return parser::CharBlock{position.begin() + i, position.end()}; 931 } 932 return position; 933 } 934 935 ProxyForScope ParentScope( 936 const std::vector<ScopeInfo> &scopes, ProxyForScope scope) { 937 return scopes[scope].parent; 938 } 939 940 void CheckLabelDoConstraints(const SourceStmtList &dos, 941 const SourceStmtList &branches, const TargetStmtMap &labels, 942 const std::vector<ScopeInfo> &scopes, SemanticsContext &context) { 943 IndexList loopBodies; 944 for (const auto &stmt : dos) { 945 const auto &label{stmt.parserLabel}; 946 const auto &scope{stmt.proxyForScope}; 947 const auto &position{stmt.parserCharBlock}; 948 auto doTarget{GetLabel(labels, label)}; 949 if (!HasScope(doTarget.proxyForScope)) { 950 // C1133 951 context.Say( 952 position, "Label '%u' cannot be found"_err_en_US, SayLabel(label)); 953 } else if (doTarget.parserCharBlock.begin() < position.begin()) { 954 // R1119 955 context.Say(position, 956 "Label '%u' doesn't lexically follow DO stmt"_err_en_US, 957 SayLabel(label)); 958 959 } else if ((InInclusiveScope(scopes, scope, doTarget.proxyForScope) && 960 doTarget.labeledStmtClassificationSet.test( 961 TargetStatementEnum::CompatibleDo)) || 962 (doTarget.isExecutableConstructEndStmt && 963 ParentScope(scopes, doTarget.proxyForScope) == scope)) { 964 if (context.ShouldWarn( 965 common::LanguageFeature::OldLabelDoEndStatements)) { 966 context 967 .Say(position, 968 "A DO loop should terminate with an END DO or CONTINUE"_port_en_US) 969 .Attach(doTarget.parserCharBlock, 970 "DO loop currently ends at statement:"_en_US); 971 } 972 } else if (!InInclusiveScope(scopes, scope, doTarget.proxyForScope)) { 973 context.Say(position, "Label '%u' is not in DO loop scope"_err_en_US, 974 SayLabel(label)); 975 } else if (!doTarget.labeledStmtClassificationSet.test( 976 TargetStatementEnum::Do)) { 977 context.Say(doTarget.parserCharBlock, 978 "A DO loop should terminate with an END DO or CONTINUE"_err_en_US); 979 } else { 980 loopBodies.emplace_back(SkipLabel(position), doTarget.parserCharBlock); 981 } 982 } 983 984 CheckBranchesIntoDoBody(branches, labels, loopBodies, context); 985 CheckDoNesting(loopBodies, context); 986 } 987 988 // 6.2.5 989 void CheckScopeConstraints(const SourceStmtList &stmts, 990 const TargetStmtMap &labels, const std::vector<ScopeInfo> &scopes, 991 SemanticsContext &context) { 992 for (const auto &stmt : stmts) { 993 const auto &label{stmt.parserLabel}; 994 const auto &scope{stmt.proxyForScope}; 995 const auto &position{stmt.parserCharBlock}; 996 auto target{GetLabel(labels, label)}; 997 if (!HasScope(target.proxyForScope)) { 998 context.Say( 999 position, "Label '%u' was not found"_err_en_US, SayLabel(label)); 1000 } else if (!InInclusiveScope(scopes, scope, target.proxyForScope)) { 1001 // Clause 11.1.2.1 prohibits transfer of control to the interior of a 1002 // block from outside the block, but this does not apply to formats. 1003 // C1038 and C1034 forbid statements in FORALL and WHERE constructs 1004 // (resp.) from being branch targets. 1005 if (target.labeledStmtClassificationSet.test( 1006 TargetStatementEnum::Format)) { 1007 continue; 1008 } 1009 bool isFatal{false}; 1010 ProxyForScope fromScope{scope}; 1011 for (ProxyForScope toScope{target.proxyForScope}; fromScope != toScope; 1012 toScope = scopes[toScope].parent) { 1013 if (scopes[toScope].isExteriorGotoFatal) { 1014 isFatal = true; 1015 break; 1016 } 1017 if (scopes[toScope].depth == scopes[fromScope].depth) { 1018 fromScope = scopes[fromScope].parent; 1019 } 1020 } 1021 context.Say(position, 1022 isFatal 1023 ? "Label '%u' is in a construct that prevents its use as a branch target here"_err_en_US 1024 : "Label '%u' is in a construct that should not be used as a branch target here"_warn_en_US, 1025 SayLabel(label)); 1026 } 1027 } 1028 } 1029 1030 void CheckBranchTargetConstraints(const SourceStmtList &stmts, 1031 const TargetStmtMap &labels, SemanticsContext &context) { 1032 for (const auto &stmt : stmts) { 1033 const auto &label{stmt.parserLabel}; 1034 auto branchTarget{GetLabel(labels, label)}; 1035 if (HasScope(branchTarget.proxyForScope)) { 1036 if (!branchTarget.labeledStmtClassificationSet.test( 1037 TargetStatementEnum::Branch) && 1038 !branchTarget.labeledStmtClassificationSet.test( 1039 TargetStatementEnum::CompatibleBranch)) { // error 1040 context 1041 .Say(branchTarget.parserCharBlock, 1042 "Label '%u' is not a branch target"_err_en_US, SayLabel(label)) 1043 .Attach(stmt.parserCharBlock, "Control flow use of '%u'"_en_US, 1044 SayLabel(label)); 1045 } else if (!branchTarget.labeledStmtClassificationSet.test( 1046 TargetStatementEnum::Branch)) { // warning 1047 context 1048 .Say(branchTarget.parserCharBlock, 1049 "Label '%u' is not a branch target"_warn_en_US, SayLabel(label)) 1050 .Attach(stmt.parserCharBlock, "Control flow use of '%u'"_en_US, 1051 SayLabel(label)); 1052 } 1053 } 1054 } 1055 } 1056 1057 void CheckBranchConstraints(const SourceStmtList &branches, 1058 const TargetStmtMap &labels, const std::vector<ScopeInfo> &scopes, 1059 SemanticsContext &context) { 1060 CheckScopeConstraints(branches, labels, scopes, context); 1061 CheckBranchTargetConstraints(branches, labels, context); 1062 } 1063 1064 void CheckDataXferTargetConstraints(const SourceStmtList &stmts, 1065 const TargetStmtMap &labels, SemanticsContext &context) { 1066 for (const auto &stmt : stmts) { 1067 const auto &label{stmt.parserLabel}; 1068 auto ioTarget{GetLabel(labels, label)}; 1069 if (HasScope(ioTarget.proxyForScope)) { 1070 if (!ioTarget.labeledStmtClassificationSet.test( 1071 TargetStatementEnum::Format)) { 1072 context 1073 .Say(ioTarget.parserCharBlock, "'%u' not a FORMAT"_err_en_US, 1074 SayLabel(label)) 1075 .Attach(stmt.parserCharBlock, "data transfer use of '%u'"_en_US, 1076 SayLabel(label)); 1077 } 1078 } 1079 } 1080 } 1081 1082 void CheckDataTransferConstraints(const SourceStmtList &dataTransfers, 1083 const TargetStmtMap &labels, const std::vector<ScopeInfo> &scopes, 1084 SemanticsContext &context) { 1085 CheckScopeConstraints(dataTransfers, labels, scopes, context); 1086 CheckDataXferTargetConstraints(dataTransfers, labels, context); 1087 } 1088 1089 void CheckAssignTargetConstraints(const SourceStmtList &stmts, 1090 const TargetStmtMap &labels, SemanticsContext &context) { 1091 for (const auto &stmt : stmts) { 1092 const auto &label{stmt.parserLabel}; 1093 auto target{GetLabel(labels, label)}; 1094 if (HasScope(target.proxyForScope) && 1095 !target.labeledStmtClassificationSet.test( 1096 TargetStatementEnum::Branch) && 1097 !target.labeledStmtClassificationSet.test( 1098 TargetStatementEnum::Format)) { 1099 context 1100 .Say(target.parserCharBlock, 1101 target.labeledStmtClassificationSet.test( 1102 TargetStatementEnum::CompatibleBranch) 1103 ? "Label '%u' is not a branch target or FORMAT"_warn_en_US 1104 : "Label '%u' is not a branch target or FORMAT"_err_en_US, 1105 SayLabel(label)) 1106 .Attach(stmt.parserCharBlock, "ASSIGN statement use of '%u'"_en_US, 1107 SayLabel(label)); 1108 } 1109 } 1110 } 1111 1112 void CheckAssignConstraints(const SourceStmtList &assigns, 1113 const TargetStmtMap &labels, const std::vector<ScopeInfo> &scopes, 1114 SemanticsContext &context) { 1115 CheckScopeConstraints(assigns, labels, scopes, context); 1116 CheckAssignTargetConstraints(assigns, labels, context); 1117 } 1118 1119 bool CheckConstraints(ParseTreeAnalyzer &&parseTreeAnalysis) { 1120 auto &context{parseTreeAnalysis.ErrorHandler()}; 1121 for (const auto &programUnit : parseTreeAnalysis.ProgramUnits()) { 1122 const auto &dos{programUnit.doStmtSources}; 1123 const auto &branches{programUnit.otherStmtSources}; 1124 const auto &labels{programUnit.targetStmts}; 1125 const auto &scopes{programUnit.scopeModel}; 1126 CheckLabelDoConstraints(dos, branches, labels, scopes, context); 1127 CheckBranchConstraints(branches, labels, scopes, context); 1128 const auto &dataTransfers{programUnit.formatStmtSources}; 1129 CheckDataTransferConstraints(dataTransfers, labels, scopes, context); 1130 const auto &assigns{programUnit.assignStmtSources}; 1131 CheckAssignConstraints(assigns, labels, scopes, context); 1132 } 1133 return !context.AnyFatalError(); 1134 } 1135 1136 bool ValidateLabels(SemanticsContext &context, const parser::Program &program) { 1137 return CheckConstraints(LabelAnalysis(context, program)); 1138 } 1139 } // namespace Fortran::semantics 1140