xref: /llvm-project/flang/lib/Evaluate/check-expression.cpp (revision 9696355484152eda5684e0ec6249f4c423f08e42)
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 &param) 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