forked from OSchip/llvm-project
726 lines
26 KiB
C++
726 lines
26 KiB
C++
//===-- lib/Semantics/resolve-names-utils.cpp -----------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "resolve-names-utils.h"
|
|
#include "flang/Common/Fortran-features.h"
|
|
#include "flang/Common/idioms.h"
|
|
#include "flang/Common/indirection.h"
|
|
#include "flang/Evaluate/fold.h"
|
|
#include "flang/Evaluate/tools.h"
|
|
#include "flang/Evaluate/type.h"
|
|
#include "flang/Parser/char-block.h"
|
|
#include "flang/Parser/parse-tree.h"
|
|
#include "flang/Semantics/expression.h"
|
|
#include "flang/Semantics/semantics.h"
|
|
#include "flang/Semantics/tools.h"
|
|
#include <initializer_list>
|
|
#include <variant>
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
using common::LanguageFeature;
|
|
using common::LogicalOperator;
|
|
using common::NumericOperator;
|
|
using common::RelationalOperator;
|
|
using IntrinsicOperator = parser::DefinedOperator::IntrinsicOperator;
|
|
|
|
static constexpr const char *operatorPrefix{"operator("};
|
|
|
|
static GenericKind MapIntrinsicOperator(IntrinsicOperator);
|
|
|
|
Symbol *Resolve(const parser::Name &name, Symbol *symbol) {
|
|
if (symbol && !name.symbol) {
|
|
name.symbol = symbol;
|
|
}
|
|
return symbol;
|
|
}
|
|
Symbol &Resolve(const parser::Name &name, Symbol &symbol) {
|
|
return *Resolve(name, &symbol);
|
|
}
|
|
|
|
parser::MessageFixedText WithIsFatal(
|
|
const parser::MessageFixedText &msg, bool isFatal) {
|
|
return parser::MessageFixedText{
|
|
msg.text().begin(), msg.text().size(), isFatal};
|
|
}
|
|
|
|
bool IsIntrinsicOperator(
|
|
const SemanticsContext &context, const SourceName &name) {
|
|
std::string str{name.ToString()};
|
|
for (int i{0}; i != common::LogicalOperator_enumSize; ++i) {
|
|
auto names{context.languageFeatures().GetNames(LogicalOperator{i})};
|
|
if (std::find(names.begin(), names.end(), str) != names.end()) {
|
|
return true;
|
|
}
|
|
}
|
|
for (int i{0}; i != common::RelationalOperator_enumSize; ++i) {
|
|
auto names{context.languageFeatures().GetNames(RelationalOperator{i})};
|
|
if (std::find(names.begin(), names.end(), str) != names.end()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
template <typename E>
|
|
std::forward_list<std::string> GetOperatorNames(
|
|
const SemanticsContext &context, E opr) {
|
|
std::forward_list<std::string> result;
|
|
for (const char *name : context.languageFeatures().GetNames(opr)) {
|
|
result.emplace_front(std::string{operatorPrefix} + name + ')');
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::forward_list<std::string> GetAllNames(
|
|
const SemanticsContext &context, const SourceName &name) {
|
|
std::string str{name.ToString()};
|
|
if (!name.empty() && name.end()[-1] == ')' &&
|
|
name.ToString().rfind(std::string{operatorPrefix}, 0) == 0) {
|
|
for (int i{0}; i != common::LogicalOperator_enumSize; ++i) {
|
|
auto names{GetOperatorNames(context, LogicalOperator{i})};
|
|
if (std::find(names.begin(), names.end(), str) != names.end()) {
|
|
return names;
|
|
}
|
|
}
|
|
for (int i{0}; i != common::RelationalOperator_enumSize; ++i) {
|
|
auto names{GetOperatorNames(context, RelationalOperator{i})};
|
|
if (std::find(names.begin(), names.end(), str) != names.end()) {
|
|
return names;
|
|
}
|
|
}
|
|
}
|
|
return {str};
|
|
}
|
|
|
|
bool IsLogicalConstant(
|
|
const SemanticsContext &context, const SourceName &name) {
|
|
std::string str{name.ToString()};
|
|
return str == ".true." || str == ".false." ||
|
|
(context.IsEnabled(LanguageFeature::LogicalAbbreviations) &&
|
|
(str == ".t" || str == ".f."));
|
|
}
|
|
|
|
void GenericSpecInfo::Resolve(Symbol *symbol) const {
|
|
if (symbol) {
|
|
if (auto *details{symbol->detailsIf<GenericDetails>()}) {
|
|
details->set_kind(kind_);
|
|
}
|
|
if (parseName_) {
|
|
semantics::Resolve(*parseName_, symbol);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GenericSpecInfo::Analyze(const parser::DefinedOpName &name) {
|
|
kind_ = GenericKind::OtherKind::DefinedOp;
|
|
parseName_ = &name.v;
|
|
symbolName_ = name.v.source;
|
|
}
|
|
|
|
void GenericSpecInfo::Analyze(const parser::GenericSpec &x) {
|
|
symbolName_ = x.source;
|
|
kind_ = std::visit(
|
|
common::visitors{
|
|
[&](const parser::Name &y) -> GenericKind {
|
|
parseName_ = &y;
|
|
symbolName_ = y.source;
|
|
return GenericKind::OtherKind::Name;
|
|
},
|
|
[&](const parser::DefinedOperator &y) {
|
|
return std::visit(
|
|
common::visitors{
|
|
[&](const parser::DefinedOpName &z) -> GenericKind {
|
|
Analyze(z);
|
|
return GenericKind::OtherKind::DefinedOp;
|
|
},
|
|
[&](const IntrinsicOperator &z) {
|
|
return MapIntrinsicOperator(z);
|
|
},
|
|
},
|
|
y.u);
|
|
},
|
|
[&](const parser::GenericSpec::Assignment &) -> GenericKind {
|
|
return GenericKind::OtherKind::Assignment;
|
|
},
|
|
[&](const parser::GenericSpec::ReadFormatted &) -> GenericKind {
|
|
return GenericKind::DefinedIo::ReadFormatted;
|
|
},
|
|
[&](const parser::GenericSpec::ReadUnformatted &) -> GenericKind {
|
|
return GenericKind::DefinedIo::ReadUnformatted;
|
|
},
|
|
[&](const parser::GenericSpec::WriteFormatted &) -> GenericKind {
|
|
return GenericKind::DefinedIo::WriteFormatted;
|
|
},
|
|
[&](const parser::GenericSpec::WriteUnformatted &) -> GenericKind {
|
|
return GenericKind::DefinedIo::WriteUnformatted;
|
|
},
|
|
},
|
|
x.u);
|
|
}
|
|
|
|
llvm::raw_ostream &operator<<(
|
|
llvm::raw_ostream &os, const GenericSpecInfo &info) {
|
|
os << "GenericSpecInfo: kind=" << info.kind_.ToString();
|
|
os << " parseName="
|
|
<< (info.parseName_ ? info.parseName_->ToString() : "null");
|
|
os << " symbolName="
|
|
<< (info.symbolName_ ? info.symbolName_->ToString() : "null");
|
|
return os;
|
|
}
|
|
|
|
// parser::DefinedOperator::IntrinsicOperator -> GenericKind
|
|
static GenericKind MapIntrinsicOperator(IntrinsicOperator op) {
|
|
switch (op) {
|
|
SWITCH_COVERS_ALL_CASES
|
|
case IntrinsicOperator::Concat:
|
|
return GenericKind::OtherKind::Concat;
|
|
case IntrinsicOperator::Power:
|
|
return NumericOperator::Power;
|
|
case IntrinsicOperator::Multiply:
|
|
return NumericOperator::Multiply;
|
|
case IntrinsicOperator::Divide:
|
|
return NumericOperator::Divide;
|
|
case IntrinsicOperator::Add:
|
|
return NumericOperator::Add;
|
|
case IntrinsicOperator::Subtract:
|
|
return NumericOperator::Subtract;
|
|
case IntrinsicOperator::AND:
|
|
return LogicalOperator::And;
|
|
case IntrinsicOperator::OR:
|
|
return LogicalOperator::Or;
|
|
case IntrinsicOperator::EQV:
|
|
return LogicalOperator::Eqv;
|
|
case IntrinsicOperator::NEQV:
|
|
return LogicalOperator::Neqv;
|
|
case IntrinsicOperator::NOT:
|
|
return LogicalOperator::Not;
|
|
case IntrinsicOperator::LT:
|
|
return RelationalOperator::LT;
|
|
case IntrinsicOperator::LE:
|
|
return RelationalOperator::LE;
|
|
case IntrinsicOperator::EQ:
|
|
return RelationalOperator::EQ;
|
|
case IntrinsicOperator::NE:
|
|
return RelationalOperator::NE;
|
|
case IntrinsicOperator::GE:
|
|
return RelationalOperator::GE;
|
|
case IntrinsicOperator::GT:
|
|
return RelationalOperator::GT;
|
|
}
|
|
}
|
|
|
|
class ArraySpecAnalyzer {
|
|
public:
|
|
ArraySpecAnalyzer(SemanticsContext &context) : context_{context} {}
|
|
ArraySpec Analyze(const parser::ArraySpec &);
|
|
ArraySpec AnalyzeDeferredShapeSpecList(const parser::DeferredShapeSpecList &);
|
|
ArraySpec Analyze(const parser::ComponentArraySpec &);
|
|
ArraySpec Analyze(const parser::CoarraySpec &);
|
|
|
|
private:
|
|
SemanticsContext &context_;
|
|
ArraySpec arraySpec_;
|
|
|
|
template <typename T> void Analyze(const std::list<T> &list) {
|
|
for (const auto &elem : list) {
|
|
Analyze(elem);
|
|
}
|
|
}
|
|
void Analyze(const parser::AssumedShapeSpec &);
|
|
void Analyze(const parser::ExplicitShapeSpec &);
|
|
void Analyze(const parser::AssumedImpliedSpec &);
|
|
void Analyze(const parser::DeferredShapeSpecList &);
|
|
void Analyze(const parser::AssumedRankSpec &);
|
|
void MakeExplicit(const std::optional<parser::SpecificationExpr> &,
|
|
const parser::SpecificationExpr &);
|
|
void MakeImplied(const std::optional<parser::SpecificationExpr> &);
|
|
void MakeDeferred(int);
|
|
Bound GetBound(const std::optional<parser::SpecificationExpr> &);
|
|
Bound GetBound(const parser::SpecificationExpr &);
|
|
};
|
|
|
|
ArraySpec AnalyzeArraySpec(
|
|
SemanticsContext &context, const parser::ArraySpec &arraySpec) {
|
|
return ArraySpecAnalyzer{context}.Analyze(arraySpec);
|
|
}
|
|
ArraySpec AnalyzeArraySpec(
|
|
SemanticsContext &context, const parser::ComponentArraySpec &arraySpec) {
|
|
return ArraySpecAnalyzer{context}.Analyze(arraySpec);
|
|
}
|
|
ArraySpec AnalyzeDeferredShapeSpecList(SemanticsContext &context,
|
|
const parser::DeferredShapeSpecList &deferredShapeSpecs) {
|
|
return ArraySpecAnalyzer{context}.AnalyzeDeferredShapeSpecList(
|
|
deferredShapeSpecs);
|
|
}
|
|
ArraySpec AnalyzeCoarraySpec(
|
|
SemanticsContext &context, const parser::CoarraySpec &coarraySpec) {
|
|
return ArraySpecAnalyzer{context}.Analyze(coarraySpec);
|
|
}
|
|
|
|
ArraySpec ArraySpecAnalyzer::Analyze(const parser::ComponentArraySpec &x) {
|
|
std::visit([this](const auto &y) { Analyze(y); }, x.u);
|
|
CHECK(!arraySpec_.empty());
|
|
return arraySpec_;
|
|
}
|
|
ArraySpec ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
|
|
std::visit(common::visitors{
|
|
[&](const parser::AssumedSizeSpec &y) {
|
|
Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
|
|
Analyze(std::get<parser::AssumedImpliedSpec>(y.t));
|
|
},
|
|
[&](const parser::ImpliedShapeSpec &y) { Analyze(y.v); },
|
|
[&](const auto &y) { Analyze(y); },
|
|
},
|
|
x.u);
|
|
CHECK(!arraySpec_.empty());
|
|
return arraySpec_;
|
|
}
|
|
ArraySpec ArraySpecAnalyzer::AnalyzeDeferredShapeSpecList(
|
|
const parser::DeferredShapeSpecList &x) {
|
|
Analyze(x);
|
|
CHECK(!arraySpec_.empty());
|
|
return arraySpec_;
|
|
}
|
|
ArraySpec ArraySpecAnalyzer::Analyze(const parser::CoarraySpec &x) {
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const parser::DeferredCoshapeSpecList &y) { MakeDeferred(y.v); },
|
|
[&](const parser::ExplicitCoshapeSpec &y) {
|
|
Analyze(std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
|
|
MakeImplied(
|
|
std::get<std::optional<parser::SpecificationExpr>>(y.t));
|
|
},
|
|
},
|
|
x.u);
|
|
CHECK(!arraySpec_.empty());
|
|
return arraySpec_;
|
|
}
|
|
|
|
void ArraySpecAnalyzer::Analyze(const parser::AssumedShapeSpec &x) {
|
|
arraySpec_.push_back(ShapeSpec::MakeAssumed(GetBound(x.v)));
|
|
}
|
|
void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeSpec &x) {
|
|
MakeExplicit(std::get<std::optional<parser::SpecificationExpr>>(x.t),
|
|
std::get<parser::SpecificationExpr>(x.t));
|
|
}
|
|
void ArraySpecAnalyzer::Analyze(const parser::AssumedImpliedSpec &x) {
|
|
MakeImplied(x.v);
|
|
}
|
|
void ArraySpecAnalyzer::Analyze(const parser::DeferredShapeSpecList &x) {
|
|
MakeDeferred(x.v);
|
|
}
|
|
void ArraySpecAnalyzer::Analyze(const parser::AssumedRankSpec &) {
|
|
arraySpec_.push_back(ShapeSpec::MakeAssumedRank());
|
|
}
|
|
|
|
void ArraySpecAnalyzer::MakeExplicit(
|
|
const std::optional<parser::SpecificationExpr> &lb,
|
|
const parser::SpecificationExpr &ub) {
|
|
arraySpec_.push_back(ShapeSpec::MakeExplicit(GetBound(lb), GetBound(ub)));
|
|
}
|
|
void ArraySpecAnalyzer::MakeImplied(
|
|
const std::optional<parser::SpecificationExpr> &lb) {
|
|
arraySpec_.push_back(ShapeSpec::MakeImplied(GetBound(lb)));
|
|
}
|
|
void ArraySpecAnalyzer::MakeDeferred(int n) {
|
|
for (int i = 0; i < n; ++i) {
|
|
arraySpec_.push_back(ShapeSpec::MakeDeferred());
|
|
}
|
|
}
|
|
|
|
Bound ArraySpecAnalyzer::GetBound(
|
|
const std::optional<parser::SpecificationExpr> &x) {
|
|
return x ? GetBound(*x) : Bound{1};
|
|
}
|
|
Bound ArraySpecAnalyzer::GetBound(const parser::SpecificationExpr &x) {
|
|
MaybeSubscriptIntExpr expr;
|
|
if (MaybeExpr maybeExpr{AnalyzeExpr(context_, x.v)}) {
|
|
if (auto *intExpr{evaluate::UnwrapExpr<SomeIntExpr>(*maybeExpr)}) {
|
|
expr = evaluate::Fold(context_.foldingContext(),
|
|
evaluate::ConvertToType<evaluate::SubscriptInteger>(
|
|
std::move(*intExpr)));
|
|
}
|
|
}
|
|
return Bound{std::move(expr)};
|
|
}
|
|
|
|
// If SAVE is set on src, set it on all members of dst
|
|
static void PropagateSaveAttr(
|
|
const EquivalenceObject &src, EquivalenceSet &dst) {
|
|
if (src.symbol.attrs().test(Attr::SAVE)) {
|
|
for (auto &obj : dst) {
|
|
obj.symbol.attrs().set(Attr::SAVE);
|
|
}
|
|
}
|
|
}
|
|
static void PropagateSaveAttr(const EquivalenceSet &src, EquivalenceSet &dst) {
|
|
if (!src.empty()) {
|
|
PropagateSaveAttr(src.front(), dst);
|
|
}
|
|
}
|
|
|
|
void EquivalenceSets::AddToSet(const parser::Designator &designator) {
|
|
if (CheckDesignator(designator)) {
|
|
Symbol &symbol{*currObject_.symbol};
|
|
if (!currSet_.empty()) {
|
|
// check this symbol against first of set for compatibility
|
|
Symbol &first{currSet_.front().symbol};
|
|
CheckCanEquivalence(designator.source, first, symbol) &&
|
|
CheckCanEquivalence(designator.source, symbol, first);
|
|
}
|
|
auto subscripts{currObject_.subscripts};
|
|
if (subscripts.empty() && symbol.IsObjectArray()) {
|
|
// record a whole array as its first element
|
|
for (const ShapeSpec &spec : symbol.get<ObjectEntityDetails>().shape()) {
|
|
auto &lbound{spec.lbound().GetExplicit().value()};
|
|
subscripts.push_back(evaluate::ToInt64(lbound).value());
|
|
}
|
|
}
|
|
auto substringStart{currObject_.substringStart};
|
|
currSet_.emplace_back(
|
|
symbol, subscripts, substringStart, designator.source);
|
|
PropagateSaveAttr(currSet_.back(), currSet_);
|
|
}
|
|
currObject_ = {};
|
|
}
|
|
|
|
void EquivalenceSets::FinishSet(const parser::CharBlock &source) {
|
|
std::set<std::size_t> existing; // indices of sets intersecting this one
|
|
for (auto &obj : currSet_) {
|
|
auto it{objectToSet_.find(obj)};
|
|
if (it != objectToSet_.end()) {
|
|
existing.insert(it->second); // symbol already in this set
|
|
}
|
|
}
|
|
if (existing.empty()) {
|
|
sets_.push_back({}); // create a new equivalence set
|
|
MergeInto(source, currSet_, sets_.size() - 1);
|
|
} else {
|
|
auto it{existing.begin()};
|
|
std::size_t dstIndex{*it};
|
|
MergeInto(source, currSet_, dstIndex);
|
|
while (++it != existing.end()) {
|
|
MergeInto(source, sets_[*it], dstIndex);
|
|
}
|
|
}
|
|
currSet_.clear();
|
|
}
|
|
|
|
// Report an error if sym1 and sym2 cannot be in the same equivalence set.
|
|
bool EquivalenceSets::CheckCanEquivalence(
|
|
const parser::CharBlock &source, const Symbol &sym1, const Symbol &sym2) {
|
|
std::optional<parser::MessageFixedText> msg;
|
|
const DeclTypeSpec *type1{sym1.GetType()};
|
|
const DeclTypeSpec *type2{sym2.GetType()};
|
|
bool isNum1{IsNumericSequenceType(type1)};
|
|
bool isNum2{IsNumericSequenceType(type2)};
|
|
bool isChar1{IsCharacterSequenceType(type1)};
|
|
bool isChar2{IsCharacterSequenceType(type2)};
|
|
if (sym1.attrs().test(Attr::PROTECTED) &&
|
|
!sym2.attrs().test(Attr::PROTECTED)) { // C8114
|
|
msg = "Equivalence set cannot contain '%s'"
|
|
" with PROTECTED attribute and '%s' without"_err_en_US;
|
|
} else if (isNum1) {
|
|
if (isChar2) {
|
|
if (context_.ShouldWarn(
|
|
LanguageFeature::EquivalenceNumericWithCharacter)) {
|
|
msg = "Equivalence set contains '%s' that is numeric sequence "
|
|
"type and '%s' that is character"_en_US;
|
|
}
|
|
} else if (!isNum2) { // C8110
|
|
msg = "Equivalence set cannot contain '%s'"
|
|
" that is numeric sequence type and '%s' that is not"_err_en_US;
|
|
}
|
|
} else if (isChar1) {
|
|
if (isNum2) {
|
|
if (context_.ShouldWarn(
|
|
LanguageFeature::EquivalenceNumericWithCharacter)) {
|
|
msg = "Equivalence set contains '%s' that is character sequence "
|
|
"type and '%s' that is numeric"_en_US;
|
|
}
|
|
} else if (!isChar2) { // C8111
|
|
msg = "Equivalence set cannot contain '%s'"
|
|
" that is character sequence type and '%s' that is not"_err_en_US;
|
|
}
|
|
} else if (!isNum2 && !isChar2 && *type1 != *type2) { // C8112, C8113
|
|
msg = "Equivalence set cannot contain '%s' and '%s' with different types"
|
|
" that are neither numeric nor character sequence types"_err_en_US;
|
|
}
|
|
if (msg) {
|
|
context_.Say(source, std::move(*msg), sym1.name(), sym2.name());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Move objects from src to sets_[dstIndex]
|
|
void EquivalenceSets::MergeInto(const parser::CharBlock &source,
|
|
EquivalenceSet &src, std::size_t dstIndex) {
|
|
EquivalenceSet &dst{sets_[dstIndex]};
|
|
PropagateSaveAttr(dst, src);
|
|
for (const auto &obj : src) {
|
|
dst.push_back(obj);
|
|
objectToSet_[obj] = dstIndex;
|
|
}
|
|
PropagateSaveAttr(src, dst);
|
|
src.clear();
|
|
}
|
|
|
|
// If set has an object with this symbol, return it.
|
|
const EquivalenceObject *EquivalenceSets::Find(
|
|
const EquivalenceSet &set, const Symbol &symbol) {
|
|
for (const auto &obj : set) {
|
|
if (obj.symbol == symbol) {
|
|
return &obj;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool EquivalenceSets::CheckDesignator(const parser::Designator &designator) {
|
|
return std::visit(
|
|
common::visitors{
|
|
[&](const parser::DataRef &x) {
|
|
return CheckDataRef(designator.source, x);
|
|
},
|
|
[&](const parser::Substring &x) {
|
|
const auto &dataRef{std::get<parser::DataRef>(x.t)};
|
|
const auto &range{std::get<parser::SubstringRange>(x.t)};
|
|
bool ok{CheckDataRef(designator.source, dataRef)};
|
|
if (const auto &lb{std::get<0>(range.t)}) {
|
|
ok &= CheckSubstringBound(lb->thing.thing.value(), true);
|
|
} else {
|
|
currObject_.substringStart = 1;
|
|
}
|
|
if (const auto &ub{std::get<1>(range.t)}) {
|
|
ok &= CheckSubstringBound(ub->thing.thing.value(), false);
|
|
}
|
|
return ok;
|
|
},
|
|
},
|
|
designator.u);
|
|
}
|
|
|
|
bool EquivalenceSets::CheckDataRef(
|
|
const parser::CharBlock &source, const parser::DataRef &x) {
|
|
return std::visit(
|
|
common::visitors{
|
|
[&](const parser::Name &name) { return CheckObject(name); },
|
|
[&](const common::Indirection<parser::StructureComponent> &) {
|
|
context_.Say(source, // C8107
|
|
"Derived type component '%s' is not allowed in an equivalence set"_err_en_US,
|
|
source);
|
|
return false;
|
|
},
|
|
[&](const common::Indirection<parser::ArrayElement> &elem) {
|
|
bool ok{CheckDataRef(source, elem.value().base)};
|
|
for (const auto &subscript : elem.value().subscripts) {
|
|
ok &= std::visit(
|
|
common::visitors{
|
|
[&](const parser::SubscriptTriplet &) {
|
|
context_.Say(source, // C924, R872
|
|
"Array section '%s' is not allowed in an equivalence set"_err_en_US,
|
|
source);
|
|
return false;
|
|
},
|
|
[&](const parser::IntExpr &y) {
|
|
return CheckArrayBound(y.thing.value());
|
|
},
|
|
},
|
|
subscript.u);
|
|
}
|
|
return ok;
|
|
},
|
|
[&](const common::Indirection<parser::CoindexedNamedObject> &) {
|
|
context_.Say(source, // C924 (R872)
|
|
"Coindexed object '%s' is not allowed in an equivalence set"_err_en_US,
|
|
source);
|
|
return false;
|
|
},
|
|
},
|
|
x.u);
|
|
}
|
|
|
|
static bool InCommonWithBind(const Symbol &symbol) {
|
|
if (const auto *details{symbol.detailsIf<ObjectEntityDetails>()}) {
|
|
const Symbol *commonBlock{details->commonBlock()};
|
|
return commonBlock && commonBlock->attrs().test(Attr::BIND_C);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// If symbol can't be in equivalence set report error and return false;
|
|
bool EquivalenceSets::CheckObject(const parser::Name &name) {
|
|
if (!name.symbol) {
|
|
return false; // an error has already occurred
|
|
}
|
|
currObject_.symbol = name.symbol;
|
|
parser::MessageFixedText msg{"", 0};
|
|
const Symbol &symbol{*name.symbol};
|
|
if (symbol.owner().IsDerivedType()) { // C8107
|
|
msg = "Derived type component '%s'"
|
|
" is not allowed in an equivalence set"_err_en_US;
|
|
} else if (IsDummy(symbol)) { // C8106
|
|
msg = "Dummy argument '%s' is not allowed in an equivalence set"_err_en_US;
|
|
} else if (symbol.IsFuncResult()) { // C8106
|
|
msg = "Function result '%s' is not allow in an equivalence set"_err_en_US;
|
|
} else if (IsPointer(symbol)) { // C8106
|
|
msg = "Pointer '%s' is not allowed in an equivalence set"_err_en_US;
|
|
} else if (IsAllocatable(symbol)) { // C8106
|
|
msg = "Allocatable variable '%s'"
|
|
" is not allowed in an equivalence set"_err_en_US;
|
|
} else if (symbol.Corank() > 0) { // C8106
|
|
msg = "Coarray '%s' is not allowed in an equivalence set"_err_en_US;
|
|
} else if (symbol.has<UseDetails>()) { // C8115
|
|
msg = "Use-associated variable '%s'"
|
|
" is not allowed in an equivalence set"_err_en_US;
|
|
} else if (symbol.attrs().test(Attr::BIND_C)) { // C8106
|
|
msg = "Variable '%s' with BIND attribute"
|
|
" is not allowed in an equivalence set"_err_en_US;
|
|
} else if (symbol.attrs().test(Attr::TARGET)) { // C8108
|
|
msg = "Variable '%s' with TARGET attribute"
|
|
" is not allowed in an equivalence set"_err_en_US;
|
|
} else if (IsNamedConstant(symbol)) { // C8106
|
|
msg = "Named constant '%s' is not allowed in an equivalence set"_err_en_US;
|
|
} else if (InCommonWithBind(symbol)) { // C8106
|
|
msg = "Variable '%s' in common block with BIND attribute"
|
|
" is not allowed in an equivalence set"_err_en_US;
|
|
} else if (const auto *type{symbol.GetType()}) {
|
|
if (const auto *derived{type->AsDerived()}) {
|
|
if (const auto *comp{FindUltimateComponent(
|
|
*derived, IsAllocatableOrPointer)}) { // C8106
|
|
msg = IsPointer(*comp)
|
|
? "Derived type object '%s' with pointer ultimate component"
|
|
" is not allowed in an equivalence set"_err_en_US
|
|
: "Derived type object '%s' with allocatable ultimate component"
|
|
" is not allowed in an equivalence set"_err_en_US;
|
|
} else if (!derived->typeSymbol().get<DerivedTypeDetails>().sequence()) {
|
|
msg = "Nonsequence derived type object '%s'"
|
|
" is not allowed in an equivalence set"_err_en_US;
|
|
}
|
|
} else if (IsAutomaticObject(symbol)) {
|
|
msg = "Automatic object '%s'"
|
|
" is not allowed in an equivalence set"_err_en_US;
|
|
}
|
|
}
|
|
if (!msg.text().empty()) {
|
|
context_.Say(name.source, std::move(msg), name.source);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool EquivalenceSets::CheckArrayBound(const parser::Expr &bound) {
|
|
MaybeExpr expr{
|
|
evaluate::Fold(context_.foldingContext(), AnalyzeExpr(context_, bound))};
|
|
if (!expr) {
|
|
return false;
|
|
}
|
|
if (expr->Rank() > 0) {
|
|
context_.Say(bound.source, // C924, R872
|
|
"Array with vector subscript '%s' is not allowed in an equivalence set"_err_en_US,
|
|
bound.source);
|
|
return false;
|
|
}
|
|
auto subscript{evaluate::ToInt64(*expr)};
|
|
if (!subscript) {
|
|
context_.Say(bound.source, // C8109
|
|
"Array with nonconstant subscript '%s' is not allowed in an equivalence set"_err_en_US,
|
|
bound.source);
|
|
return false;
|
|
}
|
|
currObject_.subscripts.push_back(*subscript);
|
|
return true;
|
|
}
|
|
|
|
bool EquivalenceSets::CheckSubstringBound(
|
|
const parser::Expr &bound, bool isStart) {
|
|
MaybeExpr expr{
|
|
evaluate::Fold(context_.foldingContext(), AnalyzeExpr(context_, bound))};
|
|
if (!expr) {
|
|
return false;
|
|
}
|
|
auto subscript{evaluate::ToInt64(*expr)};
|
|
if (!subscript) {
|
|
context_.Say(bound.source, // C8109
|
|
"Substring with nonconstant bound '%s' is not allowed in an equivalence set"_err_en_US,
|
|
bound.source);
|
|
return false;
|
|
}
|
|
if (!isStart) {
|
|
auto start{currObject_.substringStart};
|
|
if (*subscript < (start ? *start : 1)) {
|
|
context_.Say(bound.source, // C8116
|
|
"Substring with zero length is not allowed in an equivalence set"_err_en_US);
|
|
return false;
|
|
}
|
|
} else if (*subscript != 1) {
|
|
currObject_.substringStart = *subscript;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool EquivalenceSets::IsCharacterSequenceType(const DeclTypeSpec *type) {
|
|
return IsSequenceType(type, [&](const IntrinsicTypeSpec &type) {
|
|
auto kind{evaluate::ToInt64(type.kind())};
|
|
return type.category() == TypeCategory::Character && kind &&
|
|
kind.value() == context_.GetDefaultKind(TypeCategory::Character);
|
|
});
|
|
}
|
|
|
|
// Numeric or logical type of default kind or DOUBLE PRECISION or DOUBLE COMPLEX
|
|
bool EquivalenceSets::IsDefaultKindNumericType(const IntrinsicTypeSpec &type) {
|
|
if (auto kind{evaluate::ToInt64(type.kind())}) {
|
|
auto category{type.category()};
|
|
auto defaultKind{context_.GetDefaultKind(category)};
|
|
switch (category) {
|
|
case TypeCategory::Integer:
|
|
case TypeCategory::Logical:
|
|
return *kind == defaultKind;
|
|
case TypeCategory::Real:
|
|
case TypeCategory::Complex:
|
|
return *kind == defaultKind || *kind == context_.doublePrecisionKind();
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool EquivalenceSets::IsNumericSequenceType(const DeclTypeSpec *type) {
|
|
return IsSequenceType(type, [&](const IntrinsicTypeSpec &type) {
|
|
return IsDefaultKindNumericType(type);
|
|
});
|
|
}
|
|
|
|
// Is type an intrinsic type that satisfies predicate or a sequence type
|
|
// whose components do.
|
|
bool EquivalenceSets::IsSequenceType(const DeclTypeSpec *type,
|
|
std::function<bool(const IntrinsicTypeSpec &)> predicate) {
|
|
if (!type) {
|
|
return false;
|
|
} else if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
|
|
return predicate(*intrinsic);
|
|
} else if (const DerivedTypeSpec * derived{type->AsDerived()}) {
|
|
for (const auto &pair : *derived->typeSymbol().scope()) {
|
|
const Symbol &component{*pair.second};
|
|
if (IsAllocatableOrPointer(component) ||
|
|
!IsSequenceType(component.GetType(), predicate)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} // namespace Fortran::semantics
|