[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:
peter klausler 2018-09-19 14:27:13 -07:00
parent 0787d7f2df
commit ad2fda8932
6 changed files with 264 additions and 131 deletions

View File

@ -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)} {}

View File

@ -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_

View File

@ -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() {

View File

@ -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

View File

@ -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_

View File

@ -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