// Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "resolve-labels.h" #include "../common/enum-set.h" #include "../common/template.h" #include "../parser/message.h" #include "../parser/parse-tree-visitor.h" #include #include #include namespace Fortran::semantics { using namespace parser::literals; ENUM_CLASS( TargetStatementEnum, Do, Branch, Format, CompatibleDo, CompatibleBranch) using LabeledStmtClassificationSet = common::EnumSet; using IndexList = std::vector>; // A ProxyForScope is an integral proxy for a Fortran scope. This is required // because the parse tree does not actually have the scopes required. using ProxyForScope = unsigned; struct LabeledStatementInfoTuplePOD { ProxyForScope proxyForScope; parser::CharBlock parserCharBlock; LabeledStmtClassificationSet labeledStmtClassificationSet; }; using TargetStmtMap = std::map; struct SourceStatementInfoTuplePOD { SourceStatementInfoTuplePOD(const parser::Label &parserLabel, const ProxyForScope &proxyForScope, const parser::CharBlock &parserCharBlock) : parserLabel{parserLabel}, proxyForScope{proxyForScope}, parserCharBlock{parserCharBlock} {} parser::Label parserLabel; ProxyForScope proxyForScope; parser::CharBlock parserCharBlock; }; using SourceStmtList = std::vector; enum class Legality { never, always, formerly }; bool HasScope(ProxyForScope scope) { return scope != ProxyForScope{0u}; } // F18:R1131 template constexpr Legality IsLegalDoTerm(const parser::Statement &) { if (std::is_same_v> || std::is_same_v) { return Legality::always; } else { return Legality::never; } } constexpr Legality IsLegalDoTerm( const parser::Statement &actionStmt) { if (std::holds_alternative(actionStmt.statement.u)) { // See F08:C816 return Legality::always; } else if (!(std::holds_alternative< common::Indirection>( actionStmt.statement.u) || std::holds_alternative>( actionStmt.statement.u) || std::holds_alternative>( actionStmt.statement.u) || std::holds_alternative>( actionStmt.statement.u) || std::holds_alternative>( actionStmt.statement.u) || std::holds_alternative< common::Indirection>( actionStmt.statement.u))) { return Legality::formerly; } else { return Legality::never; } } template constexpr bool IsFormat(const parser::Statement &) { return std::is_same_v>; } template constexpr Legality IsLegalBranchTarget(const parser::Statement &) { if (std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v> || std::is_same_v || std::is_same_v || std::is_same_v> || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v) { return Legality::always; } else { return Legality::never; } } constexpr Legality IsLegalBranchTarget( const parser::Statement &actionStmt) { if (!(std::holds_alternative>( actionStmt.statement.u) || std::holds_alternative>( actionStmt.statement.u) || std::holds_alternative>( actionStmt.statement.u) || std::holds_alternative>( actionStmt.statement.u))) { return Legality::always; } else { return Legality::formerly; } } template constexpr LabeledStmtClassificationSet constructBranchTargetFlags( const parser::Statement &statement) { LabeledStmtClassificationSet labeledStmtClassificationSet{}; if (IsLegalDoTerm(statement) == Legality::always) { labeledStmtClassificationSet.set(TargetStatementEnum::Do); } else if (IsLegalDoTerm(statement) == Legality::formerly) { labeledStmtClassificationSet.set(TargetStatementEnum::CompatibleDo); } if (IsLegalBranchTarget(statement) == Legality::always) { labeledStmtClassificationSet.set(TargetStatementEnum::Branch); } else if (IsLegalBranchTarget(statement) == Legality::formerly) { labeledStmtClassificationSet.set(TargetStatementEnum::CompatibleBranch); } if (IsFormat(statement)) { labeledStmtClassificationSet.set(TargetStatementEnum::Format); } return labeledStmtClassificationSet; } static unsigned SayLabel(parser::Label label) { return static_cast(label); } struct UnitAnalysis { UnitAnalysis() { scopeModel.push_back(0); } UnitAnalysis(UnitAnalysis &&that) : doStmtSources{std::move(that.doStmtSources)}, formatStmtSources{std::move(that.formatStmtSources)}, otherStmtSources{std::move(that.otherStmtSources)}, targetStmts{std::move(that.targetStmts)}, scopeModel{std::move( that.scopeModel)} {} SourceStmtList doStmtSources; SourceStmtList formatStmtSources; SourceStmtList otherStmtSources; TargetStmtMap targetStmts; std::vector scopeModel; }; // Some parse tree record for statements simply wrap construct names; // others include them as tuple components. Given a statement, // return a pointer to its name if it has one. template const parser::CharBlock *GetStmtName(const parser::Statement &stmt) { const std::optional *name{nullptr}; if constexpr (WrapperTrait) { if constexpr (std::is_same_v) { return &stmt.statement.v.source; } else { name = &stmt.statement.v; } } else if constexpr (std::is_same_v || std::is_same_v) { name = &std::get<0>(stmt.statement.t); } else if constexpr (common::HasMember) { return &std::get(stmt.statement.t).source; } else { name = &std::get>(stmt.statement.t); } if (name && name->has_value()) { return &(*name)->source; } return nullptr; } class ParseTreeAnalyzer { public: ParseTreeAnalyzer(ParseTreeAnalyzer &&that) = default; ParseTreeAnalyzer(parser::Messages &errorHandler) : errorHandler_{errorHandler} {} template constexpr bool Pre(const A &) { return true; } template constexpr void Post(const A &) {} template bool Pre(const parser::Statement &statement) { currentPosition_ = statement.source; if (statement.label.has_value()) { addTargetLabelDefinition( statement.label.value(), constructBranchTargetFlags(statement)); } return true; } // see 11.1.1 bool Pre(const parser::ProgramUnit &) { return PushNewScope(); } bool Pre(const parser::AssociateConstruct &associateConstruct) { return pushConstructName(associateConstruct); } bool Pre(const parser::BlockConstruct &blockConstruct) { return pushConstructName(blockConstruct); } bool Pre(const parser::ChangeTeamConstruct &changeTeamConstruct) { return pushConstructName(changeTeamConstruct); } bool Pre(const parser::CriticalConstruct &criticalConstruct) { return pushConstructName(criticalConstruct); } bool Pre(const parser::DoConstruct &doConstruct) { return pushConstructName(doConstruct); } bool Pre(const parser::IfConstruct &ifConstruct) { return pushConstructName(ifConstruct); } bool Pre(const parser::IfConstruct::ElseIfBlock &) { return switchToNewScope(); } bool Pre(const parser::IfConstruct::ElseBlock &) { return switchToNewScope(); } bool Pre(const parser::CaseConstruct &caseConstruct) { return pushConstructName(caseConstruct); } bool Pre(const parser::CaseConstruct::Case &) { return switchToNewScope(); } bool Pre(const parser::SelectRankConstruct &selectRankConstruct) { return pushConstructName(selectRankConstruct); } bool Pre(const parser::SelectRankConstruct::RankCase &) { return switchToNewScope(); } bool Pre(const parser::SelectTypeConstruct &selectTypeConstruct) { return pushConstructName(selectTypeConstruct); } bool Pre(const parser::SelectTypeConstruct::TypeCase &) { return switchToNewScope(); } bool Pre(const parser::WhereConstruct &whereConstruct) { return pushConstructNameWithoutBlock(whereConstruct); } bool Pre(const parser::ForallConstruct &forallConstruct) { return pushConstructNameWithoutBlock(forallConstruct); } void Post(const parser::ProgramUnit &) { PopScope(); } void Post(const parser::AssociateConstruct &associateConstruct) { popConstructName(associateConstruct); } void Post(const parser::BlockConstruct &blockConstruct) { popConstructName(blockConstruct); } void Post(const parser::ChangeTeamConstruct &changeTeamConstruct) { popConstructName(changeTeamConstruct); } void Post(const parser::CriticalConstruct &criticalConstruct) { popConstructName(criticalConstruct); } void Post(const parser::DoConstruct &doConstruct) { popConstructName(doConstruct); } void Post(const parser::IfConstruct &ifConstruct) { popConstructName(ifConstruct); } void Post(const parser::CaseConstruct &caseConstruct) { popConstructName(caseConstruct); } void Post(const parser::SelectRankConstruct &selectRankConstruct) { popConstructName(selectRankConstruct); } void Post(const parser::SelectTypeConstruct &selectTypeConstruct) { popConstructName(selectTypeConstruct); } void Post(const parser::WhereConstruct &whereConstruct) { popConstructNameWithoutBlock(whereConstruct); } void Post(const parser::ForallConstruct &forallConstruct) { popConstructNameWithoutBlock(forallConstruct); } // Checks for missing or mismatching names on various constructs (e.g., IF) // and their intermediate or terminal statements that allow optional // construct names(e.g., ELSE). When an optional construct name is present, // the construct as a whole must have a name that matches. template void CheckOptionalName(const char *constructTag, const CONSTRUCT &a, const parser::Statement &stmt) { if (const parser::CharBlock * name{GetStmtName(stmt)}) { const auto &firstStmt{std::get>(a.t)}; if (const parser::CharBlock * firstName{GetStmtName(firstStmt)}) { if (*firstName != *name) { errorHandler_ .Say(*name, parser::MessageFormattedText{ "%s name mismatch"_err_en_US, constructTag}) .Attach(*firstName, "should be"_en_US); } } else { errorHandler_ .Say(*name, parser::MessageFormattedText{ "%s name not allowed"_err_en_US, constructTag}) .Attach(firstStmt.source, "in unnamed %s"_en_US, constructTag); } } } // C1414 void Post(const parser::BlockData &blockData) { CheckOptionalName("BLOCK DATA subprogram", blockData, std::get>(blockData.t)); } // C1564 void Post(const parser::FunctionSubprogram &functionSubprogram) { CheckOptionalName("FUNCTION", functionSubprogram, std::get>( functionSubprogram.t)); } void Post(const parser::InterfaceBlock &interfaceBlock) { auto &interfaceStmt{ std::get>(interfaceBlock.t)}; if (const auto *optionalGenericSpecPointer{ std::get_if>( &interfaceStmt.statement.u)}) { if (optionalGenericSpecPointer->has_value()) { if (const auto *namePointer{ std::get_if(&(*optionalGenericSpecPointer)->u)}) { auto &optionalGenericSpec{ std::get>( interfaceBlock.t) .statement.v}; if (optionalGenericSpec.has_value()) { if (const auto *otherPointer{ std::get_if(&optionalGenericSpec->u)}) { if (namePointer->ToString() != otherPointer->ToString()) { errorHandler_ .Say(currentPosition_, parser::MessageFormattedText{ "INTERFACE generic-name (%s) mismatch"_en_US, namePointer->ToString().c_str()}) .Attach(interfaceStmt.source, "mismatched INTERFACE"_en_US); } } } } } } } // C1402 void Post(const parser::Module &module) { CheckOptionalName("MODULE", module, std::get>(module.t)); } // C1569 void Post(const parser::SeparateModuleSubprogram &separateModuleSubprogram) { CheckOptionalName("MODULE PROCEDURE", separateModuleSubprogram, std::get>( separateModuleSubprogram.t)); } // C1401 void Post(const parser::MainProgram &mainProgram) { if (const parser::CharBlock * endName{GetStmtName(std::get>( mainProgram.t))}) { if (const auto &program{ std::get>>( mainProgram.t)}) { if (*endName != program->statement.v.source) { errorHandler_.Say(*endName, "END PROGRAM name mismatch"_err_en_US) .Attach(program->statement.v.source, "should be"_en_US); } } else { errorHandler_.Say(*endName, parser::MessageFormattedText{ "END PROGRAM has name without PROGRAM statement"_err_en_US}); } } } // C1413 void Post(const parser::Submodule &submodule) { CheckOptionalName("SUBMODULE", submodule, std::get>(submodule.t)); } // C1567 void Post(const parser::SubroutineSubprogram &subroutineSubprogram) { CheckOptionalName("SUBROUTINE", subroutineSubprogram, std::get>( subroutineSubprogram.t)); } // C739 void Post(const parser::DerivedTypeDef &derivedTypeDef) { CheckOptionalName("derived type definition", derivedTypeDef, std::get>(derivedTypeDef.t)); } void Post(const parser::LabelDoStmt &labelDoStmt) { addLabelReferenceFromDoStmt(std::get(labelDoStmt.t)); } void Post(const parser::GotoStmt &gotoStmt) { addLabelReference(gotoStmt.v); } void Post(const parser::ComputedGotoStmt &computedGotoStmt) { addLabelReference(std::get>(computedGotoStmt.t)); } void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) { addLabelReference(std::get<1>(arithmeticIfStmt.t)); addLabelReference(std::get<2>(arithmeticIfStmt.t)); addLabelReference(std::get<3>(arithmeticIfStmt.t)); } void Post(const parser::AssignStmt &assignStmt) { addLabelReference(std::get(assignStmt.t)); } void Post(const parser::AssignedGotoStmt &assignedGotoStmt) { addLabelReference(std::get>(assignedGotoStmt.t)); } void Post(const parser::AltReturnSpec &altReturnSpec) { addLabelReference(altReturnSpec.v); } void Post(const parser::ErrLabel &errLabel) { addLabelReference(errLabel.v); } void Post(const parser::EndLabel &endLabel) { addLabelReference(endLabel.v); } void Post(const parser::EorLabel &eorLabel) { addLabelReference(eorLabel.v); } void Post(const parser::Format &format) { if (const auto *labelPointer{std::get_if(&format.u)}) { addLabelReferenceToFormatStmt(*labelPointer); } } void Post(const parser::CycleStmt &cycleStmt) { if (cycleStmt.v.has_value()) { CheckLabelContext("CYCLE", cycleStmt.v->source); } } void Post(const parser::ExitStmt &exitStmt) { if (exitStmt.v.has_value()) { CheckLabelContext("EXIT", exitStmt.v->source); } } const std::vector &programUnits() const { return programUnits_; } parser::Messages &errorHandler() { return errorHandler_; } private: bool pushSubscope() { programUnits_.back().scopeModel.push_back(currentScope_); currentScope_ = programUnits_.back().scopeModel.size() - 1; return true; } bool PushNewScope() { programUnits_.emplace_back(UnitAnalysis{}); return pushSubscope(); } void PopScope() { currentScope_ = programUnits_.back().scopeModel[currentScope_]; } bool switchToNewScope() { PopScope(); return pushSubscope(); } template bool pushConstructName(const A &a) { const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)}; if (optionalName.has_value()) { constructNames_.emplace_back(optionalName->ToString()); } return pushSubscope(); } bool pushConstructName(const parser::BlockConstruct &blockConstruct) { const auto &optionalName{ std::get>(blockConstruct.t) .statement.v}; if (optionalName.has_value()) { constructNames_.emplace_back(optionalName->ToString()); } return pushSubscope(); } template bool pushConstructNameWithoutBlock(const A &a) { const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)}; if (optionalName.has_value()) { constructNames_.emplace_back(optionalName->ToString()); } return true; } template void popConstructNameWithoutBlock(const A &a) { CheckName(a); popConstructNameIfPresent(a); } template void popConstructNameIfPresent(const A &a) { const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)}; if (optionalName.has_value()) { constructNames_.pop_back(); } } void popConstructNameIfPresent(const parser::BlockConstruct &blockConstruct) { const auto &optionalName{ std::get>(blockConstruct.t) .statement.v}; if (optionalName.has_value()) { constructNames_.pop_back(); } } template void popConstructName(const A &a) { CheckName(a); PopScope(); popConstructNameIfPresent(a); } template void CheckSelectNames(const char *tag, const CONSTRUCT &construct) { CheckEndName(tag, construct); for (const auto &inner : std::get>(construct.t)) { CheckOptionalName( tag, construct, std::get>(inner.t)); } } // C1144 void popConstructName(const parser::CaseConstruct &caseConstruct) { CheckSelectNames("SELECT CASE", caseConstruct); PopScope(); popConstructNameIfPresent(caseConstruct); } // C1154, C1156 void popConstructName( const parser::SelectRankConstruct &selectRankConstruct) { CheckSelectNames( "SELECT RANK", selectRankConstruct); PopScope(); popConstructNameIfPresent(selectRankConstruct); } // C1165 void popConstructName( const parser::SelectTypeConstruct &selectTypeConstruct) { CheckSelectNames( "SELECT TYPE", selectTypeConstruct); PopScope(); popConstructNameIfPresent(selectTypeConstruct); } // Checks for missing or mismatching names on various constructs (e.g., BLOCK) // and their END statements. Both names must be present if either one is. template void CheckEndName(const char *constructTag, const CONSTRUCT &a) { const auto &constructStmt{std::get>(a.t)}; const auto &endStmt{std::get>(a.t)}; const parser::CharBlock *endName{GetStmtName(endStmt)}; if (const parser::CharBlock * constructName{GetStmtName(constructStmt)}) { if (endName) { if (*constructName != *endName) { errorHandler_ .Say(*endName, parser::MessageFormattedText{ "%s construct name mismatch"_err_en_US, constructTag}) .Attach(*constructName, "should be"_en_US); } } else { errorHandler_ .Say(endStmt.source, parser::MessageFormattedText{ "%s construct name required but missing"_err_en_US, constructTag}) .Attach(*constructName, "should be"_en_US); } } else if (endName) { errorHandler_ .Say(*endName, parser::MessageFormattedText{ "%s construct name unexpected"_err_en_US, constructTag}) .Attach( constructStmt.source, "unnamed %s statement"_en_US, constructTag); } } // C1106 void CheckName(const parser::AssociateConstruct &associateConstruct) { CheckEndName( "ASSOCIATE", associateConstruct); } // C1117 void CheckName(const parser::CriticalConstruct &criticalConstruct) { CheckEndName( "CRITICAL", criticalConstruct); } // C1131 void CheckName(const parser::DoConstruct &doConstruct) { CheckEndName("DO", doConstruct); } // C1035 void CheckName(const parser::ForallConstruct &forallConstruct) { CheckEndName( "FORALL", forallConstruct); } // C1109 void CheckName(const parser::BlockConstruct &blockConstruct) { CheckEndName( "BLOCK", blockConstruct); } // C1112 void CheckName(const parser::ChangeTeamConstruct &changeTeamConstruct) { CheckEndName( "CHANGE TEAM", changeTeamConstruct); } // C1142 void CheckName(const parser::IfConstruct &ifConstruct) { CheckEndName("IF", ifConstruct); for (const auto &elseIfBlock : std::get>(ifConstruct.t)) { CheckOptionalName("IF construct", ifConstruct, std::get>(elseIfBlock.t)); } if (const auto &elseBlock{ std::get>( ifConstruct.t)}) { CheckOptionalName("IF construct", ifConstruct, std::get>(elseBlock->t)); } } // C1033 void CheckName(const parser::WhereConstruct &whereConstruct) { CheckEndName( "WHERE", whereConstruct); for (const auto &maskedElsewhere : std::get>( whereConstruct.t)) { CheckOptionalName("WHERE construct", whereConstruct, std::get>( maskedElsewhere.t)); } if (const auto &elsewhere{ std::get>( whereConstruct.t)}) { CheckOptionalName("WHERE construct", whereConstruct, std::get>(elsewhere->t)); } } // C1134, C1166 void CheckLabelContext( const char *const stmtString, const parser::CharBlock &constructName) { const auto iter{std::find(constructNames_.crbegin(), constructNames_.crend(), constructName.ToString())}; if (iter == constructNames_.crend()) { errorHandler_.Say(constructName, parser::MessageFormattedText{ "%s construct-name is not in scope"_err_en_US, stmtString}); } } // 6.2.5, paragraph 2 void CheckLabelInRange(parser::Label label) { if (label < 1 || label > 99999) { errorHandler_.Say(currentPosition_, parser::MessageFormattedText{ "label '%u' is out of range"_err_en_US, SayLabel(label)}); } } // 6.2.5., paragraph 2 void addTargetLabelDefinition(parser::Label label, LabeledStmtClassificationSet labeledStmtClassificationSet) { CheckLabelInRange(label); const auto pair{programUnits_.back().targetStmts.emplace(label, LabeledStatementInfoTuplePOD{ currentScope_, currentPosition_, labeledStmtClassificationSet})}; if (!pair.second) { errorHandler_.Say(currentPosition_, parser::MessageFormattedText{ "label '%u' is not distinct"_err_en_US, SayLabel(label)}); } } void addLabelReferenceFromDoStmt(parser::Label label) { CheckLabelInRange(label); programUnits_.back().doStmtSources.emplace_back( label, currentScope_, currentPosition_); } void addLabelReferenceToFormatStmt(parser::Label label) { CheckLabelInRange(label); programUnits_.back().formatStmtSources.emplace_back( label, currentScope_, currentPosition_); } void addLabelReference(parser::Label label) { CheckLabelInRange(label); programUnits_.back().otherStmtSources.emplace_back( label, currentScope_, currentPosition_); } void addLabelReference(const std::list &labels) { for (const parser::Label &label : labels) { addLabelReference(label); } } std::vector programUnits_; parser::Messages &errorHandler_; parser::CharBlock currentPosition_{nullptr}; ProxyForScope currentScope_{0}; std::vector constructNames_; }; bool InInclusiveScope(const std::vector &scopes, ProxyForScope tail, ProxyForScope head) { for (; tail != head; tail = scopes[tail]) { if (!HasScope(tail)) { return false; } } return true; } ParseTreeAnalyzer LabelAnalysis( parser::Messages &errorHandler, const parser::Program &program) { ParseTreeAnalyzer analysis{errorHandler}; Walk(program, analysis); return analysis; } bool InBody(const parser::CharBlock &position, const std::pair &pair) { if (position.begin() >= pair.first.begin()) { if (position.begin() < pair.second.end()) { return true; } } return false; } LabeledStatementInfoTuplePOD GetLabel( const TargetStmtMap &labels, const parser::Label &label) { const auto iter{labels.find(label)}; if (iter == labels.cend()) { return {0u, nullptr, LabeledStmtClassificationSet{}}; } else { return iter->second; } } // 11.1.7.3 void CheckBranchesIntoDoBody(const SourceStmtList &branches, const TargetStmtMap &labels, const std::vector &scopes, const IndexList &loopBodies, parser::Messages &errorHandler) { for (const auto branch : branches) { const auto &label{branch.parserLabel}; auto branchTarget{GetLabel(labels, label)}; if (HasScope(branchTarget.proxyForScope)) { const auto &fromPosition{branch.parserCharBlock}; const auto &toPosition{branchTarget.parserCharBlock}; for (const auto body : loopBodies) { if (!InBody(fromPosition, body) && InBody(toPosition, body)) { errorHandler .Say(fromPosition, "branch into loop body from outside"_en_US) .Attach(body.first, "the loop branched into"_en_US); } } } } } void CheckDoNesting( const IndexList &loopBodies, parser::Messages &errorHandler) { for (auto i1{loopBodies.cbegin()}; i1 != loopBodies.cend(); ++i1) { const auto &v1{*i1}; for (auto i2{i1 + 1}; i2 != loopBodies.cend(); ++i2) { const auto &v2{*i2}; if (v2.first.begin() < v1.second.end() && v1.second.begin() < v2.second.begin()) { errorHandler.Say(v1.first, "DO loop doesn't properly nest"_err_en_US) .Attach(v2.first, "DO loop conflicts"_en_US); } } } } parser::CharBlock SkipLabel(const parser::CharBlock &position) { const std::size_t maxPosition{position.size()}; if (maxPosition && parser::IsDecimalDigit(position[0])) { std::size_t i{1l}; for (; (i < maxPosition) && parser::IsDecimalDigit(position[i]); ++i) { } for (; (i < maxPosition) && std::isspace(position[i]); ++i) { } return parser::CharBlock{position.begin() + i, position.end()}; } return position; } void CheckLabelDoConstraints(const SourceStmtList &dos, const SourceStmtList &branches, const TargetStmtMap &labels, const std::vector &scopes, parser::Messages &errorHandler) { IndexList loopBodies; for (const auto stmt : dos) { const auto &label{stmt.parserLabel}; const auto &scope{stmt.proxyForScope}; const auto &position{stmt.parserCharBlock}; auto doTarget{GetLabel(labels, label)}; if (!HasScope(doTarget.proxyForScope)) { // C1133 errorHandler.Say(position, parser::MessageFormattedText{ "label '%u' cannot be found"_err_en_US, SayLabel(label)}); } else if (doTarget.parserCharBlock.begin() < position.begin()) { // R1119 errorHandler.Say(position, parser::MessageFormattedText{ "label '%u' doesn't lexically follow DO stmt"_err_en_US, SayLabel(label)}); } else if (!InInclusiveScope(scopes, scope, doTarget.proxyForScope)) { // C1133 errorHandler.Say(position, parser::MessageFormattedText{ "label '%u' is not in scope"_en_US, SayLabel(label)}); } else if (!doTarget.labeledStmtClassificationSet.test( TargetStatementEnum::Do) && !doTarget.labeledStmtClassificationSet.test( TargetStatementEnum::CompatibleDo)) { errorHandler.Say(doTarget.parserCharBlock, parser::MessageFormattedText{ "'%u' invalid DO terminal statement"_err_en_US, SayLabel(label)}); } else if (!doTarget.labeledStmtClassificationSet.test( TargetStatementEnum::Do)) { errorHandler.Say(doTarget.parserCharBlock, parser::MessageFormattedText{ "'%u' invalid DO terminal statement"_en_US, SayLabel(label)}); } else { loopBodies.emplace_back(SkipLabel(position), doTarget.parserCharBlock); } } CheckBranchesIntoDoBody(branches, labels, scopes, loopBodies, errorHandler); CheckDoNesting(loopBodies, errorHandler); } // 6.2.5 void CheckScopeConstraints(const SourceStmtList &stmts, const TargetStmtMap &labels, const std::vector &scopes, parser::Messages &errorHandler) { for (const auto stmt : stmts) { const auto &label{stmt.parserLabel}; const auto &scope{stmt.proxyForScope}; const auto &position{stmt.parserCharBlock}; auto target{GetLabel(labels, label)}; if (!HasScope(target.proxyForScope)) { errorHandler.Say(position, parser::MessageFormattedText{ "label '%u' was not found"_err_en_US, SayLabel(label)}); } else if (!InInclusiveScope(scopes, scope, target.proxyForScope)) { errorHandler.Say(position, parser::MessageFormattedText{ "label '%u' is not in scope"_en_US, SayLabel(label)}); } } } void CheckBranchTargetConstraints(const SourceStmtList &stmts, const TargetStmtMap &labels, parser::Messages &errorHandler) { for (const auto stmt : stmts) { const auto &label{stmt.parserLabel}; auto branchTarget{GetLabel(labels, label)}; if (HasScope(branchTarget.proxyForScope)) { if (!branchTarget.labeledStmtClassificationSet.test( TargetStatementEnum::Branch) && !branchTarget.labeledStmtClassificationSet.test( TargetStatementEnum::CompatibleBranch)) { errorHandler .Say(branchTarget.parserCharBlock, parser::MessageFormattedText{ "'%u' not a branch target"_err_en_US, SayLabel(label)}) .Attach(stmt.parserCharBlock, parser::MessageFormattedText{ "control flow use of '%u'"_en_US, SayLabel(label)}); } else if (!branchTarget.labeledStmtClassificationSet.test( TargetStatementEnum::Branch)) { errorHandler .Say(branchTarget.parserCharBlock, parser::MessageFormattedText{ "'%u' not a branch target"_en_US, SayLabel(label)}) .Attach(stmt.parserCharBlock, parser::MessageFormattedText{ "control flow use of '%u'"_en_US, SayLabel(label)}); } } } } void CheckBranchConstraints(const SourceStmtList &branches, const TargetStmtMap &labels, const std::vector &scopes, parser::Messages &errorHandler) { CheckScopeConstraints(branches, labels, scopes, errorHandler); CheckBranchTargetConstraints(branches, labels, errorHandler); } void CheckDataXferTargetConstraints(const SourceStmtList &stmts, const TargetStmtMap &labels, parser::Messages &errorHandler) { for (const auto stmt : stmts) { const auto &label{stmt.parserLabel}; auto ioTarget{GetLabel(labels, label)}; if (HasScope(ioTarget.proxyForScope)) { if (!ioTarget.labeledStmtClassificationSet.test( TargetStatementEnum::Format)) { errorHandler .Say(ioTarget.parserCharBlock, parser::MessageFormattedText{ "'%u' not a FORMAT"_err_en_US, SayLabel(label)}) .Attach(stmt.parserCharBlock, parser::MessageFormattedText{ "data transfer use of '%u'"_en_US, SayLabel(label)}); } } } } void CheckDataTransferConstraints(const SourceStmtList &dataTransfers, const TargetStmtMap &labels, const std::vector &scopes, parser::Messages &errorHandler) { CheckScopeConstraints(dataTransfers, labels, scopes, errorHandler); CheckDataXferTargetConstraints(dataTransfers, labels, errorHandler); } bool CheckConstraints(ParseTreeAnalyzer &&parseTreeAnalysis) { auto &errorHandler{parseTreeAnalysis.errorHandler()}; for (const auto &programUnit : parseTreeAnalysis.programUnits()) { const auto &dos{programUnit.doStmtSources}; const auto &branches{programUnit.otherStmtSources}; const auto &labels{programUnit.targetStmts}; const auto &scopes{programUnit.scopeModel}; CheckLabelDoConstraints(dos, branches, labels, scopes, errorHandler); CheckBranchConstraints(branches, labels, scopes, errorHandler); const auto &dataTransfers{programUnit.formatStmtSources}; CheckDataTransferConstraints(dataTransfers, labels, scopes, errorHandler); } return !errorHandler.AnyFatalError(); } bool ValidateLabels( parser::Messages &errorHandler, const parser::Program &program) { return CheckConstraints(LabelAnalysis(errorHandler, program)); } }