-/* -*- 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
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);
}
// -----------------------------------------------
// 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});
}
}
}
/// \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