forked from OSchip/llvm-project
[flang] substrings and better rank checks
Original-commit: flang-compiler/f18@4fa483ac49 Reviewed-on: https://github.com/flang-compiler/f18/pull/195 Tree-same-pre-rewrite: false
This commit is contained in:
parent
0787d7f2df
commit
ad2fda8932
|
@ -642,7 +642,8 @@ public:
|
||||||
CLASS_BOILERPLATE(Expr)
|
CLASS_BOILERPLATE(Expr)
|
||||||
|
|
||||||
template<typename A>
|
template<typename A>
|
||||||
explicit Expr(const Result &r, const A &x) : result{r}, u{x} {}
|
explicit Expr(const semantics::DerivedTypeSpec &dts, const A &x)
|
||||||
|
: result{dts}, u{x} {}
|
||||||
template<typename A>
|
template<typename A>
|
||||||
explicit Expr(Result &&r, std::enable_if_t<!std::is_reference_v<A>, A> &&x)
|
explicit Expr(Result &&r, std::enable_if_t<!std::is_reference_v<A>, A> &&x)
|
||||||
: result{std::move(r)}, u{std::move(x)} {}
|
: result{std::move(r)}, u{std::move(x)} {}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
namespace Fortran::evaluate {
|
namespace Fortran::evaluate {
|
||||||
|
|
||||||
ENUM_CLASS(IntrinsicProcedure, LEN, MAX, MIN)
|
ENUM_CLASS(IntrinsicProcedure, IAND, IEOR, IOR, LEN, MAX, MIN)
|
||||||
|
|
||||||
} // namespace Fortran::evaluate
|
} // namespace Fortran::evaluate
|
||||||
#endif // FORTRAN_EVALUATE_INTRINSICS_H_
|
#endif // FORTRAN_EVALUATE_INTRINSICS_H_
|
||||||
|
|
|
@ -43,12 +43,19 @@ namespace Fortran::evaluate {
|
||||||
|
|
||||||
using common::TypeCategory;
|
using common::TypeCategory;
|
||||||
|
|
||||||
|
struct DynamicType {
|
||||||
|
TypeCategory category;
|
||||||
|
int kind{0};
|
||||||
|
const semantics::DerivedTypeSpec *derived{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
// Specific intrinsic types are represented by specializations of
|
// Specific intrinsic types are represented by specializations of
|
||||||
// this class template Type<CATEGORY, KIND>.
|
// this class template Type<CATEGORY, KIND>.
|
||||||
template<TypeCategory CATEGORY, int KIND = 0> class Type;
|
template<TypeCategory CATEGORY, int KIND = 0> class Type;
|
||||||
|
|
||||||
template<TypeCategory CATEGORY, int KIND> struct TypeBase {
|
template<TypeCategory CATEGORY, int KIND> struct TypeBase {
|
||||||
static constexpr bool isSpecificType{true};
|
static constexpr bool isSpecificType{true};
|
||||||
|
static constexpr DynamicType dynamicType{CATEGORY, KIND};
|
||||||
static constexpr TypeCategory category{CATEGORY};
|
static constexpr TypeCategory category{CATEGORY};
|
||||||
static constexpr int kind{KIND};
|
static constexpr int kind{KIND};
|
||||||
static std::string Dump() {
|
static std::string Dump() {
|
||||||
|
|
|
@ -315,9 +315,6 @@ std::ostream &ProcedureRef<ARG>::Dump(std::ostream &o) const {
|
||||||
|
|
||||||
std::ostream &Variable::Dump(std::ostream &o) const { return Emit(o, u); }
|
std::ostream &Variable::Dump(std::ostream &o) const { return Emit(o, u); }
|
||||||
|
|
||||||
std::ostream &ActualFunctionArg::Dump(std::ostream &o) const {
|
|
||||||
return Emit(o, u);
|
|
||||||
}
|
|
||||||
std::ostream &ActualSubroutineArg::Dump(std::ostream &o) const {
|
std::ostream &ActualSubroutineArg::Dump(std::ostream &o) const {
|
||||||
return Emit(o, u);
|
return Emit(o, u);
|
||||||
}
|
}
|
||||||
|
@ -370,7 +367,24 @@ Expr<SubscriptInteger> ProcedureDesignator::LEN() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rank()
|
// Rank()
|
||||||
int Component::Rank() const { return symbol_->Rank(); }
|
int Component::Rank() const {
|
||||||
|
int baseRank{base_->Rank()};
|
||||||
|
int symbolRank{symbol_->Rank()};
|
||||||
|
CHECK(baseRank == 0 || symbolRank == 0);
|
||||||
|
return baseRank + symbolRank;
|
||||||
|
}
|
||||||
|
template<typename A> int ProcedureRef<A>::Rank() const {
|
||||||
|
if constexpr (std::is_same_v<A, ActualFunctionArg>) { // FunctionRef
|
||||||
|
// TODO: Rank of elemental function reference depends on actual arguments
|
||||||
|
return std::visit(
|
||||||
|
common::visitors{[](IntrinsicProcedure) { return 0 /*TODO!!*/; },
|
||||||
|
[](const Symbol *sym) { return sym->Rank(); },
|
||||||
|
[](const Component &c) { return c.symbol().Rank(); }},
|
||||||
|
proc().u);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
int Subscript::Rank() const {
|
int Subscript::Rank() const {
|
||||||
return std::visit(common::visitors{[](const IndirectSubscriptIntegerExpr &x) {
|
return std::visit(common::visitors{[](const IndirectSubscriptIntegerExpr &x) {
|
||||||
int rank{x->Rank()};
|
int rank{x->Rank()};
|
||||||
|
@ -385,7 +399,12 @@ int ArrayRef::Rank() const {
|
||||||
for (std::size_t j{0}; j < subscript.size(); ++j) {
|
for (std::size_t j{0}; j < subscript.size(); ++j) {
|
||||||
rank += subscript[j].Rank();
|
rank += subscript[j].Rank();
|
||||||
}
|
}
|
||||||
return rank;
|
int baseRank{std::visit(
|
||||||
|
common::visitors{[](const Symbol *symbol) { return symbol->Rank(); },
|
||||||
|
[](const auto &x) { return x.Rank(); }},
|
||||||
|
u)};
|
||||||
|
CHECK(rank == 0 || baseRank == 0);
|
||||||
|
return baseRank + rank;
|
||||||
}
|
}
|
||||||
int CoarrayRef::Rank() const {
|
int CoarrayRef::Rank() const {
|
||||||
int rank{0};
|
int rank{0};
|
||||||
|
@ -406,25 +425,9 @@ int Substring::Rank() const {
|
||||||
u_);
|
u_);
|
||||||
}
|
}
|
||||||
int ComplexPart::Rank() const { return complex_.Rank(); }
|
int ComplexPart::Rank() const { return complex_.Rank(); }
|
||||||
template<> int FunctionRef::Rank() const {
|
|
||||||
// TODO: Rank of elemental function reference depends on actual arguments
|
|
||||||
return std::visit(
|
|
||||||
common::visitors{[](IntrinsicProcedure) { return 0 /*TODO!!*/; },
|
|
||||||
[](const Symbol *sym) { return sym->Rank(); },
|
|
||||||
[](const Component &c) { return c.symbol().Rank(); }},
|
|
||||||
proc().u);
|
|
||||||
}
|
|
||||||
int Variable::Rank() const {
|
int Variable::Rank() const {
|
||||||
return std::visit([](const auto &x) { return x.Rank(); }, u);
|
return std::visit([](const auto &x) { return x.Rank(); }, u);
|
||||||
}
|
}
|
||||||
int ActualFunctionArg::Rank() const {
|
|
||||||
return std::visit(
|
|
||||||
common::visitors{[](const CopyableIndirection<Expr<SomeType>> &x) {
|
|
||||||
return x->Rank();
|
|
||||||
},
|
|
||||||
[](const auto &x) { return x.Rank(); }},
|
|
||||||
u);
|
|
||||||
}
|
|
||||||
int ActualSubroutineArg::Rank() const {
|
int ActualSubroutineArg::Rank() const {
|
||||||
return std::visit(
|
return std::visit(
|
||||||
common::visitors{[](const CopyableIndirection<Expr<SomeType>> &x) {
|
common::visitors{[](const CopyableIndirection<Expr<SomeType>> &x) {
|
||||||
|
@ -435,7 +438,33 @@ int ActualSubroutineArg::Rank() const {
|
||||||
u);
|
u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSymbol
|
||||||
|
const Symbol *Component::GetSymbol(bool first) const {
|
||||||
|
return base_->GetSymbol(first);
|
||||||
|
}
|
||||||
|
const Symbol *ArrayRef::GetSymbol(bool first) const {
|
||||||
|
return std::visit(common::visitors{[](const Symbol *sym) { return sym; },
|
||||||
|
[=](const Component &component) {
|
||||||
|
return component.GetSymbol(first);
|
||||||
|
}},
|
||||||
|
u);
|
||||||
|
}
|
||||||
|
const Symbol *DataRef::GetSymbol(bool first) const {
|
||||||
|
return std::visit(common::visitors{[](const Symbol *sym) { return sym; },
|
||||||
|
[=](const auto &x) { return x.GetSymbol(first); }},
|
||||||
|
u);
|
||||||
|
}
|
||||||
|
const Symbol *Substring::GetSymbol(bool first) const {
|
||||||
|
if (const DataRef * dataRef{std::get_if<DataRef>(&u_)}) {
|
||||||
|
return dataRef->GetSymbol(first);
|
||||||
|
} else {
|
||||||
|
return nullptr; // substring of character literal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template class Designator<Type<TypeCategory::Character, 1>>;
|
template class Designator<Type<TypeCategory::Character, 1>>;
|
||||||
template class Designator<Type<TypeCategory::Character, 2>>;
|
template class Designator<Type<TypeCategory::Character, 2>>;
|
||||||
template class Designator<Type<TypeCategory::Character, 4>>;
|
template class Designator<Type<TypeCategory::Character, 4>>;
|
||||||
|
template class ProcedureRef<ActualFunctionArg>; // FunctionRef
|
||||||
|
template class ProcedureRef<ActualSubroutineArg>;
|
||||||
} // namespace Fortran::evaluate
|
} // namespace Fortran::evaluate
|
||||||
|
|
|
@ -40,7 +40,6 @@ using semantics::Symbol;
|
||||||
template<typename A> class Expr;
|
template<typename A> class Expr;
|
||||||
struct DataRef;
|
struct DataRef;
|
||||||
struct Variable;
|
struct Variable;
|
||||||
struct ActualFunctionArg;
|
|
||||||
|
|
||||||
// Subscript and cosubscript expressions are of a kind that matches the
|
// Subscript and cosubscript expressions are of a kind that matches the
|
||||||
// address size, at least at the top level.
|
// address size, at least at the top level.
|
||||||
|
@ -64,6 +63,7 @@ public:
|
||||||
DataRef &base() { return *base_; }
|
DataRef &base() { return *base_; }
|
||||||
const Symbol &symbol() const { return *symbol_; }
|
const Symbol &symbol() const { return *symbol_; }
|
||||||
int Rank() const;
|
int Rank() const;
|
||||||
|
const Symbol *GetSymbol(bool first) const;
|
||||||
Expr<SubscriptInteger> LEN() const;
|
Expr<SubscriptInteger> LEN() const;
|
||||||
std::ostream &Dump(std::ostream &) const;
|
std::ostream &Dump(std::ostream &) const;
|
||||||
|
|
||||||
|
@ -112,6 +112,7 @@ struct ArrayRef {
|
||||||
: u{std::move(c)}, subscript(std::move(ss)) {}
|
: u{std::move(c)}, subscript(std::move(ss)) {}
|
||||||
|
|
||||||
int Rank() const;
|
int Rank() const;
|
||||||
|
const Symbol *GetSymbol(bool first) const;
|
||||||
Expr<SubscriptInteger> LEN() const;
|
Expr<SubscriptInteger> LEN() const;
|
||||||
std::ostream &Dump(std::ostream &) const;
|
std::ostream &Dump(std::ostream &) const;
|
||||||
|
|
||||||
|
@ -134,7 +135,15 @@ public:
|
||||||
std::vector<Expr<SubscriptInteger>> &&); // TODO: stat & team?
|
std::vector<Expr<SubscriptInteger>> &&); // TODO: stat & team?
|
||||||
CoarrayRef &setStat(Variable &&);
|
CoarrayRef &setStat(Variable &&);
|
||||||
CoarrayRef &setTeam(Variable &&, bool isTeamNumber = false);
|
CoarrayRef &setTeam(Variable &&, bool isTeamNumber = false);
|
||||||
|
|
||||||
int Rank() const;
|
int Rank() const;
|
||||||
|
const Symbol *GetSymbol(bool first) const {
|
||||||
|
if (first) {
|
||||||
|
return base_.front();
|
||||||
|
} else {
|
||||||
|
return base_.back();
|
||||||
|
}
|
||||||
|
}
|
||||||
Expr<SubscriptInteger> LEN() const;
|
Expr<SubscriptInteger> LEN() const;
|
||||||
std::ostream &Dump(std::ostream &) const;
|
std::ostream &Dump(std::ostream &) const;
|
||||||
|
|
||||||
|
@ -155,6 +164,7 @@ struct DataRef {
|
||||||
explicit DataRef(const Symbol &n) : u{&n} {}
|
explicit DataRef(const Symbol &n) : u{&n} {}
|
||||||
|
|
||||||
int Rank() const;
|
int Rank() const;
|
||||||
|
const Symbol *GetSymbol(bool first) const;
|
||||||
Expr<SubscriptInteger> LEN() const;
|
Expr<SubscriptInteger> LEN() const;
|
||||||
std::ostream &Dump(std::ostream &) const;
|
std::ostream &Dump(std::ostream &) const;
|
||||||
|
|
||||||
|
@ -177,6 +187,7 @@ public:
|
||||||
Expr<SubscriptInteger> first() const;
|
Expr<SubscriptInteger> first() const;
|
||||||
Expr<SubscriptInteger> last() const;
|
Expr<SubscriptInteger> last() const;
|
||||||
int Rank() const;
|
int Rank() const;
|
||||||
|
const Symbol *GetSymbol(bool first) const;
|
||||||
Expr<SubscriptInteger> LEN() const;
|
Expr<SubscriptInteger> LEN() const;
|
||||||
std::optional<std::string> Fold(FoldingContext &);
|
std::optional<std::string> Fold(FoldingContext &);
|
||||||
std::ostream &Dump(std::ostream &) const;
|
std::ostream &Dump(std::ostream &) const;
|
||||||
|
@ -198,6 +209,9 @@ public:
|
||||||
const DataRef &complex() const { return complex_; }
|
const DataRef &complex() const { return complex_; }
|
||||||
Part part() const { return part_; }
|
Part part() const { return part_; }
|
||||||
int Rank() const;
|
int Rank() const;
|
||||||
|
const Symbol *GetSymbol(bool first) const {
|
||||||
|
return complex_.GetSymbol(first);
|
||||||
|
}
|
||||||
std::ostream &Dump(std::ostream &) const;
|
std::ostream &Dump(std::ostream &) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -236,6 +250,12 @@ public:
|
||||||
u);
|
u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Symbol *GetSymbol(bool first) const {
|
||||||
|
return std::visit(common::visitors{[](const Symbol *sym) { return sym; },
|
||||||
|
[=](const auto &x) { return x.GetSymbol(first); }},
|
||||||
|
u);
|
||||||
|
}
|
||||||
|
|
||||||
Expr<SubscriptInteger> LEN() const;
|
Expr<SubscriptInteger> LEN() const;
|
||||||
|
|
||||||
std::ostream &Dump(std::ostream &o) const {
|
std::ostream &Dump(std::ostream &o) const {
|
||||||
|
@ -250,10 +270,6 @@ public:
|
||||||
Variant u;
|
Variant u;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern template class Designator<Type<TypeCategory::Character, 1>>;
|
|
||||||
extern template class Designator<Type<TypeCategory::Character, 2>>;
|
|
||||||
extern template class Designator<Type<TypeCategory::Character, 4>>;
|
|
||||||
|
|
||||||
struct ProcedureDesignator {
|
struct ProcedureDesignator {
|
||||||
EVALUATE_UNION_CLASS_BOILERPLATE(ProcedureDesignator)
|
EVALUATE_UNION_CLASS_BOILERPLATE(ProcedureDesignator)
|
||||||
explicit ProcedureDesignator(IntrinsicProcedure p) : u{p} {}
|
explicit ProcedureDesignator(IntrinsicProcedure p) : u{p} {}
|
||||||
|
@ -280,6 +296,10 @@ private:
|
||||||
std::vector<ArgumentType> argument_;
|
std::vector<ArgumentType> argument_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Subtlety: There is a distinction that must be maintained here between an
|
||||||
|
// actual argument expression that *is* a variable and one that is not,
|
||||||
|
// e.g. between X and (X).
|
||||||
|
using ActualFunctionArg = CopyableIndirection<Expr<SomeType>>;
|
||||||
using FunctionRef = ProcedureRef<ActualFunctionArg>;
|
using FunctionRef = ProcedureRef<ActualFunctionArg>;
|
||||||
|
|
||||||
struct Variable {
|
struct Variable {
|
||||||
|
@ -289,17 +309,6 @@ struct Variable {
|
||||||
std::variant<DataRef, Substring, ComplexPart, FunctionRef> u;
|
std::variant<DataRef, Substring, ComplexPart, FunctionRef> u;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ActualFunctionArg {
|
|
||||||
EVALUATE_UNION_CLASS_BOILERPLATE(ActualFunctionArg)
|
|
||||||
explicit ActualFunctionArg(Expr<SomeType> &&x) : u{std::move(x)} {}
|
|
||||||
int Rank() const;
|
|
||||||
std::ostream &Dump(std::ostream &) const;
|
|
||||||
|
|
||||||
// Subtlety: There is a distinction to be respected here between a variable
|
|
||||||
// and an expression that is a variable, e.g. X vs. (X).
|
|
||||||
std::variant<CopyableIndirection<Expr<SomeType>>, Variable> u;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Label { // TODO: this is a placeholder
|
struct Label { // TODO: this is a placeholder
|
||||||
CLASS_BOILERPLATE(Label)
|
CLASS_BOILERPLATE(Label)
|
||||||
explicit Label(int lab) : label{lab} {}
|
explicit Label(int lab) : label{lab} {}
|
||||||
|
@ -321,6 +330,12 @@ public:
|
||||||
|
|
||||||
using SubroutineRef = ProcedureRef<ActualSubroutineArg>;
|
using SubroutineRef = ProcedureRef<ActualSubroutineArg>;
|
||||||
|
|
||||||
|
extern template class Designator<Type<TypeCategory::Character, 1>>;
|
||||||
|
extern template class Designator<Type<TypeCategory::Character, 2>>;
|
||||||
|
extern template class Designator<Type<TypeCategory::Character, 4>>;
|
||||||
|
extern template class ProcedureRef<ActualFunctionArg>; // FunctionRef
|
||||||
|
extern template class ProcedureRef<ActualSubroutineArg>;
|
||||||
|
|
||||||
} // namespace Fortran::evaluate
|
} // namespace Fortran::evaluate
|
||||||
|
|
||||||
#endif // FORTRAN_EVALUATE_VARIABLE_H_
|
#endif // FORTRAN_EVALUATE_VARIABLE_H_
|
||||||
|
|
|
@ -170,11 +170,15 @@ struct ExprAnalyzer {
|
||||||
std::vector<Subscript> Analyze(const std::list<parser::SectionSubscript> &);
|
std::vector<Subscript> Analyze(const std::list<parser::SectionSubscript> &);
|
||||||
|
|
||||||
std::optional<Expr<SubscriptInteger>> AsSubscript(MaybeExpr &&);
|
std::optional<Expr<SubscriptInteger>> AsSubscript(MaybeExpr &&);
|
||||||
|
std::optional<Expr<SubscriptInteger>> GetSubstringBound(
|
||||||
|
const std::optional<parser::ScalarIntExpr> &);
|
||||||
std::optional<Expr<SubscriptInteger>> TripletPart(
|
std::optional<Expr<SubscriptInteger>> TripletPart(
|
||||||
const std::optional<parser::Subscript> &);
|
const std::optional<parser::Subscript> &);
|
||||||
MaybeExpr Subscripts(const Symbol &, ArrayRef &&);
|
MaybeExpr ApplySubscripts(DataRef &&, std::vector<Subscript> &&);
|
||||||
|
MaybeExpr CompleteSubscripts(ArrayRef &&);
|
||||||
|
|
||||||
void ComponentRankCheck(const Component &);
|
MaybeExpr TopLevelChecks(DataRef &&);
|
||||||
|
void CheckUnsubscriptedComponent(const Component &);
|
||||||
|
|
||||||
FoldingContext context;
|
FoldingContext context;
|
||||||
const semantics::IntrinsicTypeDefaultKinds &defaults;
|
const semantics::IntrinsicTypeDefaultKinds &defaults;
|
||||||
|
@ -239,30 +243,11 @@ MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const common::Indirection<A> &x) {
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const parser::Designator &d) {
|
MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const parser::Designator &d) {
|
||||||
// These check have to be deferred to these "top level" data-refs where
|
// These checks have to be deferred to these "top level" data-refs where
|
||||||
// we can be sure that there are no following subscripts.
|
// we can be sure that there are no following subscripts (yet).
|
||||||
if (MaybeExpr result{AnalyzeHelper(ea, d.u)}) {
|
if (MaybeExpr result{AnalyzeHelper(ea, d.u)}) {
|
||||||
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(result))}) {
|
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(result))}) {
|
||||||
if (Component * component{std::get_if<Component>(&dataRef->u)}) {
|
return ea.TopLevelChecks(std::move(*dataRef));
|
||||||
ea.ComponentRankCheck(*component);
|
|
||||||
} else if (const Symbol **symbolPointer{
|
|
||||||
std::get_if<const Symbol *>(&dataRef->u)}) {
|
|
||||||
const Symbol &symbol{**symbolPointer};
|
|
||||||
if (const auto *details{
|
|
||||||
symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
|
|
||||||
if (details->isArray()) {
|
|
||||||
if (details->isAssumedSize()) { // C1002
|
|
||||||
// TODO: it's okay to forward an assumed-size array as an argument
|
|
||||||
// to many functions and all subroutines, though
|
|
||||||
ea.context.messages.Say(
|
|
||||||
"assumed-size array '%s' must have subscripts in expression"_err_en_US,
|
|
||||||
symbol.name().ToString().data());
|
|
||||||
}
|
|
||||||
// TODO: Whole array reference: append : subscripts, enforce C1002
|
|
||||||
// Possibly use EA::Subscripts() below.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -495,64 +480,70 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::BOZLiteralConstant &x) {
|
||||||
return {AsGenericExpr(std::move(value.value))};
|
return {AsGenericExpr(std::move(value.value))};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<TypeCategory CATEGORY>
|
template<TypeCategory CATEGORY, typename DATAREF = DataRef>
|
||||||
MaybeExpr DesignateHelper(int kind, DataRef &&dataRef) {
|
MaybeExpr DesignateHelper(int kind, DATAREF &&dataRef) {
|
||||||
return common::SearchDynamicTypes(
|
return common::SearchDynamicTypes(
|
||||||
TypeKindVisitor<CATEGORY, Designator, DataRef>{kind, std::move(dataRef)});
|
TypeKindVisitor<CATEGORY, Designator, DATAREF>{kind, std::move(dataRef)});
|
||||||
}
|
}
|
||||||
|
|
||||||
static MaybeExpr Designate(const semantics::Symbol &symbol, DataRef &&dataRef) {
|
static std::optional<DynamicType> CategorizeSymbolType(const Symbol &symbol) {
|
||||||
if (auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
|
if (auto *details{symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
|
||||||
if (details->type().has_value()) {
|
if (details->type().has_value()) {
|
||||||
switch (details->type()->category()) {
|
switch (details->type()->category()) {
|
||||||
case semantics::DeclTypeSpec::Category::Intrinsic: {
|
case semantics::DeclTypeSpec::Category::Intrinsic:
|
||||||
TypeCategory category{details->type()->intrinsicTypeSpec().category()};
|
return std::make_optional(
|
||||||
int kind{details->type()->intrinsicTypeSpec().kind()};
|
DynamicType{details->type()->intrinsicTypeSpec().category(),
|
||||||
switch (category) {
|
details->type()->intrinsicTypeSpec().kind()});
|
||||||
case TypeCategory::Integer:
|
|
||||||
return DesignateHelper<TypeCategory::Integer>(
|
|
||||||
kind, std::move(dataRef));
|
|
||||||
case TypeCategory::Real:
|
|
||||||
return DesignateHelper<TypeCategory::Real>(kind, std::move(dataRef));
|
|
||||||
case TypeCategory::Complex:
|
|
||||||
return DesignateHelper<TypeCategory::Complex>(
|
|
||||||
kind, std::move(dataRef));
|
|
||||||
case TypeCategory::Character:
|
|
||||||
return DesignateHelper<TypeCategory::Character>(
|
|
||||||
kind, std::move(dataRef));
|
|
||||||
case TypeCategory::Logical:
|
|
||||||
return DesignateHelper<TypeCategory::Logical>(
|
|
||||||
kind, std::move(dataRef));
|
|
||||||
default: CRASH_NO_CASE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case semantics::DeclTypeSpec::Category::TypeDerived:
|
case semantics::DeclTypeSpec::Category::TypeDerived:
|
||||||
case semantics::DeclTypeSpec::Category::ClassDerived:
|
case semantics::DeclTypeSpec::Category::ClassDerived:
|
||||||
return AsGenericExpr(
|
return std::make_optional(DynamicType{TypeCategory::Derived});
|
||||||
Expr<SomeDerived>{SomeDerived{details->type()->derivedTypeSpec()},
|
default:;
|
||||||
Designator<SomeDerived>{std::move(dataRef)}});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// TODO: graceful errors on CLASS(*) and TYPE(*) misusage
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wraps a data reference in a typed Designator<>.
|
||||||
|
static MaybeExpr Designate(DataRef &&dataRef) {
|
||||||
|
const Symbol &symbol{*dataRef.GetSymbol(false)};
|
||||||
|
if (std::optional<DynamicType> dynamicType{CategorizeSymbolType(symbol)}) {
|
||||||
|
switch (dynamicType->category) {
|
||||||
|
case TypeCategory::Integer:
|
||||||
|
return DesignateHelper<TypeCategory::Integer>(
|
||||||
|
dynamicType->kind, std::move(dataRef));
|
||||||
|
case TypeCategory::Real:
|
||||||
|
return DesignateHelper<TypeCategory::Real>(
|
||||||
|
dynamicType->kind, std::move(dataRef));
|
||||||
|
case TypeCategory::Complex:
|
||||||
|
return DesignateHelper<TypeCategory::Complex>(
|
||||||
|
dynamicType->kind, std::move(dataRef));
|
||||||
|
case TypeCategory::Character:
|
||||||
|
return DesignateHelper<TypeCategory::Character>(
|
||||||
|
dynamicType->kind, std::move(dataRef));
|
||||||
|
case TypeCategory::Logical:
|
||||||
|
return DesignateHelper<TypeCategory::Logical>(
|
||||||
|
dynamicType->kind, std::move(dataRef));
|
||||||
|
case TypeCategory::Derived:
|
||||||
|
return AsGenericExpr(Expr<SomeDerived>{
|
||||||
|
*dynamicType->derived, Designator<SomeDerived>{std::move(dataRef)}});
|
||||||
|
// TODO: graceful errors on CLASS(*) and TYPE(*) misusage
|
||||||
|
default: CRASH_NO_CASE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Name &n) {
|
MaybeExpr ExprAnalyzer::Analyze(const parser::Name &n) {
|
||||||
if (n.symbol == nullptr) {
|
if (n.symbol == nullptr) {
|
||||||
// TODO: convert this to a CHECK later
|
|
||||||
context.messages.Say(
|
context.messages.Say(
|
||||||
n.source, "name was not resolved to a symbol"_err_en_US);
|
n.source, "TODO INTERNAL: name was not resolved to a symbol"_err_en_US);
|
||||||
} else if (n.symbol->attrs().test(semantics::Attr::PARAMETER)) {
|
} else if (n.symbol->attrs().test(semantics::Attr::PARAMETER)) {
|
||||||
context.messages.Say(
|
context.messages.Say(
|
||||||
"TODO: PARAMETER references not yet implemented"_err_en_US);
|
"TODO: PARAMETER references not yet implemented"_err_en_US);
|
||||||
// TODO: enumerators, do they have the PARAMETER attribute?
|
// TODO: enumerators, do they have the PARAMETER attribute?
|
||||||
} else {
|
} else {
|
||||||
if (MaybeExpr result{Designate(*n.symbol, DataRef{*n.symbol})}) {
|
if (MaybeExpr result{Designate(DataRef{*n.symbol})}) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
context.messages.Say(
|
context.messages.Say(
|
||||||
|
@ -572,23 +563,46 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::NamedConstant &n) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Substring &ss) {
|
MaybeExpr ExprAnalyzer::Analyze(const parser::Substring &ss) {
|
||||||
context.messages.Say("TODO: Substring unimplemented"_err_en_US);
|
if (MaybeExpr baseExpr{
|
||||||
// TODO: be sure to run ComponentRankCheck() here on base of substring if
|
AnalyzeHelper(*this, std::get<parser::DataRef>(ss.t))}) {
|
||||||
// it's a Component.
|
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(*baseExpr))}) {
|
||||||
|
if (MaybeExpr newBaseExpr{TopLevelChecks(std::move(*dataRef))}) {
|
||||||
|
if (std::optional<DataRef> checked{
|
||||||
|
ExtractDataRef(std::move(*newBaseExpr))}) {
|
||||||
|
const parser::SubstringRange &range{
|
||||||
|
std::get<parser::SubstringRange>(ss.t)};
|
||||||
|
std::optional<Expr<SubscriptInteger>> first{
|
||||||
|
GetSubstringBound(std::get<0>(range.t))};
|
||||||
|
std::optional<Expr<SubscriptInteger>> last{
|
||||||
|
GetSubstringBound(std::get<1>(range.t))};
|
||||||
|
const Symbol &symbol{*checked->GetSymbol(false)};
|
||||||
|
if (std::optional<DynamicType> dynamicType{
|
||||||
|
CategorizeSymbolType(symbol)}) {
|
||||||
|
if (dynamicType->category == TypeCategory::Character) {
|
||||||
|
return DesignateHelper<TypeCategory::Character, Substring>(
|
||||||
|
dynamicType->kind,
|
||||||
|
Substring{
|
||||||
|
std::move(*checked), std::move(first), std::move(last)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.messages.Say(
|
||||||
|
"substring may apply only to CHARACTER"_err_en_US);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Expr<SubscriptInteger>> ExprAnalyzer::AsSubscript(
|
std::optional<Expr<SubscriptInteger>> ExprAnalyzer::AsSubscript(
|
||||||
MaybeExpr &&expr) {
|
MaybeExpr &&expr) {
|
||||||
if (expr.has_value()) {
|
if (expr.has_value()) {
|
||||||
|
if (expr->Rank() > 1) {
|
||||||
|
context.messages.Say(
|
||||||
|
"subscript expression has rank %d"_err_en_US, expr->Rank());
|
||||||
|
}
|
||||||
if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) {
|
if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) {
|
||||||
if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) {
|
if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) {
|
||||||
int rank{ssIntExpr->Rank()};
|
|
||||||
if (rank > 1) {
|
|
||||||
context.messages.Say(
|
|
||||||
"subscript expression has rank %d"_err_en_US, rank);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return {std::move(*ssIntExpr)};
|
return {std::move(*ssIntExpr)};
|
||||||
}
|
}
|
||||||
return {Expr<SubscriptInteger>{
|
return {Expr<SubscriptInteger>{
|
||||||
|
@ -601,6 +615,30 @@ std::optional<Expr<SubscriptInteger>> ExprAnalyzer::AsSubscript(
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<Expr<SubscriptInteger>> ExprAnalyzer::GetSubstringBound(
|
||||||
|
const std::optional<parser::ScalarIntExpr> &bound) {
|
||||||
|
if (bound.has_value()) {
|
||||||
|
if (MaybeExpr expr{AnalyzeHelper(*this, *bound)}) {
|
||||||
|
if (expr->Rank() > 1) {
|
||||||
|
context.messages.Say(
|
||||||
|
"substring bound expression has rank %d"_err_en_US, expr->Rank());
|
||||||
|
}
|
||||||
|
if (auto *intExpr{std::get_if<Expr<SomeInteger>>(&expr->u)}) {
|
||||||
|
if (auto *ssIntExpr{std::get_if<Expr<SubscriptInteger>>(&intExpr->u)}) {
|
||||||
|
return {std::move(*ssIntExpr)};
|
||||||
|
}
|
||||||
|
return {Expr<SubscriptInteger>{
|
||||||
|
Convert<SubscriptInteger, TypeCategory::Integer>{
|
||||||
|
std::move(*intExpr)}}};
|
||||||
|
} else {
|
||||||
|
context.messages.Say(
|
||||||
|
"substring bound expression is not INTEGER"_err_en_US);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<Expr<SubscriptInteger>> ExprAnalyzer::TripletPart(
|
std::optional<Expr<SubscriptInteger>> ExprAnalyzer::TripletPart(
|
||||||
const std::optional<parser::Subscript> &s) {
|
const std::optional<parser::Subscript> &s) {
|
||||||
if (s.has_value()) {
|
if (s.has_value()) {
|
||||||
|
@ -630,7 +668,6 @@ std::optional<Subscript> ExprAnalyzer::Analyze(
|
||||||
|
|
||||||
std::vector<Subscript> ExprAnalyzer::Analyze(
|
std::vector<Subscript> ExprAnalyzer::Analyze(
|
||||||
const std::list<parser::SectionSubscript> &sss) {
|
const std::list<parser::SectionSubscript> &sss) {
|
||||||
// TODO: enforce restrictions on vector-valued subscripts
|
|
||||||
std::vector<Subscript> subscripts;
|
std::vector<Subscript> subscripts;
|
||||||
for (const auto &s : sss) {
|
for (const auto &s : sss) {
|
||||||
if (auto subscript{Analyze(s)}) {
|
if (auto subscript{Analyze(s)}) {
|
||||||
|
@ -640,7 +677,26 @@ std::vector<Subscript> ExprAnalyzer::Analyze(
|
||||||
return subscripts;
|
return subscripts;
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeExpr ExprAnalyzer::Subscripts(const Symbol &symbol, ArrayRef &&ref) {
|
MaybeExpr ExprAnalyzer::ApplySubscripts(
|
||||||
|
DataRef &&dataRef, std::vector<Subscript> &&subscripts) {
|
||||||
|
return std::visit(
|
||||||
|
common::visitors{
|
||||||
|
[&](const Symbol *symbol) {
|
||||||
|
return CompleteSubscripts(ArrayRef{*symbol, std::move(subscripts)});
|
||||||
|
},
|
||||||
|
[&](auto &&base) -> MaybeExpr {
|
||||||
|
using Ty = std::decay_t<decltype(base)>;
|
||||||
|
if constexpr (common::HasMember<Ty, decltype(ArrayRef::u)>) {
|
||||||
|
return CompleteSubscripts(
|
||||||
|
ArrayRef{std::move(base), std::move(subscripts)});
|
||||||
|
}
|
||||||
|
return std::nullopt;
|
||||||
|
}},
|
||||||
|
std::move(dataRef.u));
|
||||||
|
}
|
||||||
|
|
||||||
|
MaybeExpr ExprAnalyzer::CompleteSubscripts(ArrayRef &&ref) {
|
||||||
|
const Symbol &symbol{*ref.GetSymbol(false)};
|
||||||
int symbolRank{symbol.Rank()};
|
int symbolRank{symbol.Rank()};
|
||||||
if (ref.subscript.empty()) {
|
if (ref.subscript.empty()) {
|
||||||
// A -> A(:,:)
|
// A -> A(:,:)
|
||||||
|
@ -653,10 +709,7 @@ MaybeExpr ExprAnalyzer::Subscripts(const Symbol &symbol, ArrayRef &&ref) {
|
||||||
context.messages.Say(
|
context.messages.Say(
|
||||||
"reference to rank-%d object '%s' has %d subscripts"_err_en_US,
|
"reference to rank-%d object '%s' has %d subscripts"_err_en_US,
|
||||||
symbolRank, symbol.name().ToString().data(), subscripts);
|
symbolRank, symbol.name().ToString().data(), subscripts);
|
||||||
}
|
} else if (Component * component{std::get_if<Component>(&ref.u)}) {
|
||||||
// TODO: fill in bounds of triplets?
|
|
||||||
// TODO: enforce constraints, like lack of uppermost bound on assumed-size
|
|
||||||
if (Component * component{std::get_if<Component>(&ref.u)}) {
|
|
||||||
int baseRank{component->Rank()};
|
int baseRank{component->Rank()};
|
||||||
if (baseRank > 0) {
|
if (baseRank > 0) {
|
||||||
int rank{ref.Rank()};
|
int rank{ref.Rank()};
|
||||||
|
@ -666,19 +719,27 @@ MaybeExpr ExprAnalyzer::Subscripts(const Symbol &symbol, ArrayRef &&ref) {
|
||||||
baseRank, rank);
|
baseRank, rank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (const auto *details{
|
||||||
|
symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
|
||||||
|
// C928 & C1002
|
||||||
|
if (Triplet * last{std::get_if<Triplet>(&ref.subscript.back().u)}) {
|
||||||
|
if (!last->upper().has_value() && details->isAssumedSize()) {
|
||||||
|
context.messages.Say(
|
||||||
|
"assumed-size array '%s' must have explicit final subscript upper bound value"_err_en_US,
|
||||||
|
symbol.name().ToString().data());
|
||||||
}
|
}
|
||||||
return Designate(symbol, DataRef{std::move(ref)});
|
}
|
||||||
|
}
|
||||||
|
return Designate(DataRef{std::move(ref)});
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeExpr ExprAnalyzer::Analyze(const parser::ArrayElement &ae) {
|
MaybeExpr ExprAnalyzer::Analyze(const parser::ArrayElement &ae) {
|
||||||
std::vector<Subscript> subscripts{Analyze(ae.subscripts)};
|
std::vector<Subscript> subscripts{Analyze(ae.subscripts)};
|
||||||
if (MaybeExpr baseExpr{AnalyzeHelper(*this, ae.base)}) {
|
if (MaybeExpr baseExpr{AnalyzeHelper(*this, ae.base)}) {
|
||||||
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(*baseExpr))}) {
|
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(*baseExpr))}) {
|
||||||
if (const Symbol **symbol{std::get_if<const Symbol *>(&dataRef->u)}) {
|
if (MaybeExpr result{
|
||||||
return Subscripts(**symbol, ArrayRef{**symbol, std::move(subscripts)});
|
ApplySubscripts(std::move(*dataRef), std::move(subscripts))}) {
|
||||||
} else if (Component * component{std::get_if<Component>(&dataRef->u)}) {
|
return result;
|
||||||
return Subscripts(component->symbol(),
|
|
||||||
ArrayRef{std::move(*component), std::move(subscripts)});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -705,7 +766,7 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::StructureComponent &sc) {
|
||||||
} else if (std::optional<DataRef> dataRef{
|
} else if (std::optional<DataRef> dataRef{
|
||||||
ExtractDataRef(std::move(*dtExpr))}) {
|
ExtractDataRef(std::move(*dtExpr))}) {
|
||||||
Component component{std::move(*dataRef), *sym};
|
Component component{std::move(*dataRef), *sym};
|
||||||
return Designate(*sym, DataRef{std::move(component)});
|
return Designate(DataRef{std::move(component)});
|
||||||
} else {
|
} else {
|
||||||
context.messages.Say(sc.component.source,
|
context.messages.Say(sc.component.source,
|
||||||
"base of component reference must be a data reference"_err_en_US);
|
"base of component reference must be a data reference"_err_en_US);
|
||||||
|
@ -738,7 +799,7 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::StructureComponent &sc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeExpr ExprAnalyzer::Analyze(const parser::CoindexedNamedObject &co) {
|
MaybeExpr ExprAnalyzer::Analyze(const parser::CoindexedNamedObject &co) {
|
||||||
// TODO: ComponentRankCheck or its equivalent
|
// TODO: CheckUnsubscriptedComponent or its equivalent
|
||||||
context.messages.Say("TODO: CoindexedNamedObject unimplemented"_err_en_US);
|
context.messages.Say("TODO: CoindexedNamedObject unimplemented"_err_en_US);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -760,13 +821,16 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::StructureConstructor &) {
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeExpr ExprAnalyzer::Analyze(const parser::FunctionReference &) {
|
MaybeExpr ExprAnalyzer::Analyze(const parser::FunctionReference &) {
|
||||||
// TODO: C1003: A parenthesized function reference may not return a
|
// TODO: C1002: Allow a whole assumed-size array to appear if the dummy
|
||||||
// procedure pointer.
|
// argument would accept it. Handle by special-casing the context
|
||||||
|
// ActualArg -> Variable -> Designator.
|
||||||
context.messages.Say("TODO: FunctionReference unimplemented"_err_en_US);
|
context.messages.Say("TODO: FunctionReference unimplemented"_err_en_US);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
|
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
|
||||||
|
// TODO: C1003: A parenthesized function reference may not return a
|
||||||
|
// procedure pointer.
|
||||||
if (MaybeExpr operand{AnalyzeHelper(*this, *x.v)}) {
|
if (MaybeExpr operand{AnalyzeHelper(*this, *x.v)}) {
|
||||||
return std::visit(
|
return std::visit(
|
||||||
common::visitors{
|
common::visitors{
|
||||||
|
@ -823,7 +887,8 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NOT &x) {
|
||||||
LogicalNegation(std::move(lx)))};
|
LogicalNegation(std::move(lx)))};
|
||||||
},
|
},
|
||||||
[=](auto &&) -> MaybeExpr {
|
[=](auto &&) -> MaybeExpr {
|
||||||
// TODO: accept INTEGER operand if not overridden
|
// TODO: accept INTEGER operand and maybe typeless
|
||||||
|
// if not overridden
|
||||||
context.messages.Say(
|
context.messages.Say(
|
||||||
"Operand of .NOT. must be LOGICAL"_err_en_US);
|
"Operand of .NOT. must be LOGICAL"_err_en_US);
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -970,6 +1035,7 @@ MaybeExpr LogicalHelper(
|
||||||
[&](auto &&, auto &&) -> MaybeExpr {
|
[&](auto &&, auto &&) -> MaybeExpr {
|
||||||
// TODO: extension: INTEGER and typeless operands
|
// TODO: extension: INTEGER and typeless operands
|
||||||
// ifort and PGI accept them if not overridden
|
// ifort and PGI accept them if not overridden
|
||||||
|
// need to define IAND, IOR, IEOR intrinsic representation
|
||||||
ea.context.messages.Say(
|
ea.context.messages.Say(
|
||||||
"operands to LOGICAL operation must be LOGICAL"_err_en_US);
|
"operands to LOGICAL operation must be LOGICAL"_err_en_US);
|
||||||
return {};
|
return {};
|
||||||
|
@ -1004,15 +1070,30 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedBinary &) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExprAnalyzer::ComponentRankCheck(const Component &component) {
|
MaybeExpr ExprAnalyzer::TopLevelChecks(DataRef &&dataRef) {
|
||||||
|
if (Component * component{std::get_if<Component>(&dataRef.u)}) {
|
||||||
|
CheckUnsubscriptedComponent(*component);
|
||||||
|
}
|
||||||
|
if (dataRef.Rank() > 0) {
|
||||||
|
if (MaybeExpr subscripted{
|
||||||
|
ApplySubscripts(std::move(dataRef), std::vector<Subscript>{})}) {
|
||||||
|
return subscripted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Designate(std::move(dataRef));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExprAnalyzer::CheckUnsubscriptedComponent(const Component &component) {
|
||||||
int baseRank{component.base().Rank()};
|
int baseRank{component.base().Rank()};
|
||||||
|
if (baseRank > 0) {
|
||||||
int componentRank{component.symbol().Rank()};
|
int componentRank{component.symbol().Rank()};
|
||||||
if (baseRank > 0 && componentRank > 0) {
|
if (componentRank > 0) {
|
||||||
context.messages.Say(
|
context.messages.Say(
|
||||||
"reference to rank-%d component '%%%s' of rank-%d array of derived type is not allowed"_err_en_US,
|
"reference to whole rank-%d component '%%%s' of rank-%d array of derived type is not allowed"_err_en_US,
|
||||||
componentRank, component.symbol().name().ToString().data(), baseRank);
|
componentRank, component.symbol().name().ToString().data(), baseRank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Fortran::evaluate
|
} // namespace Fortran::evaluate
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue