2018-08-21 01:47:18 +02:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
#include "resolve-labels.h"
|
2018-08-31 19:26:19 +02:00
|
|
|
#include "../common/enum-set.h"
|
2018-08-21 01:47:18 +02:00
|
|
|
#include "../parser/message.h"
|
|
|
|
#include "../parser/parse-tree-visitor.h"
|
2018-08-31 19:26:19 +02:00
|
|
|
#include <cctype>
|
2018-08-21 01:47:18 +02:00
|
|
|
#include <cstdarg>
|
|
|
|
#include <iostream>
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
namespace Fortran::semantics {
|
2018-08-21 01:47:18 +02:00
|
|
|
|
|
|
|
using namespace parser::literals;
|
2018-08-31 19:26:19 +02:00
|
|
|
|
|
|
|
ENUM_CLASS(TargetStatementEnum, Do, Branch, Format)
|
2018-09-06 00:26:41 +02:00
|
|
|
using LabeledStmtClassificationSet =
|
|
|
|
common::EnumSet<TargetStatementEnum, TargetStatementEnum_enumSize>;
|
2018-08-31 19:26:19 +02:00
|
|
|
|
|
|
|
using IndexList = std::vector<std::pair<parser::CharBlock, parser::CharBlock>>;
|
2018-09-06 18:06:21 +02:00
|
|
|
// A ProxyForScope is an integral proxy for a Fortran scope. This is required
|
|
|
|
// because the parse tree does not actually have the scopes required.
|
2018-09-06 01:04:56 +02:00
|
|
|
using ProxyForScope = unsigned;
|
2018-09-06 18:06:21 +02:00
|
|
|
struct LabeledStatementInfoTuplePOD {
|
2018-09-06 22:33:51 +02:00
|
|
|
LabeledStatementInfoTuplePOD(const ProxyForScope &proxyForScope,
|
|
|
|
const parser::CharBlock &parserCharBlock,
|
|
|
|
const LabeledStmtClassificationSet &labeledStmtClassificationSet)
|
|
|
|
: proxyForScope{proxyForScope}, parserCharBlock{parserCharBlock},
|
|
|
|
labeledStmtClassificationSet{labeledStmtClassificationSet} {}
|
2018-09-06 18:06:21 +02:00
|
|
|
ProxyForScope proxyForScope;
|
|
|
|
parser::CharBlock parserCharBlock;
|
|
|
|
LabeledStmtClassificationSet labeledStmtClassificationSet;
|
|
|
|
};
|
|
|
|
using TargetStmtMap = std::map<parser::Label, LabeledStatementInfoTuplePOD>;
|
|
|
|
struct SourceStatementInfoTuplePOD {
|
2018-09-06 22:33:51 +02:00
|
|
|
SourceStatementInfoTuplePOD(const parser::Label &parserLabel,
|
|
|
|
const ProxyForScope &proxyForScope,
|
|
|
|
const parser::CharBlock &parserCharBlock)
|
|
|
|
: parserLabel{parserLabel}, proxyForScope{proxyForScope},
|
|
|
|
parserCharBlock{parserCharBlock} {}
|
2018-09-06 18:06:21 +02:00
|
|
|
parser::Label parserLabel;
|
|
|
|
ProxyForScope proxyForScope;
|
|
|
|
parser::CharBlock parserCharBlock;
|
|
|
|
};
|
|
|
|
using SourceStmtList = std::vector<SourceStatementInfoTuplePOD>;
|
2018-08-31 19:26:19 +02:00
|
|
|
|
|
|
|
const bool isStrictF18{false}; // FIXME - make a command-line option
|
|
|
|
|
2018-09-06 19:04:18 +02:00
|
|
|
bool HasScope(ProxyForScope scope) { return scope != ProxyForScope{0u}; }
|
2018-08-31 19:26:19 +02:00
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// F18:R1131
|
2018-08-31 19:26:19 +02:00
|
|
|
template<typename A>
|
|
|
|
constexpr bool IsLegalDoTerm(const parser::Statement<A> &) {
|
2018-09-06 19:04:18 +02:00
|
|
|
return std::is_same_v<A, common::Indirection<parser::EndDoStmt>> ||
|
|
|
|
std::is_same_v<A, parser::EndDoStmt>;
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
template<>
|
|
|
|
constexpr bool IsLegalDoTerm(
|
|
|
|
const parser::Statement<parser::ActionStmt> &actionStmt) {
|
|
|
|
if (std::holds_alternative<parser::ContinueStmt>(actionStmt.statement.u)) {
|
|
|
|
// See F08:C816
|
2018-08-21 01:47:18 +02:00
|
|
|
return true;
|
2018-08-31 19:26:19 +02:00
|
|
|
} else if (isStrictF18) {
|
2018-08-21 01:47:18 +02:00
|
|
|
return false;
|
2018-08-31 19:26:19 +02:00
|
|
|
} else {
|
2018-09-06 19:04:18 +02:00
|
|
|
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));
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
template<typename A> constexpr bool IsFormat(const parser::Statement<A> &) {
|
|
|
|
return std::is_same_v<A, common::Indirection<parser::FormatStmt>>;
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
template<typename A>
|
|
|
|
constexpr bool IsLegalBranchTarget(const parser::Statement<A> &) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return std::is_same_v<A, parser::AssociateStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndAssociateStmt> ||
|
|
|
|
std::is_same_v<A, parser::IfThenStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndIfStmt> ||
|
|
|
|
std::is_same_v<A, parser::SelectCaseStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndSelectStmt> ||
|
|
|
|
std::is_same_v<A, parser::SelectRankStmt> ||
|
|
|
|
std::is_same_v<A, parser::SelectTypeStmt> ||
|
|
|
|
std::is_same_v<A, common::Indirection<parser::LabelDoStmt>> ||
|
|
|
|
std::is_same_v<A, parser::NonLabelDoStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndDoStmt> ||
|
|
|
|
std::is_same_v<A, common::Indirection<parser::EndDoStmt>> ||
|
|
|
|
std::is_same_v<A, parser::BlockStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndBlockStmt> ||
|
|
|
|
std::is_same_v<A, parser::CriticalStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndCriticalStmt> ||
|
|
|
|
std::is_same_v<A, parser::ForallConstructStmt> ||
|
|
|
|
std::is_same_v<A, parser::ForallStmt> ||
|
|
|
|
std::is_same_v<A, parser::WhereConstructStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndFunctionStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndMpSubprogramStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndProgramStmt> ||
|
|
|
|
std::is_same_v<A, parser::EndSubroutineStmt>;
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
template<>
|
|
|
|
constexpr bool IsLegalBranchTarget(
|
|
|
|
const parser::Statement<parser::ActionStmt> &actionStmt) {
|
2018-09-06 01:04:56 +02:00
|
|
|
if (!isStrictF18) {
|
|
|
|
return true;
|
|
|
|
} else {
|
2018-09-06 19:04:18 +02:00
|
|
|
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));
|
2018-09-06 01:04:56 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-28 19:14:43 +02:00
|
|
|
|
2018-08-21 01:47:18 +02:00
|
|
|
template<typename A>
|
2018-09-06 00:26:41 +02:00
|
|
|
constexpr LabeledStmtClassificationSet constructBranchTargetFlags(
|
|
|
|
const parser::Statement<A> &statement) {
|
|
|
|
LabeledStmtClassificationSet labeledStmtClassificationSet{};
|
2018-08-31 19:26:19 +02:00
|
|
|
if (IsLegalDoTerm(statement)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
labeledStmtClassificationSet.set(TargetStatementEnum::Do);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
if (IsLegalBranchTarget(statement)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
labeledStmtClassificationSet.set(TargetStatementEnum::Branch);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
if (IsFormat(statement)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
labeledStmtClassificationSet.set(TargetStatementEnum::Format);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
return labeledStmtClassificationSet;
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
bool BothEqOrNone(const std::optional<parser::Name> &name_a,
|
|
|
|
const std::optional<parser::Name> &name_b) {
|
|
|
|
if (name_a.has_value()) {
|
|
|
|
if (name_b.has_value()) {
|
2018-09-06 19:04:18 +02:00
|
|
|
return name_a->ToString() == name_b->ToString();
|
2018-09-06 00:26:41 +02:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
2018-09-06 19:04:18 +02:00
|
|
|
return !name_b.has_value();
|
2018-09-06 00:26:41 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
bool PresentAndEq(const std::optional<parser::Name> &name_a,
|
|
|
|
const std::optional<parser::Name> &name_b) {
|
|
|
|
if (!name_a.has_value()) {
|
|
|
|
return true;
|
|
|
|
} else if (name_b.has_value()) {
|
2018-09-06 19:04:18 +02:00
|
|
|
return name_a->ToString() == name_b->ToString();
|
2018-09-06 00:26:41 +02:00
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
struct UnitAnalysis {
|
|
|
|
UnitAnalysis() { scopeModel.push_back(0); }
|
|
|
|
UnitAnalysis(UnitAnalysis &&that)
|
2018-09-06 18:06:21 +02:00
|
|
|
: doStmtSources{std::move(that.doStmtSources)},
|
|
|
|
formatStmtSources{std::move(that.formatStmtSources)},
|
|
|
|
otherStmtSources{std::move(that.otherStmtSources)},
|
|
|
|
targetStmts{std::move(that.targetStmts)}, scopeModel{std::move(
|
|
|
|
that.scopeModel)} {}
|
2018-09-06 00:26:41 +02:00
|
|
|
|
|
|
|
SourceStmtList doStmtSources;
|
|
|
|
SourceStmtList formatStmtSources;
|
|
|
|
SourceStmtList otherStmtSources;
|
|
|
|
TargetStmtMap targetStmts;
|
2018-09-06 01:04:56 +02:00
|
|
|
std::vector<ProxyForScope> scopeModel;
|
2018-09-06 00:26:41 +02:00
|
|
|
};
|
2018-08-21 01:47:18 +02:00
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
class ParseTreeAnalyzer {
|
|
|
|
public:
|
2018-08-31 19:26:19 +02:00
|
|
|
ParseTreeAnalyzer() {}
|
2018-09-06 00:26:41 +02:00
|
|
|
ParseTreeAnalyzer(ParseTreeAnalyzer &&that)
|
2018-09-06 18:06:21 +02:00
|
|
|
: programUnits_{std::move(that.programUnits_)},
|
|
|
|
errorHandler_{std::move(that.errorHandler_)}, currentPosition_{std::move(
|
|
|
|
that.currentPosition_)},
|
|
|
|
constructNames_{std::move(that.constructNames_)} {}
|
2018-08-21 01:47:18 +02:00
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
template<typename A> constexpr bool Pre(const A &) { return true; }
|
|
|
|
template<typename A> constexpr void Post(const A &) {}
|
2018-08-21 01:47:18 +02:00
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
template<typename A> bool Pre(const parser::Statement<A> &statement) {
|
|
|
|
currentPosition_ = statement.source;
|
|
|
|
if (statement.label.has_value())
|
|
|
|
addTargetLabelDefinition(
|
|
|
|
statement.label.value(), constructBranchTargetFlags(statement));
|
2018-08-21 01:47:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// see 11.1.1
|
2018-08-31 19:26:19 +02:00
|
|
|
bool Pre(const parser::ProgramUnit &) { return PushNewScope(); }
|
|
|
|
bool Pre(const parser::AssociateConstruct &associateConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructName(associateConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
bool Pre(const parser::BlockConstruct &blockConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructName(blockConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
bool Pre(const parser::ChangeTeamConstruct &changeTeamConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructName(changeTeamConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
bool Pre(const parser::CriticalConstruct &criticalConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructName(criticalConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
bool Pre(const parser::DoConstruct &doConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructName(doConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
bool Pre(const parser::IfConstruct &ifConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructName(ifConstruct);
|
|
|
|
}
|
|
|
|
bool Pre(const parser::IfConstruct::ElseIfBlock &) {
|
|
|
|
return switchToNewScope();
|
|
|
|
}
|
|
|
|
bool Pre(const parser::IfConstruct::ElseBlock &) {
|
|
|
|
return switchToNewScope();
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
bool Pre(const parser::CaseConstruct &caseConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructName(caseConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
bool Pre(const parser::CaseConstruct::Case &) { return switchToNewScope(); }
|
2018-08-31 19:26:19 +02:00
|
|
|
bool Pre(const parser::SelectRankConstruct &selectRankConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructName(selectRankConstruct);
|
|
|
|
}
|
|
|
|
bool Pre(const parser::SelectRankConstruct::RankCase &) {
|
|
|
|
return switchToNewScope();
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
bool Pre(const parser::SelectTypeConstruct &selectTypeConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructName(selectTypeConstruct);
|
|
|
|
}
|
|
|
|
bool Pre(const parser::SelectTypeConstruct::TypeCase &) {
|
|
|
|
return switchToNewScope();
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
bool Pre(const parser::WhereConstruct &whereConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructNameWithoutBlock(whereConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
bool Pre(const parser::ForallConstruct &forallConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushConstructNameWithoutBlock(forallConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Post(const parser::ProgramUnit &) { PopScope(); }
|
|
|
|
void Post(const parser::AssociateConstruct &associateConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
popConstructName(associateConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::BlockConstruct &blockConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
popConstructName(blockConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::ChangeTeamConstruct &changeTeamConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
popConstructName(changeTeamConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::CriticalConstruct &criticalConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
popConstructName(criticalConstruct);
|
|
|
|
}
|
|
|
|
void Post(const parser::DoConstruct &doConstruct) {
|
|
|
|
popConstructName(doConstruct);
|
|
|
|
}
|
|
|
|
void Post(const parser::IfConstruct &ifConstruct) {
|
|
|
|
popConstructName(ifConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::CaseConstruct &caseConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
popConstructName(caseConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::SelectRankConstruct &selectRankConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
popConstructName(selectRankConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::SelectTypeConstruct &selectTypeConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
popConstructName(selectTypeConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
void Post(const parser::WhereConstruct &whereConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
popConstructNameWithoutBlock(whereConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::ForallConstruct &forallConstruct) {
|
2018-09-06 00:26:41 +02:00
|
|
|
popConstructNameWithoutBlock(forallConstruct);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
void Post(const parser::LabelDoStmt &labelDoStmt) {
|
2018-09-06 00:26:41 +02:00
|
|
|
addLabelReferenceFromDoStmt(std::get<parser::Label>(labelDoStmt.t));
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
void Post(const parser::GotoStmt &gotoStmt) { addLabelReference(gotoStmt.v); }
|
2018-08-31 19:26:19 +02:00
|
|
|
void Post(const parser::ComputedGotoStmt &computedGotoStmt) {
|
2018-09-06 00:26:41 +02:00
|
|
|
addLabelReference(std::get<std::list<parser::Label>>(computedGotoStmt.t));
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
|
2018-09-06 00:26:41 +02:00
|
|
|
addLabelReference(std::get<1>(arithmeticIfStmt.t));
|
|
|
|
addLabelReference(std::get<2>(arithmeticIfStmt.t));
|
|
|
|
addLabelReference(std::get<3>(arithmeticIfStmt.t));
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::AssignStmt &assignStmt) {
|
2018-09-06 00:26:41 +02:00
|
|
|
addLabelReference(std::get<parser::Label>(assignStmt.t));
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
|
2018-09-06 00:26:41 +02:00
|
|
|
addLabelReference(std::get<std::list<parser::Label>>(assignedGotoStmt.t));
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void Post(const parser::AltReturnSpec &altReturnSpec) {
|
2018-09-06 00:26:41 +02:00
|
|
|
addLabelReference(altReturnSpec.v);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void Post(const parser::ErrLabel &errLabel) { addLabelReference(errLabel.v); }
|
|
|
|
void Post(const parser::EndLabel &endLabel) { addLabelReference(endLabel.v); }
|
|
|
|
void Post(const parser::EorLabel &eorLabel) { addLabelReference(eorLabel.v); }
|
2018-08-31 19:26:19 +02:00
|
|
|
void Post(const parser::Format &format) {
|
2018-08-21 01:47:18 +02:00
|
|
|
// BUG: the label is saved as an IntLiteralConstant rather than a Label
|
|
|
|
#if 0
|
2018-08-31 19:26:19 +02:00
|
|
|
if (const auto *P{std::get_if<parser::Label>(&format.u)}) {
|
2018-09-06 00:26:41 +02:00
|
|
|
addLabelReferenceFromFormatStmt(*P);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
#else
|
2018-08-31 19:26:19 +02:00
|
|
|
if (const auto *P{std::get_if<0>(&format.u)}) {
|
2018-09-06 00:26:41 +02:00
|
|
|
addLabelReferenceFromFormatStmt(
|
|
|
|
parser::Label{std::get<0>(std::get<parser::IntLiteralConstant>(
|
|
|
|
std::get<parser::LiteralConstant>((*P->thing).u).u)
|
|
|
|
.t)});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
void Post(const parser::CycleStmt &cycleStmt) {
|
|
|
|
if (cycleStmt.v.has_value()) {
|
2018-09-06 00:26:41 +02:00
|
|
|
CheckLabelContext("CYCLE", cycleStmt.v->ToString());
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
void Post(const parser::ExitStmt &exitStmt) {
|
|
|
|
if (exitStmt.v.has_value()) {
|
2018-09-06 00:26:41 +02:00
|
|
|
CheckLabelContext("EXIT", exitStmt.v->ToString());
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
const std::vector<UnitAnalysis> &programUnits() const {
|
2018-08-31 19:26:19 +02:00
|
|
|
return programUnits_;
|
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
parser::Messages &errorHandler() { return errorHandler_; }
|
2018-08-21 01:47:18 +02:00
|
|
|
|
|
|
|
private:
|
2018-09-06 00:26:41 +02:00
|
|
|
bool pushSubscope() {
|
|
|
|
programUnits_.back().scopeModel.push_back(currentScope_);
|
|
|
|
currentScope_ = programUnits_.back().scopeModel.size() - 1;
|
2018-08-21 01:47:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool PushNewScope() {
|
2018-08-31 19:26:19 +02:00
|
|
|
programUnits_.emplace_back(UnitAnalysis{});
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushSubscope();
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
void PopScope() {
|
2018-09-06 00:26:41 +02:00
|
|
|
currentScope_ = programUnits_.back().scopeModel[currentScope_];
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
bool switchToNewScope() {
|
2018-08-31 19:26:19 +02:00
|
|
|
PopScope();
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushSubscope();
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
template<typename A> bool pushConstructName(const A &a) {
|
2018-08-31 19:26:19 +02:00
|
|
|
const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)};
|
|
|
|
if (optionalName.has_value()) {
|
2018-09-06 00:26:41 +02:00
|
|
|
constructNames_.emplace_back(optionalName->ToString());
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushSubscope();
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
bool pushConstructName(const parser::BlockConstruct &blockConstruct) {
|
2018-08-31 19:26:19 +02:00
|
|
|
const auto &optionalName{
|
|
|
|
std::get<parser::Statement<parser::BlockStmt>>(blockConstruct.t)
|
|
|
|
.statement.v};
|
|
|
|
if (optionalName.has_value()) {
|
2018-09-06 00:26:41 +02:00
|
|
|
constructNames_.emplace_back(optionalName->ToString());
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
return pushSubscope();
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
template<typename A> bool pushConstructNameWithoutBlock(const A &a) {
|
2018-08-31 19:26:19 +02:00
|
|
|
const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)};
|
|
|
|
if (optionalName.has_value()) {
|
2018-09-06 00:26:41 +02:00
|
|
|
constructNames_.emplace_back(optionalName->ToString());
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
template<typename A> void popConstructNameWithoutBlock(const A &a) {
|
2018-08-31 19:26:19 +02:00
|
|
|
CheckName(a);
|
2018-09-06 01:04:56 +02:00
|
|
|
popConstructNameIfPresent(a);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-09-06 01:04:56 +02:00
|
|
|
template<typename A> void popConstructNameIfPresent(const A &a) {
|
2018-08-31 19:26:19 +02:00
|
|
|
const auto &optionalName{std::get<0>(std::get<0>(a.t).statement.t)};
|
|
|
|
if (optionalName.has_value()) {
|
|
|
|
constructNames_.pop_back();
|
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-09-06 01:04:56 +02:00
|
|
|
void popConstructNameIfPresent(const parser::BlockConstruct &blockConstruct) {
|
2018-08-31 19:26:19 +02:00
|
|
|
const auto &optionalName{
|
|
|
|
std::get<parser::Statement<parser::BlockStmt>>(blockConstruct.t)
|
|
|
|
.statement.v};
|
|
|
|
if (optionalName.has_value()) {
|
|
|
|
constructNames_.pop_back();
|
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
template<typename A> void popConstructName(const A &a) {
|
2018-08-31 19:26:19 +02:00
|
|
|
CheckName(a);
|
|
|
|
PopScope();
|
2018-09-06 01:04:56 +02:00
|
|
|
popConstructNameIfPresent(a);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1144
|
|
|
|
void popConstructName(const parser::CaseConstruct &caseConstruct) {
|
2018-08-31 19:26:19 +02:00
|
|
|
CheckName(caseConstruct, "CASE");
|
|
|
|
PopScope();
|
2018-09-06 01:04:56 +02:00
|
|
|
popConstructNameIfPresent(caseConstruct);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1154, C1156
|
|
|
|
void popConstructName(
|
|
|
|
const parser::SelectRankConstruct &selectRankConstruct) {
|
2018-08-31 19:26:19 +02:00
|
|
|
CheckName(selectRankConstruct, "RANK", "RANK ");
|
|
|
|
PopScope();
|
2018-09-06 01:04:56 +02:00
|
|
|
popConstructNameIfPresent(selectRankConstruct);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1165
|
|
|
|
void popConstructName(
|
|
|
|
const parser::SelectTypeConstruct &selectTypeConstruct) {
|
2018-08-31 19:26:19 +02:00
|
|
|
CheckName(selectTypeConstruct, "TYPE", "TYPE ");
|
|
|
|
PopScope();
|
2018-09-06 01:04:56 +02:00
|
|
|
popConstructNameIfPresent(selectTypeConstruct);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1106
|
2018-08-31 19:26:19 +02:00
|
|
|
void CheckName(const parser::AssociateConstruct &associateConstruct) {
|
|
|
|
CheckName("ASSOCIATE", associateConstruct);
|
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1117
|
2018-08-31 19:26:19 +02:00
|
|
|
void CheckName(const parser::CriticalConstruct &criticalConstruct) {
|
|
|
|
CheckName("CRITICAL", criticalConstruct);
|
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1131
|
2018-08-31 19:26:19 +02:00
|
|
|
void CheckName(const parser::DoConstruct &doConstruct) {
|
|
|
|
CheckName("DO", doConstruct);
|
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1035
|
2018-08-31 19:26:19 +02:00
|
|
|
void CheckName(const parser::ForallConstruct &forallConstruct) {
|
|
|
|
CheckName("FORALL", forallConstruct);
|
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
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)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"%s construct name mismatch"_err_en_US, constructTag});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1109
|
2018-08-31 19:26:19 +02:00
|
|
|
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)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"BLOCK construct name mismatch"_err_en_US});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1112
|
2018-08-31 19:26:19 +02:00
|
|
|
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))) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"CHANGE TEAM construct name mismatch"_err_en_US});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1142
|
2018-08-31 19:26:19 +02:00
|
|
|
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)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{"IF construct name mismatch"_err_en_US});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
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)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"ELSE IF statement name mismatch"_err_en_US});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
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)
|
2018-09-06 19:38:52 +02:00
|
|
|
->t)
|
2018-08-31 19:26:19 +02:00
|
|
|
.statement.v,
|
|
|
|
constructName)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"ELSE statement name mismatch"_err_en_US});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
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)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"SELECT %s construct name mismatch"_err_en_US, selectTag});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
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)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"%sCASE statement name mismatch"_err_en_US, selectSubTag});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1033
|
2018-08-31 19:26:19 +02:00
|
|
|
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)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"WHERE construct name mismatch"_err_en_US});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
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)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"ELSEWHERE (<mask>) statement name mismatch"_err_en_US});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
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)
|
2018-09-06 00:26:41 +02:00
|
|
|
->t)
|
2018-08-31 19:26:19 +02:00
|
|
|
.statement.v,
|
|
|
|
constructName)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"ELSEWHERE statement name mismatch"_err_en_US});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1134, C1166
|
2018-08-31 19:26:19 +02:00
|
|
|
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()) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"%s construct-name '%s' is not in scope"_err_en_US, stmtString,
|
|
|
|
constructName.c_str()});
|
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// 6.2.5, paragraph 2
|
2018-08-31 19:26:19 +02:00
|
|
|
void CheckLabelInRange(parser::Label label) {
|
2018-09-06 00:26:41 +02:00
|
|
|
if (label < 1 || label > 99999) {
|
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"label '%" PRIu64 "' is out of range"_err_en_US, label});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// 6.2.5., paragraph 2
|
|
|
|
void addTargetLabelDefinition(parser::Label label,
|
|
|
|
LabeledStmtClassificationSet labeledStmtClassificationSet) {
|
2018-08-31 19:26:19 +02:00
|
|
|
CheckLabelInRange(label);
|
2018-09-06 19:31:47 +02:00
|
|
|
const auto pair{programUnits_.back().targetStmts.emplace(label,
|
|
|
|
LabeledStatementInfoTuplePOD{
|
|
|
|
currentScope_, currentPosition_, labeledStmtClassificationSet})};
|
2018-08-31 19:26:19 +02:00
|
|
|
if (!pair.second) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler_.Say(currentPosition_,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"label '%" PRIu64 "' is not distinct"_err_en_US, label});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void addLabelReferenceFromDoStmt(parser::Label label) {
|
2018-08-31 19:26:19 +02:00
|
|
|
CheckLabelInRange(label);
|
2018-09-06 00:26:41 +02:00
|
|
|
programUnits_.back().doStmtSources.emplace_back(
|
2018-09-06 18:06:21 +02:00
|
|
|
label, currentScope_, currentPosition_);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void addLabelReferenceFromFormatStmt(parser::Label label) {
|
2018-08-31 19:26:19 +02:00
|
|
|
CheckLabelInRange(label);
|
2018-09-06 00:26:41 +02:00
|
|
|
programUnits_.back().formatStmtSources.emplace_back(
|
2018-09-06 18:06:21 +02:00
|
|
|
label, currentScope_, currentPosition_);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void addLabelReference(parser::Label label) {
|
2018-08-31 19:26:19 +02:00
|
|
|
CheckLabelInRange(label);
|
2018-09-06 00:26:41 +02:00
|
|
|
programUnits_.back().otherStmtSources.emplace_back(
|
2018-09-06 18:06:21 +02:00
|
|
|
label, currentScope_, currentPosition_);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void addLabelReference(const std::list<parser::Label> &labels) {
|
2018-08-31 19:26:19 +02:00
|
|
|
for (const parser::Label &label : labels) {
|
2018-09-06 00:26:41 +02:00
|
|
|
addLabelReference(label);
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
std::vector<UnitAnalysis> programUnits_;
|
|
|
|
parser::Messages errorHandler_;
|
|
|
|
parser::CharBlock currentPosition_{nullptr};
|
2018-09-06 01:04:56 +02:00
|
|
|
ProxyForScope currentScope_{0};
|
2018-08-31 19:26:19 +02:00
|
|
|
std::vector<std::string> constructNames_;
|
2018-08-21 01:47:18 +02:00
|
|
|
};
|
|
|
|
|
2018-09-06 01:04:56 +02:00
|
|
|
bool InInclusiveScope(const std::vector<ProxyForScope> &scopes,
|
|
|
|
ProxyForScope tail, ProxyForScope head) {
|
2018-09-06 00:26:41 +02:00
|
|
|
for (; tail != head; tail = scopes[tail]) {
|
|
|
|
if (!HasScope(tail)) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-09-06 00:26:41 +02:00
|
|
|
return true;
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
ParseTreeAnalyzer LabelAnalysis(const parser::Program &program) {
|
|
|
|
ParseTreeAnalyzer analysis;
|
|
|
|
Walk(program, analysis);
|
|
|
|
return analysis;
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
bool InBody(const parser::CharBlock &position,
|
|
|
|
const std::pair<parser::CharBlock, parser::CharBlock> &pair) {
|
2018-09-06 01:04:56 +02:00
|
|
|
if (position.begin() >= pair.first.begin()) {
|
|
|
|
if (position.begin() < pair.second.end()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 18:06:21 +02:00
|
|
|
LabeledStatementInfoTuplePOD GetLabel(
|
2018-09-06 00:26:41 +02:00
|
|
|
const TargetStmtMap &labels, const parser::Label &label) {
|
2018-08-31 19:26:19 +02:00
|
|
|
const auto iter{labels.find(label)};
|
|
|
|
if (iter == labels.cend()) {
|
2018-09-06 00:26:41 +02:00
|
|
|
return {0u, nullptr, LabeledStmtClassificationSet{}};
|
2018-08-31 19:26:19 +02:00
|
|
|
} else {
|
|
|
|
return iter->second;
|
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// 11.1.7.3
|
|
|
|
void CheckBranchesIntoDoBody(const SourceStmtList &branches,
|
2018-09-06 01:04:56 +02:00
|
|
|
const TargetStmtMap &labels, const std::vector<ProxyForScope> &scopes,
|
2018-09-06 00:26:41 +02:00
|
|
|
const IndexList &loopBodies, parser::Messages &errorHandler) {
|
2018-08-31 19:26:19 +02:00
|
|
|
for (const auto branch : branches) {
|
2018-09-06 18:06:21 +02:00
|
|
|
const auto &label{branch.parserLabel};
|
2018-08-31 19:26:19 +02:00
|
|
|
auto branchTarget{GetLabel(labels, label)};
|
2018-09-06 18:06:21 +02:00
|
|
|
if (HasScope(branchTarget.proxyForScope)) {
|
|
|
|
const auto &fromPosition{branch.parserCharBlock};
|
|
|
|
const auto &toPosition{branchTarget.parserCharBlock};
|
2018-08-31 19:26:19 +02:00
|
|
|
for (const auto body : loopBodies) {
|
|
|
|
if (!InBody(fromPosition, body) && InBody(toPosition, body)) {
|
|
|
|
if (isStrictF18) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler.Say(fromPosition,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"branch into '%s' from another scope"_err_en_US,
|
|
|
|
body.first.ToString().c_str()});
|
|
|
|
} else {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler.Say(fromPosition,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
|
|
|
"branch into '%s' from another scope"_en_US,
|
|
|
|
body.first.ToString().c_str()});
|
|
|
|
}
|
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void CheckDoNesting(
|
|
|
|
const IndexList &loopBodies, parser::Messages &errorHandler) {
|
2018-08-31 19:26:19 +02:00
|
|
|
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};
|
2018-09-06 00:26:41 +02:00
|
|
|
if (v2.first.begin() < v1.second.end() &&
|
|
|
|
v1.second.begin() < v2.second.begin()) {
|
|
|
|
errorHandler.Say(v2.second,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{"'%s' doesn't properly nest"_err_en_US,
|
|
|
|
v1.first.ToString().c_str()});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
parser::CharBlock SkipLabel(const parser::CharBlock &position) {
|
2018-08-31 19:26:19 +02:00
|
|
|
const long maxPosition{position.end() - position.begin()};
|
|
|
|
if (maxPosition && (position[0] >= '0') && (position[0] <= '9')) {
|
2018-08-21 01:47:18 +02:00
|
|
|
long i{1l};
|
2018-09-06 00:26:41 +02:00
|
|
|
for (; (i < maxPosition) && std::isdigit(position[i]); ++i) {
|
|
|
|
}
|
|
|
|
for (; (i < maxPosition) && std::isspace(position[i]); ++i) {
|
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
return parser::CharBlock{position.begin() + i, position.end()};
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
return position;
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void CheckLabelDoConstraints(const SourceStmtList &dos,
|
|
|
|
const SourceStmtList &branches, const TargetStmtMap &labels,
|
2018-09-06 01:04:56 +02:00
|
|
|
const std::vector<ProxyForScope> &scopes, parser::Messages &errorHandler) {
|
2018-08-31 19:26:19 +02:00
|
|
|
IndexList loopBodies;
|
|
|
|
for (const auto stmt : dos) {
|
2018-09-06 18:06:21 +02:00
|
|
|
const auto &label{stmt.parserLabel};
|
|
|
|
const auto &scope{stmt.proxyForScope};
|
|
|
|
const auto &position{stmt.parserCharBlock};
|
2018-08-31 19:26:19 +02:00
|
|
|
auto doTarget{GetLabel(labels, label)};
|
2018-09-06 18:06:21 +02:00
|
|
|
if (!HasScope(doTarget.proxyForScope)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1133
|
|
|
|
errorHandler.Say(position,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"label '%" PRIu64 "' cannot be found"_err_en_US, label});
|
2018-09-06 18:06:21 +02:00
|
|
|
} else if (doTarget.parserCharBlock.begin() < position.begin()) {
|
2018-09-06 00:26:41 +02:00
|
|
|
// R1119
|
|
|
|
errorHandler.Say(position,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"label '%" PRIu64 "' doesn't lexically follow DO stmt"_err_en_US,
|
|
|
|
label});
|
2018-09-06 18:06:21 +02:00
|
|
|
} else if (!InInclusiveScope(scopes, scope, doTarget.proxyForScope)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
// C1133
|
2018-08-31 19:26:19 +02:00
|
|
|
if (isStrictF18) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler.Say(position,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"label '%" PRIu64 "' is not in scope"_err_en_US, label});
|
2018-08-21 01:47:18 +02:00
|
|
|
} else {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler.Say(position,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"label '%" PRIu64 "' is not in scope"_en_US, label});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-09-06 18:06:21 +02:00
|
|
|
} else if (!doTarget.labeledStmtClassificationSet.test(
|
2018-09-06 00:26:41 +02:00
|
|
|
TargetStatementEnum::Do)) {
|
2018-09-06 18:06:21 +02:00
|
|
|
errorHandler.Say(doTarget.parserCharBlock,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"'%" PRIu64 "' invalid DO terminal statement"_err_en_US, label});
|
2018-08-31 19:26:19 +02:00
|
|
|
} else {
|
2018-09-06 18:06:21 +02:00
|
|
|
loopBodies.emplace_back(SkipLabel(position), doTarget.parserCharBlock);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
CheckBranchesIntoDoBody(branches, labels, scopes, loopBodies, errorHandler);
|
|
|
|
CheckDoNesting(loopBodies, errorHandler);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
// 6.2.5
|
|
|
|
void CheckScopeConstraints(const SourceStmtList &stmts,
|
2018-09-06 01:04:56 +02:00
|
|
|
const TargetStmtMap &labels, const std::vector<ProxyForScope> &scopes,
|
2018-09-06 00:26:41 +02:00
|
|
|
parser::Messages &errorHandler) {
|
2018-08-31 19:26:19 +02:00
|
|
|
for (const auto stmt : stmts) {
|
2018-09-06 18:06:21 +02:00
|
|
|
const auto &label{stmt.parserLabel};
|
|
|
|
const auto &scope{stmt.proxyForScope};
|
|
|
|
const auto &position{stmt.parserCharBlock};
|
2018-08-31 19:26:19 +02:00
|
|
|
auto target{GetLabel(labels, label)};
|
2018-09-06 18:06:21 +02:00
|
|
|
if (!HasScope(target.proxyForScope)) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler.Say(position,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"label '%" PRIu64 "' was not found"_err_en_US, label});
|
2018-09-06 18:06:21 +02:00
|
|
|
} else if (!InInclusiveScope(scopes, scope, target.proxyForScope)) {
|
2018-08-31 19:26:19 +02:00
|
|
|
if (isStrictF18) {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler.Say(position,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"label '%" PRIu64 "' is not in scope"_err_en_US, label});
|
2018-08-21 01:47:18 +02:00
|
|
|
} else {
|
2018-09-06 00:26:41 +02:00
|
|
|
errorHandler.Say(position,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"label '%" PRIu64 "' is not in scope"_en_US, label});
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void CheckBranchTargetConstraints(const SourceStmtList &stmts,
|
|
|
|
const TargetStmtMap &labels, parser::Messages &errorHandler) {
|
2018-08-31 19:26:19 +02:00
|
|
|
for (const auto stmt : stmts) {
|
2018-09-06 18:06:21 +02:00
|
|
|
const auto &label{stmt.parserLabel};
|
2018-08-31 19:26:19 +02:00
|
|
|
auto branchTarget{GetLabel(labels, label)};
|
2018-09-06 18:06:21 +02:00
|
|
|
if (HasScope(branchTarget.proxyForScope)) {
|
|
|
|
if (!branchTarget.labeledStmtClassificationSet.test(
|
|
|
|
TargetStatementEnum::Branch)) {
|
|
|
|
errorHandler.Say(branchTarget.parserCharBlock,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"'%" PRIu64 "' not a branch target"_err_en_US, label});
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void CheckBranchConstraints(const SourceStmtList &branches,
|
2018-09-06 01:04:56 +02:00
|
|
|
const TargetStmtMap &labels, const std::vector<ProxyForScope> &scopes,
|
2018-09-06 00:26:41 +02:00
|
|
|
parser::Messages &errorHandler) {
|
|
|
|
CheckScopeConstraints(branches, labels, scopes, errorHandler);
|
|
|
|
CheckBranchTargetConstraints(branches, labels, errorHandler);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void CheckDataXferTargetConstraints(const SourceStmtList &stmts,
|
|
|
|
const TargetStmtMap &labels, parser::Messages &errorHandler) {
|
2018-08-31 19:26:19 +02:00
|
|
|
for (const auto stmt : stmts) {
|
2018-09-06 18:06:21 +02:00
|
|
|
const auto &label{stmt.parserLabel};
|
2018-08-31 19:26:19 +02:00
|
|
|
auto ioTarget{GetLabel(labels, label)};
|
2018-09-06 18:06:21 +02:00
|
|
|
if (HasScope(ioTarget.proxyForScope)) {
|
|
|
|
if (!ioTarget.labeledStmtClassificationSet.test(
|
2018-09-06 00:26:41 +02:00
|
|
|
TargetStatementEnum::Format)) {
|
2018-09-06 18:06:21 +02:00
|
|
|
errorHandler.Say(ioTarget.parserCharBlock,
|
2018-08-31 19:26:19 +02:00
|
|
|
parser::MessageFormattedText{
|
2018-09-06 00:26:41 +02:00
|
|
|
"'%" PRIu64 "' not a FORMAT"_err_en_US, label});
|
2018-08-31 19:26:19 +02:00
|
|
|
}
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-06 00:26:41 +02:00
|
|
|
void CheckDataTransferConstraints(const SourceStmtList &dataTransfers,
|
2018-09-06 01:04:56 +02:00
|
|
|
const TargetStmtMap &labels, const std::vector<ProxyForScope> &scopes,
|
2018-09-06 00:26:41 +02:00
|
|
|
parser::Messages &errorHandler) {
|
|
|
|
CheckScopeConstraints(dataTransfers, labels, scopes, errorHandler);
|
|
|
|
CheckDataXferTargetConstraints(dataTransfers, labels, errorHandler);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
bool CheckConstraints(ParseTreeAnalyzer &&parseTreeAnalysis,
|
|
|
|
const parser::CookedSource &cookedSource) {
|
2018-09-06 00:26:41 +02:00
|
|
|
auto &errorHandler{parseTreeAnalysis.errorHandler()};
|
|
|
|
for (const auto &programUnit : parseTreeAnalysis.programUnits()) {
|
|
|
|
const auto &dos{programUnit.doStmtSources};
|
|
|
|
const auto &branches{programUnit.otherStmtSources};
|
|
|
|
const auto &labels{programUnit.targetStmts};
|
|
|
|
const auto &scopes{programUnit.scopeModel};
|
|
|
|
CheckLabelDoConstraints(dos, branches, labels, scopes, errorHandler);
|
|
|
|
CheckBranchConstraints(branches, labels, scopes, errorHandler);
|
|
|
|
const auto &dataTransfers{programUnit.formatStmtSources};
|
|
|
|
CheckDataTransferConstraints(dataTransfers, labels, scopes, errorHandler);
|
|
|
|
}
|
|
|
|
if (!errorHandler.empty()) {
|
|
|
|
errorHandler.Emit(std::cerr, cookedSource);
|
|
|
|
}
|
|
|
|
return !errorHandler.AnyFatalError();
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
bool ValidateLabels(
|
|
|
|
const parser::Program &program, const parser::CookedSource &cookedSource) {
|
|
|
|
return CheckConstraints(LabelAnalysis(program), cookedSource);
|
2018-08-21 01:47:18 +02:00
|
|
|
}
|
|
|
|
|
2018-08-31 19:26:19 +02:00
|
|
|
} // namespace Fortran::semantics
|