[flang] Changes per the review comments. The majority of the changes are simply
authorEric Schweitz <eschweitz@nvidia.com>
Fri, 31 Aug 2018 17:26:19 +0000 (10:26 -0700)
committerGitHub <noreply@github.com>
Tue, 11 Sep 2018 21:01:25 +0000 (14:01 -0700)
to rename identifiers to meet the project (not LLVM) coding standard.
Includes a home brew of FileCheck for testing.

Original-commit: flang-compiler/f18@bb15490cc02327773cb680597226346f02cd4dd0
Reviewed-on: https://github.com/flang-compiler/f18/pull/170
Tree-same-pre-rewrite: false

16 files changed:
flang/lib/parser/message.cc
flang/lib/parser/message.h
flang/lib/semantics/resolve-labels.cc
flang/lib/semantics/resolve-labels.h
flang/test/semantics/CMakeLists.txt
flang/test/semantics/label01.F90
flang/test/semantics/label02.f90
flang/test/semantics/label03.f90
flang/test/semantics/label04.f90
flang/test/semantics/label05.f90
flang/test/semantics/label06.f90
flang/test/semantics/label07.f90
flang/test/semantics/label08.f90
flang/test/semantics/label09.f90
flang/test/semantics/label10.f90
flang/test/semantics/test_any.sh [new file with mode: 0755]

index 5f5af00..616c6d3 100644 (file)
@@ -35,19 +35,6 @@ std::ostream &operator<<(std::ostream &o, const MessageFixedText &t) {
 
 MessageFormattedText::MessageFormattedText(MessageFixedText text, ...)
   : isFatal_{text.isFatal()} {
-  va_list ap;
-  va_start(ap, text);
-  SetMessageFormattedText(text, ap);
-  va_end(ap);
-}
-
-MessageFormattedText::MessageFormattedText(MessageFixedText text, va_list ap)
-  : isFatal_{text.isFatal()} {
-  SetMessageFormattedText(text, ap);
-}
-
-void MessageFormattedText::SetMessageFormattedText(MessageFixedText text,
-                                                  va_list ap) {
   const char *p{text.text().begin()};
   std::string asString;
   if (*text.text().end() != '\0') {
@@ -56,7 +43,10 @@ void MessageFormattedText::SetMessageFormattedText(MessageFixedText text,
     p = asString.data();
   }
   char buffer[256];
+  va_list ap;
+  va_start(ap, text);
   vsnprintf(buffer, sizeof buffer, p, ap);
+  va_end(ap);
   string_ = buffer;
 }
 
index da47982..6368e28 100644 (file)
@@ -68,7 +68,6 @@ constexpr MessageFixedText operator""_err_en_US(
 class MessageFormattedText {
 public:
   MessageFormattedText(MessageFixedText, ...);
-  MessageFormattedText(MessageFixedText, va_list);
   MessageFormattedText(const MessageFormattedText &) = default;
   MessageFormattedText(MessageFormattedText &&) = default;
   MessageFormattedText &operator=(const MessageFormattedText &) = default;
@@ -78,7 +77,6 @@ public:
   std::string MoveString() { return std::move(string_); }
 
 private:
-  void SetMessageFormattedText(MessageFixedText, va_list);
   std::string string_;
   bool isFatal_{false};
 };
index c56f306..17d46b2 100644 (file)
@@ -1,4 +1,3 @@
-/* -*- mode: c++; c-basic-offset: 2 -*- */
 // Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // limitations under the License.
 
 #include "resolve-labels.h"
+#include "../common/enum-set.h"
 #include "../parser/message.h"
 #include "../parser/parse-tree-visitor.h"
+#include <cassert>
+#include <cctype>
 #include <cstdarg>
 #include <iostream>
-#include <cctype>
-#include <cassert>
 
 namespace {
 
 using namespace Fortran;
 using namespace parser::literals;
-using ParseTree_t = parser::Program;
-using CookedSource_t = parser::CookedSource;
-using Index_t = parser::CharBlock;
-using IndexList = std::vector<std::pair<Index_t, Index_t>>;
-using Scope_t = unsigned;
-using LblStmt_t = std::tuple<Scope_t, Index_t, unsigned>;
-using ArcTrgt_t = std::map<parser::Label, LblStmt_t>;
-using ArcBase_t = std::vector<std::tuple<parser::Label, Scope_t, Index_t>>;
-
-const bool StrictF18 = false; // FIXME - make a command-line option
-
-const unsigned DO_TERM_FLAG = 1u;
-const unsigned BRANCH_TARGET_FLAG = 2u;
-const unsigned FORMAT_STMT_FLAG = 4u;
-
-// convenient package for error reporting
-struct ErrorHandler {
-public:
-  explicit ErrorHandler(const parser::CookedSource& CookedSource)
-    : cookedSource{CookedSource}, messages{parser::Messages()} {}
-  ~ErrorHandler() = default;
-  ErrorHandler(ErrorHandler&&) = default;
-  ErrorHandler() = delete;
-  ErrorHandler(const ErrorHandler&) = delete;
-  ErrorHandler& operator=(const ErrorHandler&) = delete;
-
-  parser::Message& Report(const parser::CharBlock& CB,
-                         const parser::MessageFixedText& Fixed, ...) {
-    va_list ap;
-    va_start(ap, Fixed);
-    parser::MessageFormattedText Msg{Fixed, ap};
-    va_end(ap);
-    return messages.Say(parser::Message{CB, Msg});
-  }
-
-  const parser::CookedSource& cookedSource;
-  parser::Messages messages;
-};
+
+ENUM_CLASS(TargetStatementEnum, Do, Branch, Format)
+using TargetStmtType = common::EnumSet<TargetStatementEnum, 8>;
+
+using IndexList = std::vector<std::pair<parser::CharBlock, parser::CharBlock>>;
+using ScopeProxy = unsigned;
+using LabelStmtInfo = std::tuple<ScopeProxy, parser::CharBlock, TargetStmtType>;
+using TargetStmtMap = std::map<parser::Label, LabelStmtInfo>;
+using SourceStmtList =
+    std::vector<std::tuple<parser::Label, ScopeProxy, parser::CharBlock>>;
+using ErrorHandler = parser::Messages;
+
+const bool isStrictF18{false};  // FIXME - make a command-line option
+
+inline bool HasScope(ScopeProxy scope) { return scope != ScopeProxy{0u}; }
+
+inline bool HasNoScope(ScopeProxy scope) { return !HasScope(scope); }
+
+parser::Message &Report(ErrorHandler &eh, const parser::CharBlock &ip,
+    parser::MessageFormattedText &&msg) {
+  return eh.Say(parser::Message{ip, msg});
+}
+
+inline bool HasNoErrors(const ErrorHandler &eh) { return !eh.AnyFatalError(); }
 
 /// \brief Is this a legal DO terminator?
 /// Pattern match dependent on the standard we're enforcing
 /// F18:R1131 (must be CONTINUE or END DO)
-template<typename A> constexpr bool IsLegalDoTerm(const parser::Statement<A>&) {
-  using EDS = parser::EndDoStmt;
-  return std::disjunction<std::is_same<A,common::Indirection<EDS>>,
-                         std::is_same<A,EDS>>::value;
+template<typename A>
+constexpr bool IsLegalDoTerm(const parser::Statement<A> &) {
+  return std::disjunction_v<
+      std::is_same<A, common::Indirection<parser::EndDoStmt>>,
+      std::is_same<A, parser::EndDoStmt>>;
 }
-template<> constexpr bool IsLegalDoTerm(const parser::Statement<parser::
-                                       ActionStmt>& A) {
-  if (std::get_if<parser::ContinueStmt>(&A.statement.u)) {
-    // See F08:C816 
+template<>
+constexpr bool IsLegalDoTerm(
+    const parser::Statement<parser::ActionStmt> &actionStmt) {
+  if (std::holds_alternative<parser::ContinueStmt>(actionStmt.statement.u)) {
+    // See F08:C816
     return true;
-  }
-  if (StrictF18)
+  } else if (isStrictF18) {
     return false;
-  // Applies in F08 and earlier
-  const auto* P{&A.statement.u};
-  return !(std::get_if<common::Indirection<parser::ArithmeticIfStmt>>(P) ||
-          std::get_if<common::Indirection<parser::CycleStmt>>(P) ||
-          std::get_if<common::Indirection<parser::ExitStmt>>(P) ||
-          std::get_if<common::Indirection<parser::StopStmt>>(P) ||
-          std::get_if<common::Indirection<parser::GotoStmt>>(P) ||
-          std::get_if<common::Indirection<parser::ReturnStmt>>(P));
+  } else {
+    // Applies in F08 and earlier
+    return !(
+        std::holds_alternative<common::Indirection<parser::ArithmeticIfStmt>>(
+            actionStmt.statement.u) ||
+        std::holds_alternative<common::Indirection<parser::CycleStmt>>(
+            actionStmt.statement.u) ||
+        std::holds_alternative<common::Indirection<parser::ExitStmt>>(
+            actionStmt.statement.u) ||
+        std::holds_alternative<common::Indirection<parser::StopStmt>>(
+            actionStmt.statement.u) ||
+        std::holds_alternative<common::Indirection<parser::GotoStmt>>(
+            actionStmt.statement.u) ||
+        std::holds_alternative<common::Indirection<parser::ReturnStmt>>(
+            actionStmt.statement.u));
+  }
 }
 
 /// \brief Is this a FORMAT stmt?
 /// Pattern match for FORMAT statement
-template<typename A> constexpr bool IsFormat(const parser::Statement<A>&) {
-  return std::is_same<A,common::Indirection<parser::FormatStmt>>::value;
+template<typename A> constexpr bool IsFormat(const parser::Statement<A> &) {
+  return std::is_same_v<A, common::Indirection<parser::FormatStmt>>;
 }
 
 /// \brief Is this a legal branch target?
 /// Pattern match dependent on the standard we're enforcing
-template<typename A> constexpr bool IsLegalBranchTarget(const parser::
-                                                       Statement<A>&) {
-  using LDS = parser::LabelDoStmt;
-  using EDS = parser::EndDoStmt;
-  return std::disjunction<std::is_same<A,parser::AssociateStmt>,
-                         std::is_same<A,parser::EndAssociateStmt>,
-                         std::is_same<A,parser::IfThenStmt>,
-                         std::is_same<A,parser::EndIfStmt>,
-                         std::is_same<A,parser::SelectCaseStmt>,
-                         std::is_same<A,parser::EndSelectStmt>,
-                         std::is_same<A,parser::SelectRankStmt>,
-                         std::is_same<A,parser::SelectTypeStmt>,
-                         std::is_same<A,common::Indirection<LDS>>,
-                         std::is_same<A,parser::NonLabelDoStmt>,
-                         std::is_same<A,parser::EndDoStmt>,
-                         std::is_same<A,common::Indirection<EDS>>,
-                         std::is_same<A,parser::BlockStmt>,
-                         std::is_same<A,parser::EndBlockStmt>,
-                         std::is_same<A,parser::CriticalStmt>,
-                         std::is_same<A,parser::EndCriticalStmt>,
-                         std::is_same<A,parser::ForallConstructStmt>,
-                         std::is_same<A,parser::ForallStmt>,
-                         std::is_same<A,parser::WhereConstructStmt>,
-                         std::is_same<A,parser::EndFunctionStmt>,
-                         std::is_same<A,parser::EndMpSubprogramStmt>,
-                         std::is_same<A,parser::EndProgramStmt>,
-                         std::is_same<A,parser::EndSubroutineStmt>>::value;
+template<typename A>
+constexpr bool IsLegalBranchTarget(const parser::Statement<A> &) {
+  return std::disjunction_v<std::is_same<A, parser::AssociateStmt>,
+      std::is_same<A, parser::EndAssociateStmt>,
+      std::is_same<A, parser::IfThenStmt>, std::is_same<A, parser::EndIfStmt>,
+      std::is_same<A, parser::SelectCaseStmt>,
+      std::is_same<A, parser::EndSelectStmt>,
+      std::is_same<A, parser::SelectRankStmt>,
+      std::is_same<A, parser::SelectTypeStmt>,
+      std::is_same<A, common::Indirection<parser::LabelDoStmt>>,
+      std::is_same<A, parser::NonLabelDoStmt>,
+      std::is_same<A, parser::EndDoStmt>,
+      std::is_same<A, common::Indirection<parser::EndDoStmt>>,
+      std::is_same<A, parser::BlockStmt>, std::is_same<A, parser::EndBlockStmt>,
+      std::is_same<A, parser::CriticalStmt>,
+      std::is_same<A, parser::EndCriticalStmt>,
+      std::is_same<A, parser::ForallConstructStmt>,
+      std::is_same<A, parser::ForallStmt>,
+      std::is_same<A, parser::WhereConstructStmt>,
+      std::is_same<A, parser::EndFunctionStmt>,
+      std::is_same<A, parser::EndMpSubprogramStmt>,
+      std::is_same<A, parser::EndProgramStmt>,
+      std::is_same<A, parser::EndSubroutineStmt>>;
 }
-template<> constexpr bool IsLegalBranchTarget(const parser::Statement<parser::
-                                             ActionStmt>& A) {
-  if (!StrictF18)
+template<>
+constexpr bool IsLegalBranchTarget(
+    const parser::Statement<parser::ActionStmt> &actionStmt) {
+  if (!isStrictF18) {
     return true;
-  // XXX: do we care to flag these as errors? If we want strict F18, these
-  // statements should not even be present
-  const auto* P{&A.statement.u};
-  return !(std::get_if<common::Indirection<parser::ArithmeticIfStmt>>(P) ||
-          std::get_if<common::Indirection<parser::AssignStmt>>(P) ||
-          std::get_if<common::Indirection<parser::AssignedGotoStmt>>(P) ||
-          std::get_if<common::Indirection<parser::PauseStmt>>(P));
+  } else {
+    // XXX: do we care to flag these as errors? If we want strict F18, these
+    // statements should not even be present
+    return !(
+        std::holds_alternative<common::Indirection<parser::ArithmeticIfStmt>>(
+            actionStmt.statement.u) ||
+        std::holds_alternative<common::Indirection<parser::AssignStmt>>(
+            actionStmt.statement.u) ||
+        std::holds_alternative<common::Indirection<parser::AssignedGotoStmt>>(
+            actionStmt.statement.u) ||
+        std::holds_alternative<common::Indirection<parser::PauseStmt>>(
+            actionStmt.statement.u));
+  }
 }
 
-
 template<typename A>
-constexpr unsigned ConsTrgtFlags(const parser::Statement<A>& S) {
-  unsigned Flags{0u};
-  if (IsLegalDoTerm(S))
-    Flags |= DO_TERM_FLAG;
-  if (IsLegalBranchTarget(S))
-    Flags |= BRANCH_TARGET_FLAG;
-  if (IsFormat(S))
-    Flags |= FORMAT_STMT_FLAG;
-  return Flags;
+constexpr TargetStmtType ConsTrgtFlags(const parser::Statement<A> &statement) {
+  TargetStmtType targetStmtType{};
+  if (IsLegalDoTerm(statement)) {
+    targetStmtType.set(TargetStatementEnum::Do);
+  }
+  if (IsLegalBranchTarget(statement)) {
+    targetStmtType.set(TargetStatementEnum::Branch);
+  }
+  if (IsFormat(statement)) {
+    targetStmtType.set(TargetStatementEnum::Format);
+  }
+  return targetStmtType;
 }
 
 /// \brief \p opt1 and \p opt2 must be either present and identical or absent
-/// \param opt1  an optional construct-name (opening statement)
-/// \param opt2  an optional construct-name (ending statement)
-template<typename A> inline bool BothEqOrNone(const A& opt1, const A& opt2) {
-  return (opt1.has_value() == opt2.has_value())
-    ? (opt1.has_value() 
-       ? (opt1.value().ToString() == opt2.value().ToString()) : true)
-    : false;
+/// \param a  an optional construct-name (opening statement)
+/// \param b  an optional construct-name (ending statement)
+template<typename A> inline bool BothEqOrNone(const A &a, const A &b) {
+  return (a.has_value() == b.has_value())
+      ? (a.has_value() ? (a.value().ToString() == b.value().ToString()) : true)
+      : false;
 }
 
-/// \brief \p opt1 must either be absent or identical to \p opt2
-/// \param opt1  an optional construct-name for an optional constraint
-/// \param opt2  an optional construct-name (opening statement)
-template<typename A> inline bool PresentAndEq(const A& opt1, const A& opt2) {
-  return (!opt1.has_value()) ||
-    (opt2.has_value() &&
-     (opt1.value().ToString() == opt2.value().ToString()));
+/// \brief \p opt1 must either be absent or identical to \p b
+/// \param a  an optional construct-name for an optional constraint
+/// \param b  an optional construct-name (opening statement)
+template<typename A> inline bool PresentAndEq(const A &a, const A &b) {
+  return (!a.has_value()) ||
+      (b.has_value() && (a.value().ToString() == b.value().ToString()));
 }
 
 /// \brief Iterates over parse tree, creates the analysis result
@@ -178,194 +176,272 @@ struct ParseTreeAnalyzer {
 public:
   struct UnitAnalysis {
   public:
-    ArcBase_t DoArcBases;      ///< bases of label-do-stmts
-    ArcBase_t FmtArcBases;     ///< bases of all other stmts with labels
-    ArcBase_t ArcBases;        ///< bases of all other stmts with labels
-    ArcTrgt_t ArcTrgts;        ///< unique map of labels to stmt info
-    std::vector<Scope_t> Scopes; ///< scope stack model
-
-    explicit UnitAnalysis() { Scopes.push_back(0); }
-    UnitAnalysis(UnitAnalysis&&) = default;
+    SourceStmtList doStmtSources_;  ///< bases of label-do-stmts
+    SourceStmtList
+        formatStmtSources_;  ///< bases of all other stmts with labels
+    SourceStmtList otherStmtSources_;  ///< bases of all other stmts with labels
+    TargetStmtMap targetStmts_;  ///< unique map of labels to stmt info
+    std::vector<ScopeProxy> scopeModel_;  ///< scope stack model
+
+    UnitAnalysis() { scopeModel_.push_back(0); }
+    UnitAnalysis(UnitAnalysis &&) = default;
     ~UnitAnalysis() = default;
-    UnitAnalysis(const UnitAnalysis&) = delete;
-    UnitAnalysis& operator=(const UnitAnalysis&) = delete;
-
-    const ArcBase_t& GetLabelDos() const { return DoArcBases; }
-    const ArcBase_t& GetDataXfers() const { return FmtArcBases; }
-    const ArcBase_t& GetBranches() const { return ArcBases; }
-    const ArcTrgt_t& GetLabels() const { return ArcTrgts; }
-    const std::vector<Scope_t>& GetScopes() const { return Scopes; }
+    UnitAnalysis(const UnitAnalysis &) = delete;
+    UnitAnalysis &operator=(const UnitAnalysis &) = delete;
+
+    const SourceStmtList &GetLabelDos() const { return doStmtSources_; }
+    const SourceStmtList &GetDataXfers() const { return formatStmtSources_; }
+    const SourceStmtList &GetBranches() const { return otherStmtSources_; }
+    const TargetStmtMap &GetLabels() const { return targetStmts_; }
+    const std::vector<ScopeProxy> &GetScopes() const { return scopeModel_; }
   };
 
-  explicit ParseTreeAnalyzer(const parser::CookedSource& Src) : EH{Src} {}
+  ParseTreeAnalyzer() {}
   ~ParseTreeAnalyzer() = default;
-  ParseTreeAnalyzer(ParseTreeAnalyzer&&) = default;
-  ParseTreeAnalyzer() = delete;
-  ParseTreeAnalyzer(const ParseTreeAnalyzer&) = delete;
-  ParseTreeAnalyzer& operator=(const ParseTreeAnalyzer&) = delete;
+  ParseTreeAnalyzer(ParseTreeAnalyzer &&) = default;
+  ParseTreeAnalyzer(const ParseTreeAnalyzer &) = delete;
+  ParseTreeAnalyzer &operator=(const ParseTreeAnalyzer &) = delete;
 
   // Default Pre() and Post()
-  template<typename A> constexpr bool Pre(const A&) { return true; }
-  template<typename A> constexpr void Post(const A&) {}
+  template<typename A> constexpr bool Pre(const A &) { return true; }
+  template<typename A> constexpr void Post(const A &) {}
 
   // Specializations of Pre() and Post()
 
   /// \brief Generic handling of all statements
-  template<typename A> bool Pre(const parser::Statement<A>Stmt) {
-    Index = Stmt.source;
+  template<typename A> bool Pre(const parser::Statement<A> &Stmt) {
+    currentPosition_ = Stmt.source;
     if (Stmt.label.has_value())
       AddTrgt(Stmt.label.value(), ConsTrgtFlags(Stmt));
     return true;
   }
 
   //  Inclusive scopes (see 11.1.1)
-  bool Pre(const parser::ProgramUnit&) { return PushNewScope(); }
-  bool Pre(const parser::AssociateConstruct& A) { return PushName(A); }
-  bool Pre(const parser::BlockConstruct& Blk) { return PushName(Blk); }
-  bool Pre(const parser::ChangeTeamConstruct& Ctm) { return PushName(Ctm); }
-  bool Pre(const parser::CriticalConstruct& Crit) { return PushName(Crit); }
-  bool Pre(const parser::DoConstruct& Do) { return PushName(Do); }
-  bool Pre(const parser::IfConstruct& If) { return PushName(If); }
-  bool Pre(const parser::IfConstruct::ElseIfBlock&) { return SwScope(); }
-  bool Pre(const parser::IfConstruct::ElseBlock&) { return SwScope(); }
-  bool Pre(const parser::CaseConstruct& Case) { return PushName(Case); }
-  bool Pre(const parser::CaseConstruct::Case&) { return SwScope(); }
-  bool Pre(const parser::SelectRankConstruct& SRk) { return PushName(SRk); }
-  bool Pre(const parser::SelectRankConstruct::RankCase&) { return SwScope(); }
-  bool Pre(const parser::SelectTypeConstruct& STy) { return PushName(STy); }
-  bool Pre(const parser::SelectTypeConstruct::TypeCase&) { return SwScope(); }
-  bool Pre(const parser::WhereConstruct& W) { return PushNonBlockName(W); }
-  bool Pre(const parser::ForallConstruct& F) { return PushNonBlockName(F); }
-
-  void Post(const parser::ProgramUnit&) { PopScope(); }
-  void Post(const parser::AssociateConstruct& A) { PopName(A); }
-  void Post(const parser::BlockConstruct& Blk) { PopName(Blk); }
-  void Post(const parser::ChangeTeamConstruct& Ctm) { PopName(Ctm); }
-  void Post(const parser::CriticalConstruct& Crit) { PopName(Crit); }
-  void Post(const parser::DoConstruct& Do) { PopName(Do); }
-  void Post(const parser::IfConstruct& If) { PopName(If); }
-  void Post(const parser::CaseConstruct& Case) { PopName(Case); }
-  void Post(const parser::SelectRankConstruct& SelRk) { PopName(SelRk); }
-  void Post(const parser::SelectTypeConstruct& SelTy) { PopName(SelTy); }
+  bool Pre(const parser::ProgramUnit &) { return PushNewScope(); }
+  bool Pre(const parser::AssociateConstruct &associateConstruct) {
+    return PushName(associateConstruct);
+  }
+  bool Pre(const parser::BlockConstruct &blockConstruct) {
+    return PushName(blockConstruct);
+  }
+  bool Pre(const parser::ChangeTeamConstruct &changeTeamConstruct) {
+    return PushName(changeTeamConstruct);
+  }
+  bool Pre(const parser::CriticalConstruct &criticalConstruct) {
+    return PushName(criticalConstruct);
+  }
+  bool Pre(const parser::DoConstruct &doConstruct) {
+    return PushName(doConstruct);
+  }
+  bool Pre(const parser::IfConstruct &ifConstruct) {
+    return PushName(ifConstruct);
+  }
+  bool Pre(const parser::IfConstruct::ElseIfBlock &) { return SwScope(); }
+  bool Pre(const parser::IfConstruct::ElseBlock &) { return SwScope(); }
+  bool Pre(const parser::CaseConstruct &caseConstruct) {
+    return PushName(caseConstruct);
+  }
+  bool Pre(const parser::CaseConstruct::Case &) { return SwScope(); }
+  bool Pre(const parser::SelectRankConstruct &selectRankConstruct) {
+    return PushName(selectRankConstruct);
+  }
+  bool Pre(const parser::SelectRankConstruct::RankCase &) { return SwScope(); }
+  bool Pre(const parser::SelectTypeConstruct &selectTypeConstruct) {
+    return PushName(selectTypeConstruct);
+  }
+  bool Pre(const parser::SelectTypeConstruct::TypeCase &) { return SwScope(); }
+  bool Pre(const parser::WhereConstruct &whereConstruct) {
+    return PushNonBlockName(whereConstruct);
+  }
+  bool Pre(const parser::ForallConstruct &forallConstruct) {
+    return PushNonBlockName(forallConstruct);
+  }
+
+  void Post(const parser::ProgramUnit &) { PopScope(); }
+  void Post(const parser::AssociateConstruct &associateConstruct) {
+    PopName(associateConstruct);
+  }
+  void Post(const parser::BlockConstruct &blockConstruct) {
+    PopName(blockConstruct);
+  }
+  void Post(const parser::ChangeTeamConstruct &changeTeamConstruct) {
+    PopName(changeTeamConstruct);
+  }
+  void Post(const parser::CriticalConstruct &criticalConstruct) {
+    PopName(criticalConstruct);
+  }
+  void Post(const parser::DoConstruct &doConstruct) { PopName(doConstruct); }
+  void Post(const parser::IfConstruct &ifConstruct) { PopName(ifConstruct); }
+  void Post(const parser::CaseConstruct &caseConstruct) {
+    PopName(caseConstruct);
+  }
+  void Post(const parser::SelectRankConstruct &selectRankConstruct) {
+    PopName(selectRankConstruct);
+  }
+  void Post(const parser::SelectTypeConstruct &selectTypeConstruct) {
+    PopName(selectTypeConstruct);
+  }
 
   //  Named constructs without block scope
-  void Post(const parser::WhereConstruct& W) { PopNonBlockConstructName(W); }
-  void Post(const parser::ForallConstruct& F) { PopNonBlockConstructName(F); }
+  void Post(const parser::WhereConstruct &whereConstruct) {
+    PopNonBlockConstructName(whereConstruct);
+  }
+  void Post(const parser::ForallConstruct &forallConstruct) {
+    PopNonBlockConstructName(forallConstruct);
+  }
 
   //  Statements with label references
-  void Post(const parser::LabelDoStmt& Do) { AddDoBase(std::get<1>(Do.t)); }
-  void Post(const parser::GotoStmt& Goto) { AddBase(Goto.v); }
-  void Post(const parser::ComputedGotoStmt& C) { AddBase(std::get<0>(C.t)); }
-  void Post(const parser::ArithmeticIfStmt& AIf) {
-    AddBase(std::get<1>(AIf.t));
-    AddBase(std::get<2>(AIf.t));
-    AddBase(std::get<3>(AIf.t));
-  }
-  void Post(const parser::AssignStmt& Assn) { AddBase(std::get<0>(Assn.t)); }
-  void Post(const parser::AssignedGotoStmt& A) { AddBase(std::get<1>(A.t)); }
-  void Post(const parser::AltReturnSpec& ARS) { AddBase(ARS.v); }
-
-  void Post(const parser::ErrLabel& Err) { AddBase(Err.v); }
-  void Post(const parser::EndLabel& End) { AddBase(End.v); }
-  void Post(const parser::EorLabel& Eor) { AddBase(Eor.v); }
-  void Post(const parser::Format& Fmt) {
+  void Post(const parser::LabelDoStmt &labelDoStmt) {
+    AddDoBase(std::get<parser::Label>(labelDoStmt.t));
+  }
+  void Post(const parser::GotoStmt &gotoStmt) { AddBase(gotoStmt.v); }
+  void Post(const parser::ComputedGotoStmt &computedGotoStmt) {
+    AddBase(std::get<std::list<parser::Label>>(computedGotoStmt.t));
+  }
+  void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
+    AddBase(std::get<1>(arithmeticIfStmt.t));
+    AddBase(std::get<2>(arithmeticIfStmt.t));
+    AddBase(std::get<3>(arithmeticIfStmt.t));
+  }
+  void Post(const parser::AssignStmt &assignStmt) {
+    AddBase(std::get<parser::Label>(assignStmt.t));
+  }
+  void Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
+    AddBase(std::get<std::list<parser::Label>>(assignedGotoStmt.t));
+  }
+  void Post(const parser::AltReturnSpec &altReturnSpec) {
+    AddBase(altReturnSpec.v);
+  }
+
+  void Post(const parser::ErrLabel &errLabel) { AddBase(errLabel.v); }
+  void Post(const parser::EndLabel &endLabel) { AddBase(endLabel.v); }
+  void Post(const parser::EorLabel &eorLabel) { AddBase(eorLabel.v); }
+  void Post(const parser::Format &format) {
     // BUG: the label is saved as an IntLiteralConstant rather than a Label
 #if 0
-    if (const auto* P{std::get_if<parser::Label>(&Fmt.u)})
+    if (const auto *P{std::get_if<parser::Label>(&format.u)}) {
       AddFmtBase(*P);
+    }
 #else
     // FIXME: this is wrong, but extracts the label's value
-    if (const auto* P{std::get_if<0>(&Fmt.u)}) {
-      parser::Label L{std::get<0>(std::get<parser::IntLiteralConstant>(std::get<parser::LiteralConstant>((*P->thing).u).u).t)};
-      AddFmtBase(L);
+    if (const auto *P{std::get_if<0>(&format.u)}) {
+      AddFmtBase(parser::Label{std::get<0>(std::get<parser::IntLiteralConstant>(
+          std::get<parser::LiteralConstant>((*P->thing).u).u)
+                                               .t)});
     }
 #endif
   }
-  void Post(const parser::CycleStmt& Cycle) {
-    if (Cycle.v.has_value())
-      CheckLabelContext("CYCLE", Cycle.v.value().ToString());
+  void Post(const parser::CycleStmt &cycleStmt) {
+    if (cycleStmt.v.has_value()) {
+      CheckLabelContext("CYCLE", cycleStmt.v.value().ToString());
+    }
   }
-  void Post(const parser::ExitStmt& Exit) {
-    if (Exit.v.has_value())
-      CheckLabelContext("EXIT", Exit.v.value().ToString());
+  void Post(const parser::ExitStmt &exitStmt) {
+    if (exitStmt.v.has_value()) {
+      CheckLabelContext("EXIT", exitStmt.v.value().ToString());
+    }
   }
 
   // Getters for the results
-  const std::vector<UnitAnalysis>& GetProgramUnits() const { return PUnits; }
-  ErrorHandler& GetEH() { return EH; }
-  bool HasNoErrors() const { return NoErrors; }
+  const std::vector<UnitAnalysis> &GetProgramUnits() const {
+    return programUnits_;
+  }
+  ErrorHandler &GetEH() { return eh; }
 
 private:
   bool PushScope() {
-    PUnits.back().Scopes.push_back(CurrScope);
-    CurrScope = PUnits.back().Scopes.size() - 1;
+    programUnits_.back().scopeModel_.push_back(currentScope_);
+    currentScope_ = programUnits_.back().scopeModel_.size() - 1;
     return true;
   }
   bool PushNewScope() {
-    PUnits.emplace_back(UnitAnalysis{});
+    programUnits_.emplace_back(UnitAnalysis{});
+    return PushScope();
+  }
+  void PopScope() {
+    currentScope_ = programUnits_.back().scopeModel_[currentScope_];
+  }
+  bool SwScope() {
+    PopScope();
     return PushScope();
   }
-  void PopScope() { CurrScope = PUnits.back().Scopes[CurrScope]; }
-  bool SwScope() { PopScope(); return PushScope(); }
 
-  template<typename A> bool PushName(const A& X) {
-    const auto& OptName{std::get<0>(std::get<0>(X.t).statement.t)};
-    if (OptName.has_value())
-      Names.push_back(OptName.value().ToString());
+  template<typename A> bool PushName(const A &a) {
+    const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)};
+    if (optionalName.has_value()) {
+      constructNames_.push_back(optionalName.value().ToString());
+    }
     return PushScope();
   }
-  bool PushName(const parser::BlockConstruct& Blk) {
-    const auto& OptName{std::get<0>(Blk.t).statement.v};
-    if (OptName.has_value())
-      Names.push_back(OptName.value().ToString());
+  bool PushName(const parser::BlockConstruct &blockConstruct) {
+    const auto &optionalName{
+        std::get<parser::Statement<parser::BlockStmt>>(blockConstruct.t)
+            .statement.v};
+    if (optionalName.has_value()) {
+      constructNames_.push_back(optionalName.value().ToString());
+    }
     return PushScope();
   }
-  template<typename A> bool PushNonBlockName(const A& X) {
-    const auto& OptName{std::get<0>(std::get<0>(X.t).statement.t)};
-    if (OptName.has_value())
-      Names.push_back(OptName.value().ToString());
+  template<typename A> bool PushNonBlockName(const A &a) {
+    const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)};
+    if (optionalName.has_value()) {
+      constructNames_.push_back(optionalName.value().ToString());
+    }
     return true;
   }
 
-  template<typename A> void PopNonBlockConstructName(const A& X) {
-    CheckName(X); SelectivePopBack(X);
+  template<typename A> void PopNonBlockConstructName(const A &a) {
+    CheckName(a);
+    SelectivePopBack(a);
   }
 
-  template<typename A> void SelectivePopBack(const A& X) {
-    const auto& OptName{std::get<0>(std::get<0>(X.t).statement.t)};
-    if (OptName.has_value())
-      Names.pop_back();
+  template<typename A> void SelectivePopBack(const A &a) {
+    const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)};
+    if (optionalName.has_value()) {
+      constructNames_.pop_back();
+    }
   }
-  void SelectivePopBack(const parser::BlockConstruct& Blk) {
-    const auto& OptName{std::get<0>(Blk.t).statement.v};
-    if (OptName.has_value())
-      Names.pop_back();
+  void SelectivePopBack(const parser::BlockConstruct &blockConstruct) {
+    const auto &optionalName{
+        std::get<parser::Statement<parser::BlockStmt>>(blockConstruct.t)
+            .statement.v};
+    if (optionalName.has_value()) {
+      constructNames_.pop_back();
+    }
   }
 
   /// \brief Check constraints and pop scope
-  template<typename A> void PopName(const A& V) {
-    CheckName(V); PopScope(); SelectivePopBack(V);
+  template<typename A> void PopName(const A &a) {
+    CheckName(a);
+    PopScope();
+    SelectivePopBack(a);
   }
 
   /// \brief Check <i>case-construct-name</i> and pop the scope
   /// Constraint C1144 - opening and ending name must match if present, and
   /// <i>case-stmt</i> must either match or be unnamed
-  void PopName(const parser::CaseConstruct& Case) {
-    CheckName(Case, "CASE"); PopScope(); SelectivePopBack(Case);
+  void PopName(const parser::CaseConstruct &caseConstruct) {
+    CheckName(caseConstruct, "CASE");
+    PopScope();
+    SelectivePopBack(caseConstruct);
   }
 
   /// \brief Check <i>select-rank-construct-name</i> and pop the scope
   /// Constraints C1154, C1156 - opening and ending name must match if present,
   /// and <i>select-rank-case-stmt</i> must either match or be unnamed
-  void PopName(const parser::SelectRankConstruct& SelRk) {
-    CheckName(SelRk, "RANK","RANK "); PopScope(); SelectivePopBack(SelRk);
+  void PopName(const parser::SelectRankConstruct &selectRankConstruct) {
+    CheckName(selectRankConstruct, "RANK", "RANK ");
+    PopScope();
+    SelectivePopBack(selectRankConstruct);
   }
 
   /// \brief Check <i>select-construct-name</i> and pop the scope
   /// Constraint C1165 - opening and ending name must match if present, and
   /// <i>type-guard-stmt</i> must either match or be unnamed
-  void PopName(const parser::SelectTypeConstruct& SelTy) {
-    CheckName(SelTy, "TYPE", "TYPE "); PopScope(); SelectivePopBack(SelTy);
+  void PopName(const parser::SelectTypeConstruct &selectTypeConstruct) {
+    CheckName(selectTypeConstruct, "TYPE", "TYPE ");
+    PopScope();
+    SelectivePopBack(selectTypeConstruct);
   }
 
   // -----------------------------------------------
@@ -373,103 +449,171 @@ private:
   // Case 1: construct name must be absent or specified & identical on END
 
   /// \brief Check <i>associate-construct-name</i>, constraint C1106
-  void CheckName(const parser::AssociateConstruct& A) { ChkNm(A, "ASSOCIATE"); }
+  void CheckName(const parser::AssociateConstruct &associateConstruct) {
+    CheckName("ASSOCIATE", associateConstruct);
+  }
   /// \brief Check <i>critical-construct-name</i>, constraint C1117
-  void CheckName(const parser::CriticalConstruct& C) { ChkNm(C, "CRITICAL"); }
+  void CheckName(const parser::CriticalConstruct &criticalConstruct) {
+    CheckName("CRITICAL", criticalConstruct);
+  }
   /// \brief Check <i>do-construct-name</i>, constraint C1131
-  void CheckName(const parser::DoConstruct& Do) { ChkNm(Do, "DO"); }
+  void CheckName(const parser::DoConstruct &doConstruct) {
+    CheckName("DO", doConstruct);
+  }
   /// \brief Check <i>forall-construct-name</i>, constraint C1035
-  void CheckName(const parser::ForallConstruct& F) { ChkNm(F, "FORALL"); }
+  void CheckName(const parser::ForallConstruct &forallConstruct) {
+    CheckName("FORALL", forallConstruct);
+  }
   /// \brief Common code for ASSOCIATE, CRITICAL, DO, and FORALL
-  template<typename A> void ChkNm(const A& V, const char *const Con) {
-    if (!BothEqOrNone(std::get<0>(std::get<0>(V.t).statement.t),
-                     std::get<2>(V.t).statement.v)) {
-      EH.Report(Index, "%s construct name mismatch"_err_en_US, Con);
-      NoErrors = false;
+  template<typename A>
+  void CheckName(const char *const constructTag, const A &a) {
+    if (!BothEqOrNone(
+            std::get<std::optional<parser::Name>>(std::get<0>(a.t).statement.t),
+            std::get<2>(a.t).statement.v)) {
+      Report(eh, currentPosition_,
+          parser::MessageFormattedText{
+              "%s construct name mismatch"_err_en_US, constructTag});
     }
   }
-  
+
   /// \brief Check <i>do-construct-name</i>, constraint C1109
-  void CheckName(const parser::BlockConstruct& B) {
-    if (!BothEqOrNone(std::get<0>(B.t).statement.v,
-                     std::get<3>(B.t).statement.v)) {
-      EH.Report(Index, "BLOCK construct name mismatch"_err_en_US);
-      NoErrors = false;
+  void CheckName(const parser::BlockConstruct &blockConstruct) {
+    if (!BothEqOrNone(
+            std::get<parser::Statement<parser::BlockStmt>>(blockConstruct.t)
+                .statement.v,
+            std::get<parser::Statement<parser::EndBlockStmt>>(blockConstruct.t)
+                .statement.v)) {
+      Report(eh, currentPosition_,
+          parser::MessageFormattedText{
+              "BLOCK construct name mismatch"_err_en_US});
     }
   }
   /// \brief Check <i>team-cosntruct-name</i>, constraint C1112
-  void CheckName(const parser::ChangeTeamConstruct& C) {
-    if (!BothEqOrNone(std::get<0>(std::get<0>(C.t).statement.t),
-                     std::get<1>(std::get<2>(C.t).statement.t))) {
-      EH.Report(Index, "CHANGE TEAM construct name mismatch"_err_en_US);
-      NoErrors = false;
+  void CheckName(const parser::ChangeTeamConstruct &changeTeamConstruct) {
+    if (!BothEqOrNone(std::get<std::optional<parser::Name>>(
+                          std::get<parser::Statement<parser::ChangeTeamStmt>>(
+                              changeTeamConstruct.t)
+                              .statement.t),
+            std::get<std::optional<parser::Name>>(
+                std::get<parser::Statement<parser::EndChangeTeamStmt>>(
+                    changeTeamConstruct.t)
+                    .statement.t))) {
+      Report(eh, currentPosition_,
+          parser::MessageFormattedText{
+              "CHANGE TEAM construct name mismatch"_err_en_US});
     }
   }
 
   // -----------------------------------------------
-  // Case 2: same as case 1, but subblock statement construct-names are 
+  // Case 2: same as case 1, but subblock statement construct-names are
   // optional but if they are specified their values must be identical
 
   /// \brief Check <i>if-construct-name</i>
   /// Constraint C1142 - opening and ending name must match if present, and
   /// <i>else-if-stmt</i> and <i>else-stmt</i> must either match or be unnamed
-  void CheckName(const parser::IfConstruct& If) {
-    const auto& Name{std::get<0>(std::get<0>(If.t).statement.t)};
-    if (!BothEqOrNone(Name, std::get<4>(If.t).statement.v)) {
-      EH.Report(Index, "IF construct name mismatch"_err_en_US);
-      NoErrors = false;
+  void CheckName(const parser::IfConstruct &ifConstruct) {
+    const auto &constructName{std::get<std::optional<parser::Name>>(
+        std::get<parser::Statement<parser::IfThenStmt>>(ifConstruct.t)
+            .statement.t)};
+    if (!BothEqOrNone(constructName,
+            std::get<parser::Statement<parser::EndIfStmt>>(ifConstruct.t)
+                .statement.v)) {
+      Report(eh, currentPosition_,
+          parser::MessageFormattedText{"IF construct name mismatch"_err_en_US});
     }
-    for (const auto& ElseIfBlock : std::get<2>(If.t)) {
-      const auto& E{std::get<0>(ElseIfBlock.t).statement.t};
-      if (!PresentAndEq(std::get<1>(E), Name)) {
-       EH.Report(Index, "ELSE IF statement name mismatch"_err_en_US);
-       NoErrors = false;
+    for (const auto &elseIfBlock :
+        std::get<std::list<parser::IfConstruct::ElseIfBlock>>(ifConstruct.t)) {
+      if (!PresentAndEq(
+              std::get<std::optional<parser::Name>>(
+                  std::get<parser::Statement<parser::ElseIfStmt>>(elseIfBlock.t)
+                      .statement.t),
+              constructName)) {
+        Report(eh, currentPosition_,
+            parser::MessageFormattedText{
+                "ELSE IF statement name mismatch"_err_en_US});
       }
     }
-    if (std::get<3>(If.t).has_value()) {
-      const auto& E{std::get<3>(If.t).value().t};
-      if (!PresentAndEq(std::get<0>(E).statement.v, Name)) {
-       EH.Report(Index, "ELSE statement name mismatch"_err_en_US);
-       NoErrors = false;
+    if (std::get<std::optional<parser::IfConstruct::ElseBlock>>(ifConstruct.t)
+            .has_value()) {
+      if (!PresentAndEq(
+              std::get<parser::Statement<parser::ElseStmt>>(
+                  std::get<std::optional<parser::IfConstruct::ElseBlock>>(
+                      ifConstruct.t)
+                      .value()
+                      .t)
+                  .statement.v,
+              constructName)) {
+        Report(eh, currentPosition_,
+            parser::MessageFormattedText{
+                "ELSE statement name mismatch"_err_en_US});
       }
     }
   }
   /// \brief Common code for SELECT CASE, SELECT RANK, and SELECT TYPE
-  template<typename A> void CheckName(const A& Case, const char *const Sel1,
-                                     const char *const Sel2 = "") {
-    const auto& Name{std::get<0>(std::get<0>(Case.t).statement.t)};
-    if (!BothEqOrNone(Name, std::get<2>(Case.t).statement.v)) {
-      EH.Report(Index, "SELECT %s construct name mismatch"_err_en_US, Sel1);
-      NoErrors = false;
+  template<typename A>
+  void CheckName(const A &a, const char *const selectTag,
+      const char *const selectSubTag = "") {
+    const auto &constructName{std::get<0>(std::get<0>(a.t).statement.t)};
+    if (!BothEqOrNone(constructName, std::get<2>(a.t).statement.v)) {
+      Report(eh, currentPosition_,
+          parser::MessageFormattedText{
+              "SELECT %s construct name mismatch"_err_en_US, selectTag});
     }
-    for (const auto& CS : std::get<1>(Case.t))
-      if (!PresentAndEq(std::get<1>(std::get<0>(CS.t).statement.t), Name)) {
-       EH.Report(Index, "%sCASE statement name mismatch"_err_en_US, Sel2);
-       NoErrors = false;
+    for (const auto &subpart : std::get<1>(a.t)) {
+      if (!PresentAndEq(std::get<std::optional<parser::Name>>(
+                            std::get<0>(subpart.t).statement.t),
+              constructName)) {
+        Report(eh, currentPosition_,
+            parser::MessageFormattedText{
+                "%sCASE statement name mismatch"_err_en_US, selectSubTag});
       }
+    }
   }
 
   /// \brief Check <i>where-construct-name</i>
   /// Constraint C1033 - opening and ending name must match if present, and
   /// <i>masked-elsewhere-stmt</i> and <i>elsewhere-stmt</i> either match
   /// or be unnamed
-  void CheckName(const parser::WhereConstruct& Where) {
-    const auto& Name{std::get<0>(std::get<0>(Where.t).statement.t)};
-    if (!BothEqOrNone(Name, std::get<4>(Where.t).statement.v)) {
-      EH.Report(Index, "WHERE construct name mismatch"_err_en_US);
-      NoErrors = false;
+  void CheckName(const parser::WhereConstruct &whereConstruct) {
+    const auto &constructName{std::get<std::optional<parser::Name>>(
+        std::get<parser::Statement<parser::WhereConstructStmt>>(
+            whereConstruct.t)
+            .statement.t)};
+    if (!BothEqOrNone(constructName,
+            std::get<parser::Statement<parser::EndWhereStmt>>(whereConstruct.t)
+                .statement.v)) {
+      Report(eh, currentPosition_,
+          parser::MessageFormattedText{
+              "WHERE construct name mismatch"_err_en_US});
     }
-    for (const auto& W : std::get<2>(Where.t))
-      if (!PresentAndEq(std::get<1>(std::get<0>(W.t).statement.t), Name)) {
-       EH.Report(Index,
-                 "ELSEWHERE (<mask>) statement name mismatch"_err_en_US);
-       NoErrors = false;
+    for (const auto &maskedElsewhere :
+        std::get<std::list<parser::WhereConstruct::MaskedElsewhere>>(
+            whereConstruct.t)) {
+      if (!PresentAndEq(
+              std::get<std::optional<parser::Name>>(
+                  std::get<parser::Statement<parser::MaskedElsewhereStmt>>(
+                      maskedElsewhere.t)
+                      .statement.t),
+              constructName)) {
+        Report(eh, currentPosition_,
+            parser::MessageFormattedText{
+                "ELSEWHERE (<mask>) statement name mismatch"_err_en_US});
       }
-    if (std::get<3>(Where.t).has_value()) {
-      const auto& E{std::get<3>(Where.t).value().t};
-      if (!PresentAndEq(std::get<0>(E).statement.v, Name)) {
-       EH.Report(Index, "ELSEWHERE statement name mismatch"_err_en_US);
-       NoErrors = false;
+    }
+    if (std::get<std::optional<parser::WhereConstruct::Elsewhere>>(
+            whereConstruct.t)
+            .has_value()) {
+      if (!PresentAndEq(
+              std::get<parser::Statement<parser::ElsewhereStmt>>(
+                  std::get<std::optional<parser::WhereConstruct::Elsewhere>>(
+                      whereConstruct.t)
+                      .value()
+                      .t)
+                  .statement.v,
+              constructName)) {
+        Report(eh, currentPosition_,
+            parser::MessageFormattedText{
+                "ELSEWHERE statement name mismatch"_err_en_US});
       }
     }
   }
@@ -477,341 +621,349 @@ private:
   /// \brief Check constraint <i>construct-name</i> in scope (C1134 and C1166)
   /// \param SStr  a string to specify the statement, \c CYCLE or \c EXIT
   /// \param Label the name used by the \c CYCLE or \c EXIT
-  template<typename A> void CheckLabelContext(const char* const SStr,
-                                             const A& Name) {
-    const auto E{Names.crend()};
-    const auto I{std::find(Names.crbegin(), E, Name)};
-    if (I != E)
-       return;
-    EH.Report(Index, "%s construct-name '%s' is not in scope"_err_en_US,
-             SStr, Name.c_str());
-    NoErrors = false;
+  template<typename A>
+  void CheckLabelContext(const char *const stmtString, const A &constructName) {
+    const auto I{std::find(
+        constructNames_.crbegin(), constructNames_.crend(), constructName)};
+    if (I == constructNames_.crend()) {
+      Report(eh, currentPosition_,
+          parser::MessageFormattedText{
+              "%s construct-name '%s' is not in scope"_err_en_US, stmtString,
+              constructName.c_str()});
+    }
   }
 
   /// \brief Check label range
   /// Constraint per section 6.2.5, paragraph 2
-  void LabelInRange(parser::Label Label) {
-    if ((Label < 1) || (Label > 99999)) {
+  void CheckLabelInRange(parser::Label label) {
+    if ((label < 1) || (label > 99999)) {
       // this is an error: labels must have a value 1 to 99999, inclusive
-      EH.Report(Index, "label '%lu' is out of range"_err_en_US, Label);
-      NoErrors = false;
+      Report(eh, currentPosition_,
+          parser::MessageFormattedText{
+              "label '%lu' is out of range"_err_en_US, label});
     }
   }
 
   /// \brief Add a labeled statement (label must be distinct)
   /// Constraint per section 6.2.5., paragraph 2
-  void AddTrgt(parser::Label Label, unsigned Flags) {
-    LabelInRange(Label);
-    const auto Pair{PUnits.back().ArcTrgts.insert({Label,
-           {CurrScope, Index, Flags}})};
-    if (!Pair.second) {
+  void AddTrgt(parser::Label label, TargetStmtType targetStmtType) {
+    CheckLabelInRange(label);
+    const auto pair{programUnits_.back().targetStmts_.insert(
+        {label, {currentScope_, currentPosition_, targetStmtType}})};
+    if (!pair.second) {
       // this is an error: labels must be pairwise distinct
-      EH.Report(Index, "label '%lu' is not distinct"_err_en_US, Label);
-      NoErrors = false;
+      Report(eh, currentPosition_,
+          parser::MessageFormattedText{
+              "label '%lu' is not distinct"_err_en_US, label});
     }
     // Don't enforce a limit to the cardinality of labels
   }
 
   /// \brief Reference to a labeled statement from a DO statement
-  void AddDoBase(parser::Label Label) {
-    LabelInRange(Label);
-    PUnits.back().DoArcBases.push_back({Label, CurrScope, Index});
+  void AddDoBase(parser::Label label) {
+    CheckLabelInRange(label);
+    programUnits_.back().doStmtSources_.push_back(
+        {label, currentScope_, currentPosition_});
   }
 
   /// \brief Reference to a labeled FORMAT statement
-  void AddFmtBase(parser::Label Label) {
-    LabelInRange(Label);
-    PUnits.back().FmtArcBases.push_back({Label, CurrScope, Index});
+  void AddFmtBase(parser::Label label) {
+    CheckLabelInRange(label);
+    programUnits_.back().formatStmtSources_.push_back(
+        {label, currentScope_, currentPosition_});
   }
 
   /// \brief Reference to a labeled statement as a (possible) branch
-  void AddBase(parser::Label Label) {
-    LabelInRange(Label);
-    PUnits.back().ArcBases.push_back({Label, CurrScope, Index});
+  void AddBase(parser::Label label) {
+    CheckLabelInRange(label);
+    programUnits_.back().otherStmtSources_.push_back(
+        {label, currentScope_, currentPosition_});
   }
 
   /// \brief References to labeled statements as (possible) branches
-  void AddBase(const std::list<parser::Label>& Labels) {
-    for (const parser::Label& L : Labels)
-      AddBase(L);
+  void AddBase(const std::list<parser::Label> &labels) {
+    for (const parser::Label &label : labels) {
+      AddBase(label);
+    }
   }
 
-  std::vector<UnitAnalysis> PUnits; ///< results for each program unit
-  ErrorHandler EH;             ///< error handler, collects messages
-  Index_t Index{nullptr};      ///< current location in parse tree
-  Scope_t CurrScope{0};                ///< current scope in the model
-  bool NoErrors{true};         ///< no semantic errors found?
-  std::vector<std::string> Names;
+  std::vector<UnitAnalysis> programUnits_;  ///< results for each program unit
+  ErrorHandler eh;  ///< error handler, collects messages
+  parser::CharBlock currentPosition_{
+      nullptr};  ///< current location in parse tree
+  ScopeProxy currentScope_{0};  ///< current scope in the model
+  std::vector<std::string> constructNames_;
 };
 
 template<typename A, typename B>
-bool InInclusiveScope(const A& Scopes, B Tl, const B& Hd) {
-  assert(Hd > 0);
-  assert(Tl > 0);
-  while (Tl && (Tl != Hd))
-    Tl = Scopes[Tl];
-  return Tl == Hd;
+bool InInclusiveScope(const A &scopes, B tail, const B &head) {
+  assert(HasScope(head));
+  assert(HasScope(tail));
+  while (HasScope(tail) && (tail != head)) {
+    tail = scopes[tail];
+  }
+  return tail == head;
 }
 
-ParseTreeAnalyzer LabelAnalysis(const ParseTree_t& ParseTree,
-                               const CookedSource_t& Source) {
-  ParseTreeAnalyzer Analysis{Source};
-  Walk(ParseTree, Analysis);
-  return Analysis;
+ParseTreeAnalyzer LabelAnalysis(const parser::Program &program) {
+  ParseTreeAnalyzer analysis;
+  Walk(program, analysis);
+  return analysis;
 }
 
 template<typename A, typename B>
-inline bool InBody(const A& CP, const B& Pair) {
-  assert(Pair.first.begin() < Pair.second.begin());
-  return (CP.begin() >= Pair.first.begin()) &&
-    (CP.begin() < Pair.second.end());
+inline bool InBody(const A &position, const B &pair) {
+  assert(pair.first.begin() < pair.second.begin());
+  return (position.begin() >= pair.first.begin()) &&
+      (position.begin() < pair.second.end());
 }
 
 template<typename A, typename B>
-LblStmt_t GetLabel(const A& Labels, const B& Label) {
-  const auto Iter{Labels.find(Label)};
-  if (Iter == Labels.cend())
-    return {0, 0, 0};
-  return Iter->second;
+LabelStmtInfo GetLabel(const A &labels, const B &label) {
+  const auto iter{labels.find(label)};
+  if (iter == labels.cend()) {
+    return {0u, nullptr, TargetStmtType{}};
+  } else {
+    return iter->second;
+  }
 }
 
 /// \brief Check branches into a <i>label-do-stmt</i>
 /// Relates to 11.1.7.3, loop activation
 template<typename A, typename B, typename C, typename D>
-inline bool CheckBranchesIntoDoBody(const A& Branches, const B& Labels,
-                                   const C& Scopes, const D& LoopBodies,
-                                   ErrorHandler& EH) {
-  auto NoErrors{true};
-  for (const auto Branch : Branches) {
-    const auto& Label{std::get<0>(Branch)};
-    auto Trgt{GetLabel(Labels, Label)};
-    if (!std::get<0>(Trgt))
-      continue;
-    const auto& FmIdx{std::get<2>(Branch)};
-    const auto& ToIdx{std::get<1>(Trgt)};
-    for (const auto Body : LoopBodies) {
-      if (!InBody(FmIdx, Body) && InBody(ToIdx, Body)) {
-       // this is an error: branch into labeled DO body
-       if (StrictF18) {
-         EH.Report(FmIdx, "branch into '%s' from another scope"_err_en_US,
-                   Body.first.ToString().c_str());
-         NoErrors = false;
-       } else {
-         EH.Report(FmIdx, "branch into '%s' from another scope"_en_US,
-                   Body.first.ToString().c_str());
-       }
+inline void CheckBranchesIntoDoBody(const A &branches, const B &labels,
+    const C &scopes, const D &loopBodies, ErrorHandler &eh) {
+  for (const auto branch : branches) {
+    const auto &label{std::get<parser::Label>(branch)};
+    auto branchTarget{GetLabel(labels, label)};
+    if (HasScope(std::get<ScopeProxy>(branchTarget))) {
+      const auto &fromPosition{std::get<parser::CharBlock>(branch)};
+      const auto &toPosition{std::get<parser::CharBlock>(branchTarget)};
+      for (const auto body : loopBodies) {
+        if (!InBody(fromPosition, body) && InBody(toPosition, body)) {
+          // this is an error: branch into labeled DO body
+          if (isStrictF18) {
+            Report(eh, fromPosition,
+                parser::MessageFormattedText{
+                    "branch into '%s' from another scope"_err_en_US,
+                    body.first.ToString().c_str()});
+          } else {
+            Report(eh, fromPosition,
+                parser::MessageFormattedText{
+                    "branch into '%s' from another scope"_en_US,
+                    body.first.ToString().c_str()});
+          }
+        }
       }
     }
   }
-  return NoErrors;
 }
 
 /// \brief Check that DO loops properly nest
 template<typename A>
-inline bool CheckDoNesting(const A& LoopBodies, ErrorHandler& EH) {
-  auto NoErrors{true};
-  auto E{LoopBodies.cend()};
-  for (auto I1{LoopBodies.cbegin()}; I1 != E; ++I1) {
-    const auto& L1{*I1};
-    for (auto I2{I1 + 1}; I2 != E; ++I2) {
-      const auto& L2{*I2};
-      assert(L1.first.begin() != L2.first.begin());
-      if ((L2.first.begin() < L1.second.end()) &&
-         (L1.second.begin() < L2.second.begin())) {
-       // this is an error: DOs do not properly nest
-       EH.Report(L2.second, "'%s' doesn't properly nest"_err_en_US,
-                 L1.first.ToString().c_str());
-       NoErrors = false;
+inline void CheckDoNesting(const A &loopBodies, ErrorHandler &eh) {
+  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};
+      assert(v1.first.begin() != v2.first.begin());
+      if ((v2.first.begin() < v1.second.end()) &&
+          (v1.second.begin() < v2.second.begin())) {
+        // this is an error: DOs do not properly nest
+        Report(eh, v2.second,
+            parser::MessageFormattedText{"'%s' doesn't properly nest"_err_en_US,
+                v1.first.ToString().c_str()});
       }
     }
   }
-  return NoErrors;
 }
 
 /// \brief Advance \p Pos past any label and whitespace
 /// Want the statement without its label for error messages, range checking
-template<typename A> inline A SkipLabel(const A& Pos) {
-  const long Max{Pos.end() - Pos.begin()};
-  if (Max && (Pos[0] >= '0') && (Pos[0] <= '9')) {
+template<typename A> inline A SkipLabel(const A &position) {
+  const long maxPosition{position.end() - position.begin()};
+  if (maxPosition && (position[0] >= '0') && (position[0] <= '9')) {
     long i{1l};
-    for (;(i < Max) && std::isdigit(Pos[i]); ++i);
-    for (;(i < Max) && std::isspace(Pos[i]); ++i);
-    return Index_t{Pos.begin() + i, Pos.end()};
+    for (; (i < maxPosition) && std::isdigit(position[i]); ++i)
+      ;
+    for (; (i < maxPosition) && std::isspace(position[i]); ++i)
+      ;
+    return parser::CharBlock{position.begin() + i, position.end()};
   }
-  return Pos;
+  return position;
 }
 
 /// \brief Check constraints on <i>label-do-stmt</i>
 template<typename A, typename B, typename C>
-inline bool CheckLabelDoConstraints(const A& Dos, const A& Branches,
-                                   const B& Labels, const C& Scopes,
-                                   ErrorHandler& EH) {
-  auto NoErrors{true};
-  IndexList LoopBodies;
-  for (const auto Stmt : Dos) {
-    const auto& Label{std::get<0>(Stmt)};
-    const auto& Scope{std::get<1>(Stmt)};
-    const auto& Index{std::get<2>(Stmt)};
-    auto Trgt{GetLabel(Labels, Label)};
-    if (!std::get<0>(Trgt)) {
+inline void CheckLabelDoConstraints(const A &dos, const A &branches,
+    const B &labels, const C &scopes, ErrorHandler &eh) {
+  IndexList loopBodies;
+  for (const auto stmt : dos) {
+    const auto &label{std::get<parser::Label>(stmt)};
+    const auto &scope{std::get<ScopeProxy>(stmt)};
+    const auto &position{std::get<parser::CharBlock>(stmt)};
+    auto doTarget{GetLabel(labels, label)};
+    if (HasNoScope(std::get<ScopeProxy>(doTarget))) {
       // C1133: this is an error: label not found
-      EH.Report(Index, "label '%lu' cannot be found"_err_en_US, Label);
-      NoErrors = false;
-      continue;
-    }
-    if (std::get<1>(Trgt).begin() < Index.begin()) {
+      Report(eh, position,
+          parser::MessageFormattedText{
+              "label '%lu' cannot be found"_err_en_US, label});
+    } else if (std::get<parser::CharBlock>(doTarget).begin() <
+        position.begin()) {
       // R1119: this is an error: label does not follow DO
-      EH.Report(Index, "label '%lu' doesn't lexically follow DO stmt"_err_en_US,
-               Label);
-      NoErrors = false;
-      continue;
-    }
-    if (!InInclusiveScope(Scopes, Scope, std::get<0>(Trgt))) {
+      Report(eh, position,
+          parser::MessageFormattedText{
+              "label '%lu' doesn't lexically follow DO stmt"_err_en_US, label});
+    } else if (!InInclusiveScope(
+                   scopes, scope, std::get<ScopeProxy>(doTarget))) {
       // C1133: this is an error: label is not in scope
-      if (StrictF18) {
-       EH.Report(Index, "label '%lu' is not in scope"_err_en_US, Label);
-       NoErrors = false;
+      if (isStrictF18) {
+        Report(eh, position,
+            parser::MessageFormattedText{
+                "label '%lu' is not in scope"_err_en_US, label});
       } else {
-       EH.Report(Index, "label '%lu' is not in scope"_en_US, Label);
+        Report(eh, position,
+            parser::MessageFormattedText{
+                "label '%lu' is not in scope"_en_US, label});
       }
-      continue;
-    }
-    if (!(std::get<2>(Trgt) & DO_TERM_FLAG)) {
-      EH.Report(std::get<Index_t>(Trgt),
-               "'%lu' invalid DO terminal statement"_err_en_US, Label);
-      NoErrors = false;
+    } else if ((std::get<TargetStmtType>(doTarget) &
+                   TargetStmtType{TargetStatementEnum::Do})
+                   .none()) {
+      Report(eh, std::get<parser::CharBlock>(doTarget),
+          parser::MessageFormattedText{
+              "'%lu' invalid DO terminal statement"_err_en_US, label});
+    } else {
+      // save the loop body marks
+      loopBodies.push_back(
+          {SkipLabel(position), std::get<parser::CharBlock>(doTarget)});
     }
-    // save the loop body marks
-    LoopBodies.push_back({SkipLabel(Index), std::get<1>(Trgt)});
-  }
-  
-  if (NoErrors) {
-    NoErrors =
-      // check that nothing jumps into the block
-      CheckBranchesIntoDoBody(Branches, Labels, Scopes, LoopBodies, EH) &
-      // check that do loops properly nest
-      CheckDoNesting(LoopBodies, EH);
-  }
-  return NoErrors;
+  }
+
+  // check that nothing jumps into the block
+  CheckBranchesIntoDoBody(branches, labels, scopes, loopBodies, eh);
+  // check that do loops properly nest
+  CheckDoNesting(loopBodies, eh);
 }
 
 /// \brief General constraint, control transfers within inclusive scope
 /// See, for example, section 6.2.5.
 template<typename A, typename B, typename C>
-bool CheckScopeConstraints(const A& Stmts, const B& Labels,
-                          const C& Scopes, ErrorHandler& EH) {
-  auto NoErrors{true};
-  for (const auto Stmt : Stmts) {
-    const auto& Label{std::get<0>(Stmt)};
-    const auto& Scope{std::get<1>(Stmt)};
-    const auto& Index{std::get<2>(Stmt)};
-    auto Trgt{GetLabel(Labels, Label)};
-    if (!std::get<0>(Trgt)) {
+void CheckScopeConstraints(
+    const A &stmts, const B &labels, const C &scopes, ErrorHandler &eh) {
+  for (const auto stmt : stmts) {
+    const auto &label{std::get<parser::Label>(stmt)};
+    const auto &scope{std::get<ScopeProxy>(stmt)};
+    const auto &position{std::get<parser::CharBlock>(stmt)};
+    auto target{GetLabel(labels, label)};
+    if (HasNoScope(std::get<ScopeProxy>(target))) {
       // this is an error: label not found
-      EH.Report(Index, "label '%lu' was not found"_err_en_US, Label);
-      NoErrors = false;
-      continue;
-    }
-    if (!InInclusiveScope(Scopes, Scope, std::get<0>(Trgt))) {
+      Report(eh, position,
+          parser::MessageFormattedText{
+              "label '%lu' was not found"_err_en_US, label});
+    } else if (!InInclusiveScope(scopes, scope, std::get<ScopeProxy>(target))) {
       // this is an error: label not in scope
-      if (StrictF18) {
-       EH.Report(Index, "label '%lu' is not in scope"_err_en_US, Label);
-       NoErrors = false;
+      if (isStrictF18) {
+        Report(eh, position,
+            parser::MessageFormattedText{
+                "label '%lu' is not in scope"_err_en_US, label});
       } else {
-       EH.Report(Index, "label '%lu' is not in scope"_en_US, Label);
+        Report(eh, position,
+            parser::MessageFormattedText{
+                "label '%lu' is not in scope"_en_US, label});
       }
     }
   }
-  return NoErrors;
 }
 
 template<typename A, typename B>
-inline bool CheckBranchTargetConstraints(const A& Stmts, const B& Labels,
-                                        ErrorHandler& EH) {
-  auto NoErrors{true};
-  for (const auto Stmt : Stmts) {
-    const auto& Label{std::get<0>(Stmt)};
-    auto Trgt{GetLabel(Labels, Label)};
-    if (!std::get<0>(Trgt))
-      continue;
-    if (!(std::get<2>(Trgt) & BRANCH_TARGET_FLAG)) {
-      // this is an error: label statement is not a branch target
-      EH.Report(std::get<Index_t>(Trgt), "'%lu' not a branch target"_err_en_US,
-               Label);
-      NoErrors = false;
+inline void CheckBranchTargetConstraints(
+    const A &stmts, const B &labels, ErrorHandler &eh) {
+  for (const auto stmt : stmts) {
+    const auto &label{std::get<parser::Label>(stmt)};
+    auto branchTarget{GetLabel(labels, label)};
+    if (HasScope(std::get<ScopeProxy>(branchTarget))) {
+      if ((std::get<TargetStmtType>(branchTarget) &
+              TargetStmtType{TargetStatementEnum::Branch})
+              .none()) {
+        // this is an error: label statement is not a branch target
+        Report(eh, std::get<parser::CharBlock>(branchTarget),
+            parser::MessageFormattedText{
+                "'%lu' not a branch target"_err_en_US, label});
+      }
     }
   }
-  return NoErrors;
 }
 
 /// \brief Validate the constraints on branches
 /// \param Analysis  the analysis result
 template<typename A, typename B, typename C>
-inline bool CheckBranchConstraints(const A& Branches, const B& Labels,
-                                  const C& Scopes, ErrorHandler& EH) {
-  return CheckScopeConstraints(Branches, Labels, Scopes, EH) &
-    CheckBranchTargetConstraints(Branches, Labels, EH);
+inline void CheckBranchConstraints(
+    const A &branches, const B &labels, const C &scopes, ErrorHandler &eh) {
+  CheckScopeConstraints(branches, labels, scopes, eh);
+  CheckBranchTargetConstraints(branches, labels, eh);
 }
 
 template<typename A, typename B>
-inline bool CheckDataXferTargetConstraints(const A& Stmts, const B& Labels,
-                                          ErrorHandler& EH) {
-  auto NoErrors{true};
-  for (const auto Stmt : Stmts) {
-    const auto& Label{std::get<0>(Stmt)};
-    auto Trgt{GetLabel(Labels, Label)};
-    if (!std::get<0>(Trgt))
-      continue;
-    if (!(std::get<2>(Trgt) & FORMAT_STMT_FLAG)) {
-      // this is an error: label not a FORMAT
-      EH.Report(std::get<Index_t>(Trgt), "'%lu' not a FORMAT"_err_en_US, Label);
-      NoErrors = false;
+inline void CheckDataXferTargetConstraints(
+    const A &stmts, const B &labels, ErrorHandler &eh) {
+  for (const auto stmt : stmts) {
+    const auto &label{std::get<parser::Label>(stmt)};
+    auto ioTarget{GetLabel(labels, label)};
+    if (HasScope(std::get<ScopeProxy>(ioTarget))) {
+      if ((std::get<TargetStmtType>(ioTarget) &
+              TargetStmtType{TargetStatementEnum::Format})
+              .none()) {
+        // this is an error: label not a FORMAT
+        Report(eh, std::get<parser::CharBlock>(ioTarget),
+            parser::MessageFormattedText{
+                "'%lu' not a FORMAT"_err_en_US, label});
+      }
     }
   }
-  return NoErrors;
 }
 
 /// \brief Validate that data transfers reference FORMATs in scope
 /// \param Analysis  the analysis result
 /// These label uses are disjoint from branching (control flow)
 template<typename A, typename B, typename C>
-inline bool CheckDataTransferConstraints(const A& DataXfers, const B& Labels,
-                                        const C& Scopes, ErrorHandler& EH) {
-  return CheckScopeConstraints(DataXfers, Labels, Scopes, EH) &
-    CheckDataXferTargetConstraints(DataXfers, Labels, EH);
+inline void CheckDataTransferConstraints(const A &dataTransfers,
+    const B &labels, const C &scopes, ErrorHandler &eh) {
+  CheckScopeConstraints(dataTransfers, labels, scopes, eh);
+  CheckDataXferTargetConstraints(dataTransfers, labels, eh);
 }
 
 /// \brief Validate label related constraints on the parse tree
-/// \param Analysis  the analysis results as run of the parse tree
-/// \param EH        the error handler
+/// \param analysis  the analysis results as run of the parse tree
+/// \param cookedSrc cooked source for error report
 /// \return true iff all the semantics checks passed
-bool CheckConstraints(ParseTreeAnalyzer&& Analysis) {
-  auto result{Analysis.HasNoErrors()};
-  auto& EH{Analysis.GetEH()};
-  for (const auto& A : Analysis.GetProgramUnits()) {
-    const auto& Dos{A.GetLabelDos()};
-    const auto& Branches{A.GetBranches()};
-    const auto& DataXfers{A.GetDataXfers()};
-    const auto& Labels{A.GetLabels()};
-    const auto& Scopes{A.GetScopes()};
-    result &= CheckLabelDoConstraints(Dos, Branches, Labels, Scopes, EH) &
-      CheckBranchConstraints(Branches, Labels, Scopes, EH) &
-      CheckDataTransferConstraints(DataXfers, Labels, Scopes, EH);
-  }
-  if (!EH.messages.empty())
-    EH.messages.Emit(std::cerr, EH.cookedSource);
-  return result;
+bool CheckConstraints(ParseTreeAnalyzer &&parseTreeAnalysis,
+    const parser::CookedSource &cookedSource) {
+  auto &eh{parseTreeAnalysis.GetEH()};
+  for (const auto &programUnit : parseTreeAnalysis.GetProgramUnits()) {
+    const auto &dos{programUnit.GetLabelDos()};
+    const auto &branches{programUnit.GetBranches()};
+    const auto &labels{programUnit.GetLabels()};
+    const auto &scopes{programUnit.GetScopes()};
+    CheckLabelDoConstraints(dos, branches, labels, scopes, eh);
+    CheckBranchConstraints(branches, labels, scopes, eh);
+    const auto &dataTransfers{programUnit.GetDataXfers()};
+    CheckDataTransferConstraints(dataTransfers, labels, scopes, eh);
+  }
+  if (!eh.empty()) {
+    eh.Emit(std::cerr, cookedSource);
+  }
+  return HasNoErrors(eh);
 }
 
-} // <anonymous>
+}  // namespace
 
 namespace Fortran::semantics {
 
 /// \brief Check the semantics of LABELs in the program
 /// \return true iff the program's use of LABELs is semantically correct
-bool ValidateLabels(const parser::Program& ParseTree,
-                   const parser::CookedSource& Source) {
-  return CheckConstraints(LabelAnalysis(ParseTree, Source));
+bool ValidateLabels(
+    const parser::Program &program, const parser::CookedSource &cookedSource) {
+  return CheckConstraints(LabelAnalysis(program), cookedSource);
 }
 
-} // Fortran::semantics
+}  // namespace Fortran::semantics
index 6154af6..42c9a9d 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c++; c-basic-offset: 2 -*- */
 // Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 #ifndef FORTRAN_SEMANTICS_RESOLVE_LABELS_H_
 #define FORTRAN_SEMANTICS_RESOLVE_LABELS_H_
 
-namespace Fortran {
-namespace parser {
+namespace Fortran::parser {
 struct Program;
 class CookedSource;
-} // parser
+}  // namespace Fortran::parser
 
-namespace semantics {
+namespace Fortran::semantics {
 
 /// \brief Validate the labels in the program
 /// \param ParseTree  the parse tree
 /// \param Source     the cooked source
 /// \return true, iff the program's labels pass semantics checks
-bool ValidateLabels(const parser::Program &ParseTree,
-                   const parser::CookedSource &Source);
-} // semantics
-} // Fortran
+bool ValidateLabels(
+    const parser::Program &ParseTree, const parser::CookedSource &Source);
+}  // namespace Fortran::semantics
 
-#endif // FORTRAN_SEMANTICS_RESOLVE_LABELS_H_
-
-// Local Variables:
-// mode: C++
-// c-basic-offset: 2
-// End:
+#endif  // FORTRAN_SEMANTICS_RESOLVE_LABELS_H_
index 419e856..05866bb 100644 (file)
@@ -84,6 +84,10 @@ set(MODFILE_TESTS
   modfile11.f90
 )
 
+set(LABEL_TESTS
+  label*.[Ff]90
+)
+
 foreach(test ${ERROR_TESTS})
   add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_errors.sh ${test})
 endforeach()
@@ -95,3 +99,7 @@ endforeach()
 foreach(test ${MODFILE_TESTS})
   add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_modfile.sh ${test})
 endforeach()
+
+foreach(test ${LABEL_TESTS})
+  add_test(NAME ${test} COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test_any.sh ${test})
+endforeach()
index a964d0f..b243f61 100644 (file)
 ! See the License for the specific language governing permissions and
 ! limitations under the License.
 
-! RUN: f18 < %s | FileCheck %s
-! CHECK-NOT: 
+! RUN: ${F18} -funparse-with-symbols %s -o /dev/null 2>&1 | grep -v 'procedure conflicts' | ${FileCheck} %s
+! CHECK-NOT: error:[[:space:]]
+
+! FIXME: filter out the array/function syntax issues (procedure conflicts)
+! for now...
 
 ! these are the conformance tests
 ! define STRICT_F18 to eliminate tests of features not in F18
 ! define ARCHAIC_FORTRAN to add test of feature found in Fortran before F95
 
+
 subroutine sub00(a,b,n,m)
   real a(n)
   real :: b(m)
index d6580ad..5200ab9 100644 (file)
@@ -14,7 +14,7 @@
 
 ! negative test -- invalid labels, out of range
 
-! RUN: f18 < %s | FileCheck %s
+! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
 ! CHECK: label '0' is out of range
 ! CHECK: label '100000' is out of range
 ! CHECK: label '123456' is out of range
index 1d0d9dc..b5a470b 100644 (file)
@@ -14,7 +14,7 @@
 
 ! negative test -- invalid labels, out of range
 
-! RUN: f18 < %s | FileCheck %s
+! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
 ! CHECK: 'do 10 i = 1, m' doesn't properly nest
 ! CHECK: label '30' cannot be found
 ! CHECK: label '40' cannot be found
index 5666772..c8c65e8 100644 (file)
@@ -14,7 +14,7 @@
 
 ! negative test -- invalid labels, out of range
 
-! RUN: f18 < %s | FileCheck %s
+! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
 ! CHECK: branch into 'do 10 i = 1, m' from another scope
 ! CHECK: branch into 'do 20 j = 1, n' from another scope
 
index b15e781..947d2bf 100644 (file)
 
 ! negative test -- invalid labels, out of range
 
-! RUN: f18 < %s | FileCheck %s
+! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
 ! CHECK: label '50' was not found
 ! CHECK: label '55' is not in scope
-! CHECK: label '70' is not an action stmt
+! CHECK: '70' not a branch target
 
 subroutine sub00(a,b,n,m)
   real a(n,m)
index dc866bc..fb0aa7c 100644 (file)
 
 ! negative test -- invalid labels, out of range
 
-! RUN: f18 < %s | FileCheck %s
+! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
 ! CHECK: label '10' is not in scope
 ! CHECK: label '20' was not found
 ! CHECK: label '40' is not in scope
-! CHECK: label '50' is not in scope (FIXME is that correct?)
+! CHECK: label '50' is not in scope
 
 subroutine sub00(n)
   GOTO (10,20,30) n
index 1dd38d5..e9c8552 100644 (file)
 
 ! negative test -- invalid labels, out of range
 
-! RUN: f18 < %s | FileCheck %s
+! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
 ! CHECK: label '10' is not in scope
 ! CHECK: label '20' was not found
-! CHECK: label '30' is not an action stmt
+! CHECK: '30' not a branch target
 ! CHECK: label '60' was not found
 
 subroutine sub00(n,m)
index ea02f59..b86ab02 100644 (file)
 
 ! negative test -- invalid labels, out of range
 
-! RUN: f18 < %s | FileCheck %s
+! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
 ! CHECK: IF construct name mismatch
 ! CHECK: DO construct name mismatch
-! CHECK: CYCLE construct name mismatch
+! CHECK: CYCLE construct-name 'label3' is not in scope
 
 subroutine sub00(a,b,n,m)
   real a(n,m)
index 1a2221a..8535cff 100644 (file)
@@ -12,8 +12,8 @@
 ! See the License for the specific language governing permissions and
 ! limitations under the License.
 
-! RUN: f18 < %s | FileCheck %s
-! CHECK: 
+! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
+! CHECK: label '60' was not found
 
 subroutine s(a)
   real a(10)
index de9eb3a..a56377a 100644 (file)
@@ -12,8 +12,8 @@
 ! See the License for the specific language governing permissions and
 ! limitations under the License.
 
-! RUN: f18 < %s | FileCheck %s
-! CHECK: 
+! RUN: ${F18} -funparse-with-symbols %s 2>&1 | ${FileCheck} %s
+! CHECK: '60' not a FORMAT
 
 subroutine s(a)
   real a(10)
diff --git a/flang/test/semantics/test_any.sh b/flang/test/semantics/test_any.sh
new file mode 100755 (executable)
index 0000000..214c6f6
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+# Copyright (c) 2018, 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.
+
+# Compile a source file with '-funparse-with-symbols' and verify
+# we get the right symbols in the output, i.e. the output should be
+# the same as the input, except for the copyright comment.
+# Change the compiler by setting the F18 environment variable.
+
+PATH=/usr/bin:/bin
+srcdir=$(dirname $0)
+F18=${F18:=../../tools/f18/f18}
+FileCheck=${FileCheck:=internal_check}
+
+function internal_check() {
+  r=true
+  linput="$1"
+  lstdin=`mktemp`
+  lcheck=`mktemp`
+  cat - > ${lstdin}
+  egrep '^[[:space:]]*![[:space:]]*CHECK:[[:space:]]*' ${linput} | sed -e 's/^[[:space:]]*![[:space:]]*CHECK:[[:space:]]*//' > ${lcheck} 2>/dev/null
+  while read p; do
+    if egrep "${p}" ${lstdin} >/dev/null 2>&1; then
+      true
+    else
+      echo "Not found: ${p}" >&2
+      r=false
+    fi
+  done < ${lcheck}
+  egrep '^[[:space:]]*![[:space:]]*CHECK-NOT:[[:space:]]*' ${linput} | sed -e 's/^[[:space:]]*![[:space:]]*CHECK-NOT:[[:space:]]*//' > ${lcheck} 2>/dev/null
+  while read p; do
+    if egrep ${p} ${lstdin} >/dev/null 2>&1; then
+      echo "Found: ${p}" >&2
+      r=false
+    fi
+  done < ${lcheck}
+  rm -f ${lstdin} ${lcheck}
+  ${r}
+}
+
+r=0
+for input in $*; do
+  CMD=$(cat ${input} | egrep '^[[:space:]]*![[:space:]]*RUN:[[:space:]]*' | sed -e 's/^[[:space:]]*![[:space:]]*RUN:[[:space:]]*//')
+  CMD=$(echo ${CMD} | sed -e "s:%s:${input}:g")
+  eval "( ${CMD} )" || (echo "test ${input} failed"; r=1)
+done
+exit $r