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)
|
||||
|
||||
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>
|
||||
explicit Expr(Result &&r, std::enable_if_t<!std::is_reference_v<A>, A> &&x)
|
||||
: result{std::move(r)}, u{std::move(x)} {}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
namespace Fortran::evaluate {
|
||||
|
||||
ENUM_CLASS(IntrinsicProcedure, LEN, MAX, MIN)
|
||||
ENUM_CLASS(IntrinsicProcedure, IAND, IEOR, IOR, LEN, MAX, MIN)
|
||||
|
||||
} // namespace Fortran::evaluate
|
||||
#endif // FORTRAN_EVALUATE_INTRINSICS_H_
|
||||
|
|
|
@ -43,12 +43,19 @@ namespace Fortran::evaluate {
|
|||
|
||||
using common::TypeCategory;
|
||||
|
||||
struct DynamicType {
|
||||
TypeCategory category;
|
||||
int kind{0};
|
||||
const semantics::DerivedTypeSpec *derived{nullptr};
|
||||
};
|
||||
|
||||
// Specific intrinsic types are represented by specializations of
|
||||
// this class template Type<CATEGORY, KIND>.
|
||||
template<TypeCategory CATEGORY, int KIND = 0> class Type;
|
||||
|
||||
template<TypeCategory CATEGORY, int KIND> struct TypeBase {
|
||||
static constexpr bool isSpecificType{true};
|
||||
static constexpr DynamicType dynamicType{CATEGORY, KIND};
|
||||
static constexpr TypeCategory category{CATEGORY};
|
||||
static constexpr int kind{KIND};
|
||||
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 &ActualFunctionArg::Dump(std::ostream &o) const {
|
||||
return Emit(o, u);
|
||||
}
|
||||
std::ostream &ActualSubroutineArg::Dump(std::ostream &o) const {
|
||||
return Emit(o, u);
|
||||
}
|
||||
|
@ -370,7 +367,24 @@ Expr<SubscriptInteger> ProcedureDesignator::LEN() const {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
return std::visit(common::visitors{[](const IndirectSubscriptIntegerExpr &x) {
|
||||
int rank{x->Rank()};
|
||||
|
@ -385,7 +399,12 @@ int ArrayRef::Rank() const {
|
|||
for (std::size_t j{0}; j < subscript.size(); ++j) {
|
||||
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 rank{0};
|
||||
|
@ -406,25 +425,9 @@ int Substring::Rank() const {
|
|||
u_);
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
return std::visit(
|
||||
common::visitors{[](const CopyableIndirection<Expr<SomeType>> &x) {
|
||||
|
@ -435,7 +438,33 @@ int ActualSubroutineArg::Rank() const {
|
|||
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, 2>>;
|
||||
template class Designator<Type<TypeCategory::Character, 4>>;
|
||||
template class ProcedureRef<ActualFunctionArg>; // FunctionRef
|
||||
template class ProcedureRef<ActualSubroutineArg>;
|
||||
} // namespace Fortran::evaluate
|
||||
|
|
|
@ -40,7 +40,6 @@ using semantics::Symbol;
|
|||
template<typename A> class Expr;
|
||||
struct DataRef;
|
||||
struct Variable;
|
||||
struct ActualFunctionArg;
|
||||
|
||||
// Subscript and cosubscript expressions are of a kind that matches the
|
||||
// address size, at least at the top level.
|
||||
|
@ -64,6 +63,7 @@ public:
|
|||
DataRef &base() { return *base_; }
|
||||
const Symbol &symbol() const { return *symbol_; }
|
||||
int Rank() const;
|
||||
const Symbol *GetSymbol(bool first) const;
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
std::ostream &Dump(std::ostream &) const;
|
||||
|
||||
|
@ -112,6 +112,7 @@ struct ArrayRef {
|
|||
: u{std::move(c)}, subscript(std::move(ss)) {}
|
||||
|
||||
int Rank() const;
|
||||
const Symbol *GetSymbol(bool first) const;
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
std::ostream &Dump(std::ostream &) const;
|
||||
|
||||
|
@ -134,7 +135,15 @@ public:
|
|||
std::vector<Expr<SubscriptInteger>> &&); // TODO: stat & team?
|
||||
CoarrayRef &setStat(Variable &&);
|
||||
CoarrayRef &setTeam(Variable &&, bool isTeamNumber = false);
|
||||
|
||||
int Rank() const;
|
||||
const Symbol *GetSymbol(bool first) const {
|
||||
if (first) {
|
||||
return base_.front();
|
||||
} else {
|
||||
return base_.back();
|
||||
}
|
||||
}
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
std::ostream &Dump(std::ostream &) const;
|
||||
|
||||
|
@ -155,6 +164,7 @@ struct DataRef {
|
|||
explicit DataRef(const Symbol &n) : u{&n} {}
|
||||
|
||||
int Rank() const;
|
||||
const Symbol *GetSymbol(bool first) const;
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
std::ostream &Dump(std::ostream &) const;
|
||||
|
||||
|
@ -177,6 +187,7 @@ public:
|
|||
Expr<SubscriptInteger> first() const;
|
||||
Expr<SubscriptInteger> last() const;
|
||||
int Rank() const;
|
||||
const Symbol *GetSymbol(bool first) const;
|
||||
Expr<SubscriptInteger> LEN() const;
|
||||
std::optional<std::string> Fold(FoldingContext &);
|
||||
std::ostream &Dump(std::ostream &) const;
|
||||
|
@ -198,6 +209,9 @@ public:
|
|||
const DataRef &complex() const { return complex_; }
|
||||
Part part() const { return part_; }
|
||||
int Rank() const;
|
||||
const Symbol *GetSymbol(bool first) const {
|
||||
return complex_.GetSymbol(first);
|
||||
}
|
||||
std::ostream &Dump(std::ostream &) const;
|
||||
|
||||
private:
|
||||
|
@ -236,6 +250,12 @@ public:
|
|||
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;
|
||||
|
||||
std::ostream &Dump(std::ostream &o) const {
|
||||
|
@ -250,10 +270,6 @@ public:
|
|||
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 {
|
||||
EVALUATE_UNION_CLASS_BOILERPLATE(ProcedureDesignator)
|
||||
explicit ProcedureDesignator(IntrinsicProcedure p) : u{p} {}
|
||||
|
@ -280,6 +296,10 @@ private:
|
|||
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>;
|
||||
|
||||
struct Variable {
|
||||
|
@ -289,17 +309,6 @@ struct Variable {
|
|||
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
|
||||
CLASS_BOILERPLATE(Label)
|
||||
explicit Label(int lab) : label{lab} {}
|
||||
|
@ -321,6 +330,12 @@ public:
|
|||
|
||||
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
|
||||
|
||||
#endif // FORTRAN_EVALUATE_VARIABLE_H_
|
||||
|
|
|
@ -170,11 +170,15 @@ struct ExprAnalyzer {
|
|||
std::vector<Subscript> Analyze(const std::list<parser::SectionSubscript> &);
|
||||
|
||||
std::optional<Expr<SubscriptInteger>> AsSubscript(MaybeExpr &&);
|
||||
std::optional<Expr<SubscriptInteger>> GetSubstringBound(
|
||||
const std::optional<parser::ScalarIntExpr> &);
|
||||
std::optional<Expr<SubscriptInteger>> TripletPart(
|
||||
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;
|
||||
const semantics::IntrinsicTypeDefaultKinds &defaults;
|
||||
|
@ -239,30 +243,11 @@ MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const common::Indirection<A> &x) {
|
|||
|
||||
template<>
|
||||
MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const parser::Designator &d) {
|
||||
// These check have to be deferred to these "top level" data-refs where
|
||||
// we can be sure that there are no following subscripts.
|
||||
// These checks have to be deferred to these "top level" data-refs where
|
||||
// we can be sure that there are no following subscripts (yet).
|
||||
if (MaybeExpr result{AnalyzeHelper(ea, d.u)}) {
|
||||
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(result))}) {
|
||||
if (Component * component{std::get_if<Component>(&dataRef->u)}) {
|
||||
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 ea.TopLevelChecks(std::move(*dataRef));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -495,64 +480,70 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::BOZLiteralConstant &x) {
|
|||
return {AsGenericExpr(std::move(value.value))};
|
||||
}
|
||||
|
||||
template<TypeCategory CATEGORY>
|
||||
MaybeExpr DesignateHelper(int kind, DataRef &&dataRef) {
|
||||
template<TypeCategory CATEGORY, typename DATAREF = DataRef>
|
||||
MaybeExpr DesignateHelper(int kind, DATAREF &&dataRef) {
|
||||
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 (details->type().has_value()) {
|
||||
switch (details->type()->category()) {
|
||||
case semantics::DeclTypeSpec::Category::Intrinsic: {
|
||||
TypeCategory category{details->type()->intrinsicTypeSpec().category()};
|
||||
int kind{details->type()->intrinsicTypeSpec().kind()};
|
||||
switch (category) {
|
||||
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::Intrinsic:
|
||||
return std::make_optional(
|
||||
DynamicType{details->type()->intrinsicTypeSpec().category(),
|
||||
details->type()->intrinsicTypeSpec().kind()});
|
||||
case semantics::DeclTypeSpec::Category::TypeDerived:
|
||||
case semantics::DeclTypeSpec::Category::ClassDerived:
|
||||
return AsGenericExpr(
|
||||
Expr<SomeDerived>{SomeDerived{details->type()->derivedTypeSpec()},
|
||||
Designator<SomeDerived>{std::move(dataRef)}});
|
||||
break;
|
||||
default:
|
||||
// TODO: graceful errors on CLASS(*) and TYPE(*) misusage
|
||||
break;
|
||||
return std::make_optional(DynamicType{TypeCategory::Derived});
|
||||
default:;
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (n.symbol == nullptr) {
|
||||
// TODO: convert this to a CHECK later
|
||||
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)) {
|
||||
context.messages.Say(
|
||||
"TODO: PARAMETER references not yet implemented"_err_en_US);
|
||||
// TODO: enumerators, do they have the PARAMETER attribute?
|
||||
} else {
|
||||
if (MaybeExpr result{Designate(*n.symbol, DataRef{*n.symbol})}) {
|
||||
if (MaybeExpr result{Designate(DataRef{*n.symbol})}) {
|
||||
return result;
|
||||
}
|
||||
context.messages.Say(
|
||||
|
@ -572,23 +563,46 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::NamedConstant &n) {
|
|||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Substring &ss) {
|
||||
context.messages.Say("TODO: Substring unimplemented"_err_en_US);
|
||||
// TODO: be sure to run ComponentRankCheck() here on base of substring if
|
||||
// it's a Component.
|
||||
if (MaybeExpr baseExpr{
|
||||
AnalyzeHelper(*this, std::get<parser::DataRef>(ss.t))}) {
|
||||
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;
|
||||
}
|
||||
|
||||
std::optional<Expr<SubscriptInteger>> ExprAnalyzer::AsSubscript(
|
||||
MaybeExpr &&expr) {
|
||||
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 *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 {Expr<SubscriptInteger>{
|
||||
|
@ -601,6 +615,30 @@ std::optional<Expr<SubscriptInteger>> ExprAnalyzer::AsSubscript(
|
|||
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(
|
||||
const std::optional<parser::Subscript> &s) {
|
||||
if (s.has_value()) {
|
||||
|
@ -630,7 +668,6 @@ std::optional<Subscript> ExprAnalyzer::Analyze(
|
|||
|
||||
std::vector<Subscript> ExprAnalyzer::Analyze(
|
||||
const std::list<parser::SectionSubscript> &sss) {
|
||||
// TODO: enforce restrictions on vector-valued subscripts
|
||||
std::vector<Subscript> subscripts;
|
||||
for (const auto &s : sss) {
|
||||
if (auto subscript{Analyze(s)}) {
|
||||
|
@ -640,7 +677,26 @@ std::vector<Subscript> ExprAnalyzer::Analyze(
|
|||
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()};
|
||||
if (ref.subscript.empty()) {
|
||||
// A -> A(:,:)
|
||||
|
@ -653,10 +709,7 @@ MaybeExpr ExprAnalyzer::Subscripts(const Symbol &symbol, ArrayRef &&ref) {
|
|||
context.messages.Say(
|
||||
"reference to rank-%d object '%s' has %d subscripts"_err_en_US,
|
||||
symbolRank, symbol.name().ToString().data(), subscripts);
|
||||
}
|
||||
// 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)}) {
|
||||
} else if (Component * component{std::get_if<Component>(&ref.u)}) {
|
||||
int baseRank{component->Rank()};
|
||||
if (baseRank > 0) {
|
||||
int rank{ref.Rank()};
|
||||
|
@ -666,19 +719,27 @@ MaybeExpr ExprAnalyzer::Subscripts(const Symbol &symbol, ArrayRef &&ref) {
|
|||
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) {
|
||||
std::vector<Subscript> subscripts{Analyze(ae.subscripts)};
|
||||
if (MaybeExpr baseExpr{AnalyzeHelper(*this, ae.base)}) {
|
||||
if (std::optional<DataRef> dataRef{ExtractDataRef(std::move(*baseExpr))}) {
|
||||
if (const Symbol **symbol{std::get_if<const Symbol *>(&dataRef->u)}) {
|
||||
return Subscripts(**symbol, ArrayRef{**symbol, std::move(subscripts)});
|
||||
} else if (Component * component{std::get_if<Component>(&dataRef->u)}) {
|
||||
return Subscripts(component->symbol(),
|
||||
ArrayRef{std::move(*component), std::move(subscripts)});
|
||||
if (MaybeExpr result{
|
||||
ApplySubscripts(std::move(*dataRef), std::move(subscripts))}) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -705,7 +766,7 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::StructureComponent &sc) {
|
|||
} else if (std::optional<DataRef> dataRef{
|
||||
ExtractDataRef(std::move(*dtExpr))}) {
|
||||
Component component{std::move(*dataRef), *sym};
|
||||
return Designate(*sym, DataRef{std::move(component)});
|
||||
return Designate(DataRef{std::move(component)});
|
||||
} else {
|
||||
context.messages.Say(sc.component.source,
|
||||
"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) {
|
||||
// TODO: ComponentRankCheck or its equivalent
|
||||
// TODO: CheckUnsubscriptedComponent or its equivalent
|
||||
context.messages.Say("TODO: CoindexedNamedObject unimplemented"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -760,13 +821,16 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::StructureConstructor &) {
|
|||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::FunctionReference &) {
|
||||
// TODO: C1003: A parenthesized function reference may not return a
|
||||
// procedure pointer.
|
||||
// TODO: C1002: Allow a whole assumed-size array to appear if the dummy
|
||||
// argument would accept it. Handle by special-casing the context
|
||||
// ActualArg -> Variable -> Designator.
|
||||
context.messages.Say("TODO: FunctionReference unimplemented"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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)}) {
|
||||
return std::visit(
|
||||
common::visitors{
|
||||
|
@ -823,7 +887,8 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NOT &x) {
|
|||
LogicalNegation(std::move(lx)))};
|
||||
},
|
||||
[=](auto &&) -> MaybeExpr {
|
||||
// TODO: accept INTEGER operand if not overridden
|
||||
// TODO: accept INTEGER operand and maybe typeless
|
||||
// if not overridden
|
||||
context.messages.Say(
|
||||
"Operand of .NOT. must be LOGICAL"_err_en_US);
|
||||
return std::nullopt;
|
||||
|
@ -970,6 +1035,7 @@ MaybeExpr LogicalHelper(
|
|||
[&](auto &&, auto &&) -> MaybeExpr {
|
||||
// TODO: extension: INTEGER and typeless operands
|
||||
// ifort and PGI accept them if not overridden
|
||||
// need to define IAND, IOR, IEOR intrinsic representation
|
||||
ea.context.messages.Say(
|
||||
"operands to LOGICAL operation must be LOGICAL"_err_en_US);
|
||||
return {};
|
||||
|
@ -1004,13 +1070,28 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedBinary &) {
|
|||
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 componentRank{component.symbol().Rank()};
|
||||
if (baseRank > 0 && componentRank > 0) {
|
||||
context.messages.Say(
|
||||
"reference to rank-%d component '%%%s' of rank-%d array of derived type is not allowed"_err_en_US,
|
||||
componentRank, component.symbol().name().ToString().data(), baseRank);
|
||||
if (baseRank > 0) {
|
||||
int componentRank{component.symbol().Rank()};
|
||||
if (componentRank > 0) {
|
||||
context.messages.Say(
|
||||
"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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue