xref: /llvm-project/flang/lib/Evaluate/check-expression.cpp (revision 5349f99114cfcf597220cbc9b9253f178a43fabd)
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"
108d0c3c05SPete Steinfeld #include "flang/Evaluate/intrinsics.h"
1164ab3302SCarolineConcatto #include "flang/Evaluate/traverse.h"
1264ab3302SCarolineConcatto #include "flang/Evaluate/type.h"
1364ab3302SCarolineConcatto #include "flang/Semantics/symbol.h"
1464ab3302SCarolineConcatto #include "flang/Semantics/tools.h"
158d0c3c05SPete Steinfeld #include <set>
168d0c3c05SPete Steinfeld #include <string>
1764ab3302SCarolineConcatto 
1864ab3302SCarolineConcatto namespace Fortran::evaluate {
1964ab3302SCarolineConcatto 
2064ab3302SCarolineConcatto // Constant expression predicate IsConstantExpr().
2164ab3302SCarolineConcatto // This code determines whether an expression is a "constant expression"
2264ab3302SCarolineConcatto // in the sense of section 10.1.12.  This is not the same thing as being
2364ab3302SCarolineConcatto // able to fold it (yet) into a known constant value; specifically,
2464ab3302SCarolineConcatto // the expression may reference derived type kind parameters whose values
2564ab3302SCarolineConcatto // are not yet known.
2664ab3302SCarolineConcatto class IsConstantExprHelper : public AllTraverse<IsConstantExprHelper, true> {
2764ab3302SCarolineConcatto public:
2864ab3302SCarolineConcatto   using Base = AllTraverse<IsConstantExprHelper, true>;
2964ab3302SCarolineConcatto   IsConstantExprHelper() : Base{*this} {}
3064ab3302SCarolineConcatto   using Base::operator();
3164ab3302SCarolineConcatto 
324cbfd93aSpeter klausler   bool operator()(const TypeParamInquiry &inq) const {
3364ab3302SCarolineConcatto     return IsKindTypeParameter(inq.parameter());
3464ab3302SCarolineConcatto   }
3564ab3302SCarolineConcatto   bool operator()(const semantics::Symbol &symbol) const {
364171f80dSpeter klausler     const auto &ultimate{symbol.GetUltimate()};
374171f80dSpeter klausler     return IsNamedConstant(ultimate) || IsImpliedDoIndex(ultimate) ||
384171f80dSpeter klausler         IsInitialProcedureTarget(ultimate);
3964ab3302SCarolineConcatto   }
4064ab3302SCarolineConcatto   bool operator()(const CoarrayRef &) const { return false; }
4164ab3302SCarolineConcatto   bool operator()(const semantics::ParamValue &param) const {
4264ab3302SCarolineConcatto     return param.isExplicit() && (*this)(param.GetExplicit());
4364ab3302SCarolineConcatto   }
4464ab3302SCarolineConcatto   template <typename T> bool operator()(const FunctionRef<T> &call) const {
4564ab3302SCarolineConcatto     if (const auto *intrinsic{std::get_if<SpecificIntrinsic>(&call.proc().u)}) {
462cf52504SPeter Steinfeld       // kind is always a constant, and we avoid cascading errors by calling
472cf52504SPeter Steinfeld       // invalid calls to intrinsics constant
482cf52504SPeter Steinfeld       return intrinsic->name == "kind" ||
492cf52504SPeter Steinfeld           intrinsic->name == IntrinsicProcTable::InvalidName;
5064ab3302SCarolineConcatto       // TODO: other inquiry intrinsics
5164ab3302SCarolineConcatto     } else {
5264ab3302SCarolineConcatto       return false;
5364ab3302SCarolineConcatto     }
5464ab3302SCarolineConcatto   }
553a1afd8cSpeter klausler   bool operator()(const StructureConstructor &constructor) const {
563a1afd8cSpeter klausler     for (const auto &[symRef, expr] : constructor) {
574171f80dSpeter klausler       if (!IsConstantStructureConstructorComponent(*symRef, expr.value())) {
583a1afd8cSpeter klausler         return false;
593a1afd8cSpeter klausler       }
603a1afd8cSpeter klausler     }
613a1afd8cSpeter klausler     return true;
623a1afd8cSpeter klausler   }
6370f1b4b4SAnchu Rajendran   bool operator()(const Component &component) const {
6470f1b4b4SAnchu Rajendran     return (*this)(component.base());
6570f1b4b4SAnchu Rajendran   }
6664ab3302SCarolineConcatto   // Forbid integer division by zero in constants.
6764ab3302SCarolineConcatto   template <int KIND>
6864ab3302SCarolineConcatto   bool operator()(
6964ab3302SCarolineConcatto       const Divide<Type<TypeCategory::Integer, KIND>> &division) const {
7064ab3302SCarolineConcatto     using T = Type<TypeCategory::Integer, KIND>;
7164ab3302SCarolineConcatto     if (const auto divisor{GetScalarConstantValue<T>(division.right())}) {
7262b127eeSJean Perier       return !divisor->IsZero() && (*this)(division.left());
7364ab3302SCarolineConcatto     } else {
7464ab3302SCarolineConcatto       return false;
7564ab3302SCarolineConcatto     }
7664ab3302SCarolineConcatto   }
774171f80dSpeter klausler 
784171f80dSpeter klausler   bool operator()(const Constant<SomeDerived> &) const { return true; }
794171f80dSpeter klausler 
804171f80dSpeter klausler private:
814171f80dSpeter klausler   bool IsConstantStructureConstructorComponent(
824171f80dSpeter klausler       const Symbol &component, const Expr<SomeType> &expr) const {
834171f80dSpeter klausler     if (IsAllocatable(component)) {
844171f80dSpeter klausler       return IsNullPointer(expr);
854171f80dSpeter klausler     } else if (IsPointer(component)) {
864171f80dSpeter klausler       return IsNullPointer(expr) || IsInitialDataTarget(expr) ||
874171f80dSpeter klausler           IsInitialProcedureTarget(expr);
884171f80dSpeter klausler     } else {
894171f80dSpeter klausler       return (*this)(expr);
904171f80dSpeter klausler     }
914171f80dSpeter klausler   }
9264ab3302SCarolineConcatto };
9364ab3302SCarolineConcatto 
9464ab3302SCarolineConcatto template <typename A> bool IsConstantExpr(const A &x) {
9564ab3302SCarolineConcatto   return IsConstantExprHelper{}(x);
9664ab3302SCarolineConcatto }
9764ab3302SCarolineConcatto template bool IsConstantExpr(const Expr<SomeType> &);
9864ab3302SCarolineConcatto template bool IsConstantExpr(const Expr<SomeInteger> &);
999977b24aSpeter klausler template bool IsConstantExpr(const Expr<SubscriptInteger> &);
1004171f80dSpeter klausler template bool IsConstantExpr(const StructureConstructor &);
10164ab3302SCarolineConcatto 
10264ab3302SCarolineConcatto // Object pointer initialization checking predicate IsInitialDataTarget().
10364ab3302SCarolineConcatto // This code determines whether an expression is allowable as the static
10464ab3302SCarolineConcatto // data address used to initialize a pointer with "=> x".  See C765.
1053a1afd8cSpeter klausler class IsInitialDataTargetHelper
10664ab3302SCarolineConcatto     : public AllTraverse<IsInitialDataTargetHelper, true> {
1073a1afd8cSpeter klausler public:
10864ab3302SCarolineConcatto   using Base = AllTraverse<IsInitialDataTargetHelper, true>;
10964ab3302SCarolineConcatto   using Base::operator();
1103a1afd8cSpeter klausler   explicit IsInitialDataTargetHelper(parser::ContextualMessages *m)
11164ab3302SCarolineConcatto       : Base{*this}, messages_{m} {}
11264ab3302SCarolineConcatto 
1134171f80dSpeter klausler   bool emittedMessage() const { return emittedMessage_; }
1144171f80dSpeter klausler 
11564ab3302SCarolineConcatto   bool operator()(const BOZLiteralConstant &) const { return false; }
11664ab3302SCarolineConcatto   bool operator()(const NullPointer &) const { return true; }
11764ab3302SCarolineConcatto   template <typename T> bool operator()(const Constant<T> &) const {
11864ab3302SCarolineConcatto     return false;
11964ab3302SCarolineConcatto   }
1204171f80dSpeter klausler   bool operator()(const semantics::Symbol &symbol) {
12164ab3302SCarolineConcatto     const Symbol &ultimate{symbol.GetUltimate()};
12264ab3302SCarolineConcatto     if (IsAllocatable(ultimate)) {
1233a1afd8cSpeter klausler       if (messages_) {
1243a1afd8cSpeter klausler         messages_->Say(
12564ab3302SCarolineConcatto             "An initial data target may not be a reference to an ALLOCATABLE '%s'"_err_en_US,
12664ab3302SCarolineConcatto             ultimate.name());
1274171f80dSpeter klausler         emittedMessage_ = true;
1283a1afd8cSpeter klausler       }
1294171f80dSpeter klausler       return false;
13064ab3302SCarolineConcatto     } else if (ultimate.Corank() > 0) {
1313a1afd8cSpeter klausler       if (messages_) {
1323a1afd8cSpeter klausler         messages_->Say(
13364ab3302SCarolineConcatto             "An initial data target may not be a reference to a coarray '%s'"_err_en_US,
13464ab3302SCarolineConcatto             ultimate.name());
1354171f80dSpeter klausler         emittedMessage_ = true;
1363a1afd8cSpeter klausler       }
1374171f80dSpeter klausler       return false;
13864ab3302SCarolineConcatto     } else if (!ultimate.attrs().test(semantics::Attr::TARGET)) {
1393a1afd8cSpeter klausler       if (messages_) {
1403a1afd8cSpeter klausler         messages_->Say(
14164ab3302SCarolineConcatto             "An initial data target may not be a reference to an object '%s' that lacks the TARGET attribute"_err_en_US,
14264ab3302SCarolineConcatto             ultimate.name());
1434171f80dSpeter klausler         emittedMessage_ = true;
1443a1afd8cSpeter klausler       }
1454171f80dSpeter klausler       return false;
14664ab3302SCarolineConcatto     } else if (!IsSaved(ultimate)) {
1473a1afd8cSpeter klausler       if (messages_) {
1483a1afd8cSpeter klausler         messages_->Say(
14964ab3302SCarolineConcatto             "An initial data target may not be a reference to an object '%s' that lacks the SAVE attribute"_err_en_US,
15064ab3302SCarolineConcatto             ultimate.name());
1514171f80dSpeter klausler         emittedMessage_ = true;
1523a1afd8cSpeter klausler       }
1534171f80dSpeter klausler       return false;
15464ab3302SCarolineConcatto     }
15564ab3302SCarolineConcatto     return true;
15664ab3302SCarolineConcatto   }
15764ab3302SCarolineConcatto   bool operator()(const StaticDataObject &) const { return false; }
1584cbfd93aSpeter klausler   bool operator()(const TypeParamInquiry &) const { return false; }
15964ab3302SCarolineConcatto   bool operator()(const Triplet &x) const {
16064ab3302SCarolineConcatto     return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) &&
16164ab3302SCarolineConcatto         IsConstantExpr(x.stride());
16264ab3302SCarolineConcatto   }
16364ab3302SCarolineConcatto   bool operator()(const Subscript &x) const {
1641f879005STim Keith     return std::visit(common::visitors{
16564ab3302SCarolineConcatto                           [&](const Triplet &t) { return (*this)(t); },
16664ab3302SCarolineConcatto                           [&](const auto &y) {
1671f879005STim Keith                             return y.value().Rank() == 0 &&
1681f879005STim Keith                                 IsConstantExpr(y.value());
16964ab3302SCarolineConcatto                           },
17064ab3302SCarolineConcatto                       },
17164ab3302SCarolineConcatto         x.u);
17264ab3302SCarolineConcatto   }
17364ab3302SCarolineConcatto   bool operator()(const CoarrayRef &) const { return false; }
17464ab3302SCarolineConcatto   bool operator()(const Substring &x) const {
17564ab3302SCarolineConcatto     return IsConstantExpr(x.lower()) && IsConstantExpr(x.upper()) &&
17664ab3302SCarolineConcatto         (*this)(x.parent());
17764ab3302SCarolineConcatto   }
17864ab3302SCarolineConcatto   bool operator()(const DescriptorInquiry &) const { return false; }
17964ab3302SCarolineConcatto   template <typename T> bool operator()(const ArrayConstructor<T> &) const {
18064ab3302SCarolineConcatto     return false;
18164ab3302SCarolineConcatto   }
18264ab3302SCarolineConcatto   bool operator()(const StructureConstructor &) const { return false; }
1831f879005STim Keith   template <typename T> bool operator()(const FunctionRef<T> &) {
1841f879005STim Keith     return false;
1851f879005STim Keith   }
18664ab3302SCarolineConcatto   template <typename D, typename R, typename... O>
18764ab3302SCarolineConcatto   bool operator()(const Operation<D, R, O...> &) const {
18864ab3302SCarolineConcatto     return false;
18964ab3302SCarolineConcatto   }
19064ab3302SCarolineConcatto   template <typename T> bool operator()(const Parentheses<T> &x) const {
19164ab3302SCarolineConcatto     return (*this)(x.left());
19264ab3302SCarolineConcatto   }
193*5349f991Speter klausler   template <typename T> bool operator()(const FunctionRef<T> &x) const {
194*5349f991Speter klausler     return false;
195*5349f991Speter klausler   }
19664ab3302SCarolineConcatto   bool operator()(const Relational<SomeType> &) const { return false; }
1978d0c3c05SPete Steinfeld 
19864ab3302SCarolineConcatto private:
1993a1afd8cSpeter klausler   parser::ContextualMessages *messages_;
2004171f80dSpeter klausler   bool emittedMessage_{false};
20164ab3302SCarolineConcatto };
20264ab3302SCarolineConcatto 
20364ab3302SCarolineConcatto bool IsInitialDataTarget(
2043a1afd8cSpeter klausler     const Expr<SomeType> &x, parser::ContextualMessages *messages) {
2054171f80dSpeter klausler   IsInitialDataTargetHelper helper{messages};
2064171f80dSpeter klausler   bool result{helper(x)};
2074171f80dSpeter klausler   if (!result && messages && !helper.emittedMessage()) {
2084171f80dSpeter klausler     messages->Say(
2094171f80dSpeter klausler         "An initial data target must be a designator with constant subscripts"_err_en_US);
2104171f80dSpeter klausler   }
2114171f80dSpeter klausler   return result;
2124171f80dSpeter klausler }
2134171f80dSpeter klausler 
2144171f80dSpeter klausler bool IsInitialProcedureTarget(const semantics::Symbol &symbol) {
2154171f80dSpeter klausler   const auto &ultimate{symbol.GetUltimate()};
2164171f80dSpeter klausler   return std::visit(
2174171f80dSpeter klausler       common::visitors{
2184171f80dSpeter klausler           [](const semantics::SubprogramDetails &) { return true; },
2194171f80dSpeter klausler           [](const semantics::SubprogramNameDetails &) { return true; },
2204171f80dSpeter klausler           [&](const semantics::ProcEntityDetails &proc) {
2214171f80dSpeter klausler             return !semantics::IsPointer(ultimate) && !proc.isDummy();
2224171f80dSpeter klausler           },
2234171f80dSpeter klausler           [](const auto &) { return false; },
2244171f80dSpeter klausler       },
2254171f80dSpeter klausler       ultimate.details());
2264171f80dSpeter klausler }
2274171f80dSpeter klausler 
2284171f80dSpeter klausler bool IsInitialProcedureTarget(const ProcedureDesignator &proc) {
2294171f80dSpeter klausler   if (const auto *intrin{proc.GetSpecificIntrinsic()}) {
2304171f80dSpeter klausler     return !intrin->isRestrictedSpecific;
2314171f80dSpeter klausler   } else if (proc.GetComponent()) {
2324171f80dSpeter klausler     return false;
2334171f80dSpeter klausler   } else {
2344171f80dSpeter klausler     return IsInitialProcedureTarget(DEREF(proc.GetSymbol()));
2354171f80dSpeter klausler   }
2364171f80dSpeter klausler }
2374171f80dSpeter klausler 
2384171f80dSpeter klausler bool IsInitialProcedureTarget(const Expr<SomeType> &expr) {
2394171f80dSpeter klausler   if (const auto *proc{std::get_if<ProcedureDesignator>(&expr.u)}) {
2404171f80dSpeter klausler     return IsInitialProcedureTarget(*proc);
2414171f80dSpeter klausler   } else {
2424171f80dSpeter klausler     return IsNullPointer(expr);
2434171f80dSpeter klausler   }
24464ab3302SCarolineConcatto }
24564ab3302SCarolineConcatto 
24664ab3302SCarolineConcatto // Specification expression validation (10.1.11(2), C1010)
24764ab3302SCarolineConcatto class CheckSpecificationExprHelper
24864ab3302SCarolineConcatto     : public AnyTraverse<CheckSpecificationExprHelper,
24964ab3302SCarolineConcatto           std::optional<std::string>> {
25064ab3302SCarolineConcatto public:
25164ab3302SCarolineConcatto   using Result = std::optional<std::string>;
25264ab3302SCarolineConcatto   using Base = AnyTraverse<CheckSpecificationExprHelper, Result>;
25338095549SPete Steinfeld   explicit CheckSpecificationExprHelper(
25438095549SPete Steinfeld       const semantics::Scope &s, const IntrinsicProcTable &table)
25538095549SPete Steinfeld       : Base{*this}, scope_{s}, table_{table} {}
25664ab3302SCarolineConcatto   using Base::operator();
25764ab3302SCarolineConcatto 
25864ab3302SCarolineConcatto   Result operator()(const ProcedureDesignator &) const {
25964ab3302SCarolineConcatto     return "dummy procedure argument";
26064ab3302SCarolineConcatto   }
26164ab3302SCarolineConcatto   Result operator()(const CoarrayRef &) const { return "coindexed reference"; }
26264ab3302SCarolineConcatto 
26364ab3302SCarolineConcatto   Result operator()(const semantics::Symbol &symbol) const {
26467b13e97Speter klausler     const auto &ultimate{symbol.GetUltimate()};
26567b13e97Speter klausler     if (semantics::IsNamedConstant(ultimate) || ultimate.owner().IsModule() ||
26667b13e97Speter klausler         ultimate.owner().IsSubmodule()) {
26764ab3302SCarolineConcatto       return std::nullopt;
26867b13e97Speter klausler     } else if (scope_.IsDerivedType() &&
26967b13e97Speter klausler         IsVariableName(ultimate)) { // C750, C754
27038095549SPete Steinfeld       return "derived type component or type parameter value not allowed to "
27138095549SPete Steinfeld              "reference variable '"s +
27267b13e97Speter klausler           ultimate.name().ToString() + "'";
27367b13e97Speter klausler     } else if (IsDummy(ultimate)) {
27467b13e97Speter klausler       if (ultimate.attrs().test(semantics::Attr::OPTIONAL)) {
27564ab3302SCarolineConcatto         return "reference to OPTIONAL dummy argument '"s +
27667b13e97Speter klausler             ultimate.name().ToString() + "'";
27767b13e97Speter klausler       } else if (ultimate.attrs().test(semantics::Attr::INTENT_OUT)) {
27864ab3302SCarolineConcatto         return "reference to INTENT(OUT) dummy argument '"s +
27967b13e97Speter klausler             ultimate.name().ToString() + "'";
28067b13e97Speter klausler       } else if (ultimate.has<semantics::ObjectEntityDetails>()) {
28164ab3302SCarolineConcatto         return std::nullopt;
28264ab3302SCarolineConcatto       } else {
28364ab3302SCarolineConcatto         return "dummy procedure argument";
28464ab3302SCarolineConcatto       }
28564ab3302SCarolineConcatto     } else if (const auto *object{
28667b13e97Speter klausler                    ultimate.detailsIf<semantics::ObjectEntityDetails>()}) {
28764ab3302SCarolineConcatto       // TODO: what about EQUIVALENCE with data in COMMON?
28864ab3302SCarolineConcatto       // TODO: does this work for blank COMMON?
28964ab3302SCarolineConcatto       if (object->commonBlock()) {
29064ab3302SCarolineConcatto         return std::nullopt;
29164ab3302SCarolineConcatto       }
29264ab3302SCarolineConcatto     }
29364ab3302SCarolineConcatto     for (const semantics::Scope *s{&scope_}; !s->IsGlobal();) {
29464ab3302SCarolineConcatto       s = &s->parent();
29567b13e97Speter klausler       if (s == &ultimate.owner()) {
29664ab3302SCarolineConcatto         return std::nullopt;
29764ab3302SCarolineConcatto       }
29864ab3302SCarolineConcatto     }
29967b13e97Speter klausler     return "reference to local entity '"s + ultimate.name().ToString() + "'";
30064ab3302SCarolineConcatto   }
30164ab3302SCarolineConcatto 
30264ab3302SCarolineConcatto   Result operator()(const Component &x) const {
30364ab3302SCarolineConcatto     // Don't look at the component symbol.
30464ab3302SCarolineConcatto     return (*this)(x.base());
30564ab3302SCarolineConcatto   }
30664ab3302SCarolineConcatto   Result operator()(const DescriptorInquiry &) const {
30764ab3302SCarolineConcatto     // Subtle: Uses of SIZE(), LBOUND(), &c. that are valid in specification
30864ab3302SCarolineConcatto     // expressions will have been converted to expressions over descriptor
30964ab3302SCarolineConcatto     // inquiries by Fold().
31064ab3302SCarolineConcatto     return std::nullopt;
31164ab3302SCarolineConcatto   }
31264ab3302SCarolineConcatto 
3134cbfd93aSpeter klausler   Result operator()(const TypeParamInquiry &inq) const {
3148d0c3c05SPete Steinfeld     if (scope_.IsDerivedType() && !IsConstantExpr(inq) &&
315f862d858Speter klausler         inq.base() /* X%T, not local T */) { // C750, C754
31638095549SPete Steinfeld       return "non-constant reference to a type parameter inquiry not "
31738095549SPete Steinfeld              "allowed for derived type components or type parameter values";
3188d0c3c05SPete Steinfeld     }
3198d0c3c05SPete Steinfeld     return std::nullopt;
3208d0c3c05SPete Steinfeld   }
3218d0c3c05SPete Steinfeld 
32264ab3302SCarolineConcatto   template <typename T> Result operator()(const FunctionRef<T> &x) const {
32364ab3302SCarolineConcatto     if (const auto *symbol{x.proc().GetSymbol()}) {
32464ab3302SCarolineConcatto       if (!semantics::IsPureProcedure(*symbol)) {
32564ab3302SCarolineConcatto         return "reference to impure function '"s + symbol->name().ToString() +
32664ab3302SCarolineConcatto             "'";
32764ab3302SCarolineConcatto       }
3288d0c3c05SPete Steinfeld       if (semantics::IsStmtFunction(*symbol)) {
3298d0c3c05SPete Steinfeld         return "reference to statement function '"s +
3308d0c3c05SPete Steinfeld             symbol->name().ToString() + "'";
3318d0c3c05SPete Steinfeld       }
33238095549SPete Steinfeld       if (scope_.IsDerivedType()) { // C750, C754
3338d0c3c05SPete Steinfeld         return "reference to function '"s + symbol->name().ToString() +
33438095549SPete Steinfeld             "' not allowed for derived type components or type parameter"
33538095549SPete Steinfeld             " values";
3368d0c3c05SPete Steinfeld       }
33764ab3302SCarolineConcatto       // TODO: other checks for standard module procedures
33864ab3302SCarolineConcatto     } else {
33964ab3302SCarolineConcatto       const SpecificIntrinsic &intrin{DEREF(x.proc().GetSpecificIntrinsic())};
34038095549SPete Steinfeld       if (scope_.IsDerivedType()) { // C750, C754
3418d0c3c05SPete Steinfeld         if ((table_.IsIntrinsic(intrin.name) &&
3428d0c3c05SPete Steinfeld                 badIntrinsicsForComponents_.find(intrin.name) !=
3438d0c3c05SPete Steinfeld                     badIntrinsicsForComponents_.end()) ||
3448d0c3c05SPete Steinfeld             IsProhibitedFunction(intrin.name)) {
3458d0c3c05SPete Steinfeld           return "reference to intrinsic '"s + intrin.name +
34638095549SPete Steinfeld               "' not allowed for derived type components or type parameter"
34738095549SPete Steinfeld               " values";
3488d0c3c05SPete Steinfeld         }
3498d0c3c05SPete Steinfeld         if (table_.GetIntrinsicClass(intrin.name) ==
3508d0c3c05SPete Steinfeld                 IntrinsicClass::inquiryFunction &&
3518d0c3c05SPete Steinfeld             !IsConstantExpr(x)) {
3528d0c3c05SPete Steinfeld           return "non-constant reference to inquiry intrinsic '"s +
35338095549SPete Steinfeld               intrin.name +
35438095549SPete Steinfeld               "' not allowed for derived type components or type"
35538095549SPete Steinfeld               " parameter values";
3568d0c3c05SPete Steinfeld         }
3578d0c3c05SPete Steinfeld       } else if (intrin.name == "present") {
35864ab3302SCarolineConcatto         return std::nullopt; // no need to check argument(s)
35964ab3302SCarolineConcatto       }
36064ab3302SCarolineConcatto       if (IsConstantExpr(x)) {
36164ab3302SCarolineConcatto         // inquiry functions may not need to check argument(s)
36264ab3302SCarolineConcatto         return std::nullopt;
36364ab3302SCarolineConcatto       }
36464ab3302SCarolineConcatto     }
36564ab3302SCarolineConcatto     return (*this)(x.arguments());
36664ab3302SCarolineConcatto   }
36764ab3302SCarolineConcatto 
36864ab3302SCarolineConcatto private:
36964ab3302SCarolineConcatto   const semantics::Scope &scope_;
3708d0c3c05SPete Steinfeld   const IntrinsicProcTable &table_;
3718d0c3c05SPete Steinfeld   const std::set<std::string> badIntrinsicsForComponents_{
3728d0c3c05SPete Steinfeld       "allocated", "associated", "extends_type_of", "present", "same_type_as"};
3738d0c3c05SPete Steinfeld   static bool IsProhibitedFunction(std::string name) { return false; }
37464ab3302SCarolineConcatto };
37564ab3302SCarolineConcatto 
37664ab3302SCarolineConcatto template <typename A>
37764ab3302SCarolineConcatto void CheckSpecificationExpr(const A &x, parser::ContextualMessages &messages,
37838095549SPete Steinfeld     const semantics::Scope &scope, const IntrinsicProcTable &table) {
37938095549SPete Steinfeld   if (auto why{CheckSpecificationExprHelper{scope, table}(x)}) {
38064ab3302SCarolineConcatto     messages.Say("Invalid specification expression: %s"_err_en_US, *why);
38164ab3302SCarolineConcatto   }
38264ab3302SCarolineConcatto }
38364ab3302SCarolineConcatto 
38464ab3302SCarolineConcatto template void CheckSpecificationExpr(const Expr<SomeType> &,
3858d0c3c05SPete Steinfeld     parser::ContextualMessages &, const semantics::Scope &,
38638095549SPete Steinfeld     const IntrinsicProcTable &);
3879977b24aSpeter klausler template void CheckSpecificationExpr(const Expr<SomeInteger> &,
3888d0c3c05SPete Steinfeld     parser::ContextualMessages &, const semantics::Scope &,
38938095549SPete Steinfeld     const IntrinsicProcTable &);
3909977b24aSpeter klausler template void CheckSpecificationExpr(const Expr<SubscriptInteger> &,
3918d0c3c05SPete Steinfeld     parser::ContextualMessages &, const semantics::Scope &,
39238095549SPete Steinfeld     const IntrinsicProcTable &);
3939977b24aSpeter klausler template void CheckSpecificationExpr(const std::optional<Expr<SomeType>> &,
3948d0c3c05SPete Steinfeld     parser::ContextualMessages &, const semantics::Scope &,
39538095549SPete Steinfeld     const IntrinsicProcTable &);
39664ab3302SCarolineConcatto template void CheckSpecificationExpr(const std::optional<Expr<SomeInteger>> &,
3978d0c3c05SPete Steinfeld     parser::ContextualMessages &, const semantics::Scope &,
39838095549SPete Steinfeld     const IntrinsicProcTable &);
39964ab3302SCarolineConcatto template void CheckSpecificationExpr(
40064ab3302SCarolineConcatto     const std::optional<Expr<SubscriptInteger>> &, parser::ContextualMessages &,
40138095549SPete Steinfeld     const semantics::Scope &, const IntrinsicProcTable &);
40264ab3302SCarolineConcatto 
40364ab3302SCarolineConcatto // IsSimplyContiguous() -- 9.5.4
40464ab3302SCarolineConcatto class IsSimplyContiguousHelper
40564ab3302SCarolineConcatto     : public AnyTraverse<IsSimplyContiguousHelper, std::optional<bool>> {
40664ab3302SCarolineConcatto public:
40764ab3302SCarolineConcatto   using Result = std::optional<bool>; // tri-state
40864ab3302SCarolineConcatto   using Base = AnyTraverse<IsSimplyContiguousHelper, Result>;
40964ab3302SCarolineConcatto   explicit IsSimplyContiguousHelper(const IntrinsicProcTable &t)
41064ab3302SCarolineConcatto       : Base{*this}, table_{t} {}
41164ab3302SCarolineConcatto   using Base::operator();
41264ab3302SCarolineConcatto 
41364ab3302SCarolineConcatto   Result operator()(const semantics::Symbol &symbol) const {
41464ab3302SCarolineConcatto     if (symbol.attrs().test(semantics::Attr::CONTIGUOUS) ||
41564ab3302SCarolineConcatto         symbol.Rank() == 0) {
41664ab3302SCarolineConcatto       return true;
41764ab3302SCarolineConcatto     } else if (semantics::IsPointer(symbol)) {
41864ab3302SCarolineConcatto       return false;
41964ab3302SCarolineConcatto     } else if (const auto *details{
42064ab3302SCarolineConcatto                    symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
42164ab3302SCarolineConcatto       // N.B. ALLOCATABLEs are deferred shape, not assumed, and
42264ab3302SCarolineConcatto       // are obviously contiguous.
42364ab3302SCarolineConcatto       return !details->IsAssumedShape() && !details->IsAssumedRank();
42464ab3302SCarolineConcatto     } else {
42564ab3302SCarolineConcatto       return false;
42664ab3302SCarolineConcatto     }
42764ab3302SCarolineConcatto   }
42864ab3302SCarolineConcatto 
42964ab3302SCarolineConcatto   Result operator()(const ArrayRef &x) const {
43064ab3302SCarolineConcatto     const auto &symbol{x.GetLastSymbol()};
43164ab3302SCarolineConcatto     if (!(*this)(symbol)) {
43264ab3302SCarolineConcatto       return false;
43364ab3302SCarolineConcatto     } else if (auto rank{CheckSubscripts(x.subscript())}) {
43464ab3302SCarolineConcatto       // a(:)%b(1,1) is not contiguous; a(1)%b(:,:) is
43564ab3302SCarolineConcatto       return *rank > 0 || x.Rank() == 0;
43664ab3302SCarolineConcatto     } else {
43764ab3302SCarolineConcatto       return false;
43864ab3302SCarolineConcatto     }
43964ab3302SCarolineConcatto   }
44064ab3302SCarolineConcatto   Result operator()(const CoarrayRef &x) const {
44164ab3302SCarolineConcatto     return CheckSubscripts(x.subscript()).has_value();
44264ab3302SCarolineConcatto   }
44364ab3302SCarolineConcatto   Result operator()(const Component &x) const {
44464ab3302SCarolineConcatto     return x.base().Rank() == 0 && (*this)(x.GetLastSymbol());
44564ab3302SCarolineConcatto   }
44664ab3302SCarolineConcatto   Result operator()(const ComplexPart &) const { return false; }
44764ab3302SCarolineConcatto   Result operator()(const Substring &) const { return false; }
44864ab3302SCarolineConcatto 
44964ab3302SCarolineConcatto   template <typename T> Result operator()(const FunctionRef<T> &x) const {
45064ab3302SCarolineConcatto     if (auto chars{
45164ab3302SCarolineConcatto             characteristics::Procedure::Characterize(x.proc(), table_)}) {
45264ab3302SCarolineConcatto       if (chars->functionResult) {
45364ab3302SCarolineConcatto         const auto &result{*chars->functionResult};
45464ab3302SCarolineConcatto         return !result.IsProcedurePointer() &&
45564ab3302SCarolineConcatto             result.attrs.test(characteristics::FunctionResult::Attr::Pointer) &&
45664ab3302SCarolineConcatto             result.attrs.test(
45764ab3302SCarolineConcatto                 characteristics::FunctionResult::Attr::Contiguous);
45864ab3302SCarolineConcatto       }
45964ab3302SCarolineConcatto     }
46064ab3302SCarolineConcatto     return false;
46164ab3302SCarolineConcatto   }
46264ab3302SCarolineConcatto 
46364ab3302SCarolineConcatto private:
46464ab3302SCarolineConcatto   // If the subscripts can possibly be on a simply-contiguous array reference,
46564ab3302SCarolineConcatto   // return the rank.
46664ab3302SCarolineConcatto   static std::optional<int> CheckSubscripts(
46764ab3302SCarolineConcatto       const std::vector<Subscript> &subscript) {
46864ab3302SCarolineConcatto     bool anyTriplet{false};
46964ab3302SCarolineConcatto     int rank{0};
47064ab3302SCarolineConcatto     for (auto j{subscript.size()}; j-- > 0;) {
47164ab3302SCarolineConcatto       if (const auto *triplet{std::get_if<Triplet>(&subscript[j].u)}) {
47264ab3302SCarolineConcatto         if (!triplet->IsStrideOne()) {
47364ab3302SCarolineConcatto           return std::nullopt;
47464ab3302SCarolineConcatto         } else if (anyTriplet) {
47564ab3302SCarolineConcatto           if (triplet->lower() || triplet->upper()) {
47664ab3302SCarolineConcatto             // all triplets before the last one must be just ":"
47764ab3302SCarolineConcatto             return std::nullopt;
47864ab3302SCarolineConcatto           }
47964ab3302SCarolineConcatto         } else {
48064ab3302SCarolineConcatto           anyTriplet = true;
48164ab3302SCarolineConcatto         }
48264ab3302SCarolineConcatto         ++rank;
48364ab3302SCarolineConcatto       } else if (anyTriplet || subscript[j].Rank() > 0) {
48464ab3302SCarolineConcatto         return std::nullopt;
48564ab3302SCarolineConcatto       }
48664ab3302SCarolineConcatto     }
48764ab3302SCarolineConcatto     return rank;
48864ab3302SCarolineConcatto   }
48964ab3302SCarolineConcatto 
49064ab3302SCarolineConcatto   const IntrinsicProcTable &table_;
49164ab3302SCarolineConcatto };
49264ab3302SCarolineConcatto 
49364ab3302SCarolineConcatto template <typename A>
49464ab3302SCarolineConcatto bool IsSimplyContiguous(const A &x, const IntrinsicProcTable &table) {
49564ab3302SCarolineConcatto   if (IsVariable(x)) {
49664ab3302SCarolineConcatto     auto known{IsSimplyContiguousHelper{table}(x)};
49764ab3302SCarolineConcatto     return known && *known;
49864ab3302SCarolineConcatto   } else {
49964ab3302SCarolineConcatto     return true; // not a variable
50064ab3302SCarolineConcatto   }
50164ab3302SCarolineConcatto }
50264ab3302SCarolineConcatto 
50364ab3302SCarolineConcatto template bool IsSimplyContiguous(
50464ab3302SCarolineConcatto     const Expr<SomeType> &, const IntrinsicProcTable &);
50564ab3302SCarolineConcatto 
5061f879005STim Keith } // namespace Fortran::evaluate
507