2020-02-25 23:11:52 +08:00
|
|
|
//===-- lib/Semantics/resolve-names-utils.cpp -----------------------------===//
|
2019-03-19 02:48:02 +08:00
|
|
|
//
|
2019-12-21 04:52:07 +08:00
|
|
|
// 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
|
2019-03-19 02:48:02 +08:00
|
|
|
//
|
2020-01-11 04:12:03 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2019-03-19 02:48:02 +08:00
|
|
|
|
|
|
|
#include "resolve-names-utils.h"
|
2020-02-25 23:11:52 +08:00
|
|
|
#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"
|
[flang] Handle alternative names for relational operators
10.1.6.2 says:
> The operators <, <=, >, >=, ==, and /= always have the same interpretations
> as the operators .LT., .LE., .GT., .GE., .EQ., and .NE., respectively.
That means we have to treat `operator(<)` like `operator(.lt.)`,
for example. `<>` is a third alias for `.NE.`.
We can't just choose always to use one form (e.g. replacing `operator(.lt.)`
with `operator(<)`). This is because all symbols names are `CharBlock`s
referring to the cooked character stream so that they have proper source
provenance. Also, if a user prefers one style and uses it consistently,
that's the form they should see in messages.
So the fix is to use whatever form is found in the source, but also to
look up symbols by the other names when necessary. To assist this, add
`GenericSpecInfo::GetAllNames()` to return all of the names of a generic
spec. Each place a generic spec can occur we have to use these to look
for the symbol.
Also reorganize the `AddUse()` overloads to work with this change.
Fixes flang-compiler/f18#746.
Original-commit: flang-compiler/f18@7f06f175d5033f0728f67b1be25ecd53df1f8de5
Reviewed-on: https://github.com/flang-compiler/f18/pull/752
2019-09-18 07:57:09 +08:00
|
|
|
#include <initializer_list>
|
2019-03-19 02:48:02 +08:00
|
|
|
#include <variant>
|
|
|
|
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
|
2019-11-07 03:15:03 +08:00
|
|
|
using common::LanguageFeature;
|
2019-11-07 07:54:26 +08:00
|
|
|
using common::LogicalOperator;
|
2019-11-23 04:40:37 +08:00
|
|
|
using common::NumericOperator;
|
2019-11-07 07:54:26 +08:00
|
|
|
using common::RelationalOperator;
|
2019-03-19 02:48:02 +08:00
|
|
|
using IntrinsicOperator = parser::DefinedOperator::IntrinsicOperator;
|
|
|
|
|
2020-12-16 23:06:53 +08:00
|
|
|
static constexpr const char *operatorPrefix{"operator("};
|
|
|
|
|
2019-03-19 02:48:02 +08:00
|
|
|
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};
|
|
|
|
}
|
|
|
|
|
2019-06-27 06:30:53 +08:00
|
|
|
bool IsIntrinsicOperator(
|
2019-03-19 02:48:02 +08:00
|
|
|
const SemanticsContext &context, const SourceName &name) {
|
|
|
|
std::string str{name.ToString()};
|
2019-11-07 07:54:26 +08:00
|
|
|
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;
|
|
|
|
}
|
2019-03-19 02:48:02 +08:00
|
|
|
}
|
2019-11-07 07:54:26 +08:00
|
|
|
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;
|
|
|
|
}
|
2019-03-19 02:48:02 +08:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-16 23:06:53 +08:00
|
|
|
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};
|
|
|
|
}
|
|
|
|
|
2019-03-19 02:48:02 +08:00
|
|
|
bool IsLogicalConstant(
|
|
|
|
const SemanticsContext &context, const SourceName &name) {
|
|
|
|
std::string str{name.ToString()};
|
|
|
|
return str == ".true." || str == ".false." ||
|
2019-11-07 03:15:03 +08:00
|
|
|
(context.IsEnabled(LanguageFeature::LogicalAbbreviations) &&
|
2019-03-19 02:48:02 +08:00
|
|
|
(str == ".t" || str == ".f."));
|
|
|
|
}
|
|
|
|
|
[flang] Handle alternative names for relational operators
10.1.6.2 says:
> The operators <, <=, >, >=, ==, and /= always have the same interpretations
> as the operators .LT., .LE., .GT., .GE., .EQ., and .NE., respectively.
That means we have to treat `operator(<)` like `operator(.lt.)`,
for example. `<>` is a third alias for `.NE.`.
We can't just choose always to use one form (e.g. replacing `operator(.lt.)`
with `operator(<)`). This is because all symbols names are `CharBlock`s
referring to the cooked character stream so that they have proper source
provenance. Also, if a user prefers one style and uses it consistently,
that's the form they should see in messages.
So the fix is to use whatever form is found in the source, but also to
look up symbols by the other names when necessary. To assist this, add
`GenericSpecInfo::GetAllNames()` to return all of the names of a generic
spec. Each place a generic spec can occur we have to use these to look
for the symbol.
Also reorganize the `AddUse()` overloads to work with this change.
Fixes flang-compiler/f18#746.
Original-commit: flang-compiler/f18@7f06f175d5033f0728f67b1be25ecd53df1f8de5
Reviewed-on: https://github.com/flang-compiler/f18/pull/752
2019-09-18 07:57:09 +08:00
|
|
|
void GenericSpecInfo::Resolve(Symbol *symbol) const {
|
2019-03-19 02:48:02 +08:00
|
|
|
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) {
|
2019-11-23 04:40:37 +08:00
|
|
|
kind_ = GenericKind::OtherKind::DefinedOp;
|
2019-03-19 02:48:02 +08:00
|
|
|
parseName_ = &name.v;
|
2019-08-21 20:33:03 +08:00
|
|
|
symbolName_ = name.v.source;
|
2019-03-19 02:48:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void GenericSpecInfo::Analyze(const parser::GenericSpec &x) {
|
2019-08-21 20:33:03 +08:00
|
|
|
symbolName_ = x.source;
|
2019-03-19 02:48:02 +08:00
|
|
|
kind_ = std::visit(
|
|
|
|
common::visitors{
|
2019-11-23 04:40:37 +08:00
|
|
|
[&](const parser::Name &y) -> GenericKind {
|
2019-03-19 02:48:02 +08:00
|
|
|
parseName_ = &y;
|
2019-08-21 20:33:03 +08:00
|
|
|
symbolName_ = y.source;
|
2019-11-23 04:40:37 +08:00
|
|
|
return GenericKind::OtherKind::Name;
|
2019-03-19 02:48:02 +08:00
|
|
|
},
|
|
|
|
[&](const parser::DefinedOperator &y) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
2019-11-23 04:40:37 +08:00
|
|
|
[&](const parser::DefinedOpName &z) -> GenericKind {
|
2019-03-19 02:48:02 +08:00
|
|
|
Analyze(z);
|
2019-11-23 04:40:37 +08:00
|
|
|
return GenericKind::OtherKind::DefinedOp;
|
2019-03-19 02:48:02 +08:00
|
|
|
},
|
|
|
|
[&](const IntrinsicOperator &z) {
|
|
|
|
return MapIntrinsicOperator(z);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
y.u);
|
|
|
|
},
|
2019-11-23 04:40:37 +08:00
|
|
|
[&](const parser::GenericSpec::Assignment &) -> GenericKind {
|
|
|
|
return GenericKind::OtherKind::Assignment;
|
2019-03-19 02:48:02 +08:00
|
|
|
},
|
2019-11-23 04:40:37 +08:00
|
|
|
[&](const parser::GenericSpec::ReadFormatted &) -> GenericKind {
|
|
|
|
return GenericKind::DefinedIo::ReadFormatted;
|
2019-03-19 02:48:02 +08:00
|
|
|
},
|
2019-11-23 04:40:37 +08:00
|
|
|
[&](const parser::GenericSpec::ReadUnformatted &) -> GenericKind {
|
|
|
|
return GenericKind::DefinedIo::ReadUnformatted;
|
2019-03-19 02:48:02 +08:00
|
|
|
},
|
2019-11-23 04:40:37 +08:00
|
|
|
[&](const parser::GenericSpec::WriteFormatted &) -> GenericKind {
|
|
|
|
return GenericKind::DefinedIo::WriteFormatted;
|
2019-03-19 02:48:02 +08:00
|
|
|
},
|
2019-11-23 04:40:37 +08:00
|
|
|
[&](const parser::GenericSpec::WriteUnformatted &) -> GenericKind {
|
|
|
|
return GenericKind::DefinedIo::WriteUnformatted;
|
2019-03-19 02:48:02 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
x.u);
|
|
|
|
}
|
|
|
|
|
2020-12-16 23:06:53 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-03-19 02:48:02 +08:00
|
|
|
// parser::DefinedOperator::IntrinsicOperator -> GenericKind
|
|
|
|
static GenericKind MapIntrinsicOperator(IntrinsicOperator op) {
|
|
|
|
switch (op) {
|
2019-10-03 06:48:20 +08:00
|
|
|
SWITCH_COVERS_ALL_CASES
|
2020-03-29 12:00:16 +08:00
|
|
|
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;
|
2019-03-19 02:48:02 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-05 05:46:40 +08:00
|
|
|
class ArraySpecAnalyzer {
|
|
|
|
public:
|
2019-04-05 07:32:37 +08:00
|
|
|
ArraySpecAnalyzer(SemanticsContext &context) : context_{context} {}
|
|
|
|
ArraySpec Analyze(const parser::ArraySpec &);
|
2021-01-21 04:34:08 +08:00
|
|
|
ArraySpec AnalyzeDeferredShapeSpecList(const parser::DeferredShapeSpecList &);
|
2019-04-26 06:05:41 +08:00
|
|
|
ArraySpec Analyze(const parser::ComponentArraySpec &);
|
2019-04-05 07:32:37 +08:00
|
|
|
ArraySpec Analyze(const parser::CoarraySpec &);
|
2019-04-05 05:46:40 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
SemanticsContext &context_;
|
2019-04-05 07:32:37 +08:00
|
|
|
ArraySpec arraySpec_;
|
2019-04-05 05:46:40 +08:00
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename T> void Analyze(const std::list<T> &list) {
|
2019-04-05 05:46:40 +08:00
|
|
|
for (const auto &elem : list) {
|
|
|
|
Analyze(elem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void Analyze(const parser::AssumedShapeSpec &);
|
|
|
|
void Analyze(const parser::ExplicitShapeSpec &);
|
|
|
|
void Analyze(const parser::AssumedImpliedSpec &);
|
2019-04-26 06:05:41 +08:00
|
|
|
void Analyze(const parser::DeferredShapeSpecList &);
|
2019-04-05 05:46:40 +08:00
|
|
|
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 &);
|
|
|
|
};
|
|
|
|
|
2019-04-05 07:32:37 +08:00
|
|
|
ArraySpec AnalyzeArraySpec(
|
|
|
|
SemanticsContext &context, const parser::ArraySpec &arraySpec) {
|
|
|
|
return ArraySpecAnalyzer{context}.Analyze(arraySpec);
|
2019-04-05 05:46:40 +08:00
|
|
|
}
|
2019-04-26 06:05:41 +08:00
|
|
|
ArraySpec AnalyzeArraySpec(
|
|
|
|
SemanticsContext &context, const parser::ComponentArraySpec &arraySpec) {
|
|
|
|
return ArraySpecAnalyzer{context}.Analyze(arraySpec);
|
|
|
|
}
|
2021-01-21 04:34:08 +08:00
|
|
|
ArraySpec AnalyzeDeferredShapeSpecList(SemanticsContext &context,
|
|
|
|
const parser::DeferredShapeSpecList &deferredShapeSpecs) {
|
|
|
|
return ArraySpecAnalyzer{context}.AnalyzeDeferredShapeSpecList(
|
|
|
|
deferredShapeSpecs);
|
|
|
|
}
|
2019-04-05 07:32:37 +08:00
|
|
|
ArraySpec AnalyzeCoarraySpec(
|
|
|
|
SemanticsContext &context, const parser::CoarraySpec &coarraySpec) {
|
|
|
|
return ArraySpecAnalyzer{context}.Analyze(coarraySpec);
|
2019-04-05 05:46:40 +08:00
|
|
|
}
|
|
|
|
|
2019-04-26 06:05:41 +08:00
|
|
|
ArraySpec ArraySpecAnalyzer::Analyze(const parser::ComponentArraySpec &x) {
|
|
|
|
std::visit([this](const auto &y) { Analyze(y); }, x.u);
|
2019-08-07 04:36:18 +08:00
|
|
|
CHECK(!arraySpec_.empty());
|
2019-04-26 06:05:41 +08:00
|
|
|
return arraySpec_;
|
|
|
|
}
|
2019-04-05 07:32:37 +08:00
|
|
|
ArraySpec ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
|
2020-03-29 12:00:16 +08:00
|
|
|
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); },
|
|
|
|
},
|
2019-04-05 05:46:40 +08:00
|
|
|
x.u);
|
2019-08-07 04:36:18 +08:00
|
|
|
CHECK(!arraySpec_.empty());
|
2019-04-05 07:32:37 +08:00
|
|
|
return arraySpec_;
|
2019-04-05 05:46:40 +08:00
|
|
|
}
|
2021-01-21 04:34:08 +08:00
|
|
|
ArraySpec ArraySpecAnalyzer::AnalyzeDeferredShapeSpecList(
|
|
|
|
const parser::DeferredShapeSpecList &x) {
|
|
|
|
Analyze(x);
|
|
|
|
CHECK(!arraySpec_.empty());
|
|
|
|
return arraySpec_;
|
|
|
|
}
|
2019-04-05 07:32:37 +08:00
|
|
|
ArraySpec ArraySpecAnalyzer::Analyze(const parser::CoarraySpec &x) {
|
2019-04-05 05:46:40 +08:00
|
|
|
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);
|
2019-08-07 04:36:18 +08:00
|
|
|
CHECK(!arraySpec_.empty());
|
2019-04-05 07:32:37 +08:00
|
|
|
return arraySpec_;
|
2019-04-05 05:46:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2019-04-26 06:05:41 +08:00
|
|
|
void ArraySpecAnalyzer::Analyze(const parser::DeferredShapeSpecList &x) {
|
|
|
|
MakeDeferred(x.v);
|
|
|
|
}
|
2019-04-05 05:46:40 +08:00
|
|
|
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)};
|
|
|
|
}
|
|
|
|
|
2019-06-12 09:26:48 +08:00
|
|
|
// 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()) {
|
2019-06-13 03:38:04 +08:00
|
|
|
auto &lbound{spec.lbound().GetExplicit().value()};
|
|
|
|
subscripts.push_back(evaluate::ToInt64(lbound).value());
|
2019-06-12 09:26:48 +08:00
|
|
|
}
|
|
|
|
}
|
2019-06-13 03:38:04 +08:00
|
|
|
auto substringStart{currObject_.substringStart};
|
2020-07-02 02:45:38 +08:00
|
|
|
currSet_.emplace_back(
|
|
|
|
symbol, subscripts, substringStart, designator.source);
|
2019-06-12 09:26:48 +08:00
|
|
|
PropagateSaveAttr(currSet_.back(), currSet_);
|
|
|
|
}
|
|
|
|
currObject_ = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
void EquivalenceSets::FinishSet(const parser::CharBlock &source) {
|
2020-03-29 12:00:16 +08:00
|
|
|
std::set<std::size_t> existing; // indices of sets intersecting this one
|
2019-06-12 09:26:48 +08:00
|
|
|
for (auto &obj : currSet_) {
|
|
|
|
auto it{objectToSet_.find(obj)};
|
|
|
|
if (it != objectToSet_.end()) {
|
2020-03-29 12:00:16 +08:00
|
|
|
existing.insert(it->second); // symbol already in this set
|
2019-06-12 09:26:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (existing.empty()) {
|
2020-03-29 12:00:16 +08:00
|
|
|
sets_.push_back({}); // create a new equivalence set
|
2019-06-12 09:26:48 +08:00
|
|
|
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) {
|
2019-06-13 03:38:04 +08:00
|
|
|
std::optional<parser::MessageFixedText> msg;
|
2019-06-12 09:26:48 +08:00
|
|
|
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) &&
|
2020-03-29 12:00:16 +08:00
|
|
|
!sym2.attrs().test(Attr::PROTECTED)) { // C8114
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Equivalence set cannot contain '%s'"
|
|
|
|
" with PROTECTED attribute and '%s' without"_err_en_US;
|
|
|
|
} else if (isNum1) {
|
2019-06-20 07:34:22 +08:00
|
|
|
if (isChar2) {
|
|
|
|
if (context_.ShouldWarn(
|
2019-11-07 03:15:03 +08:00
|
|
|
LanguageFeature::EquivalenceNumericWithCharacter)) {
|
2019-06-20 07:34:22 +08:00
|
|
|
msg = "Equivalence set contains '%s' that is numeric sequence "
|
|
|
|
"type and '%s' that is character"_en_US;
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (!isNum2) { // C8110
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Equivalence set cannot contain '%s'"
|
|
|
|
" that is numeric sequence type and '%s' that is not"_err_en_US;
|
|
|
|
}
|
|
|
|
} else if (isChar1) {
|
2019-06-20 07:34:22 +08:00
|
|
|
if (isNum2) {
|
|
|
|
if (context_.ShouldWarn(
|
2019-11-07 03:15:03 +08:00
|
|
|
LanguageFeature::EquivalenceNumericWithCharacter)) {
|
2019-06-20 07:34:22 +08:00
|
|
|
msg = "Equivalence set contains '%s' that is character sequence "
|
|
|
|
"type and '%s' that is numeric"_en_US;
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (!isChar2) { // C8111
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Equivalence set cannot contain '%s'"
|
|
|
|
" that is character sequence type and '%s' that is not"_err_en_US;
|
|
|
|
}
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (!isNum2 && !isChar2 && *type1 != *type2) { // C8112, C8113
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Equivalence set cannot contain '%s' and '%s' with different types"
|
|
|
|
" that are neither numeric nor character sequence types"_err_en_US;
|
|
|
|
}
|
2019-06-13 03:38:04 +08:00
|
|
|
if (msg) {
|
|
|
|
context_.Say(source, std::move(*msg), sym1.name(), sym2.name());
|
2019-06-12 09:26:48 +08:00
|
|
|
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) {
|
2020-07-02 02:45:38 +08:00
|
|
|
dst.push_back(obj);
|
2019-06-12 09:26:48 +08:00
|
|
|
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)}) {
|
2019-06-13 03:38:04 +08:00
|
|
|
ok &= CheckSubstringBound(lb->thing.thing.value(), true);
|
|
|
|
} else {
|
|
|
|
currObject_.substringStart = 1;
|
2019-06-12 09:26:48 +08:00
|
|
|
}
|
|
|
|
if (const auto &ub{std::get<1>(range.t)}) {
|
2019-06-13 03:38:04 +08:00
|
|
|
ok &= CheckSubstringBound(ub->thing.thing.value(), false);
|
2019-06-12 09:26:48 +08:00
|
|
|
}
|
|
|
|
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> &) {
|
2020-03-29 12:00:16 +08:00
|
|
|
context_.Say(source, // C8107
|
2019-06-12 09:26:48 +08:00
|
|
|
"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{
|
2019-08-16 04:50:27 +08:00
|
|
|
[&](const parser::SubscriptTriplet &) {
|
2020-03-29 12:00:16 +08:00
|
|
|
context_.Say(source, // C924, R872
|
2019-06-12 09:26:48 +08:00
|
|
|
"Array section '%s' is not allowed in an equivalence set"_err_en_US,
|
|
|
|
source);
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
[&](const parser::IntExpr &y) {
|
2019-06-13 03:38:04 +08:00
|
|
|
return CheckArrayBound(y.thing.value());
|
2019-06-12 09:26:48 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
subscript.u);
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
},
|
|
|
|
[&](const common::Indirection<parser::CoindexedNamedObject> &) {
|
2020-03-29 12:00:16 +08:00
|
|
|
context_.Say(source, // C924 (R872)
|
2019-06-12 09:26:48 +08:00
|
|
|
"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) {
|
2020-03-29 12:00:16 +08:00
|
|
|
return false; // an error has already occurred
|
2019-06-12 09:26:48 +08:00
|
|
|
}
|
|
|
|
currObject_.symbol = name.symbol;
|
|
|
|
parser::MessageFixedText msg{"", 0};
|
|
|
|
const Symbol &symbol{*name.symbol};
|
2020-03-29 12:00:16 +08:00
|
|
|
if (symbol.owner().IsDerivedType()) { // C8107
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Derived type component '%s'"
|
|
|
|
" is not allowed in an equivalence set"_err_en_US;
|
[flang][NFC] Remove link-time dependency of Evaluate on Semantics
Summary:
Some Symbol-related functions used in Evaluate were moved to
Evaluate/tools.h. This includes changing some member functions that were
replaced by non-member functions `IsDummy`, `GetUsedModule`, and
`CountLenParameters`.
Some member functions were made inline in `Scope`, `Symbol`,
`ArraySpec`, and `DeclTypeSpec`. The definitions were preceded by a
comment explaining why they are inline.
`IsConstantShape` was expanded inline in `IsDescriptor` because it isn't
used anywhere else
After this change, at least when compiling with clang on macos,
`libFortranEvaluate.a` has no undefined symbols that are satisfied by
`libFortranSemantics.a`.
Reviewers: klausler, PeteSteinfeld, sscalpone, jdoerfert, DavidTruby
Reviewed By: PeteSteinfeld
Subscribers: llvm-commits
Tags: #flang, #llvm
Differential Revision: https://reviews.llvm.org/D80762
2020-05-30 07:39:13 +08:00
|
|
|
} else if (IsDummy(symbol)) { // C8106
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Dummy argument '%s' is not allowed in an equivalence set"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (symbol.IsFuncResult()) { // C8106
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Function result '%s' is not allow in an equivalence set"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (IsPointer(symbol)) { // C8106
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Pointer '%s' is not allowed in an equivalence set"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (IsAllocatable(symbol)) { // C8106
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Allocatable variable '%s'"
|
|
|
|
" is not allowed in an equivalence set"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (symbol.Corank() > 0) { // C8106
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Coarray '%s' is not allowed in an equivalence set"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (symbol.has<UseDetails>()) { // C8115
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Use-associated variable '%s'"
|
|
|
|
" is not allowed in an equivalence set"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (symbol.attrs().test(Attr::BIND_C)) { // C8106
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Variable '%s' with BIND attribute"
|
|
|
|
" is not allowed in an equivalence set"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (symbol.attrs().test(Attr::TARGET)) { // C8108
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Variable '%s' with TARGET attribute"
|
|
|
|
" is not allowed in an equivalence set"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (IsNamedConstant(symbol)) { // C8106
|
2019-06-12 09:26:48 +08:00
|
|
|
msg = "Named constant '%s' is not allowed in an equivalence set"_err_en_US;
|
2020-03-29 12:00:16 +08:00
|
|
|
} else if (InCommonWithBind(symbol)) { // C8106
|
2019-06-12 09:26:48 +08:00
|
|
|
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(
|
2020-03-29 12:00:16 +08:00
|
|
|
*derived, IsAllocatableOrPointer)}) { // C8106
|
2019-06-12 09:26:48 +08:00
|
|
|
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;
|
|
|
|
}
|
2020-06-03 12:56:10 +08:00
|
|
|
} else if (IsAutomaticObject(symbol)) {
|
|
|
|
msg = "Automatic object '%s'"
|
|
|
|
" is not allowed in an equivalence set"_err_en_US;
|
2019-06-12 09:26:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!msg.text().empty()) {
|
|
|
|
context_.Say(name.source, std::move(msg), name.source);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-06-13 03:38:04 +08:00
|
|
|
bool EquivalenceSets::CheckArrayBound(const parser::Expr &bound) {
|
2019-06-12 09:26:48 +08:00
|
|
|
MaybeExpr expr{
|
|
|
|
evaluate::Fold(context_.foldingContext(), AnalyzeExpr(context_, bound))};
|
|
|
|
if (!expr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (expr->Rank() > 0) {
|
2020-03-29 12:00:16 +08:00
|
|
|
context_.Say(bound.source, // C924, R872
|
2019-06-12 09:26:48 +08:00
|
|
|
"Array with vector subscript '%s' is not allowed in an equivalence set"_err_en_US,
|
|
|
|
bound.source);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
auto subscript{evaluate::ToInt64(*expr)};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!subscript) {
|
2020-03-29 12:00:16 +08:00
|
|
|
context_.Say(bound.source, // C8109
|
2019-06-13 03:38:04 +08:00
|
|
|
"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) {
|
2019-06-12 09:26:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
2019-06-13 03:38:04 +08:00
|
|
|
auto subscript{evaluate::ToInt64(*expr)};
|
2019-11-10 01:29:31 +08:00
|
|
|
if (!subscript) {
|
2020-03-29 12:00:16 +08:00
|
|
|
context_.Say(bound.source, // C8109
|
2019-06-13 03:38:04 +08:00
|
|
|
"Substring with nonconstant bound '%s' is not allowed in an equivalence set"_err_en_US,
|
|
|
|
bound.source);
|
2019-06-12 09:26:48 +08:00
|
|
|
return false;
|
|
|
|
}
|
2019-06-13 03:38:04 +08:00
|
|
|
if (!isStart) {
|
|
|
|
auto start{currObject_.substringStart};
|
|
|
|
if (*subscript < (start ? *start : 1)) {
|
2020-03-29 12:00:16 +08:00
|
|
|
context_.Say(bound.source, // C8116
|
2019-06-13 03:38:04 +08:00
|
|
|
"Substring with zero length is not allowed in an equivalence set"_err_en_US);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (*subscript != 1) {
|
|
|
|
currObject_.substringStart = *subscript;
|
|
|
|
}
|
2019-06-12 09:26:48 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EquivalenceSets::IsCharacterSequenceType(const DeclTypeSpec *type) {
|
|
|
|
return IsSequenceType(type, [&](const IntrinsicTypeSpec &type) {
|
|
|
|
auto kind{evaluate::ToInt64(type.kind())};
|
2019-11-10 01:29:31 +08:00
|
|
|
return type.category() == TypeCategory::Character && kind &&
|
2019-06-12 09:26:48 +08:00
|
|
|
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:
|
2020-03-29 12:00:16 +08:00
|
|
|
case TypeCategory::Logical:
|
|
|
|
return *kind == defaultKind;
|
2019-06-12 09:26:48 +08:00
|
|
|
case TypeCategory::Real:
|
|
|
|
case TypeCategory::Complex:
|
|
|
|
return *kind == defaultKind || *kind == context_.doublePrecisionKind();
|
2020-03-29 12:00:16 +08:00
|
|
|
default:
|
|
|
|
return false;
|
2019-06-12 09:26:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::semantics
|