diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h index 29d542784b22..a04a1285a90e 100644 --- a/flang/lib/evaluate/expression.h +++ b/flang/lib/evaluate/expression.h @@ -642,7 +642,8 @@ public: CLASS_BOILERPLATE(Expr) template - 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 explicit Expr(Result &&r, std::enable_if_t, A> &&x) : result{std::move(r)}, u{std::move(x)} {} diff --git a/flang/lib/evaluate/intrinsics.h b/flang/lib/evaluate/intrinsics.h index 3ec53e624dbb..955cf84f377b 100644 --- a/flang/lib/evaluate/intrinsics.h +++ b/flang/lib/evaluate/intrinsics.h @@ -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_ diff --git a/flang/lib/evaluate/type.h b/flang/lib/evaluate/type.h index 69915b1d7ebd..82045b248fe8 100644 --- a/flang/lib/evaluate/type.h +++ b/flang/lib/evaluate/type.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. template class Type; template 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() { diff --git a/flang/lib/evaluate/variable.cc b/flang/lib/evaluate/variable.cc index e9ec82f7d3b9..2596a8b2a54b 100644 --- a/flang/lib/evaluate/variable.cc +++ b/flang/lib/evaluate/variable.cc @@ -315,9 +315,6 @@ std::ostream &ProcedureRef::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 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 int ProcedureRef::Rank() const { + if constexpr (std::is_same_v) { // 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> &x) { - return x->Rank(); - }, - [](const auto &x) { return x.Rank(); }}, - u); -} int ActualSubroutineArg::Rank() const { return std::visit( common::visitors{[](const CopyableIndirection> &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(&u_)}) { + return dataRef->GetSymbol(first); + } else { + return nullptr; // substring of character literal + } +} + template class Designator>; template class Designator>; template class Designator>; +template class ProcedureRef; // FunctionRef +template class ProcedureRef; } // namespace Fortran::evaluate diff --git a/flang/lib/evaluate/variable.h b/flang/lib/evaluate/variable.h index d974475b8e0c..0125013c959d 100644 --- a/flang/lib/evaluate/variable.h +++ b/flang/lib/evaluate/variable.h @@ -40,7 +40,6 @@ using semantics::Symbol; template 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 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 LEN() const; std::ostream &Dump(std::ostream &) const; @@ -134,7 +135,15 @@ public: std::vector> &&); // 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 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 LEN() const; std::ostream &Dump(std::ostream &) const; @@ -177,6 +187,7 @@ public: Expr first() const; Expr last() const; int Rank() const; + const Symbol *GetSymbol(bool first) const; Expr LEN() const; std::optional 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 LEN() const; std::ostream &Dump(std::ostream &o) const { @@ -250,10 +270,6 @@ public: Variant u; }; -extern template class Designator>; -extern template class Designator>; -extern template class Designator>; - struct ProcedureDesignator { EVALUATE_UNION_CLASS_BOILERPLATE(ProcedureDesignator) explicit ProcedureDesignator(IntrinsicProcedure p) : u{p} {} @@ -280,6 +296,10 @@ private: std::vector 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>; using FunctionRef = ProcedureRef; struct Variable { @@ -289,17 +309,6 @@ struct Variable { std::variant u; }; -struct ActualFunctionArg { - EVALUATE_UNION_CLASS_BOILERPLATE(ActualFunctionArg) - explicit ActualFunctionArg(Expr &&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>, 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; +extern template class Designator>; +extern template class Designator>; +extern template class Designator>; +extern template class ProcedureRef; // FunctionRef +extern template class ProcedureRef; + } // namespace Fortran::evaluate #endif // FORTRAN_EVALUATE_VARIABLE_H_ diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc index 752c980f8f66..291ea220066c 100644 --- a/flang/lib/semantics/expression.cc +++ b/flang/lib/semantics/expression.cc @@ -170,11 +170,15 @@ struct ExprAnalyzer { std::vector Analyze(const std::list &); std::optional> AsSubscript(MaybeExpr &&); + std::optional> GetSubstringBound( + const std::optional &); std::optional> TripletPart( const std::optional &); - MaybeExpr Subscripts(const Symbol &, ArrayRef &&); + MaybeExpr ApplySubscripts(DataRef &&, std::vector &&); + 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 &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{ExtractDataRef(std::move(result))}) { - if (Component * component{std::get_if(&dataRef->u)}) { - ea.ComponentRankCheck(*component); - } else if (const Symbol **symbolPointer{ - std::get_if(&dataRef->u)}) { - const Symbol &symbol{**symbolPointer}; - if (const auto *details{ - symbol.detailsIf()}) { - 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 -MaybeExpr DesignateHelper(int kind, DataRef &&dataRef) { +template +MaybeExpr DesignateHelper(int kind, DATAREF &&dataRef) { return common::SearchDynamicTypes( - TypeKindVisitor{kind, std::move(dataRef)}); + TypeKindVisitor{kind, std::move(dataRef)}); } -static MaybeExpr Designate(const semantics::Symbol &symbol, DataRef &&dataRef) { +static std::optional CategorizeSymbolType(const Symbol &symbol) { if (auto *details{symbol.detailsIf()}) { 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( - kind, std::move(dataRef)); - case TypeCategory::Real: - return DesignateHelper(kind, std::move(dataRef)); - case TypeCategory::Complex: - return DesignateHelper( - kind, std::move(dataRef)); - case TypeCategory::Character: - return DesignateHelper( - kind, std::move(dataRef)); - case TypeCategory::Logical: - return DesignateHelper( - 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{details->type()->derivedTypeSpec()}, - Designator{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{CategorizeSymbolType(symbol)}) { + switch (dynamicType->category) { + case TypeCategory::Integer: + return DesignateHelper( + dynamicType->kind, std::move(dataRef)); + case TypeCategory::Real: + return DesignateHelper( + dynamicType->kind, std::move(dataRef)); + case TypeCategory::Complex: + return DesignateHelper( + dynamicType->kind, std::move(dataRef)); + case TypeCategory::Character: + return DesignateHelper( + dynamicType->kind, std::move(dataRef)); + case TypeCategory::Logical: + return DesignateHelper( + dynamicType->kind, std::move(dataRef)); + case TypeCategory::Derived: + return AsGenericExpr(Expr{ + *dynamicType->derived, Designator{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(ss.t))}) { + if (std::optional dataRef{ExtractDataRef(std::move(*baseExpr))}) { + if (MaybeExpr newBaseExpr{TopLevelChecks(std::move(*dataRef))}) { + if (std::optional checked{ + ExtractDataRef(std::move(*newBaseExpr))}) { + const parser::SubstringRange &range{ + std::get(ss.t)}; + std::optional> first{ + GetSubstringBound(std::get<0>(range.t))}; + std::optional> last{ + GetSubstringBound(std::get<1>(range.t))}; + const Symbol &symbol{*checked->GetSymbol(false)}; + if (std::optional dynamicType{ + CategorizeSymbolType(symbol)}) { + if (dynamicType->category == TypeCategory::Character) { + return DesignateHelper( + 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> 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->u)}) { if (auto *ssIntExpr{std::get_if>(&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{ @@ -601,6 +615,30 @@ std::optional> ExprAnalyzer::AsSubscript( return std::nullopt; } +std::optional> ExprAnalyzer::GetSubstringBound( + const std::optional &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->u)}) { + if (auto *ssIntExpr{std::get_if>(&intExpr->u)}) { + return {std::move(*ssIntExpr)}; + } + return {Expr{ + Convert{ + std::move(*intExpr)}}}; + } else { + context.messages.Say( + "substring bound expression is not INTEGER"_err_en_US); + } + } + } + return std::nullopt; +} + std::optional> ExprAnalyzer::TripletPart( const std::optional &s) { if (s.has_value()) { @@ -630,7 +668,6 @@ std::optional ExprAnalyzer::Analyze( std::vector ExprAnalyzer::Analyze( const std::list &sss) { - // TODO: enforce restrictions on vector-valued subscripts std::vector subscripts; for (const auto &s : sss) { if (auto subscript{Analyze(s)}) { @@ -640,7 +677,26 @@ std::vector ExprAnalyzer::Analyze( return subscripts; } -MaybeExpr ExprAnalyzer::Subscripts(const Symbol &symbol, ArrayRef &&ref) { +MaybeExpr ExprAnalyzer::ApplySubscripts( + DataRef &&dataRef, std::vector &&subscripts) { + return std::visit( + common::visitors{ + [&](const Symbol *symbol) { + return CompleteSubscripts(ArrayRef{*symbol, std::move(subscripts)}); + }, + [&](auto &&base) -> MaybeExpr { + using Ty = std::decay_t; + if constexpr (common::HasMember) { + 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(&ref.u)}) { + } else if (Component * component{std::get_if(&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()}) { + // C928 & C1002 + if (Triplet * last{std::get_if(&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 subscripts{Analyze(ae.subscripts)}; if (MaybeExpr baseExpr{AnalyzeHelper(*this, ae.base)}) { if (std::optional dataRef{ExtractDataRef(std::move(*baseExpr))}) { - if (const Symbol **symbol{std::get_if(&dataRef->u)}) { - return Subscripts(**symbol, ArrayRef{**symbol, std::move(subscripts)}); - } else if (Component * component{std::get_if(&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{ 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(&dataRef.u)}) { + CheckUnsubscriptedComponent(*component); + } + if (dataRef.Rank() > 0) { + if (MaybeExpr subscripted{ + ApplySubscripts(std::move(dataRef), std::vector{})}) { + 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); + } } }