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 ¶m) 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