brintos

brintos / llvm-project-archived public Read only

0
0
Text · 46.5 KiB · 9454ef9 Raw
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