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