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" 1064ab3302SCarolineConcatto #include "flang/Evaluate/traverse.h" 1164ab3302SCarolineConcatto #include "flang/Evaluate/type.h" 1264ab3302SCarolineConcatto #include "flang/Semantics/symbol.h" 1364ab3302SCarolineConcatto #include "flang/Semantics/tools.h" 1464ab3302SCarolineConcatto 1564ab3302SCarolineConcatto namespace Fortran::evaluate { 1664ab3302SCarolineConcatto 1764ab3302SCarolineConcatto // Constant expression predicate IsConstantExpr(). 1864ab3302SCarolineConcatto // This code determines whether an expression is a "constant expression" 1964ab3302SCarolineConcatto // in the sense of section 10.1.12. This is not the same thing as being 2064ab3302SCarolineConcatto // able to fold it (yet) into a known constant value; specifically, 2164ab3302SCarolineConcatto // the expression may reference derived type kind parameters whose values 2264ab3302SCarolineConcatto // are not yet known. 2364ab3302SCarolineConcatto class IsConstantExprHelper : public AllTraverse<IsConstantExprHelper, true> { 2464ab3302SCarolineConcatto public: 2564ab3302SCarolineConcatto using Base = AllTraverse<IsConstantExprHelper, true>; 2664ab3302SCarolineConcatto IsConstantExprHelper() : Base{*this} {} 2764ab3302SCarolineConcatto using Base::operator(); 2864ab3302SCarolineConcatto 2964ab3302SCarolineConcatto template <int KIND> bool operator()(const TypeParamInquiry<KIND> &inq) const { 3064ab3302SCarolineConcatto return IsKindTypeParameter(inq.parameter()); 3164ab3302SCarolineConcatto } 3264ab3302SCarolineConcatto bool operator()(const semantics::Symbol &symbol) const { 3364ab3302SCarolineConcatto return IsNamedConstant(symbol) || IsImpliedDoIndex(symbol); 3464ab3302SCarolineConcatto } 3564ab3302SCarolineConcatto bool operator()(const CoarrayRef &) const { return false; } 3664ab3302SCarolineConcatto bool operator()(const semantics::ParamValue ¶m) const { 3764ab3302SCarolineConcatto return param.isExplicit() && (*this)(param.GetExplicit()); 3864ab3302SCarolineConcatto } 3964ab3302SCarolineConcatto template <typename T> bool operator()(const FunctionRef<T> &call) const { 4064ab3302SCarolineConcatto if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&call.proc().u)}) { 4164ab3302SCarolineConcatto return intrinsic->name == "kind"; 4264ab3302SCarolineConcatto // TODO: other inquiry intrinsics 4364ab3302SCarolineConcatto } else { 4464ab3302SCarolineConcatto return false; 4564ab3302SCarolineConcatto } 4664ab3302SCarolineConcatto } 4764ab3302SCarolineConcatto 4864ab3302SCarolineConcatto // Forbid integer division by zero in constants. 4964ab3302SCarolineConcatto template <int KIND> 5064ab3302SCarolineConcatto bool operator()( 5164ab3302SCarolineConcatto const Divide<Type<TypeCategory::Integer, KIND>> &division) const { 5264ab3302SCarolineConcatto using T = Type<TypeCategory::Integer, KIND>; 5364ab3302SCarolineConcatto if (const auto divisor{GetScalarConstantValue<T>(division.right())}) { 5464ab3302SCarolineConcatto return !divisor->IsZero(); 5564ab3302SCarolineConcatto } else { 5664ab3302SCarolineConcatto return false; 5764ab3302SCarolineConcatto } 5864ab3302SCarolineConcatto } 5964ab3302SCarolineConcatto }; 6064ab3302SCarolineConcatto 6164ab3302SCarolineConcatto template <typename A> bool IsConstantExpr(const A &x) { 6264ab3302SCarolineConcatto return IsConstantExprHelper{}(x); 6364ab3302SCarolineConcatto } 6464ab3302SCarolineConcatto template bool IsConstantExpr(const Expr<SomeType> &); 6564ab3302SCarolineConcatto template bool IsConstantExpr(const Expr<SomeInteger> &); 669977b24aSpeter klausler template bool IsConstantExpr(const Expr<SubscriptInteger> &); 6764ab3302SCarolineConcatto 6864ab3302SCarolineConcatto // Object pointer initialization checking predicate IsInitialDataTarget(). 6964ab3302SCarolineConcatto // This code determines whether an expression is allowable as the static 7064ab3302SCarolineConcatto // data address used to initialize a pointer with "=> x". See C765. 7164ab3302SCarolineConcatto struct IsInitialDataTargetHelper 7264ab3302SCarolineConcatto : public AllTraverse<IsInitialDataTargetHelper, true> { 7364ab3302SCarolineConcatto using Base = AllTraverse<IsInitialDataTargetHelper, true>; 7464ab3302SCarolineConcatto using Base::operator(); 7564ab3302SCarolineConcatto explicit IsInitialDataTargetHelper(parser::ContextualMessages &m) 7664ab3302SCarolineConcatto : Base{*this}, messages_{m} {} 7764ab3302SCarolineConcatto 7864ab3302SCarolineConcatto bool operator()(const BOZLiteralConstant &) const { return false; } 7964ab3302SCarolineConcatto bool operator()(const NullPointer &) const { return true; } 8064ab3302SCarolineConcatto template <typename T> bool operator()(const Constant<T> &) const { 8164ab3302SCarolineConcatto return false; 8264ab3302SCarolineConcatto } 8364ab3302SCarolineConcatto bool operator()(const semantics::Symbol &symbol) const { 8464ab3302SCarolineConcatto const Symbol &ultimate{symbol.GetUltimate()}; 8564ab3302SCarolineConcatto if (IsAllocatable(ultimate)) { 8664ab3302SCarolineConcatto messages_.Say( 8764ab3302SCarolineConcatto "An initial data target may not be a reference to an ALLOCATABLE '%s'"_err_en_US, 8864ab3302SCarolineConcatto ultimate.name()); 8964ab3302SCarolineConcatto } else if (ultimate.Corank() > 0) { 9064ab3302SCarolineConcatto messages_.Say( 9164ab3302SCarolineConcatto "An initial data target may not be a reference to a coarray '%s'"_err_en_US, 9264ab3302SCarolineConcatto ultimate.name()); 9364ab3302SCarolineConcatto } else if (!ultimate.attrs().test(semantics::Attr::TARGET)) { 9464ab3302SCarolineConcatto messages_.Say( 9564ab3302SCarolineConcatto "An initial data target may not be a reference to an object '%s' that lacks the TARGET attribute"_err_en_US, 9664ab3302SCarolineConcatto ultimate.name()); 9764ab3302SCarolineConcatto } else if (!IsSaved(ultimate)) { 9864ab3302SCarolineConcatto messages_.Say( 9964ab3302SCarolineConcatto "An initial data target may not be a reference to an object '%s' that lacks the SAVE attribute"_err_en_US, 10064ab3302SCarolineConcatto ultimate.name()); 10164ab3302SCarolineConcatto } 10264ab3302SCarolineConcatto return true; 10364ab3302SCarolineConcatto } 10464ab3302SCarolineConcatto bool operator()(const StaticDataObject &) const { return false; } 10564ab3302SCarolineConcatto template <int KIND> bool operator()(const TypeParamInquiry<KIND> &) const { 10664ab3302SCarolineConcatto return false; 10764ab3302SCarolineConcatto } 10864ab3302SCarolineConcatto bool operator()(const Triplet &x) const { 10964ab3302SCarolineConcatto return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) && 11064ab3302SCarolineConcatto IsConstantExpr(x.stride()); 11164ab3302SCarolineConcatto } 11264ab3302SCarolineConcatto bool operator()(const Subscript &x) const { 113*1f879005STim Keith return std::visit(common::visitors{ 11464ab3302SCarolineConcatto [&](const Triplet &t) { return (*this)(t); }, 11564ab3302SCarolineConcatto [&](const auto &y) { 116*1f879005STim Keith return y.value().Rank() == 0 && 117*1f879005STim Keith IsConstantExpr(y.value()); 11864ab3302SCarolineConcatto }, 11964ab3302SCarolineConcatto }, 12064ab3302SCarolineConcatto x.u); 12164ab3302SCarolineConcatto } 12264ab3302SCarolineConcatto bool operator()(const CoarrayRef &) const { return false; } 12364ab3302SCarolineConcatto bool operator()(const Substring &x) const { 12464ab3302SCarolineConcatto return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) && 12564ab3302SCarolineConcatto (*this)(x.parent()); 12664ab3302SCarolineConcatto } 12764ab3302SCarolineConcatto bool operator()(const DescriptorInquiry &) const { return false; } 12864ab3302SCarolineConcatto template <typename T> bool operator()(const ArrayConstructor<T> &) const { 12964ab3302SCarolineConcatto return false; 13064ab3302SCarolineConcatto } 13164ab3302SCarolineConcatto bool operator()(const StructureConstructor &) const { return false; } 132*1f879005STim Keith template <typename T> bool operator()(const FunctionRef<T> &) { 133*1f879005STim Keith return false; 134*1f879005STim Keith } 13564ab3302SCarolineConcatto template <typename D, typename R, typename... O> 13664ab3302SCarolineConcatto bool operator()(const Operation<D, R, O...> &) const { 13764ab3302SCarolineConcatto return false; 13864ab3302SCarolineConcatto } 13964ab3302SCarolineConcatto template <typename T> bool operator()(const Parentheses<T> &x) const { 14064ab3302SCarolineConcatto return (*this)(x.left()); 14164ab3302SCarolineConcatto } 14264ab3302SCarolineConcatto bool operator()(const Relational<SomeType> &) const { return false; } 14364ab3302SCarolineConcatto 14464ab3302SCarolineConcatto private: 14564ab3302SCarolineConcatto parser::ContextualMessages &messages_; 14664ab3302SCarolineConcatto }; 14764ab3302SCarolineConcatto 14864ab3302SCarolineConcatto bool IsInitialDataTarget( 14964ab3302SCarolineConcatto const Expr<SomeType> &x, parser::ContextualMessages &messages) { 15064ab3302SCarolineConcatto return IsInitialDataTargetHelper{messages}(x); 15164ab3302SCarolineConcatto } 15264ab3302SCarolineConcatto 15364ab3302SCarolineConcatto // Specification expression validation (10.1.11(2), C1010) 15464ab3302SCarolineConcatto class CheckSpecificationExprHelper 15564ab3302SCarolineConcatto : public AnyTraverse<CheckSpecificationExprHelper, 15664ab3302SCarolineConcatto std::optional<std::string>> { 15764ab3302SCarolineConcatto public: 15864ab3302SCarolineConcatto using Result = std::optional<std::string>; 15964ab3302SCarolineConcatto using Base = AnyTraverse<CheckSpecificationExprHelper, Result>; 16064ab3302SCarolineConcatto explicit CheckSpecificationExprHelper(const semantics::Scope &s) 16164ab3302SCarolineConcatto : Base{*this}, scope_{s} {} 16264ab3302SCarolineConcatto using Base::operator(); 16364ab3302SCarolineConcatto 16464ab3302SCarolineConcatto Result operator()(const ProcedureDesignator &) const { 16564ab3302SCarolineConcatto return "dummy procedure argument"; 16664ab3302SCarolineConcatto } 16764ab3302SCarolineConcatto Result operator()(const CoarrayRef &) const { return "coindexed reference"; } 16864ab3302SCarolineConcatto 16964ab3302SCarolineConcatto Result operator()(const semantics::Symbol &symbol) const { 17064ab3302SCarolineConcatto if (semantics::IsNamedConstant(symbol)) { 17164ab3302SCarolineConcatto return std::nullopt; 17264ab3302SCarolineConcatto } else if (symbol.IsDummy()) { 17364ab3302SCarolineConcatto if (symbol.attrs().test(semantics::Attr::OPTIONAL)) { 17464ab3302SCarolineConcatto return "reference to OPTIONAL dummy argument '"s + 17564ab3302SCarolineConcatto symbol.name().ToString() + "'"; 17664ab3302SCarolineConcatto } else if (symbol.attrs().test(semantics::Attr::INTENT_OUT)) { 17764ab3302SCarolineConcatto return "reference to INTENT(OUT) dummy argument '"s + 17864ab3302SCarolineConcatto symbol.name().ToString() + "'"; 17964ab3302SCarolineConcatto } else if (symbol.has<semantics::ObjectEntityDetails>()) { 18064ab3302SCarolineConcatto return std::nullopt; 18164ab3302SCarolineConcatto } else { 18264ab3302SCarolineConcatto return "dummy procedure argument"; 18364ab3302SCarolineConcatto } 18464ab3302SCarolineConcatto } else if (symbol.has<semantics::UseDetails>() || 18564ab3302SCarolineConcatto symbol.has<semantics::HostAssocDetails>() || 18664ab3302SCarolineConcatto symbol.owner().kind() == semantics::Scope::Kind::Module) { 18764ab3302SCarolineConcatto return std::nullopt; 18864ab3302SCarolineConcatto } else if (const auto *object{ 18964ab3302SCarolineConcatto symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 19064ab3302SCarolineConcatto // TODO: what about EQUIVALENCE with data in COMMON? 19164ab3302SCarolineConcatto // TODO: does this work for blank COMMON? 19264ab3302SCarolineConcatto if (object->commonBlock()) { 19364ab3302SCarolineConcatto return std::nullopt; 19464ab3302SCarolineConcatto } 19564ab3302SCarolineConcatto } 19664ab3302SCarolineConcatto for (const semantics::Scope *s{&scope_}; !s->IsGlobal();) { 19764ab3302SCarolineConcatto s = &s->parent(); 19864ab3302SCarolineConcatto if (s == &symbol.owner()) { 19964ab3302SCarolineConcatto return std::nullopt; 20064ab3302SCarolineConcatto } 20164ab3302SCarolineConcatto } 20264ab3302SCarolineConcatto return "reference to local entity '"s + symbol.name().ToString() + "'"; 20364ab3302SCarolineConcatto } 20464ab3302SCarolineConcatto 20564ab3302SCarolineConcatto Result operator()(const Component &x) const { 20664ab3302SCarolineConcatto // Don't look at the component symbol. 20764ab3302SCarolineConcatto return (*this)(x.base()); 20864ab3302SCarolineConcatto } 20964ab3302SCarolineConcatto Result operator()(const DescriptorInquiry &) const { 21064ab3302SCarolineConcatto // Subtle: Uses of SIZE(), LBOUND(), &c. that are valid in specification 21164ab3302SCarolineConcatto // expressions will have been converted to expressions over descriptor 21264ab3302SCarolineConcatto // inquiries by Fold(). 21364ab3302SCarolineConcatto return std::nullopt; 21464ab3302SCarolineConcatto } 21564ab3302SCarolineConcatto 21664ab3302SCarolineConcatto template <typename T> Result operator()(const FunctionRef<T> &x) const { 21764ab3302SCarolineConcatto if (const auto *symbol{x.proc().GetSymbol()}) { 21864ab3302SCarolineConcatto if (!semantics::IsPureProcedure(*symbol)) { 21964ab3302SCarolineConcatto return "reference to impure function '"s + symbol->name().ToString() + 22064ab3302SCarolineConcatto "'"; 22164ab3302SCarolineConcatto } 22264ab3302SCarolineConcatto // TODO: other checks for standard module procedures 22364ab3302SCarolineConcatto } else { 22464ab3302SCarolineConcatto const SpecificIntrinsic &intrin{DEREF(x.proc().GetSpecificIntrinsic())}; 22564ab3302SCarolineConcatto if (intrin.name == "present") { 22664ab3302SCarolineConcatto return std::nullopt; // no need to check argument(s) 22764ab3302SCarolineConcatto } 22864ab3302SCarolineConcatto if (IsConstantExpr(x)) { 22964ab3302SCarolineConcatto // inquiry functions may not need to check argument(s) 23064ab3302SCarolineConcatto return std::nullopt; 23164ab3302SCarolineConcatto } 23264ab3302SCarolineConcatto } 23364ab3302SCarolineConcatto return (*this)(x.arguments()); 23464ab3302SCarolineConcatto } 23564ab3302SCarolineConcatto 23664ab3302SCarolineConcatto private: 23764ab3302SCarolineConcatto const semantics::Scope &scope_; 23864ab3302SCarolineConcatto }; 23964ab3302SCarolineConcatto 24064ab3302SCarolineConcatto template <typename A> 24164ab3302SCarolineConcatto void CheckSpecificationExpr(const A &x, parser::ContextualMessages &messages, 24264ab3302SCarolineConcatto const semantics::Scope &scope) { 24364ab3302SCarolineConcatto if (auto why{CheckSpecificationExprHelper{scope}(x)}) { 24464ab3302SCarolineConcatto messages.Say("Invalid specification expression: %s"_err_en_US, *why); 24564ab3302SCarolineConcatto } 24664ab3302SCarolineConcatto } 24764ab3302SCarolineConcatto 24864ab3302SCarolineConcatto template void CheckSpecificationExpr(const Expr<SomeType> &, 24964ab3302SCarolineConcatto parser::ContextualMessages &, const semantics::Scope &); 2509977b24aSpeter klausler template void CheckSpecificationExpr(const Expr<SomeInteger> &, 2519977b24aSpeter klausler parser::ContextualMessages &, const semantics::Scope &); 2529977b24aSpeter klausler template void CheckSpecificationExpr(const Expr<SubscriptInteger> &, 2539977b24aSpeter klausler parser::ContextualMessages &, const semantics::Scope &); 2549977b24aSpeter klausler template void CheckSpecificationExpr(const std::optional<Expr<SomeType>> &, 2559977b24aSpeter klausler parser::ContextualMessages &, const semantics::Scope &); 25664ab3302SCarolineConcatto template void CheckSpecificationExpr(const std::optional<Expr<SomeInteger>> &, 25764ab3302SCarolineConcatto parser::ContextualMessages &, const semantics::Scope &); 25864ab3302SCarolineConcatto template void CheckSpecificationExpr( 25964ab3302SCarolineConcatto const std::optional<Expr<SubscriptInteger>> &, parser::ContextualMessages &, 26064ab3302SCarolineConcatto const semantics::Scope &); 26164ab3302SCarolineConcatto 26264ab3302SCarolineConcatto // IsSimplyContiguous() -- 9.5.4 26364ab3302SCarolineConcatto class IsSimplyContiguousHelper 26464ab3302SCarolineConcatto : public AnyTraverse<IsSimplyContiguousHelper, std::optional<bool>> { 26564ab3302SCarolineConcatto public: 26664ab3302SCarolineConcatto using Result = std::optional<bool>; // tri-state 26764ab3302SCarolineConcatto using Base = AnyTraverse<IsSimplyContiguousHelper, Result>; 26864ab3302SCarolineConcatto explicit IsSimplyContiguousHelper(const IntrinsicProcTable &t) 26964ab3302SCarolineConcatto : Base{*this}, table_{t} {} 27064ab3302SCarolineConcatto using Base::operator(); 27164ab3302SCarolineConcatto 27264ab3302SCarolineConcatto Result operator()(const semantics::Symbol &symbol) const { 27364ab3302SCarolineConcatto if (symbol.attrs().test(semantics::Attr::CONTIGUOUS) || 27464ab3302SCarolineConcatto symbol.Rank() == 0) { 27564ab3302SCarolineConcatto return true; 27664ab3302SCarolineConcatto } else if (semantics::IsPointer(symbol)) { 27764ab3302SCarolineConcatto return false; 27864ab3302SCarolineConcatto } else if (const auto *details{ 27964ab3302SCarolineConcatto symbol.detailsIf<semantics::ObjectEntityDetails>()}) { 28064ab3302SCarolineConcatto // N.B. ALLOCATABLEs are deferred shape, not assumed, and 28164ab3302SCarolineConcatto // are obviously contiguous. 28264ab3302SCarolineConcatto return !details->IsAssumedShape() && !details->IsAssumedRank(); 28364ab3302SCarolineConcatto } else { 28464ab3302SCarolineConcatto return false; 28564ab3302SCarolineConcatto } 28664ab3302SCarolineConcatto } 28764ab3302SCarolineConcatto 28864ab3302SCarolineConcatto Result operator()(const ArrayRef &x) const { 28964ab3302SCarolineConcatto const auto &symbol{x.GetLastSymbol()}; 29064ab3302SCarolineConcatto if (!(*this)(symbol)) { 29164ab3302SCarolineConcatto return false; 29264ab3302SCarolineConcatto } else if (auto rank{CheckSubscripts(x.subscript())}) { 29364ab3302SCarolineConcatto // a(:)%b(1,1) is not contiguous; a(1)%b(:,:) is 29464ab3302SCarolineConcatto return *rank > 0 || x.Rank() == 0; 29564ab3302SCarolineConcatto } else { 29664ab3302SCarolineConcatto return false; 29764ab3302SCarolineConcatto } 29864ab3302SCarolineConcatto } 29964ab3302SCarolineConcatto Result operator()(const CoarrayRef &x) const { 30064ab3302SCarolineConcatto return CheckSubscripts(x.subscript()).has_value(); 30164ab3302SCarolineConcatto } 30264ab3302SCarolineConcatto Result operator()(const Component &x) const { 30364ab3302SCarolineConcatto return x.base().Rank() == 0 && (*this)(x.GetLastSymbol()); 30464ab3302SCarolineConcatto } 30564ab3302SCarolineConcatto Result operator()(const ComplexPart &) const { return false; } 30664ab3302SCarolineConcatto Result operator()(const Substring &) const { return false; } 30764ab3302SCarolineConcatto 30864ab3302SCarolineConcatto template <typename T> Result operator()(const FunctionRef<T> &x) const { 30964ab3302SCarolineConcatto if (auto chars{ 31064ab3302SCarolineConcatto characteristics::Procedure::Characterize(x.proc(), table_)}) { 31164ab3302SCarolineConcatto if (chars->functionResult) { 31264ab3302SCarolineConcatto const auto &result{*chars->functionResult}; 31364ab3302SCarolineConcatto return !result.IsProcedurePointer() && 31464ab3302SCarolineConcatto result.attrs.test(characteristics::FunctionResult::Attr::Pointer) && 31564ab3302SCarolineConcatto result.attrs.test( 31664ab3302SCarolineConcatto characteristics::FunctionResult::Attr::Contiguous); 31764ab3302SCarolineConcatto } 31864ab3302SCarolineConcatto } 31964ab3302SCarolineConcatto return false; 32064ab3302SCarolineConcatto } 32164ab3302SCarolineConcatto 32264ab3302SCarolineConcatto private: 32364ab3302SCarolineConcatto // If the subscripts can possibly be on a simply-contiguous array reference, 32464ab3302SCarolineConcatto // return the rank. 32564ab3302SCarolineConcatto static std::optional<int> CheckSubscripts( 32664ab3302SCarolineConcatto const std::vector<Subscript> &subscript) { 32764ab3302SCarolineConcatto bool anyTriplet{false}; 32864ab3302SCarolineConcatto int rank{0}; 32964ab3302SCarolineConcatto for (auto j{subscript.size()}; j-- > 0;) { 33064ab3302SCarolineConcatto if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) { 33164ab3302SCarolineConcatto if (!triplet->IsStrideOne()) { 33264ab3302SCarolineConcatto return std::nullopt; 33364ab3302SCarolineConcatto } else if (anyTriplet) { 33464ab3302SCarolineConcatto if (triplet->lower() || triplet->upper()) { 33564ab3302SCarolineConcatto // all triplets before the last one must be just ":" 33664ab3302SCarolineConcatto return std::nullopt; 33764ab3302SCarolineConcatto } 33864ab3302SCarolineConcatto } else { 33964ab3302SCarolineConcatto anyTriplet = true; 34064ab3302SCarolineConcatto } 34164ab3302SCarolineConcatto ++rank; 34264ab3302SCarolineConcatto } else if (anyTriplet || subscript[j].Rank() > 0) { 34364ab3302SCarolineConcatto return std::nullopt; 34464ab3302SCarolineConcatto } 34564ab3302SCarolineConcatto } 34664ab3302SCarolineConcatto return rank; 34764ab3302SCarolineConcatto } 34864ab3302SCarolineConcatto 34964ab3302SCarolineConcatto const IntrinsicProcTable &table_; 35064ab3302SCarolineConcatto }; 35164ab3302SCarolineConcatto 35264ab3302SCarolineConcatto template <typename A> 35364ab3302SCarolineConcatto bool IsSimplyContiguous(const A &x, const IntrinsicProcTable &table) { 35464ab3302SCarolineConcatto if (IsVariable(x)) { 35564ab3302SCarolineConcatto auto known{IsSimplyContiguousHelper{table}(x)}; 35664ab3302SCarolineConcatto return known && *known; 35764ab3302SCarolineConcatto } else { 35864ab3302SCarolineConcatto return true; // not a variable 35964ab3302SCarolineConcatto } 36064ab3302SCarolineConcatto } 36164ab3302SCarolineConcatto 36264ab3302SCarolineConcatto template bool IsSimplyContiguous( 36364ab3302SCarolineConcatto const Expr<SomeType> &, const IntrinsicProcTable &); 36464ab3302SCarolineConcatto 365*1f879005STim Keith } // namespace Fortran::evaluate 366