164ab3302SCarolineConcatto //===-- lib/Evaluate/check-expression.cpp ---------------------------------===// 264ab3302SCarolineConcatto // 364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information. 564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 664ab3302SCarolineConcatto // 764ab3302SCarolineConcatto //===----------------------------------------------------------------------===// 864ab3302SCarolineConcatto 964ab3302SCarolineConcatto #include "flang/Evaluate/check-expression.h" 10641ede93Speter klausler #include "flang/Evaluate/characteristics.h" 118d0c3c05SPete Steinfeld #include "flang/Evaluate/intrinsics.h" 123bbb2c2dSPeter Klausler #include "flang/Evaluate/tools.h" 1364ab3302SCarolineConcatto #include "flang/Evaluate/traverse.h" 1464ab3302SCarolineConcatto #include "flang/Evaluate/type.h" 151c91d9bdSPeter Klausler #include "flang/Semantics/semantics.h" 1664ab3302SCarolineConcatto #include "flang/Semantics/symbol.h" 1764ab3302SCarolineConcatto #include "flang/Semantics/tools.h" 188d0c3c05SPete Steinfeld #include <set> 198d0c3c05SPete Steinfeld #include <string> 2064ab3302SCarolineConcatto 2164ab3302SCarolineConcatto namespace Fortran::evaluate { 2264ab3302SCarolineConcatto 23ca474479SPeter Klausler // Constant expression predicates IsConstantExpr() & IsScopeInvariantExpr(). 2464ab3302SCarolineConcatto // This code determines whether an expression is a "constant expression" 2564ab3302SCarolineConcatto // in the sense of section 10.1.12. This is not the same thing as being 2664ab3302SCarolineConcatto // able to fold it (yet) into a known constant value; specifically, 2764ab3302SCarolineConcatto // the expression may reference derived type kind parameters whose values 2864ab3302SCarolineConcatto // are not yet known. 29ca474479SPeter Klausler // 30ca474479SPeter Klausler // The variant form (IsScopeInvariantExpr()) also accepts symbols that are 31ca474479SPeter Klausler // INTENT(IN) dummy arguments without the VALUE attribute. 32ca474479SPeter Klausler template <bool INVARIANT> 33ca474479SPeter Klausler class IsConstantExprHelper 34ca474479SPeter Klausler : public AllTraverse<IsConstantExprHelper<INVARIANT>, true> { 3564ab3302SCarolineConcatto public: 3664ab3302SCarolineConcatto using Base = AllTraverse<IsConstantExprHelper, true>; 3764ab3302SCarolineConcatto IsConstantExprHelper() : Base{*this} {} 3864ab3302SCarolineConcatto using Base::operator(); 3964ab3302SCarolineConcatto 40a50bb84eSpeter klausler // A missing expression is not considered to be constant. 41a50bb84eSpeter klausler template <typename A> bool operator()(const std::optional<A> &x) const { 42a50bb84eSpeter klausler return x && (*this)(*x); 43a50bb84eSpeter klausler } 44a50bb84eSpeter klausler 454cbfd93aSpeter klausler bool operator()(const TypeParamInquiry &inq) const { 46ca474479SPeter Klausler return INVARIANT || semantics::IsKindTypeParameter(inq.parameter()); 4764ab3302SCarolineConcatto } 4864ab3302SCarolineConcatto bool operator()(const semantics::Symbol &symbol) const { 497f8da079Speter klausler const auto &ultimate{GetAssociationRoot(symbol)}; 504171f80dSpeter klausler return IsNamedConstant(ultimate) || IsImpliedDoIndex(ultimate) || 51ca474479SPeter Klausler IsInitialProcedureTarget(ultimate) || 52ca474479SPeter Klausler ultimate.has<semantics::TypeParamDetails>() || 535d5d2a0bSPeter Klausler (INVARIANT && IsIntentIn(symbol) && !IsOptional(symbol) && 54ca474479SPeter Klausler !symbol.attrs().test(semantics::Attr::VALUE)); 5564ab3302SCarolineConcatto } 5664ab3302SCarolineConcatto bool operator()(const CoarrayRef &) const { return false; } 5764ab3302SCarolineConcatto bool operator()(const semantics::ParamValue ¶m) const { 5864ab3302SCarolineConcatto return param.isExplicit() && (*this)(param.GetExplicit()); 5964ab3302SCarolineConcatto } 60a50bb84eSpeter klausler bool operator()(const ProcedureRef &) const; 613a1afd8cSpeter klausler bool operator()(const StructureConstructor &constructor) const { 623a1afd8cSpeter klausler for (const auto &[symRef, expr] : constructor) { 634171f80dSpeter klausler if (!IsConstantStructureConstructorComponent(*symRef, expr.value())) { 643a1afd8cSpeter klausler return false; 653a1afd8cSpeter klausler } 663a1afd8cSpeter klausler } 673a1afd8cSpeter klausler return true; 683a1afd8cSpeter klausler } 6970f1b4b4SAnchu Rajendran bool operator()(const Component &component) const { 7070f1b4b4SAnchu Rajendran return (*this)(component.base()); 7170f1b4b4SAnchu Rajendran } 7264ab3302SCarolineConcatto // Forbid integer division by zero in constants. 7364ab3302SCarolineConcatto template <int KIND> 7464ab3302SCarolineConcatto bool operator()( 7564ab3302SCarolineConcatto const Divide<Type<TypeCategory::Integer, KIND>> &division) const { 7664ab3302SCarolineConcatto using T = Type<TypeCategory::Integer, KIND>; 7764ab3302SCarolineConcatto if (const auto divisor{GetScalarConstantValue<T>(division.right())}) { 7862b127eeSJean Perier return !divisor->IsZero() && (*this)(division.left()); 7964ab3302SCarolineConcatto } else { 8064ab3302SCarolineConcatto return false; 8164ab3302SCarolineConcatto } 8264ab3302SCarolineConcatto } 834171f80dSpeter klausler 844171f80dSpeter klausler bool operator()(const Constant<SomeDerived> &) const { return true; } 85ca474479SPeter Klausler bool operator()(const DescriptorInquiry &x) const { 86ca474479SPeter Klausler const Symbol &sym{x.base().GetLastSymbol()}; 87ca474479SPeter Klausler return INVARIANT && !IsAllocatable(sym) && 88ca474479SPeter Klausler (!IsDummy(sym) || 895d5d2a0bSPeter Klausler (IsIntentIn(sym) && !IsOptional(sym) && 905d5d2a0bSPeter Klausler !sym.attrs().test(semantics::Attr::VALUE))); 91ca474479SPeter Klausler } 924171f80dSpeter klausler 934171f80dSpeter klausler private: 944171f80dSpeter klausler bool IsConstantStructureConstructorComponent( 95a50bb84eSpeter klausler const Symbol &, const Expr<SomeType> &) const; 96a50bb84eSpeter klausler bool IsConstantExprShape(const Shape &) const; 97a50bb84eSpeter klausler }; 98a50bb84eSpeter klausler 99ca474479SPeter Klausler template <bool INVARIANT> 100ca474479SPeter Klausler bool IsConstantExprHelper<INVARIANT>::IsConstantStructureConstructorComponent( 1014171f80dSpeter klausler const Symbol &component, const Expr<SomeType> &expr) const { 1024171f80dSpeter klausler if (IsAllocatable(component)) { 1034e3bf225SPeter Klausler return IsNullObjectPointer(expr); 1044171f80dSpeter klausler } else if (IsPointer(component)) { 1054171f80dSpeter klausler return IsNullPointer(expr) || IsInitialDataTarget(expr) || 1064171f80dSpeter klausler IsInitialProcedureTarget(expr); 1074171f80dSpeter klausler } else { 1084171f80dSpeter klausler return (*this)(expr); 1094171f80dSpeter klausler } 1104171f80dSpeter klausler } 111a50bb84eSpeter klausler 112ca474479SPeter Klausler template <bool INVARIANT> 113ca474479SPeter Klausler bool IsConstantExprHelper<INVARIANT>::operator()( 114ca474479SPeter Klausler const ProcedureRef &call) const { 1155d5d2a0bSPeter Klausler // LBOUND, UBOUND, and SIZE with truly constant DIM= arguments will have 1165d5d2a0bSPeter Klausler // been rewritten into DescriptorInquiry operations. 117a50bb84eSpeter klausler if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&call.proc().u)}) { 1187e013d60SPeter Klausler const characteristics::Procedure &proc{intrinsic->characteristics.value()}; 119a50bb84eSpeter klausler if (intrinsic->name == "kind" || 1202cd95504SPeter Klausler intrinsic->name == IntrinsicProcTable::InvalidName || 1212cd95504SPeter Klausler call.arguments().empty() || !call.arguments()[0]) { 122a50bb84eSpeter klausler // kind is always a constant, and we avoid cascading errors by considering 123a50bb84eSpeter klausler // invalid calls to intrinsics to be constant 124a50bb84eSpeter klausler return true; 1255d5d2a0bSPeter Klausler } else if (intrinsic->name == "lbound") { 126a50bb84eSpeter klausler auto base{ExtractNamedEntity(call.arguments()[0]->UnwrapExpr())}; 1273b61587cSPeter Klausler return base && IsConstantExprShape(GetLBOUNDs(*base)); 1285d5d2a0bSPeter Klausler } else if (intrinsic->name == "ubound") { 129a50bb84eSpeter klausler auto base{ExtractNamedEntity(call.arguments()[0]->UnwrapExpr())}; 130ca46521aSJean Perier return base && IsConstantExprShape(GetUBOUNDs(*base)); 1315d5d2a0bSPeter Klausler } else if (intrinsic->name == "shape" || intrinsic->name == "size") { 132a50bb84eSpeter klausler auto shape{GetShape(call.arguments()[0]->UnwrapExpr())}; 133a50bb84eSpeter klausler return shape && IsConstantExprShape(*shape); 1347e013d60SPeter Klausler } else if (proc.IsPure()) { 1357e013d60SPeter Klausler for (const auto &arg : call.arguments()) { 1367e013d60SPeter Klausler if (!arg) { 1377e013d60SPeter Klausler return false; 1387e013d60SPeter Klausler } else if (const auto *expr{arg->UnwrapExpr()}; 1397e013d60SPeter Klausler !expr || !(*this)(*expr)) { 1407e013d60SPeter Klausler return false; 1417e013d60SPeter Klausler } 1427e013d60SPeter Klausler } 1437e013d60SPeter Klausler return true; 144a50bb84eSpeter klausler } 145a50bb84eSpeter klausler // TODO: STORAGE_SIZE 146a50bb84eSpeter klausler } 147a50bb84eSpeter klausler return false; 148a50bb84eSpeter klausler } 149a50bb84eSpeter klausler 150ca474479SPeter Klausler template <bool INVARIANT> 151ca474479SPeter Klausler bool IsConstantExprHelper<INVARIANT>::IsConstantExprShape( 152ca474479SPeter Klausler const Shape &shape) const { 153a50bb84eSpeter klausler for (const auto &extent : shape) { 154a50bb84eSpeter klausler if (!(*this)(extent)) { 155a50bb84eSpeter klausler return false; 156a50bb84eSpeter klausler } 157a50bb84eSpeter klausler } 158a50bb84eSpeter klausler return true; 159a50bb84eSpeter klausler } 16064ab3302SCarolineConcatto 16164ab3302SCarolineConcatto template <typename A> bool IsConstantExpr(const A &x) { 162ca474479SPeter Klausler return IsConstantExprHelper<false>{}(x); 16364ab3302SCarolineConcatto } 16464ab3302SCarolineConcatto template bool IsConstantExpr(const Expr<SomeType> &); 16564ab3302SCarolineConcatto template bool IsConstantExpr(const Expr<SomeInteger> &); 1669977b24aSpeter klausler template bool IsConstantExpr(const Expr<SubscriptInteger> &); 1674171f80dSpeter klausler template bool IsConstantExpr(const StructureConstructor &); 16864ab3302SCarolineConcatto 169ca474479SPeter Klausler // IsScopeInvariantExpr() 170ca474479SPeter Klausler template <typename A> bool IsScopeInvariantExpr(const A &x) { 171ca474479SPeter Klausler return IsConstantExprHelper<true>{}(x); 172ca474479SPeter Klausler } 173ca474479SPeter Klausler template bool IsScopeInvariantExpr(const Expr<SomeType> &); 174ca474479SPeter Klausler template bool IsScopeInvariantExpr(const Expr<SomeInteger> &); 175ca474479SPeter Klausler template bool IsScopeInvariantExpr(const Expr<SubscriptInteger> &); 176ca474479SPeter Klausler 177641ede93Speter klausler // IsActuallyConstant() 178641ede93Speter klausler struct IsActuallyConstantHelper { 179641ede93Speter klausler template <typename A> bool operator()(const A &) { return false; } 180641ede93Speter klausler template <typename T> bool operator()(const Constant<T> &) { return true; } 181641ede93Speter klausler template <typename T> bool operator()(const Parentheses<T> &x) { 182641ede93Speter klausler return (*this)(x.left()); 183641ede93Speter klausler } 184641ede93Speter klausler template <typename T> bool operator()(const Expr<T> &x) { 185cd03e96fSPeter Klausler return common::visit([=](const auto &y) { return (*this)(y); }, x.u); 186641ede93Speter klausler } 18796aa4810SPeter Klausler bool operator()(const Expr<SomeType> &x) { 188cd03e96fSPeter Klausler return common::visit([this](const auto &y) { return (*this)(y); }, x.u); 18996aa4810SPeter Klausler } 190cd9aff8aSPeter Klausler bool operator()(const StructureConstructor &x) { 191cd9aff8aSPeter Klausler for (const auto &pair : x) { 192cd9aff8aSPeter Klausler const Expr<SomeType> &y{pair.second.value()}; 1933d3c63daSKelvin Li const auto sym{pair.first}; 1943d3c63daSKelvin Li const bool compIsConstant{(*this)(y)}; 1953d3c63daSKelvin Li // If an allocatable component is initialized by a constant, 1963d3c63daSKelvin Li // the structure constructor is not a constant. 1973d3c63daSKelvin Li if ((!compIsConstant && !IsNullPointer(y)) || 1983d3c63daSKelvin Li (compIsConstant && IsAllocatable(sym))) { 199cd9aff8aSPeter Klausler return false; 200cd9aff8aSPeter Klausler } 201cd9aff8aSPeter Klausler } 202cd9aff8aSPeter Klausler return true; 203cd9aff8aSPeter Klausler } 204641ede93Speter klausler template <typename A> bool operator()(const A *x) { return x && (*this)(*x); } 205641ede93Speter klausler template <typename A> bool operator()(const std::optional<A> &x) { 206641ede93Speter klausler return x && (*this)(*x); 207641ede93Speter klausler } 208641ede93Speter klausler }; 209641ede93Speter klausler 210641ede93Speter klausler template <typename A> bool IsActuallyConstant(const A &x) { 211641ede93Speter klausler return IsActuallyConstantHelper{}(x); 212641ede93Speter klausler } 213641ede93Speter klausler 214641ede93Speter klausler template bool IsActuallyConstant(const Expr<SomeType> &); 215b8e8f62dSJean Perier template bool IsActuallyConstant(const Expr<SomeInteger> &); 216b8e8f62dSJean Perier template bool IsActuallyConstant(const Expr<SubscriptInteger> &); 217c4a73957SJean Perier template bool IsActuallyConstant(const std::optional<Expr<SubscriptInteger>> &); 218641ede93Speter klausler 21964ab3302SCarolineConcatto // Object pointer initialization checking predicate IsInitialDataTarget(). 22064ab3302SCarolineConcatto // This code determines whether an expression is allowable as the static 22164ab3302SCarolineConcatto // data address used to initialize a pointer with "=> x". See C765. 2223a1afd8cSpeter klausler class IsInitialDataTargetHelper 22364ab3302SCarolineConcatto : public AllTraverse<IsInitialDataTargetHelper, true> { 2243a1afd8cSpeter klausler public: 22564ab3302SCarolineConcatto using Base = AllTraverse<IsInitialDataTargetHelper, true>; 22664ab3302SCarolineConcatto using Base::operator(); 2273a1afd8cSpeter klausler explicit IsInitialDataTargetHelper(parser::ContextualMessages *m) 22864ab3302SCarolineConcatto : Base{*this}, messages_{m} {} 22964ab3302SCarolineConcatto 2304171f80dSpeter klausler bool emittedMessage() const { return emittedMessage_; } 2314171f80dSpeter klausler 23264ab3302SCarolineConcatto bool operator()(const BOZLiteralConstant &) const { return false; } 23364ab3302SCarolineConcatto bool operator()(const NullPointer &) const { return true; } 23464ab3302SCarolineConcatto template <typename T> bool operator()(const Constant<T> &) const { 23564ab3302SCarolineConcatto return false; 23664ab3302SCarolineConcatto } 2374171f80dSpeter klausler bool operator()(const semantics::Symbol &symbol) { 2387f8da079Speter klausler // This function checks only base symbols, not components. 23964ab3302SCarolineConcatto const Symbol &ultimate{symbol.GetUltimate()}; 2407f8da079Speter klausler if (const auto *assoc{ 2417f8da079Speter klausler ultimate.detailsIf<semantics::AssocEntityDetails>()}) { 2427f8da079Speter klausler if (const auto &expr{assoc->expr()}) { 2437f8da079Speter klausler if (IsVariable(*expr)) { 2447f8da079Speter klausler return (*this)(*expr); 2457f8da079Speter klausler } else if (messages_) { 2463a1afd8cSpeter klausler messages_->Say( 2477f8da079Speter klausler "An initial data target may not be an associated expression ('%s')"_err_en_US, 24864ab3302SCarolineConcatto ultimate.name()); 2494171f80dSpeter klausler emittedMessage_ = true; 2503a1afd8cSpeter klausler } 2513a1afd8cSpeter klausler } 2524171f80dSpeter klausler return false; 253233f750cSPeter Klausler } else if (!CheckVarOrComponent(ultimate)) { 254233f750cSPeter Klausler return false; 25564ab3302SCarolineConcatto } else if (!ultimate.attrs().test(semantics::Attr::TARGET)) { 2563a1afd8cSpeter klausler if (messages_) { 2573a1afd8cSpeter klausler messages_->Say( 25864ab3302SCarolineConcatto "An initial data target may not be a reference to an object '%s' that lacks the TARGET attribute"_err_en_US, 25964ab3302SCarolineConcatto ultimate.name()); 2604171f80dSpeter klausler emittedMessage_ = true; 2613a1afd8cSpeter klausler } 2624171f80dSpeter klausler return false; 26364ab3302SCarolineConcatto } else if (!IsSaved(ultimate)) { 2643a1afd8cSpeter klausler if (messages_) { 2653a1afd8cSpeter klausler messages_->Say( 26664ab3302SCarolineConcatto "An initial data target may not be a reference to an object '%s' that lacks the SAVE attribute"_err_en_US, 26764ab3302SCarolineConcatto ultimate.name()); 2684171f80dSpeter klausler emittedMessage_ = true; 2693a1afd8cSpeter klausler } 2704171f80dSpeter klausler return false; 2717f8da079Speter klausler } else { 272233f750cSPeter Klausler return true; 27364ab3302SCarolineConcatto } 27464ab3302SCarolineConcatto } 27564ab3302SCarolineConcatto bool operator()(const StaticDataObject &) const { return false; } 2764cbfd93aSpeter klausler bool operator()(const TypeParamInquiry &) const { return false; } 27764ab3302SCarolineConcatto bool operator()(const Triplet &x) const { 27864ab3302SCarolineConcatto return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) && 27964ab3302SCarolineConcatto IsConstantExpr(x.stride()); 28064ab3302SCarolineConcatto } 28164ab3302SCarolineConcatto bool operator()(const Subscript &x) const { 282cd03e96fSPeter Klausler return common::visit(common::visitors{ 28364ab3302SCarolineConcatto [&](const Triplet &t) { return (*this)(t); }, 28464ab3302SCarolineConcatto [&](const auto &y) { 2851f879005STim Keith return y.value().Rank() == 0 && 2861f879005STim Keith IsConstantExpr(y.value()); 28764ab3302SCarolineConcatto }, 28864ab3302SCarolineConcatto }, 28964ab3302SCarolineConcatto x.u); 29064ab3302SCarolineConcatto } 29164ab3302SCarolineConcatto bool operator()(const CoarrayRef &) const { return false; } 2927f8da079Speter klausler bool operator()(const Component &x) { 2937f8da079Speter klausler return CheckVarOrComponent(x.GetLastSymbol()) && (*this)(x.base()); 2947f8da079Speter klausler } 29564ab3302SCarolineConcatto bool operator()(const Substring &x) const { 29664ab3302SCarolineConcatto return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) && 29764ab3302SCarolineConcatto (*this)(x.parent()); 29864ab3302SCarolineConcatto } 29964ab3302SCarolineConcatto bool operator()(const DescriptorInquiry &) const { return false; } 30064ab3302SCarolineConcatto template <typename T> bool operator()(const ArrayConstructor<T> &) const { 30164ab3302SCarolineConcatto return false; 30264ab3302SCarolineConcatto } 30364ab3302SCarolineConcatto bool operator()(const StructureConstructor &) const { return false; } 30464ab3302SCarolineConcatto template <typename D, typename R, typename... O> 30564ab3302SCarolineConcatto bool operator()(const Operation<D, R, O...> &) const { 30664ab3302SCarolineConcatto return false; 30764ab3302SCarolineConcatto } 30864ab3302SCarolineConcatto template <typename T> bool operator()(const Parentheses<T> &x) const { 30964ab3302SCarolineConcatto return (*this)(x.left()); 31064ab3302SCarolineConcatto } 3113632e9f8SPeter Klausler bool operator()(const ProcedureRef &x) const { 3123632e9f8SPeter Klausler if (const SpecificIntrinsic * intrinsic{x.proc().GetSpecificIntrinsic()}) { 3133632e9f8SPeter Klausler return intrinsic->characteristics.value().attrs.test( 3143632e9f8SPeter Klausler characteristics::Procedure::Attr::NullPointer); 3153632e9f8SPeter Klausler } 3165349f991Speter klausler return false; 3175349f991Speter klausler } 31864ab3302SCarolineConcatto bool operator()(const Relational<SomeType> &) const { return false; } 3198d0c3c05SPete Steinfeld 32064ab3302SCarolineConcatto private: 3217f8da079Speter klausler bool CheckVarOrComponent(const semantics::Symbol &symbol) { 3227f8da079Speter klausler const Symbol &ultimate{symbol.GetUltimate()}; 323233f750cSPeter Klausler const char *unacceptable{nullptr}; 324233f750cSPeter Klausler if (ultimate.Corank() > 0) { 325233f750cSPeter Klausler unacceptable = "a coarray"; 326233f750cSPeter Klausler } else if (IsAllocatable(ultimate)) { 327233f750cSPeter Klausler unacceptable = "an ALLOCATABLE"; 328233f750cSPeter Klausler } else if (IsPointer(ultimate)) { 329233f750cSPeter Klausler unacceptable = "a POINTER"; 330233f750cSPeter Klausler } else { 3317f8da079Speter klausler return true; 3327f8da079Speter klausler } 333233f750cSPeter Klausler if (messages_) { 334233f750cSPeter Klausler messages_->Say( 335233f750cSPeter Klausler "An initial data target may not be a reference to %s '%s'"_err_en_US, 336233f750cSPeter Klausler unacceptable, ultimate.name()); 337233f750cSPeter Klausler emittedMessage_ = true; 338233f750cSPeter Klausler } 339233f750cSPeter Klausler return false; 340233f750cSPeter Klausler } 3417f8da079Speter klausler 3423a1afd8cSpeter klausler parser::ContextualMessages *messages_; 3434171f80dSpeter klausler bool emittedMessage_{false}; 34464ab3302SCarolineConcatto }; 34564ab3302SCarolineConcatto 34664ab3302SCarolineConcatto bool IsInitialDataTarget( 3473a1afd8cSpeter klausler const Expr<SomeType> &x, parser::ContextualMessages *messages) { 3484171f80dSpeter klausler IsInitialDataTargetHelper helper{messages}; 3494171f80dSpeter klausler bool result{helper(x)}; 3504171f80dSpeter klausler if (!result && messages && !helper.emittedMessage()) { 3514171f80dSpeter klausler messages->Say( 3524171f80dSpeter klausler "An initial data target must be a designator with constant subscripts"_err_en_US); 3534171f80dSpeter klausler } 3544171f80dSpeter klausler return result; 3554171f80dSpeter klausler } 3564171f80dSpeter klausler 3574171f80dSpeter klausler bool IsInitialProcedureTarget(const semantics::Symbol &symbol) { 3584171f80dSpeter klausler const auto &ultimate{symbol.GetUltimate()}; 359cd03e96fSPeter Klausler return common::visit( 3604171f80dSpeter klausler common::visitors{ 3615a0382ceSPeter Klausler [&](const semantics::SubprogramDetails &subp) { 3625a0382ceSPeter Klausler return !subp.isDummy() && !subp.stmtFunction() && 3635a0382ceSPeter Klausler symbol.owner().kind() != semantics::Scope::Kind::MainProgram && 3645a0382ceSPeter Klausler symbol.owner().kind() != semantics::Scope::Kind::Subprogram; 365c4a65434Speter klausler }, 3665a0382ceSPeter Klausler [](const semantics::SubprogramNameDetails &x) { 3675a0382ceSPeter Klausler return x.kind() != semantics::SubprogramKind::Internal; 3685a0382ceSPeter Klausler }, 3694171f80dSpeter klausler [&](const semantics::ProcEntityDetails &proc) { 3704171f80dSpeter klausler return !semantics::IsPointer(ultimate) && !proc.isDummy(); 3714171f80dSpeter klausler }, 3724171f80dSpeter klausler [](const auto &) { return false; }, 3734171f80dSpeter klausler }, 3744171f80dSpeter klausler ultimate.details()); 3754171f80dSpeter klausler } 3764171f80dSpeter klausler 3774171f80dSpeter klausler bool IsInitialProcedureTarget(const ProcedureDesignator &proc) { 3784171f80dSpeter klausler if (const auto *intrin{proc.GetSpecificIntrinsic()}) { 3794171f80dSpeter klausler return !intrin->isRestrictedSpecific; 3804171f80dSpeter klausler } else if (proc.GetComponent()) { 3814171f80dSpeter klausler return false; 3824171f80dSpeter klausler } else { 3834171f80dSpeter klausler return IsInitialProcedureTarget(DEREF(proc.GetSymbol())); 3844171f80dSpeter klausler } 3854171f80dSpeter klausler } 3864171f80dSpeter klausler 3874171f80dSpeter klausler bool IsInitialProcedureTarget(const Expr<SomeType> &expr) { 3884171f80dSpeter klausler if (const auto *proc{std::get_if<ProcedureDesignator>(&expr.u)}) { 3894171f80dSpeter klausler return IsInitialProcedureTarget(*proc); 3904171f80dSpeter klausler } else { 3914e3bf225SPeter Klausler return IsNullProcedurePointer(expr); 3924171f80dSpeter klausler } 39364ab3302SCarolineConcatto } 39464ab3302SCarolineConcatto 395641ede93Speter klausler // Converts, folds, and then checks type, rank, and shape of an 396641ede93Speter klausler // initialization expression for a named constant, a non-pointer 39700e0de05SPeter Klausler // variable static initialization, a component default initializer, 398641ede93Speter klausler // a type parameter default value, or instantiated type parameter value. 399641ede93Speter klausler std::optional<Expr<SomeType>> NonPointerInitializationExpr(const Symbol &symbol, 400641ede93Speter klausler Expr<SomeType> &&x, FoldingContext &context, 401641ede93Speter klausler const semantics::Scope *instantiation) { 402641ede93Speter klausler CHECK(!IsPointer(symbol)); 403641ede93Speter klausler if (auto symTS{ 404641ede93Speter klausler characteristics::TypeAndShape::Characterize(symbol, context)}) { 405641ede93Speter klausler auto xType{x.GetType()}; 40600e0de05SPeter Klausler auto converted{ConvertToType(symTS->type(), Expr<SomeType>{x})}; 40700e0de05SPeter Klausler if (!converted && 40800e0de05SPeter Klausler symbol.owner().context().IsEnabled( 40900e0de05SPeter Klausler common::LanguageFeature::LogicalIntegerAssignment)) { 41000e0de05SPeter Klausler converted = DataConstantConversionExtension(context, symTS->type(), x); 41100e0de05SPeter Klausler if (converted && 41200e0de05SPeter Klausler symbol.owner().context().ShouldWarn( 41300e0de05SPeter Klausler common::LanguageFeature::LogicalIntegerAssignment)) { 41400e0de05SPeter Klausler context.messages().Say( 4150f973ac7SPeter Klausler common::LanguageFeature::LogicalIntegerAssignment, 416a53967cdSPeter Klausler "nonstandard usage: initialization of %s with %s"_port_en_US, 41700e0de05SPeter Klausler symTS->type().AsFortran(), x.GetType().value().AsFortran()); 41800e0de05SPeter Klausler } 41900e0de05SPeter Klausler } 42000e0de05SPeter Klausler if (converted) { 421641ede93Speter klausler auto folded{Fold(context, std::move(*converted))}; 422641ede93Speter klausler if (IsActuallyConstant(folded)) { 42373cf0142SjeanPerier int symRank{symTS->Rank()}; 424641ede93Speter klausler if (IsImpliedShape(symbol)) { 425641ede93Speter klausler if (folded.Rank() == symRank) { 42608c6a323SPeter Klausler return ArrayConstantBoundChanger{ 42708c6a323SPeter Klausler std::move(*AsConstantExtents( 42808c6a323SPeter Klausler context, GetRawLowerBounds(context, NamedEntity{symbol})))} 42908c6a323SPeter Klausler .ChangeLbounds(std::move(folded)); 430641ede93Speter klausler } else { 431641ede93Speter klausler context.messages().Say( 432641ede93Speter klausler "Implied-shape parameter '%s' has rank %d but its initializer has rank %d"_err_en_US, 433641ede93Speter klausler symbol.name(), symRank, folded.Rank()); 434641ede93Speter klausler } 435*96963554SPeter Klausler } else if (auto extents{AsConstantExtents(context, symTS->shape())}; 436*96963554SPeter Klausler extents && !HasNegativeExtent(*extents)) { 437543cd89dSPeter Steinfeld if (folded.Rank() == 0 && symRank == 0) { 438543cd89dSPeter Steinfeld // symbol and constant are both scalars 439543cd89dSPeter Steinfeld return {std::move(folded)}; 440543cd89dSPeter Steinfeld } else if (folded.Rank() == 0 && symRank > 0) { 441543cd89dSPeter Steinfeld // expand the scalar constant to an array 442641ede93Speter klausler return ScalarConstantExpander{std::move(*extents), 443641ede93Speter klausler AsConstantExtents( 4443b61587cSPeter Klausler context, GetRawLowerBounds(context, NamedEntity{symbol}))} 445641ede93Speter klausler .Expand(std::move(folded)); 446641ede93Speter klausler } else if (auto resultShape{GetShape(context, folded)}) { 44773cf0142SjeanPerier CHECK(symTS->shape()); // Assumed-ranks cannot be initialized. 44873cf0142SjeanPerier if (CheckConformance(context.messages(), *symTS->shape(), 449dfecbcaeSpeter klausler *resultShape, CheckConformanceFlags::None, 450dfecbcaeSpeter klausler "initialized object", "initialization expression") 451dfecbcaeSpeter klausler .value_or(false /*fail if not known now to conform*/)) { 452543cd89dSPeter Steinfeld // make a constant array with adjusted lower bounds 453543cd89dSPeter Steinfeld return ArrayConstantBoundChanger{ 4543b61587cSPeter Klausler std::move(*AsConstantExtents(context, 4553b61587cSPeter Klausler GetRawLowerBounds(context, NamedEntity{symbol})))} 456543cd89dSPeter Steinfeld .ChangeLbounds(std::move(folded)); 457641ede93Speter klausler } 458641ede93Speter klausler } 459641ede93Speter klausler } else if (IsNamedConstant(symbol)) { 460641ede93Speter klausler if (IsExplicitShape(symbol)) { 461641ede93Speter klausler context.messages().Say( 462641ede93Speter klausler "Named constant '%s' array must have constant shape"_err_en_US, 463641ede93Speter klausler symbol.name()); 464641ede93Speter klausler } else { 465641ede93Speter klausler // Declaration checking handles other cases 466641ede93Speter klausler } 467641ede93Speter klausler } else { 468641ede93Speter klausler context.messages().Say( 469641ede93Speter klausler "Shape of initialized object '%s' must be constant"_err_en_US, 470641ede93Speter klausler symbol.name()); 471641ede93Speter klausler } 472641ede93Speter klausler } else if (IsErrorExpr(folded)) { 473641ede93Speter klausler } else if (IsLenTypeParameter(symbol)) { 474641ede93Speter klausler return {std::move(folded)}; 475641ede93Speter klausler } else if (IsKindTypeParameter(symbol)) { 476641ede93Speter klausler if (instantiation) { 477641ede93Speter klausler context.messages().Say( 478641ede93Speter klausler "Value of kind type parameter '%s' (%s) must be a scalar INTEGER constant"_err_en_US, 479641ede93Speter klausler symbol.name(), folded.AsFortran()); 480641ede93Speter klausler } else { 481641ede93Speter klausler return {std::move(folded)}; 482641ede93Speter klausler } 483641ede93Speter klausler } else if (IsNamedConstant(symbol)) { 484e1ad2735SPeter Klausler if (symbol.name() == "numeric_storage_size" && 485e1ad2735SPeter Klausler symbol.owner().IsModule() && 486e1ad2735SPeter Klausler DEREF(symbol.owner().symbol()).name() == "iso_fortran_env") { 487e1ad2735SPeter Klausler // Very special case: numeric_storage_size is not folded until 488e1ad2735SPeter Klausler // it read from the iso_fortran_env module file, as its value 489e1ad2735SPeter Klausler // depends on compilation options. 490e1ad2735SPeter Klausler return {std::move(folded)}; 491e1ad2735SPeter Klausler } 492641ede93Speter klausler context.messages().Say( 493641ede93Speter klausler "Value of named constant '%s' (%s) cannot be computed as a constant value"_err_en_US, 494641ede93Speter klausler symbol.name(), folded.AsFortran()); 495641ede93Speter klausler } else { 496641ede93Speter klausler context.messages().Say( 497641ede93Speter klausler "Initialization expression for '%s' (%s) cannot be computed as a constant value"_err_en_US, 4987ea78643SPeter Klausler symbol.name(), x.AsFortran()); 499641ede93Speter klausler } 500641ede93Speter klausler } else if (xType) { 501641ede93Speter klausler context.messages().Say( 502641ede93Speter klausler "Initialization expression cannot be converted to declared type of '%s' from %s"_err_en_US, 503641ede93Speter klausler symbol.name(), xType->AsFortran()); 504641ede93Speter klausler } else { 505641ede93Speter klausler context.messages().Say( 506641ede93Speter klausler "Initialization expression cannot be converted to declared type of '%s'"_err_en_US, 507641ede93Speter klausler symbol.name()); 508641ede93Speter klausler } 509641ede93Speter klausler } 510641ede93Speter klausler return std::nullopt; 511641ede93Speter klausler } 512641ede93Speter klausler 51364ab3302SCarolineConcatto // Specification expression validation (10.1.11(2), C1010) 51464ab3302SCarolineConcatto class CheckSpecificationExprHelper 51564ab3302SCarolineConcatto : public AnyTraverse<CheckSpecificationExprHelper, 51664ab3302SCarolineConcatto std::optional<std::string>> { 51764ab3302SCarolineConcatto public: 51864ab3302SCarolineConcatto using Result = std::optional<std::string>; 51964ab3302SCarolineConcatto using Base = AnyTraverse<CheckSpecificationExprHelper, Result>; 52067081badSPeter Klausler explicit CheckSpecificationExprHelper(const semantics::Scope &s, 52167081badSPeter Klausler FoldingContext &context, bool forElementalFunctionResult) 52267081badSPeter Klausler : Base{*this}, scope_{s}, context_{context}, 52367081badSPeter Klausler forElementalFunctionResult_{forElementalFunctionResult} {} 52464ab3302SCarolineConcatto using Base::operator(); 52564ab3302SCarolineConcatto 52664ab3302SCarolineConcatto Result operator()(const CoarrayRef &) const { return "coindexed reference"; } 52764ab3302SCarolineConcatto 52864ab3302SCarolineConcatto Result operator()(const semantics::Symbol &symbol) const { 52967b13e97Speter klausler const auto &ultimate{symbol.GetUltimate()}; 530d4524298SPeter Klausler const auto *object{ultimate.detailsIf<semantics::ObjectEntityDetails>()}; 531d4524298SPeter Klausler bool isInitialized{semantics::IsSaved(ultimate) && 532d4524298SPeter Klausler !IsAllocatable(ultimate) && object && 533d4524298SPeter Klausler (ultimate.test(Symbol::Flag::InDataStmt) || 534d4524298SPeter Klausler object->init().has_value())}; 5357f8da079Speter klausler if (const auto *assoc{ 5367f8da079Speter klausler ultimate.detailsIf<semantics::AssocEntityDetails>()}) { 5377f8da079Speter klausler return (*this)(assoc->expr()); 5387f8da079Speter klausler } else if (semantics::IsNamedConstant(ultimate) || 5397f8da079Speter klausler ultimate.owner().IsModule() || ultimate.owner().IsSubmodule()) { 54064ab3302SCarolineConcatto return std::nullopt; 54167b13e97Speter klausler } else if (scope_.IsDerivedType() && 54267b13e97Speter klausler IsVariableName(ultimate)) { // C750, C754 54338095549SPete Steinfeld return "derived type component or type parameter value not allowed to " 54438095549SPete Steinfeld "reference variable '"s + 54567b13e97Speter klausler ultimate.name().ToString() + "'"; 54667b13e97Speter klausler } else if (IsDummy(ultimate)) { 54767081badSPeter Klausler if (!inInquiry_ && forElementalFunctionResult_) { 54867081badSPeter Klausler return "dependence on value of dummy argument '"s + 54967081badSPeter Klausler ultimate.name().ToString() + "'"; 55067081badSPeter Klausler } else if (ultimate.attrs().test(semantics::Attr::OPTIONAL)) { 55164ab3302SCarolineConcatto return "reference to OPTIONAL dummy argument '"s + 55267b13e97Speter klausler ultimate.name().ToString() + "'"; 5535d5d2a0bSPeter Klausler } else if (!inInquiry_ && 5545d5d2a0bSPeter Klausler ultimate.attrs().test(semantics::Attr::INTENT_OUT)) { 55564ab3302SCarolineConcatto return "reference to INTENT(OUT) dummy argument '"s + 55667b13e97Speter klausler ultimate.name().ToString() + "'"; 55767b13e97Speter klausler } else if (ultimate.has<semantics::ObjectEntityDetails>()) { 55864ab3302SCarolineConcatto return std::nullopt; 55964ab3302SCarolineConcatto } else { 56064ab3302SCarolineConcatto return "dummy procedure argument"; 56164ab3302SCarolineConcatto } 5627fbabe6eSPeter Klausler } else if (&symbol.owner() != &scope_ || &ultimate.owner() != &scope_) { 5637fbabe6eSPeter Klausler return std::nullopt; // host association is in play 564d4524298SPeter Klausler } else if (isInitialized && 56526ac30bcSPeter Klausler context_.languageFeatures().IsEnabled( 56626ac30bcSPeter Klausler common::LanguageFeature::SavedLocalInSpecExpr)) { 56726ac30bcSPeter Klausler if (!scope_.IsModuleFile() && 56826ac30bcSPeter Klausler context_.languageFeatures().ShouldWarn( 56926ac30bcSPeter Klausler common::LanguageFeature::SavedLocalInSpecExpr)) { 5700f973ac7SPeter Klausler context_.messages().Say(common::LanguageFeature::SavedLocalInSpecExpr, 57126ac30bcSPeter Klausler "specification expression refers to local object '%s' (initialized and saved)"_port_en_US, 57226ac30bcSPeter Klausler ultimate.name().ToString()); 57326ac30bcSPeter Klausler } 57426ac30bcSPeter Klausler return std::nullopt; 57564ab3302SCarolineConcatto } else if (const auto *object{ 57667b13e97Speter klausler ultimate.detailsIf<semantics::ObjectEntityDetails>()}) { 57764ab3302SCarolineConcatto if (object->commonBlock()) { 57864ab3302SCarolineConcatto return std::nullopt; 57964ab3302SCarolineConcatto } 58064ab3302SCarolineConcatto } 5812cd95504SPeter Klausler if (inInquiry_) { 5822cd95504SPeter Klausler return std::nullopt; 5832cd95504SPeter Klausler } else { 58467b13e97Speter klausler return "reference to local entity '"s + ultimate.name().ToString() + "'"; 58564ab3302SCarolineConcatto } 5862cd95504SPeter Klausler } 58764ab3302SCarolineConcatto 58864ab3302SCarolineConcatto Result operator()(const Component &x) const { 58964ab3302SCarolineConcatto // Don't look at the component symbol. 59064ab3302SCarolineConcatto return (*this)(x.base()); 59164ab3302SCarolineConcatto } 5925d5d2a0bSPeter Klausler Result operator()(const ArrayRef &x) const { 5935d5d2a0bSPeter Klausler if (auto result{(*this)(x.base())}) { 5945d5d2a0bSPeter Klausler return result; 5955d5d2a0bSPeter Klausler } 5965d5d2a0bSPeter Klausler // The subscripts don't get special protection for being in a 5975d5d2a0bSPeter Klausler // specification inquiry context; 5985d5d2a0bSPeter Klausler auto restorer{common::ScopedSet(inInquiry_, false)}; 5995d5d2a0bSPeter Klausler return (*this)(x.subscript()); 6005d5d2a0bSPeter Klausler } 6015d5d2a0bSPeter Klausler Result operator()(const Substring &x) const { 6025d5d2a0bSPeter Klausler if (auto result{(*this)(x.parent())}) { 6035d5d2a0bSPeter Klausler return result; 6045d5d2a0bSPeter Klausler } 6055d5d2a0bSPeter Klausler // The bounds don't get special protection for being in a 6065d5d2a0bSPeter Klausler // specification inquiry context; 6075d5d2a0bSPeter Klausler auto restorer{common::ScopedSet(inInquiry_, false)}; 6085d5d2a0bSPeter Klausler if (auto result{(*this)(x.lower())}) { 6095d5d2a0bSPeter Klausler return result; 6105d5d2a0bSPeter Klausler } 6115d5d2a0bSPeter Klausler return (*this)(x.upper()); 6125d5d2a0bSPeter Klausler } 6135d5d2a0bSPeter Klausler Result operator()(const DescriptorInquiry &x) const { 6145d5d2a0bSPeter Klausler // Many uses of SIZE(), LBOUND(), &c. that are valid in specification 61564ab3302SCarolineConcatto // expressions will have been converted to expressions over descriptor 61664ab3302SCarolineConcatto // inquiries by Fold(). 61705e62db2SPeter Klausler // Catch REAL, ALLOCATABLE :: X(:); REAL :: Y(SIZE(X)) 61867081badSPeter Klausler if (IsPermissibleInquiry( 61967081badSPeter Klausler x.base().GetFirstSymbol(), x.base().GetLastSymbol(), x.field())) { 6205d5d2a0bSPeter Klausler auto restorer{common::ScopedSet(inInquiry_, true)}; 6215d5d2a0bSPeter Klausler return (*this)(x.base()); 62205e62db2SPeter Klausler } else if (IsConstantExpr(x)) { 62305e62db2SPeter Klausler return std::nullopt; 62405e62db2SPeter Klausler } else { 62505e62db2SPeter Klausler return "non-constant descriptor inquiry not allowed for local object"; 62605e62db2SPeter Klausler } 62764ab3302SCarolineConcatto } 62864ab3302SCarolineConcatto 6294cbfd93aSpeter klausler Result operator()(const TypeParamInquiry &inq) const { 63067081badSPeter Klausler if (scope_.IsDerivedType()) { 63167081badSPeter Klausler if (!IsConstantExpr(inq) && 632f862d858Speter klausler inq.base() /* X%T, not local T */) { // C750, C754 63367081badSPeter Klausler return "non-constant reference to a type parameter inquiry not allowed " 63467081badSPeter Klausler "for derived type components or type parameter values"; 63567081badSPeter Klausler } 63667081badSPeter Klausler } else if (inq.base() && 63767081badSPeter Klausler IsInquiryAlwaysPermissible(inq.base()->GetFirstSymbol())) { 63867081badSPeter Klausler auto restorer{common::ScopedSet(inInquiry_, true)}; 63967081badSPeter Klausler return (*this)(inq.base()); 64067081badSPeter Klausler } else if (!IsConstantExpr(inq)) { 64167081badSPeter Klausler return "non-constant type parameter inquiry not allowed for local object"; 6428d0c3c05SPete Steinfeld } 6438d0c3c05SPete Steinfeld return std::nullopt; 6448d0c3c05SPete Steinfeld } 6458d0c3c05SPete Steinfeld 6463632e9f8SPeter Klausler Result operator()(const ProcedureRef &x) const { 6475d5d2a0bSPeter Klausler bool inInquiry{false}; 64864ab3302SCarolineConcatto if (const auto *symbol{x.proc().GetSymbol()}) { 6491bd083b5Speter klausler const Symbol &ultimate{symbol->GetUltimate()}; 6501bd083b5Speter klausler if (!semantics::IsPureProcedure(ultimate)) { 6511bd083b5Speter klausler return "reference to impure function '"s + ultimate.name().ToString() + 65264ab3302SCarolineConcatto "'"; 65364ab3302SCarolineConcatto } 6541bd083b5Speter klausler if (semantics::IsStmtFunction(ultimate)) { 6558d0c3c05SPete Steinfeld return "reference to statement function '"s + 6561bd083b5Speter klausler ultimate.name().ToString() + "'"; 6578d0c3c05SPete Steinfeld } 65838095549SPete Steinfeld if (scope_.IsDerivedType()) { // C750, C754 6591bd083b5Speter klausler return "reference to function '"s + ultimate.name().ToString() + 66038095549SPete Steinfeld "' not allowed for derived type components or type parameter" 66138095549SPete Steinfeld " values"; 6628d0c3c05SPete Steinfeld } 663cb263919SPeter Klausler if (auto procChars{characteristics::Procedure::Characterize( 664cb263919SPeter Klausler x.proc(), context_, /*emitError=*/true)}) { 665338f21a4Speter klausler const auto iter{std::find_if(procChars->dummyArguments.begin(), 666338f21a4Speter klausler procChars->dummyArguments.end(), 667338f21a4Speter klausler [](const characteristics::DummyArgument &dummy) { 668338f21a4Speter klausler return std::holds_alternative<characteristics::DummyProcedure>( 669338f21a4Speter klausler dummy.u); 670338f21a4Speter klausler })}; 6713602efa7SPeter Klausler if (iter != procChars->dummyArguments.end() && 6723602efa7SPeter Klausler ultimate.name().ToString() != "__builtin_c_funloc") { 673338f21a4Speter klausler return "reference to function '"s + ultimate.name().ToString() + 674338f21a4Speter klausler "' with dummy procedure argument '" + iter->name + '\''; 675338f21a4Speter klausler } 676338f21a4Speter klausler } 677338f21a4Speter klausler // References to internal functions are caught in expression semantics. 67864ab3302SCarolineConcatto // TODO: other checks for standard module procedures 67905e62db2SPeter Klausler } else { // intrinsic 68064ab3302SCarolineConcatto const SpecificIntrinsic &intrin{DEREF(x.proc().GetSpecificIntrinsic())}; 6815d5d2a0bSPeter Klausler inInquiry = context_.intrinsics().GetIntrinsicClass(intrin.name) == 6825d5d2a0bSPeter Klausler IntrinsicClass::inquiryFunction; 68338095549SPete Steinfeld if (scope_.IsDerivedType()) { // C750, C754 684641ede93Speter klausler if ((context_.intrinsics().IsIntrinsic(intrin.name) && 6858d0c3c05SPete Steinfeld badIntrinsicsForComponents_.find(intrin.name) != 6865d5d2a0bSPeter Klausler badIntrinsicsForComponents_.end())) { 6878d0c3c05SPete Steinfeld return "reference to intrinsic '"s + intrin.name + 68838095549SPete Steinfeld "' not allowed for derived type components or type parameter" 68938095549SPete Steinfeld " values"; 6908d0c3c05SPete Steinfeld } 6915d5d2a0bSPeter Klausler if (inInquiry && !IsConstantExpr(x)) { 6928d0c3c05SPete Steinfeld return "non-constant reference to inquiry intrinsic '"s + 69338095549SPete Steinfeld intrin.name + 69438095549SPete Steinfeld "' not allowed for derived type components or type" 69538095549SPete Steinfeld " parameter values"; 6968d0c3c05SPete Steinfeld } 6975d5d2a0bSPeter Klausler } 69805e62db2SPeter Klausler // Type-determined inquiries (DIGITS, HUGE, &c.) will have already been 69905e62db2SPeter Klausler // folded and won't arrive here. Inquiries that are represented with 70005e62db2SPeter Klausler // DescriptorInquiry operations (LBOUND) are checked elsewhere. If a 70105e62db2SPeter Klausler // call that makes it to here satisfies the requirements of a constant 70205e62db2SPeter Klausler // expression (as Fortran defines it), it's fine. 70305e62db2SPeter Klausler if (IsConstantExpr(x)) { 7045d5d2a0bSPeter Klausler return std::nullopt; 70564ab3302SCarolineConcatto } 70605e62db2SPeter Klausler if (intrin.name == "present") { 70705e62db2SPeter Klausler return std::nullopt; // always ok 70805e62db2SPeter Klausler } 70905e62db2SPeter Klausler // Catch CHARACTER(:), ALLOCATABLE :: X; CHARACTER(LEN(X)) :: Y 71005e62db2SPeter Klausler if (inInquiry && x.arguments().size() >= 1) { 71105e62db2SPeter Klausler if (const auto &arg{x.arguments().at(0)}) { 71205e62db2SPeter Klausler if (auto dataRef{ExtractDataRef(*arg, true, true)}) { 71305e62db2SPeter Klausler if (intrin.name == "allocated" || intrin.name == "associated" || 71405e62db2SPeter Klausler intrin.name == "is_contiguous") { // ok 71505e62db2SPeter Klausler } else if (intrin.name == "len" && 71605e62db2SPeter Klausler IsPermissibleInquiry(dataRef->GetFirstSymbol(), 71767081badSPeter Klausler dataRef->GetLastSymbol(), 71867081badSPeter Klausler DescriptorInquiry::Field::Len)) { // ok 71905e62db2SPeter Klausler } else if (intrin.name == "lbound" && 72005e62db2SPeter Klausler IsPermissibleInquiry(dataRef->GetFirstSymbol(), 72105e62db2SPeter Klausler dataRef->GetLastSymbol(), 72267081badSPeter Klausler DescriptorInquiry::Field::LowerBound)) { // ok 72305e62db2SPeter Klausler } else if ((intrin.name == "shape" || intrin.name == "size" || 72405e62db2SPeter Klausler intrin.name == "sizeof" || 72505e62db2SPeter Klausler intrin.name == "storage_size" || 72605e62db2SPeter Klausler intrin.name == "ubound") && 72705e62db2SPeter Klausler IsPermissibleInquiry(dataRef->GetFirstSymbol(), 72867081badSPeter Klausler dataRef->GetLastSymbol(), 72967081badSPeter Klausler DescriptorInquiry::Field::Extent)) { // ok 73005e62db2SPeter Klausler } else { 73105e62db2SPeter Klausler return "non-constant inquiry function '"s + intrin.name + 73205e62db2SPeter Klausler "' not allowed for local object"; 73305e62db2SPeter Klausler } 73405e62db2SPeter Klausler } 73505e62db2SPeter Klausler } 73664ab3302SCarolineConcatto } 73764ab3302SCarolineConcatto } 7385d5d2a0bSPeter Klausler auto restorer{common::ScopedSet(inInquiry_, inInquiry)}; 73964ab3302SCarolineConcatto return (*this)(x.arguments()); 74064ab3302SCarolineConcatto } 74164ab3302SCarolineConcatto 74264ab3302SCarolineConcatto private: 74364ab3302SCarolineConcatto const semantics::Scope &scope_; 744641ede93Speter klausler FoldingContext &context_; 7455d5d2a0bSPeter Klausler // Contextual information: this flag is true when in an argument to 7465d5d2a0bSPeter Klausler // an inquiry intrinsic like SIZE(). 7475d5d2a0bSPeter Klausler mutable bool inInquiry_{false}; 74867081badSPeter Klausler bool forElementalFunctionResult_{false}; // F'2023 C15121 7498d0c3c05SPete Steinfeld const std::set<std::string> badIntrinsicsForComponents_{ 7508d0c3c05SPete Steinfeld "allocated", "associated", "extends_type_of", "present", "same_type_as"}; 75167081badSPeter Klausler 75267081badSPeter Klausler bool IsInquiryAlwaysPermissible(const semantics::Symbol &) const; 75367081badSPeter Klausler bool IsPermissibleInquiry(const semantics::Symbol &firstSymbol, 75467081badSPeter Klausler const semantics::Symbol &lastSymbol, 75567081badSPeter Klausler DescriptorInquiry::Field field) const; 75664ab3302SCarolineConcatto }; 75764ab3302SCarolineConcatto 75867081badSPeter Klausler bool CheckSpecificationExprHelper::IsInquiryAlwaysPermissible( 75967081badSPeter Klausler const semantics::Symbol &symbol) const { 76067081badSPeter Klausler if (&symbol.owner() != &scope_ || symbol.has<semantics::UseDetails>() || 76167081badSPeter Klausler symbol.owner().kind() == semantics::Scope::Kind::Module || 76267081badSPeter Klausler semantics::FindCommonBlockContaining(symbol) || 76367081badSPeter Klausler symbol.has<semantics::HostAssocDetails>()) { 76467081badSPeter Klausler return true; // it's nonlocal 76567081badSPeter Klausler } else if (semantics::IsDummy(symbol) && !forElementalFunctionResult_) { 76667081badSPeter Klausler return true; 76767081badSPeter Klausler } else { 76867081badSPeter Klausler return false; 76964ab3302SCarolineConcatto } 77064ab3302SCarolineConcatto } 77164ab3302SCarolineConcatto 77267081badSPeter Klausler bool CheckSpecificationExprHelper::IsPermissibleInquiry( 77367081badSPeter Klausler const semantics::Symbol &firstSymbol, const semantics::Symbol &lastSymbol, 77467081badSPeter Klausler DescriptorInquiry::Field field) const { 77567081badSPeter Klausler if (IsInquiryAlwaysPermissible(firstSymbol)) { 77667081badSPeter Klausler return true; 77767081badSPeter Klausler } 77867081badSPeter Klausler // Inquiries on local objects may not access a deferred bound or length. 77967081badSPeter Klausler // (This code used to be a switch, but it proved impossible to write it 78067081badSPeter Klausler // thus without running afoul of bogus warnings from different C++ 78167081badSPeter Klausler // compilers.) 78267081badSPeter Klausler if (field == DescriptorInquiry::Field::Rank) { 78367081badSPeter Klausler return true; // always known 78467081badSPeter Klausler } 78567081badSPeter Klausler const auto *object{lastSymbol.detailsIf<semantics::ObjectEntityDetails>()}; 78667081badSPeter Klausler if (field == DescriptorInquiry::Field::LowerBound || 78767081badSPeter Klausler field == DescriptorInquiry::Field::Extent || 78867081badSPeter Klausler field == DescriptorInquiry::Field::Stride) { 78967081badSPeter Klausler return object && !object->shape().CanBeDeferredShape(); 79067081badSPeter Klausler } 79167081badSPeter Klausler if (field == DescriptorInquiry::Field::Len) { 79267081badSPeter Klausler return object && object->type() && 79367081badSPeter Klausler object->type()->category() == semantics::DeclTypeSpec::Character && 79467081badSPeter Klausler !object->type()->characterTypeSpec().length().isDeferred(); 79567081badSPeter Klausler } 79667081badSPeter Klausler return false; 79767081badSPeter Klausler } 79867081badSPeter Klausler 79967081badSPeter Klausler template <typename A> 80067081badSPeter Klausler void CheckSpecificationExpr(const A &x, const semantics::Scope &scope, 80167081badSPeter Klausler FoldingContext &context, bool forElementalFunctionResult) { 80226ac30bcSPeter Klausler CheckSpecificationExprHelper helper{ 80326ac30bcSPeter Klausler scope, context, forElementalFunctionResult}; 80426ac30bcSPeter Klausler if (auto why{helper(x)}) { 80567081badSPeter Klausler context.messages().Say("Invalid specification expression%s: %s"_err_en_US, 80667081badSPeter Klausler forElementalFunctionResult ? " for elemental function result" : "", 80767081badSPeter Klausler *why); 80867081badSPeter Klausler } 80967081badSPeter Klausler } 81067081badSPeter Klausler 81167081badSPeter Klausler template void CheckSpecificationExpr(const Expr<SomeType> &, 81267081badSPeter Klausler const semantics::Scope &, FoldingContext &, 81367081badSPeter Klausler bool forElementalFunctionResult); 81467081badSPeter Klausler template void CheckSpecificationExpr(const Expr<SomeInteger> &, 81567081badSPeter Klausler const semantics::Scope &, FoldingContext &, 81667081badSPeter Klausler bool forElementalFunctionResult); 81767081badSPeter Klausler template void CheckSpecificationExpr(const Expr<SubscriptInteger> &, 81867081badSPeter Klausler const semantics::Scope &, FoldingContext &, 81967081badSPeter Klausler bool forElementalFunctionResult); 820641ede93Speter klausler template void CheckSpecificationExpr(const std::optional<Expr<SomeType>> &, 82167081badSPeter Klausler const semantics::Scope &, FoldingContext &, 82267081badSPeter Klausler bool forElementalFunctionResult); 823641ede93Speter klausler template void CheckSpecificationExpr(const std::optional<Expr<SomeInteger>> &, 82467081badSPeter Klausler const semantics::Scope &, FoldingContext &, 82567081badSPeter Klausler bool forElementalFunctionResult); 826641ede93Speter klausler template void CheckSpecificationExpr( 827641ede93Speter klausler const std::optional<Expr<SubscriptInteger>> &, const semantics::Scope &, 82867081badSPeter Klausler FoldingContext &, bool forElementalFunctionResult); 82964ab3302SCarolineConcatto 83094896994SPeter Klausler // IsContiguous() -- 9.5.4 83194896994SPeter Klausler class IsContiguousHelper 83294896994SPeter Klausler : public AnyTraverse<IsContiguousHelper, std::optional<bool>> { 83364ab3302SCarolineConcatto public: 83464ab3302SCarolineConcatto using Result = std::optional<bool>; // tri-state 83594896994SPeter Klausler using Base = AnyTraverse<IsContiguousHelper, Result>; 83694896994SPeter Klausler explicit IsContiguousHelper(FoldingContext &c) : Base{*this}, context_{c} {} 83764ab3302SCarolineConcatto using Base::operator(); 83864ab3302SCarolineConcatto 839449823e2SSlava Zakharin template <typename T> Result operator()(const Constant<T> &) const { 840449823e2SSlava Zakharin return true; 841449823e2SSlava Zakharin } 842449823e2SSlava Zakharin Result operator()(const StaticDataObject &) const { return true; } 84364ab3302SCarolineConcatto Result operator()(const semantics::Symbol &symbol) const { 8447f8da079Speter klausler const auto &ultimate{symbol.GetUltimate()}; 8456ee2aa12Speter klausler if (ultimate.attrs().test(semantics::Attr::CONTIGUOUS)) { 8466ee2aa12Speter klausler return true; 847449823e2SSlava Zakharin } else if (!IsVariable(symbol)) { 848449823e2SSlava Zakharin return true; 8496ee2aa12Speter klausler } else if (ultimate.Rank() == 0) { 8506ee2aa12Speter klausler // Extension: accept scalars as a degenerate case of 8516ee2aa12Speter klausler // simple contiguity to allow their use in contexts like 8526ee2aa12Speter klausler // data targets in pointer assignments with remapping. 85364ab3302SCarolineConcatto return true; 854d1aa9bacSjeanPerier } else if (const auto *details{ 855d1aa9bacSjeanPerier ultimate.detailsIf<semantics::AssocEntityDetails>()}) { 856d1aa9bacSjeanPerier // RANK(*) associating entity is contiguous. 857d1aa9bacSjeanPerier if (details->IsAssumedSize()) { 858d1aa9bacSjeanPerier return true; 859d1aa9bacSjeanPerier } else { 860057b6fb6SPeter Klausler return Base::operator()(ultimate); // use expr 861d1aa9bacSjeanPerier } 86244bc97c8SPeter Klausler } else if (semantics::IsPointer(ultimate) || 8636f5df419SJean Perier semantics::IsAssumedShape(ultimate) || IsAssumedRank(ultimate)) { 86494896994SPeter Klausler return std::nullopt; 8656f5df419SJean Perier } else if (ultimate.has<semantics::ObjectEntityDetails>()) { 8666f5df419SJean Perier return true; 86764ab3302SCarolineConcatto } else { 86894896994SPeter Klausler return Base::operator()(ultimate); 86964ab3302SCarolineConcatto } 87064ab3302SCarolineConcatto } 87164ab3302SCarolineConcatto 87264ab3302SCarolineConcatto Result operator()(const ArrayRef &x) const { 873394d6fcfSJean Perier if (x.Rank() == 0) { 87494896994SPeter Klausler return true; // scalars considered contiguous 87594896994SPeter Klausler } 87694896994SPeter Klausler int subscriptRank{0}; 877cebf1348SPeter Klausler auto baseLbounds{GetLBOUNDs(context_, x.base())}; 878cebf1348SPeter Klausler auto baseUbounds{GetUBOUNDs(context_, x.base())}; 879cebf1348SPeter Klausler auto subscripts{CheckSubscripts( 880cebf1348SPeter Klausler x.subscript(), subscriptRank, &baseLbounds, &baseUbounds)}; 88194896994SPeter Klausler if (!subscripts.value_or(false)) { 88294896994SPeter Klausler return subscripts; // subscripts not known to be contiguous 88394896994SPeter Klausler } else if (subscriptRank > 0) { 88494896994SPeter Klausler // a(1)%b(:,:) is contiguous if and only if a(1)%b is contiguous. 885394d6fcfSJean Perier return (*this)(x.base()); 886394d6fcfSJean Perier } else { 88794896994SPeter Klausler // a(:)%b(1,1) is (probably) not contiguous. 88894896994SPeter Klausler return std::nullopt; 88964ab3302SCarolineConcatto } 89064ab3302SCarolineConcatto } 89164ab3302SCarolineConcatto Result operator()(const CoarrayRef &x) const { 89294896994SPeter Klausler int rank{0}; 89394896994SPeter Klausler return CheckSubscripts(x.subscript(), rank).has_value(); 89464ab3302SCarolineConcatto } 89564ab3302SCarolineConcatto Result operator()(const Component &x) const { 89694896994SPeter Klausler if (x.base().Rank() == 0) { 89794896994SPeter Klausler return (*this)(x.GetLastSymbol()); 89894896994SPeter Klausler } else { 89951a2ac64SjeanPerier if (Result baseIsContiguous{(*this)(x.base())}) { 90051a2ac64SjeanPerier if (!*baseIsContiguous) { 90151a2ac64SjeanPerier return false; 90251a2ac64SjeanPerier } 90394896994SPeter Klausler // TODO could be true if base contiguous and this is only component, or 90494896994SPeter Klausler // if base has only one element? 90551a2ac64SjeanPerier } 90694896994SPeter Klausler return std::nullopt; 90764ab3302SCarolineConcatto } 90894896994SPeter Klausler } 90994896994SPeter Klausler Result operator()(const ComplexPart &x) const { 91094896994SPeter Klausler return x.complex().Rank() == 0; 91194896994SPeter Klausler } 91217daa843SPeter Klausler Result operator()(const Substring &x) const { 91317daa843SPeter Klausler if (x.Rank() == 0) { 91417daa843SPeter Klausler return true; // scalar substring always contiguous 91517daa843SPeter Klausler } 91617daa843SPeter Klausler // Substrings with rank must have DataRefs as their parents 91717daa843SPeter Klausler const DataRef &parentDataRef{DEREF(x.GetParentIf<DataRef>())}; 91817daa843SPeter Klausler std::optional<std::int64_t> len; 91917daa843SPeter Klausler if (auto lenExpr{parentDataRef.LEN()}) { 92017daa843SPeter Klausler len = ToInt64(Fold(context_, std::move(*lenExpr))); 92117daa843SPeter Klausler if (len) { 92217daa843SPeter Klausler if (*len <= 0) { 92317daa843SPeter Klausler return true; // empty substrings 92417daa843SPeter Klausler } else if (*len == 1) { 92517daa843SPeter Klausler // Substrings can't be incomplete; is base array contiguous? 92617daa843SPeter Klausler return (*this)(parentDataRef); 92717daa843SPeter Klausler } 92817daa843SPeter Klausler } 92917daa843SPeter Klausler } 93017daa843SPeter Klausler std::optional<std::int64_t> upper; 93117daa843SPeter Klausler bool upperIsLen{false}; 93217daa843SPeter Klausler if (auto upperExpr{x.upper()}) { 93317daa843SPeter Klausler upper = ToInt64(Fold(context_, common::Clone(*upperExpr))); 93417daa843SPeter Klausler if (upper) { 93517daa843SPeter Klausler if (*upper < 1) { 93617daa843SPeter Klausler return true; // substring(n:0) empty 93717daa843SPeter Klausler } 93817daa843SPeter Klausler upperIsLen = len && *upper >= *len; 93917daa843SPeter Klausler } else if (const auto *inquiry{ 94017daa843SPeter Klausler UnwrapConvertedExpr<DescriptorInquiry>(*upperExpr)}; 94117daa843SPeter Klausler inquiry && inquiry->field() == DescriptorInquiry::Field::Len) { 94217daa843SPeter Klausler upperIsLen = 94317daa843SPeter Klausler &parentDataRef.GetLastSymbol() == &inquiry->base().GetLastSymbol(); 94417daa843SPeter Klausler } 94517daa843SPeter Klausler } else { 94617daa843SPeter Klausler upperIsLen = true; // substring(n:) 94717daa843SPeter Klausler } 94817daa843SPeter Klausler if (auto lower{ToInt64(Fold(context_, x.lower()))}) { 94917daa843SPeter Klausler if (*lower == 1 && upperIsLen) { 95017daa843SPeter Klausler // known complete substring; is base contiguous? 95117daa843SPeter Klausler return (*this)(parentDataRef); 95217daa843SPeter Klausler } else if (upper) { 95317daa843SPeter Klausler if (*upper < *lower) { 95417daa843SPeter Klausler return true; // empty substring(3:2) 95517daa843SPeter Klausler } else if (*lower > 1) { 95617daa843SPeter Klausler return false; // known incomplete substring 95717daa843SPeter Klausler } else if (len && *upper < *len) { 95817daa843SPeter Klausler return false; // known incomplete substring 95917daa843SPeter Klausler } 96017daa843SPeter Klausler } 96117daa843SPeter Klausler } 96217daa843SPeter Klausler return std::nullopt; // contiguity not known 96317daa843SPeter Klausler } 96464ab3302SCarolineConcatto 9653632e9f8SPeter Klausler Result operator()(const ProcedureRef &x) const { 966cb263919SPeter Klausler if (auto chars{characteristics::Procedure::Characterize( 967cb263919SPeter Klausler x.proc(), context_, /*emitError=*/true)}) { 96864ab3302SCarolineConcatto if (chars->functionResult) { 96964ab3302SCarolineConcatto const auto &result{*chars->functionResult}; 970a88cee1fSPeter Klausler if (!result.IsProcedurePointer()) { 971a88cee1fSPeter Klausler if (result.attrs.test( 972a88cee1fSPeter Klausler characteristics::FunctionResult::Attr::Contiguous)) { 973a88cee1fSPeter Klausler return true; 974a88cee1fSPeter Klausler } 975a88cee1fSPeter Klausler if (!result.attrs.test( 976a88cee1fSPeter Klausler characteristics::FunctionResult::Attr::Pointer)) { 977a88cee1fSPeter Klausler return true; 978a88cee1fSPeter Klausler } 979a88cee1fSPeter Klausler if (const auto *type{result.GetTypeAndShape()}; 980a88cee1fSPeter Klausler type && type->Rank() == 0) { 981a88cee1fSPeter Klausler return true; // pointer to scalar 982a88cee1fSPeter Klausler } 983a88cee1fSPeter Klausler // Must be non-CONTIGUOUS pointer to array 984a88cee1fSPeter Klausler } 98564ab3302SCarolineConcatto } 98664ab3302SCarolineConcatto } 98794896994SPeter Klausler return std::nullopt; 98864ab3302SCarolineConcatto } 98964ab3302SCarolineConcatto 99094896994SPeter Klausler Result operator()(const NullPointer &) const { return true; } 99194896994SPeter Klausler 99264ab3302SCarolineConcatto private: 993cebf1348SPeter Klausler // Returns "true" for a provably empty or simply contiguous array section; 994cebf1348SPeter Klausler // return "false" for a provably nonempty discontiguous section or for use 995cebf1348SPeter Klausler // of a vector subscript. 996cebf1348SPeter Klausler std::optional<bool> CheckSubscripts(const std::vector<Subscript> &subscript, 997cebf1348SPeter Klausler int &rank, const Shape *baseLbounds = nullptr, 998cebf1348SPeter Klausler const Shape *baseUbounds = nullptr) const { 99964ab3302SCarolineConcatto bool anyTriplet{false}; 100094896994SPeter Klausler rank = 0; 1001cebf1348SPeter Klausler // Detect any provably empty dimension in this array section, which would 1002cebf1348SPeter Klausler // render the whole section empty and therefore vacuously contiguous. 1003cebf1348SPeter Klausler std::optional<bool> result; 1004449823e2SSlava Zakharin bool mayBeEmpty{false}; 1005449823e2SSlava Zakharin auto dims{subscript.size()}; 1006449823e2SSlava Zakharin std::vector<bool> knownPartialSlice(dims, false); 1007449823e2SSlava Zakharin for (auto j{dims}; j-- > 0;) { 1008449823e2SSlava Zakharin std::optional<ConstantSubscript> dimLbound; 1009449823e2SSlava Zakharin std::optional<ConstantSubscript> dimUbound; 1010449823e2SSlava Zakharin std::optional<ConstantSubscript> dimExtent; 1011449823e2SSlava Zakharin if (baseLbounds && j < baseLbounds->size()) { 1012449823e2SSlava Zakharin if (const auto &lb{baseLbounds->at(j)}) { 1013449823e2SSlava Zakharin dimLbound = ToInt64(Fold(context_, Expr<SubscriptInteger>{*lb})); 1014449823e2SSlava Zakharin } 1015449823e2SSlava Zakharin } 1016449823e2SSlava Zakharin if (baseUbounds && j < baseUbounds->size()) { 1017449823e2SSlava Zakharin if (const auto &ub{baseUbounds->at(j)}) { 1018449823e2SSlava Zakharin dimUbound = ToInt64(Fold(context_, Expr<SubscriptInteger>{*ub})); 1019449823e2SSlava Zakharin } 1020449823e2SSlava Zakharin } 1021449823e2SSlava Zakharin if (dimLbound && dimUbound) { 1022449823e2SSlava Zakharin if (*dimLbound <= *dimUbound) { 1023449823e2SSlava Zakharin dimExtent = *dimUbound - *dimLbound + 1; 1024449823e2SSlava Zakharin } else { 1025449823e2SSlava Zakharin // This is an empty dimension. 1026449823e2SSlava Zakharin result = true; 1027449823e2SSlava Zakharin dimExtent = 0; 1028449823e2SSlava Zakharin } 1029449823e2SSlava Zakharin } 1030449823e2SSlava Zakharin 103164ab3302SCarolineConcatto if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) { 1032cebf1348SPeter Klausler ++rank; 1033cebf1348SPeter Klausler if (auto stride{ToInt64(triplet->stride())}) { 1034cebf1348SPeter Klausler const Expr<SubscriptInteger> *lowerBound{triplet->GetLower()}; 1035cebf1348SPeter Klausler const Expr<SubscriptInteger> *upperBound{triplet->GetUpper()}; 1036cebf1348SPeter Klausler std::optional<ConstantSubscript> lowerVal{lowerBound 1037cebf1348SPeter Klausler ? ToInt64(Fold(context_, Expr<SubscriptInteger>{*lowerBound})) 1038449823e2SSlava Zakharin : dimLbound}; 1039cebf1348SPeter Klausler std::optional<ConstantSubscript> upperVal{upperBound 1040cebf1348SPeter Klausler ? ToInt64(Fold(context_, Expr<SubscriptInteger>{*upperBound})) 1041449823e2SSlava Zakharin : dimUbound}; 1042cebf1348SPeter Klausler if (lowerVal && upperVal) { 1043cebf1348SPeter Klausler if (*lowerVal < *upperVal) { 1044cebf1348SPeter Klausler if (*stride < 0) { 1045cebf1348SPeter Klausler result = true; // empty dimension 1046cebf1348SPeter Klausler } else if (!result && *stride > 1 && 1047cebf1348SPeter Klausler *lowerVal + *stride <= *upperVal) { 1048cebf1348SPeter Klausler result = false; // discontiguous if not empty 1049cebf1348SPeter Klausler } 1050cebf1348SPeter Klausler } else if (*lowerVal > *upperVal) { 1051cebf1348SPeter Klausler if (*stride > 0) { 1052cebf1348SPeter Klausler result = true; // empty dimension 1053cebf1348SPeter Klausler } else if (!result && *stride < 0 && 1054cebf1348SPeter Klausler *lowerVal + *stride >= *upperVal) { 1055cebf1348SPeter Klausler result = false; // discontiguous if not empty 1056cebf1348SPeter Klausler } 1057449823e2SSlava Zakharin } else { 1058449823e2SSlava Zakharin mayBeEmpty = true; 1059cebf1348SPeter Klausler } 1060449823e2SSlava Zakharin } else { 1061449823e2SSlava Zakharin mayBeEmpty = true; 1062cebf1348SPeter Klausler } 1063449823e2SSlava Zakharin } else { 1064449823e2SSlava Zakharin mayBeEmpty = true; 1065cebf1348SPeter Klausler } 1066cebf1348SPeter Klausler } else if (subscript[j].Rank() > 0) { 1067cebf1348SPeter Klausler ++rank; 1068cebf1348SPeter Klausler if (!result) { 1069cebf1348SPeter Klausler result = false; // vector subscript 1070cebf1348SPeter Klausler } 1071449823e2SSlava Zakharin mayBeEmpty = true; 1072449823e2SSlava Zakharin } else { 1073449823e2SSlava Zakharin // Scalar subscript. 1074449823e2SSlava Zakharin if (dimExtent && *dimExtent > 1) { 1075449823e2SSlava Zakharin knownPartialSlice[j] = true; 1076449823e2SSlava Zakharin } 1077cebf1348SPeter Klausler } 1078cebf1348SPeter Klausler } 1079cebf1348SPeter Klausler if (rank == 0) { 1080cebf1348SPeter Klausler result = true; // scalar 1081cebf1348SPeter Klausler } 1082cebf1348SPeter Klausler if (result) { 1083cebf1348SPeter Klausler return result; 1084cebf1348SPeter Klausler } 1085cebf1348SPeter Klausler // Not provably discontiguous at this point. 1086cebf1348SPeter Klausler // Return "true" if simply contiguous, otherwise nullopt. 1087cebf1348SPeter Klausler for (auto j{subscript.size()}; j-- > 0;) { 1088cebf1348SPeter Klausler if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) { 1089cebf1348SPeter Klausler auto stride{ToInt64(triplet->stride())}; 1090cebf1348SPeter Klausler if (!stride || stride != 1) { 1091cebf1348SPeter Klausler return std::nullopt; 109264ab3302SCarolineConcatto } else if (anyTriplet) { 1093cebf1348SPeter Klausler if (triplet->GetLower() || triplet->GetUpper()) { 109494896994SPeter Klausler // all triplets before the last one must be just ":" for 109594896994SPeter Klausler // simple contiguity 109664ab3302SCarolineConcatto return std::nullopt; 109764ab3302SCarolineConcatto } 109864ab3302SCarolineConcatto } else { 109964ab3302SCarolineConcatto anyTriplet = true; 110064ab3302SCarolineConcatto } 110164ab3302SCarolineConcatto ++rank; 1102cebf1348SPeter Klausler } else if (anyTriplet) { 1103449823e2SSlava Zakharin // If the section cannot be empty, and this dimension's 1104449823e2SSlava Zakharin // scalar subscript is known not to cover the whole 1105449823e2SSlava Zakharin // dimension, then the array section is provably 1106449823e2SSlava Zakharin // discontiguous. 1107449823e2SSlava Zakharin return (mayBeEmpty || !knownPartialSlice[j]) 1108449823e2SSlava Zakharin ? std::nullopt 1109449823e2SSlava Zakharin : std::make_optional(false); 111064ab3302SCarolineConcatto } 111164ab3302SCarolineConcatto } 1112cebf1348SPeter Klausler return true; // simply contiguous 111364ab3302SCarolineConcatto } 111464ab3302SCarolineConcatto 1115641ede93Speter klausler FoldingContext &context_; 111664ab3302SCarolineConcatto }; 111764ab3302SCarolineConcatto 111864ab3302SCarolineConcatto template <typename A> 111994896994SPeter Klausler std::optional<bool> IsContiguous(const A &x, FoldingContext &context) { 112094896994SPeter Klausler return IsContiguousHelper{context}(x); 112164ab3302SCarolineConcatto } 112264ab3302SCarolineConcatto 112394896994SPeter Klausler template std::optional<bool> IsContiguous( 112494896994SPeter Klausler const Expr<SomeType> &, FoldingContext &); 1125ffc3051dSJean Perier template std::optional<bool> IsContiguous(const ArrayRef &, FoldingContext &); 1126ffc3051dSJean Perier template std::optional<bool> IsContiguous(const Substring &, FoldingContext &); 1127ffc3051dSJean Perier template std::optional<bool> IsContiguous(const Component &, FoldingContext &); 1128ffc3051dSJean Perier template std::optional<bool> IsContiguous( 1129ffc3051dSJean Perier const ComplexPart &, FoldingContext &); 1130ffc3051dSJean Perier template std::optional<bool> IsContiguous(const CoarrayRef &, FoldingContext &); 11316f5df419SJean Perier template std::optional<bool> IsContiguous(const Symbol &, FoldingContext &); 1132641ede93Speter klausler 1133641ede93Speter klausler // IsErrorExpr() 1134641ede93Speter klausler struct IsErrorExprHelper : public AnyTraverse<IsErrorExprHelper, bool> { 1135641ede93Speter klausler using Result = bool; 1136641ede93Speter klausler using Base = AnyTraverse<IsErrorExprHelper, Result>; 1137641ede93Speter klausler IsErrorExprHelper() : Base{*this} {} 1138641ede93Speter klausler using Base::operator(); 1139641ede93Speter klausler 1140641ede93Speter klausler bool operator()(const SpecificIntrinsic &x) { 1141641ede93Speter klausler return x.name == IntrinsicProcTable::InvalidName; 1142641ede93Speter klausler } 1143641ede93Speter klausler }; 1144641ede93Speter klausler 1145641ede93Speter klausler template <typename A> bool IsErrorExpr(const A &x) { 1146641ede93Speter klausler return IsErrorExprHelper{}(x); 1147641ede93Speter klausler } 1148641ede93Speter klausler 1149641ede93Speter klausler template bool IsErrorExpr(const Expr<SomeType> &); 115064ab3302SCarolineConcatto 11511623aee4SPeter Klausler // C1577 11521623aee4SPeter Klausler // TODO: Also check C1579 & C1582 here 11531623aee4SPeter Klausler class StmtFunctionChecker 11541623aee4SPeter Klausler : public AnyTraverse<StmtFunctionChecker, std::optional<parser::Message>> { 11551623aee4SPeter Klausler public: 11561623aee4SPeter Klausler using Result = std::optional<parser::Message>; 11571623aee4SPeter Klausler using Base = AnyTraverse<StmtFunctionChecker, Result>; 11580f973ac7SPeter Klausler 11590f973ac7SPeter Klausler static constexpr auto feature{ 11600f973ac7SPeter Klausler common::LanguageFeature::StatementFunctionExtensions}; 11610f973ac7SPeter Klausler 11621623aee4SPeter Klausler StmtFunctionChecker(const Symbol &sf, FoldingContext &context) 11631c91d9bdSPeter Klausler : Base{*this}, sf_{sf}, context_{context} { 11640f973ac7SPeter Klausler if (!context_.languageFeatures().IsEnabled(feature)) { 11651c91d9bdSPeter Klausler severity_ = parser::Severity::Error; 11660f973ac7SPeter Klausler } else if (context_.languageFeatures().ShouldWarn(feature)) { 11671c91d9bdSPeter Klausler severity_ = parser::Severity::Portability; 11681c91d9bdSPeter Klausler } 11691c91d9bdSPeter Klausler } 11701623aee4SPeter Klausler using Base::operator(); 11711623aee4SPeter Klausler 11720f973ac7SPeter Klausler Result Return(parser::Message &&msg) const { 11730f973ac7SPeter Klausler if (severity_) { 11740f973ac7SPeter Klausler msg.set_severity(*severity_); 11750f973ac7SPeter Klausler if (*severity_ != parser::Severity::Error) { 11760f973ac7SPeter Klausler msg.set_languageFeature(feature); 11770f973ac7SPeter Klausler } 11780f973ac7SPeter Klausler } 11790f973ac7SPeter Klausler return std::move(msg); 11800f973ac7SPeter Klausler } 11810f973ac7SPeter Klausler 11821623aee4SPeter Klausler template <typename T> Result operator()(const ArrayConstructor<T> &) const { 11831c91d9bdSPeter Klausler if (severity_) { 11840f973ac7SPeter Klausler return Return(parser::Message{sf_.name(), 11850f973ac7SPeter Klausler "Statement function '%s' should not contain an array constructor"_port_en_US, 11860f973ac7SPeter Klausler sf_.name()}); 11871c91d9bdSPeter Klausler } else { 11881c91d9bdSPeter Klausler return std::nullopt; 11891c91d9bdSPeter Klausler } 11901623aee4SPeter Klausler } 11911623aee4SPeter Klausler Result operator()(const StructureConstructor &) const { 11921c91d9bdSPeter Klausler if (severity_) { 11930f973ac7SPeter Klausler return Return(parser::Message{sf_.name(), 11940f973ac7SPeter Klausler "Statement function '%s' should not contain a structure constructor"_port_en_US, 11950f973ac7SPeter Klausler sf_.name()}); 11961c91d9bdSPeter Klausler } else { 11971c91d9bdSPeter Klausler return std::nullopt; 11981c91d9bdSPeter Klausler } 11991623aee4SPeter Klausler } 12001623aee4SPeter Klausler Result operator()(const TypeParamInquiry &) const { 12011c91d9bdSPeter Klausler if (severity_) { 12020f973ac7SPeter Klausler return Return(parser::Message{sf_.name(), 12030f973ac7SPeter Klausler "Statement function '%s' should not contain a type parameter inquiry"_port_en_US, 12040f973ac7SPeter Klausler sf_.name()}); 12051c91d9bdSPeter Klausler } else { 12061c91d9bdSPeter Klausler return std::nullopt; 12071c91d9bdSPeter Klausler } 12081623aee4SPeter Klausler } 12091623aee4SPeter Klausler Result operator()(const ProcedureDesignator &proc) const { 12101623aee4SPeter Klausler if (const Symbol * symbol{proc.GetSymbol()}) { 12111623aee4SPeter Klausler const Symbol &ultimate{symbol->GetUltimate()}; 12121623aee4SPeter Klausler if (const auto *subp{ 12131623aee4SPeter Klausler ultimate.detailsIf<semantics::SubprogramDetails>()}) { 12141623aee4SPeter Klausler if (subp->stmtFunction() && &ultimate.owner() == &sf_.owner()) { 12151623aee4SPeter Klausler if (ultimate.name().begin() > sf_.name().begin()) { 12161623aee4SPeter Klausler return parser::Message{sf_.name(), 12171623aee4SPeter Klausler "Statement function '%s' may not reference another statement function '%s' that is defined later"_err_en_US, 12181623aee4SPeter Klausler sf_.name(), ultimate.name()}; 12191623aee4SPeter Klausler } 12201623aee4SPeter Klausler } 12211623aee4SPeter Klausler } 1222cb263919SPeter Klausler if (auto chars{characteristics::Procedure::Characterize( 1223cb263919SPeter Klausler proc, context_, /*emitError=*/true)}) { 12241623aee4SPeter Klausler if (!chars->CanBeCalledViaImplicitInterface()) { 12251c91d9bdSPeter Klausler if (severity_) { 12260f973ac7SPeter Klausler return Return(parser::Message{sf_.name(), 12270f973ac7SPeter Klausler "Statement function '%s' should not reference function '%s' that requires an explicit interface"_port_en_US, 12280f973ac7SPeter Klausler sf_.name(), symbol->name()}); 12291c91d9bdSPeter Klausler } 12301623aee4SPeter Klausler } 12311623aee4SPeter Klausler } 12321623aee4SPeter Klausler } 12331623aee4SPeter Klausler if (proc.Rank() > 0) { 12341c91d9bdSPeter Klausler if (severity_) { 12350f973ac7SPeter Klausler return Return(parser::Message{sf_.name(), 12360f973ac7SPeter Klausler "Statement function '%s' should not reference a function that returns an array"_port_en_US, 12370f973ac7SPeter Klausler sf_.name()}); 12381c91d9bdSPeter Klausler } 12391623aee4SPeter Klausler } 12401623aee4SPeter Klausler return std::nullopt; 12411623aee4SPeter Klausler } 12421623aee4SPeter Klausler Result operator()(const ActualArgument &arg) const { 12431623aee4SPeter Klausler if (const auto *expr{arg.UnwrapExpr()}) { 12441623aee4SPeter Klausler if (auto result{(*this)(*expr)}) { 12451623aee4SPeter Klausler return result; 12461623aee4SPeter Klausler } 12471623aee4SPeter Klausler if (expr->Rank() > 0 && !UnwrapWholeSymbolOrComponentDataRef(*expr)) { 12481c91d9bdSPeter Klausler if (severity_) { 12490f973ac7SPeter Klausler return Return(parser::Message{sf_.name(), 12500f973ac7SPeter Klausler "Statement function '%s' should not pass an array argument that is not a whole array"_port_en_US, 12510f973ac7SPeter Klausler sf_.name()}); 12521c91d9bdSPeter Klausler } 12531623aee4SPeter Klausler } 12541623aee4SPeter Klausler } 12551623aee4SPeter Klausler return std::nullopt; 12561623aee4SPeter Klausler } 12571623aee4SPeter Klausler 12581623aee4SPeter Klausler private: 12591623aee4SPeter Klausler const Symbol &sf_; 12601623aee4SPeter Klausler FoldingContext &context_; 12611c91d9bdSPeter Klausler std::optional<parser::Severity> severity_; 12621623aee4SPeter Klausler }; 12631623aee4SPeter Klausler 12641623aee4SPeter Klausler std::optional<parser::Message> CheckStatementFunction( 12651623aee4SPeter Klausler const Symbol &sf, const Expr<SomeType> &expr, FoldingContext &context) { 12661623aee4SPeter Klausler return StmtFunctionChecker{sf, context}(expr); 12671623aee4SPeter Klausler } 12681623aee4SPeter Klausler 12691f879005STim Keith } // namespace Fortran::evaluate 1270