164ab3302SCarolineConcatto //===-- lib/Evaluate/fold-integer.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 "fold-implementation.h" 100fdf9123SPeter Klausler #include "fold-matmul.h" 1147f18af5Speter klausler #include "fold-reduction.h" 122018dafcSPeter Steinfeld #include "flang/Evaluate/check-expression.h" 1364ab3302SCarolineConcatto 1464ab3302SCarolineConcatto namespace Fortran::evaluate { 1564ab3302SCarolineConcatto 16bd5371e4SMike Kashkarov // Given a collection of ConstantSubscripts values, package them as a Constant. 17bd5371e4SMike Kashkarov // Return scalar value if asScalar == true and shape-dim array otherwise. 18bd5371e4SMike Kashkarov template <typename T> 19bd5371e4SMike Kashkarov Expr<T> PackageConstantBounds( 20bd5371e4SMike Kashkarov const ConstantSubscripts &&bounds, bool asScalar = false) { 21bd5371e4SMike Kashkarov if (asScalar) { 22bd5371e4SMike Kashkarov return Expr<T>{Constant<T>{bounds.at(0)}}; 23bd5371e4SMike Kashkarov } else { 24bd5371e4SMike Kashkarov // As rank-dim array 25bd5371e4SMike Kashkarov const int rank{GetRank(bounds)}; 26bd5371e4SMike Kashkarov std::vector<Scalar<T>> packed(rank); 27bd5371e4SMike Kashkarov std::transform(bounds.begin(), bounds.end(), packed.begin(), 28bd5371e4SMike Kashkarov [](ConstantSubscript x) { return Scalar<T>(x); }); 29bd5371e4SMike Kashkarov return Expr<T>{Constant<T>{std::move(packed), ConstantSubscripts{rank}}}; 30bd5371e4SMike Kashkarov } 31bd5371e4SMike Kashkarov } 32bd5371e4SMike Kashkarov 33221ba64eSPeter Klausler // If a DIM= argument to LBOUND(), UBOUND(), or SIZE() exists and has a valid 34221ba64eSPeter Klausler // constant value, return in "dimVal" that value, less 1 (to make it suitable 35221ba64eSPeter Klausler // for use as a C++ vector<> index). Also check for erroneous constant values 36221ba64eSPeter Klausler // and returns false on error. 37221ba64eSPeter Klausler static bool CheckDimArg(const std::optional<ActualArgument> &dimArg, 38221ba64eSPeter Klausler const Expr<SomeType> &array, parser::ContextualMessages &messages, 39221ba64eSPeter Klausler bool isLBound, std::optional<int> &dimVal) { 40221ba64eSPeter Klausler dimVal.reset(); 41221ba64eSPeter Klausler if (int rank{array.Rank()}; rank > 0 || IsAssumedRank(array)) { 42221ba64eSPeter Klausler auto named{ExtractNamedEntity(array)}; 43221ba64eSPeter Klausler if (auto dim64{ToInt64(dimArg)}) { 44221ba64eSPeter Klausler if (*dim64 < 1) { 45221ba64eSPeter Klausler messages.Say("DIM=%jd dimension must be positive"_err_en_US, *dim64); 46221ba64eSPeter Klausler return false; 47221ba64eSPeter Klausler } else if (!IsAssumedRank(array) && *dim64 > rank) { 48221ba64eSPeter Klausler messages.Say( 49221ba64eSPeter Klausler "DIM=%jd dimension is out of range for rank-%d array"_err_en_US, 50221ba64eSPeter Klausler *dim64, rank); 51221ba64eSPeter Klausler return false; 52221ba64eSPeter Klausler } else if (!isLBound && named && 53221ba64eSPeter Klausler semantics::IsAssumedSizeArray(named->GetLastSymbol()) && 54221ba64eSPeter Klausler *dim64 == rank) { 55221ba64eSPeter Klausler messages.Say( 56221ba64eSPeter Klausler "DIM=%jd dimension is out of range for rank-%d assumed-size array"_err_en_US, 57221ba64eSPeter Klausler *dim64, rank); 58221ba64eSPeter Klausler return false; 59221ba64eSPeter Klausler } else if (IsAssumedRank(array)) { 60221ba64eSPeter Klausler if (*dim64 > common::maxRank) { 61221ba64eSPeter Klausler messages.Say( 62221ba64eSPeter Klausler "DIM=%jd dimension is too large for any array (maximum rank %d)"_err_en_US, 63221ba64eSPeter Klausler *dim64, common::maxRank); 64221ba64eSPeter Klausler return false; 65221ba64eSPeter Klausler } 66221ba64eSPeter Klausler } else { 67221ba64eSPeter Klausler dimVal = static_cast<int>(*dim64 - 1); // 1-based to 0-based 68221ba64eSPeter Klausler } 69221ba64eSPeter Klausler } 70221ba64eSPeter Klausler } 71221ba64eSPeter Klausler return true; 72221ba64eSPeter Klausler } 73221ba64eSPeter Klausler 74*94963919SPeter Klausler static bool CheckCoDimArg(const std::optional<ActualArgument> &dimArg, 75*94963919SPeter Klausler const Symbol &symbol, parser::ContextualMessages &messages, 76*94963919SPeter Klausler std::optional<int> &dimVal) { 77*94963919SPeter Klausler dimVal.reset(); 78*94963919SPeter Klausler if (int corank{symbol.Corank()}; corank > 0) { 79*94963919SPeter Klausler if (auto dim64{ToInt64(dimArg)}) { 80*94963919SPeter Klausler if (*dim64 < 1) { 81*94963919SPeter Klausler messages.Say("DIM=%jd dimension must be positive"_err_en_US, *dim64); 82*94963919SPeter Klausler return false; 83*94963919SPeter Klausler } else if (*dim64 > corank) { 84*94963919SPeter Klausler messages.Say( 85*94963919SPeter Klausler "DIM=%jd dimension is out of range for corank-%d coarray"_err_en_US, 86*94963919SPeter Klausler *dim64, corank); 87*94963919SPeter Klausler return false; 88*94963919SPeter Klausler } else { 89*94963919SPeter Klausler dimVal = static_cast<int>(*dim64 - 1); // 1-based to 0-based 90*94963919SPeter Klausler } 91*94963919SPeter Klausler } 92*94963919SPeter Klausler } 93*94963919SPeter Klausler return true; 94*94963919SPeter Klausler } 95*94963919SPeter Klausler 96d8b4ea48SMike Kashkarov // Class to retrieve the constant bound of an expression which is an 972018dafcSPeter Steinfeld // array that devolves to a type of Constant<T> 98d8b4ea48SMike Kashkarov class GetConstantArrayBoundHelper { 992018dafcSPeter Steinfeld public: 100d8b4ea48SMike Kashkarov template <typename T> 101d8b4ea48SMike Kashkarov static Expr<T> GetLbound( 102d8b4ea48SMike Kashkarov const Expr<SomeType> &array, std::optional<int> dim) { 103d8b4ea48SMike Kashkarov return PackageConstantBounds<T>( 104d8b4ea48SMike Kashkarov GetConstantArrayBoundHelper(dim, /*getLbound=*/true).Get(array), 105d8b4ea48SMike Kashkarov dim.has_value()); 106d8b4ea48SMike Kashkarov } 1072018dafcSPeter Steinfeld 108d8b4ea48SMike Kashkarov template <typename T> 109d8b4ea48SMike Kashkarov static Expr<T> GetUbound( 110d8b4ea48SMike Kashkarov const Expr<SomeType> &array, std::optional<int> dim) { 111d8b4ea48SMike Kashkarov return PackageConstantBounds<T>( 112d8b4ea48SMike Kashkarov GetConstantArrayBoundHelper(dim, /*getLbound=*/false).Get(array), 113d8b4ea48SMike Kashkarov dim.has_value()); 114d8b4ea48SMike Kashkarov } 115d8b4ea48SMike Kashkarov 116d8b4ea48SMike Kashkarov private: 117d8b4ea48SMike Kashkarov GetConstantArrayBoundHelper( 118d8b4ea48SMike Kashkarov std::optional<ConstantSubscript> dim, bool getLbound) 119d8b4ea48SMike Kashkarov : dim_{dim}, getLbound_{getLbound} {} 120d8b4ea48SMike Kashkarov 121d8b4ea48SMike Kashkarov template <typename T> ConstantSubscripts Get(const T &) { 1222018dafcSPeter Steinfeld // The method is needed for template expansion, but we should never get 1232018dafcSPeter Steinfeld // here in practice. 1242018dafcSPeter Steinfeld CHECK(false); 125bd5371e4SMike Kashkarov return {0}; 1262018dafcSPeter Steinfeld } 1272018dafcSPeter Steinfeld 128d8b4ea48SMike Kashkarov template <typename T> ConstantSubscripts Get(const Constant<T> &x) { 129d8b4ea48SMike Kashkarov if (getLbound_) { 1302018dafcSPeter Steinfeld // Return the lower bound 131bd5371e4SMike Kashkarov if (dim_) { 132bd5371e4SMike Kashkarov return {x.lbounds().at(*dim_)}; 133bd5371e4SMike Kashkarov } else { 134bd5371e4SMike Kashkarov return x.lbounds(); 135bd5371e4SMike Kashkarov } 136d8b4ea48SMike Kashkarov } else { 13792e22c97SMike Kashkarov // Return the upper bound 13892e22c97SMike Kashkarov if (arrayFromParenthesesExpr) { 13992e22c97SMike Kashkarov // Underlying array comes from (x) expression - return shapes 14092e22c97SMike Kashkarov if (dim_) { 14192e22c97SMike Kashkarov return {x.shape().at(*dim_)}; 14292e22c97SMike Kashkarov } else { 14392e22c97SMike Kashkarov return x.shape(); 14492e22c97SMike Kashkarov } 14592e22c97SMike Kashkarov } else { 146d8b4ea48SMike Kashkarov return x.ComputeUbounds(dim_); 147d8b4ea48SMike Kashkarov } 1482018dafcSPeter Steinfeld } 14992e22c97SMike Kashkarov } 1502018dafcSPeter Steinfeld 151d8b4ea48SMike Kashkarov template <typename T> ConstantSubscripts Get(const Parentheses<T> &x) { 15216c4b320SPeter Klausler // Case of temp variable inside parentheses - return [1, ... 1] for lower 15392e22c97SMike Kashkarov // bounds and shape for upper bounds 15492e22c97SMike Kashkarov if (getLbound_) { 15539ee23edSMike Kashkarov return ConstantSubscripts(x.Rank(), ConstantSubscript{1}); 15692e22c97SMike Kashkarov } else { 15792e22c97SMike Kashkarov // Indicate that underlying array comes from parentheses expression. 15892e22c97SMike Kashkarov // Continue to unwrap expression until we hit a constant 15992e22c97SMike Kashkarov arrayFromParenthesesExpr = true; 16092e22c97SMike Kashkarov return Get(x.left()); 16192e22c97SMike Kashkarov } 1622018dafcSPeter Steinfeld } 1632018dafcSPeter Steinfeld 164d8b4ea48SMike Kashkarov template <typename T> ConstantSubscripts Get(const Expr<T> &x) { 1652018dafcSPeter Steinfeld // recurse through Expr<T>'a until we hit a constant 166d8b4ea48SMike Kashkarov return common::visit([&](const auto &inner) { return Get(inner); }, 1672018dafcSPeter Steinfeld // [&](const auto &) { return 0; }, 1682018dafcSPeter Steinfeld x.u); 1692018dafcSPeter Steinfeld } 1702018dafcSPeter Steinfeld 171d8b4ea48SMike Kashkarov const std::optional<ConstantSubscript> dim_; 172d8b4ea48SMike Kashkarov const bool getLbound_; 17392e22c97SMike Kashkarov bool arrayFromParenthesesExpr{false}; 1742018dafcSPeter Steinfeld }; 1752018dafcSPeter Steinfeld 17664ab3302SCarolineConcatto template <int KIND> 17764ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> LBOUND(FoldingContext &context, 17864ab3302SCarolineConcatto FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) { 17964ab3302SCarolineConcatto using T = Type<TypeCategory::Integer, KIND>; 18064ab3302SCarolineConcatto ActualArguments &args{funcRef.arguments()}; 18164ab3302SCarolineConcatto if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) { 18264ab3302SCarolineConcatto std::optional<int> dim; 18364ab3302SCarolineConcatto if (funcRef.Rank() == 0) { 18464ab3302SCarolineConcatto // Optional DIM= argument is present: result is scalar. 185221ba64eSPeter Klausler if (!CheckDimArg(args[1], *array, context.messages(), true, dim)) { 18664ab3302SCarolineConcatto return MakeInvalidIntrinsic<T>(std::move(funcRef)); 187221ba64eSPeter Klausler } else if (!dim) { 188221ba64eSPeter Klausler // DIM= is present but not constant, or error 18964ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 19064ab3302SCarolineConcatto } 19164ab3302SCarolineConcatto } 192f9fdd4fdSPeter Klausler if (IsAssumedRank(*array)) { 193f9fdd4fdSPeter Klausler // Would like to return 1 if DIM=.. is present, but that would be 194f9fdd4fdSPeter Klausler // hiding a runtime error if the DIM= were too large (including 195f9fdd4fdSPeter Klausler // the case of an assumed-rank argument that's scalar). 196f9fdd4fdSPeter Klausler } else if (int rank{array->Rank()}; rank > 0) { 19764ab3302SCarolineConcatto bool lowerBoundsAreOne{true}; 19864ab3302SCarolineConcatto if (auto named{ExtractNamedEntity(*array)}) { 19964ab3302SCarolineConcatto const Symbol &symbol{named->GetLastSymbol()}; 20064ab3302SCarolineConcatto if (symbol.Rank() == rank) { 20164ab3302SCarolineConcatto lowerBoundsAreOne = false; 20264ab3302SCarolineConcatto if (dim) { 2033b61587cSPeter Klausler if (auto lb{GetLBOUND(context, *named, *dim)}) { 2043b61587cSPeter Klausler return Fold(context, ConvertToType<T>(std::move(*lb))); 2053b61587cSPeter Klausler } 20664ab3302SCarolineConcatto } else if (auto extents{ 2073b61587cSPeter Klausler AsExtentArrayExpr(GetLBOUNDs(context, *named))}) { 20864ab3302SCarolineConcatto return Fold(context, 20964ab3302SCarolineConcatto ConvertToType<T>(Expr<ExtentType>{std::move(*extents)})); 21064ab3302SCarolineConcatto } 21164ab3302SCarolineConcatto } else { 21264ab3302SCarolineConcatto lowerBoundsAreOne = symbol.Rank() == 0; // LBOUND(array%component) 21364ab3302SCarolineConcatto } 21464ab3302SCarolineConcatto } 2152018dafcSPeter Steinfeld if (IsActuallyConstant(*array)) { 216d8b4ea48SMike Kashkarov return GetConstantArrayBoundHelper::GetLbound<T>(*array, dim); 2172018dafcSPeter Steinfeld } 21864ab3302SCarolineConcatto if (lowerBoundsAreOne) { 219bd5371e4SMike Kashkarov ConstantSubscripts ones(rank, ConstantSubscript{1}); 220bd5371e4SMike Kashkarov return PackageConstantBounds<T>(std::move(ones), dim.has_value()); 22164ab3302SCarolineConcatto } 22264ab3302SCarolineConcatto } 22364ab3302SCarolineConcatto } 22464ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 22564ab3302SCarolineConcatto } 22664ab3302SCarolineConcatto 22764ab3302SCarolineConcatto template <int KIND> 22864ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context, 22964ab3302SCarolineConcatto FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) { 23064ab3302SCarolineConcatto using T = Type<TypeCategory::Integer, KIND>; 23164ab3302SCarolineConcatto ActualArguments &args{funcRef.arguments()}; 23264ab3302SCarolineConcatto if (auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) { 23364ab3302SCarolineConcatto std::optional<int> dim; 23464ab3302SCarolineConcatto if (funcRef.Rank() == 0) { 23564ab3302SCarolineConcatto // Optional DIM= argument is present: result is scalar. 236221ba64eSPeter Klausler if (!CheckDimArg(args[1], *array, context.messages(), false, dim)) { 23764ab3302SCarolineConcatto return MakeInvalidIntrinsic<T>(std::move(funcRef)); 238221ba64eSPeter Klausler } else if (!dim) { 239f9fdd4fdSPeter Klausler // DIM= is present but not constant, or error 24064ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 24164ab3302SCarolineConcatto } 24264ab3302SCarolineConcatto } 243f9fdd4fdSPeter Klausler if (IsAssumedRank(*array)) { 244f9fdd4fdSPeter Klausler } else if (int rank{array->Rank()}; rank > 0) { 24564ab3302SCarolineConcatto bool takeBoundsFromShape{true}; 24664ab3302SCarolineConcatto if (auto named{ExtractNamedEntity(*array)}) { 24764ab3302SCarolineConcatto const Symbol &symbol{named->GetLastSymbol()}; 24864ab3302SCarolineConcatto if (symbol.Rank() == rank) { 24964ab3302SCarolineConcatto takeBoundsFromShape = false; 25064ab3302SCarolineConcatto if (dim) { 251221ba64eSPeter Klausler if (auto ub{GetUBOUND(context, *named, *dim)}) { 25264ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(*ub))); 25364ab3302SCarolineConcatto } 25464ab3302SCarolineConcatto } else { 255ca46521aSJean Perier Shape ubounds{GetUBOUNDs(context, *named)}; 25664ab3302SCarolineConcatto if (semantics::IsAssumedSizeArray(symbol)) { 25764ab3302SCarolineConcatto CHECK(!ubounds.back()); 25864ab3302SCarolineConcatto ubounds.back() = ExtentExpr{-1}; 25964ab3302SCarolineConcatto } 26064ab3302SCarolineConcatto if (auto extents{AsExtentArrayExpr(ubounds)}) { 26164ab3302SCarolineConcatto return Fold(context, 26264ab3302SCarolineConcatto ConvertToType<T>(Expr<ExtentType>{std::move(*extents)})); 26364ab3302SCarolineConcatto } 26464ab3302SCarolineConcatto } 26564ab3302SCarolineConcatto } else { 26664ab3302SCarolineConcatto takeBoundsFromShape = symbol.Rank() == 0; // UBOUND(array%component) 26764ab3302SCarolineConcatto } 26864ab3302SCarolineConcatto } 269d8b4ea48SMike Kashkarov if (IsActuallyConstant(*array)) { 270d8b4ea48SMike Kashkarov return GetConstantArrayBoundHelper::GetUbound<T>(*array, dim); 271d8b4ea48SMike Kashkarov } 27264ab3302SCarolineConcatto if (takeBoundsFromShape) { 273fb3faa8bSJean Perier if (auto shape{GetContextFreeShape(context, *array)}) { 27464ab3302SCarolineConcatto if (dim) { 27564ab3302SCarolineConcatto if (auto &dimSize{shape->at(*dim)}) { 27664ab3302SCarolineConcatto return Fold(context, 27764ab3302SCarolineConcatto ConvertToType<T>(Expr<ExtentType>{std::move(*dimSize)})); 27864ab3302SCarolineConcatto } 27964ab3302SCarolineConcatto } else if (auto shapeExpr{AsExtentArrayExpr(*shape)}) { 28064ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(*shapeExpr))); 28164ab3302SCarolineConcatto } 28264ab3302SCarolineConcatto } 28364ab3302SCarolineConcatto } 28464ab3302SCarolineConcatto } 28564ab3302SCarolineConcatto } 28664ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 28764ab3302SCarolineConcatto } 28864ab3302SCarolineConcatto 289*94963919SPeter Klausler // LCOBOUND() & UCOBOUND() 290*94963919SPeter Klausler template <int KIND> 291*94963919SPeter Klausler Expr<Type<TypeCategory::Integer, KIND>> COBOUND(FoldingContext &context, 292*94963919SPeter Klausler FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef, bool isUCOBOUND) { 293*94963919SPeter Klausler using T = Type<TypeCategory::Integer, KIND>; 294*94963919SPeter Klausler ActualArguments &args{funcRef.arguments()}; 295*94963919SPeter Klausler if (const Symbol * coarray{UnwrapWholeSymbolOrComponentDataRef(args[0])}) { 296*94963919SPeter Klausler std::optional<int> dim; 297*94963919SPeter Klausler if (funcRef.Rank() == 0) { 298*94963919SPeter Klausler // Optional DIM= argument is present: result is scalar. 299*94963919SPeter Klausler if (!CheckCoDimArg(args[1], *coarray, context.messages(), dim)) { 300*94963919SPeter Klausler return MakeInvalidIntrinsic<T>(std::move(funcRef)); 301*94963919SPeter Klausler } else if (!dim) { 302*94963919SPeter Klausler // DIM= is present but not constant, or error 303*94963919SPeter Klausler return Expr<T>{std::move(funcRef)}; 304*94963919SPeter Klausler } 305*94963919SPeter Klausler } 306*94963919SPeter Klausler if (dim) { 307*94963919SPeter Klausler if (auto cb{isUCOBOUND ? GetUCOBOUND(*coarray, *dim) 308*94963919SPeter Klausler : GetLCOBOUND(*coarray, *dim)}) { 309*94963919SPeter Klausler return Fold(context, ConvertToType<T>(std::move(*cb))); 310*94963919SPeter Klausler } 311*94963919SPeter Klausler } else if (auto cbs{ 312*94963919SPeter Klausler AsExtentArrayExpr(isUCOBOUND ? GetUCOBOUNDs(*coarray) 313*94963919SPeter Klausler : GetLCOBOUNDs(*coarray))}) { 314*94963919SPeter Klausler return Fold(context, ConvertToType<T>(Expr<ExtentType>{std::move(*cbs)})); 315*94963919SPeter Klausler } 316*94963919SPeter Klausler } 317*94963919SPeter Klausler return Expr<T>{std::move(funcRef)}; 318*94963919SPeter Klausler } 319*94963919SPeter Klausler 32026aff847Speter klausler // COUNT() 32139f1860dSPeter Klausler template <typename T, int MASK_KIND> class CountAccumulator { 32239f1860dSPeter Klausler using MaskT = Type<TypeCategory::Logical, MASK_KIND>; 32339f1860dSPeter Klausler 32439f1860dSPeter Klausler public: 32539f1860dSPeter Klausler CountAccumulator(const Constant<MaskT> &mask) : mask_{mask} {} 32682e1e412SPeter Klausler void operator()( 32782e1e412SPeter Klausler Scalar<T> &element, const ConstantSubscripts &at, bool /*first*/) { 32839f1860dSPeter Klausler if (mask_.At(at).IsTrue()) { 32939f1860dSPeter Klausler auto incremented{element.AddSigned(Scalar<T>{1})}; 33039f1860dSPeter Klausler overflow_ |= incremented.overflow; 33139f1860dSPeter Klausler element = incremented.value; 33239f1860dSPeter Klausler } 33339f1860dSPeter Klausler } 33439f1860dSPeter Klausler bool overflow() const { return overflow_; } 33539f1860dSPeter Klausler void Done(Scalar<T> &) const {} 33639f1860dSPeter Klausler 33739f1860dSPeter Klausler private: 33839f1860dSPeter Klausler const Constant<MaskT> &mask_; 33939f1860dSPeter Klausler bool overflow_{false}; 34039f1860dSPeter Klausler }; 34139f1860dSPeter Klausler 34279dccdedSSacha Ballantyne template <typename T, int maskKind> 34326aff847Speter klausler static Expr<T> FoldCount(FoldingContext &context, FunctionRef<T> &&ref) { 34482e1e412SPeter Klausler using KindLogical = Type<TypeCategory::Logical, maskKind>; 34526aff847Speter klausler static_assert(T::category == TypeCategory::Integer); 34682568675SPeter Klausler std::optional<int> dim; 34782e1e412SPeter Klausler if (std::optional<ArrayAndMask<KindLogical>> arrayAndMask{ 34882e1e412SPeter Klausler ProcessReductionArgs<KindLogical>( 34982e1e412SPeter Klausler context, ref.arguments(), dim, /*ARRAY=*/0, /*DIM=*/1)}) { 35082e1e412SPeter Klausler CountAccumulator<T, maskKind> accumulator{arrayAndMask->array}; 35182e1e412SPeter Klausler Constant<T> result{DoReduction<T>(arrayAndMask->array, arrayAndMask->mask, 35282e1e412SPeter Klausler dim, Scalar<T>{}, accumulator)}; 353505f6da1SPeter Klausler if (accumulator.overflow() && 354505f6da1SPeter Klausler context.languageFeatures().ShouldWarn( 355505f6da1SPeter Klausler common::UsageWarning::FoldingException)) { 3560f973ac7SPeter Klausler context.messages().Say(common::UsageWarning::FoldingException, 357a280d300SPeter Klausler "Result of intrinsic function COUNT overflows its result type"_warn_en_US); 358a280d300SPeter Klausler } 359a280d300SPeter Klausler return Expr<T>{std::move(result)}; 36026aff847Speter klausler } 36126aff847Speter klausler return Expr<T>{std::move(ref)}; 36226aff847Speter klausler } 36326aff847Speter klausler 364cc1d13f9Speter klausler // FINDLOC(), MAXLOC(), & MINLOC() 365cc1d13f9Speter klausler enum class WhichLocation { Findloc, Maxloc, Minloc }; 366cc1d13f9Speter klausler template <WhichLocation WHICH> class LocationHelper { 36782568675SPeter Klausler public: 368cc1d13f9Speter klausler LocationHelper( 36982568675SPeter Klausler DynamicType &&type, ActualArguments &arg, FoldingContext &context) 37082568675SPeter Klausler : type_{type}, arg_{arg}, context_{context} {} 37182568675SPeter Klausler using Result = std::optional<Constant<SubscriptInteger>>; 372cc1d13f9Speter klausler using Types = std::conditional_t<WHICH == WhichLocation::Findloc, 373cc1d13f9Speter klausler AllIntrinsicTypes, RelationalTypes>; 37482568675SPeter Klausler 37582568675SPeter Klausler template <typename T> Result Test() const { 37682568675SPeter Klausler if (T::category != type_.category() || T::kind != type_.kind()) { 37782568675SPeter Klausler return std::nullopt; 37882568675SPeter Klausler } 379cc1d13f9Speter klausler CHECK(arg_.size() == (WHICH == WhichLocation::Findloc ? 6 : 5)); 38082568675SPeter Klausler Folder<T> folder{context_}; 38182568675SPeter Klausler Constant<T> *array{folder.Folding(arg_[0])}; 382cc1d13f9Speter klausler if (!array) { 38382568675SPeter Klausler return std::nullopt; 38482568675SPeter Klausler } 385cc1d13f9Speter klausler std::optional<Constant<T>> value; 386cc1d13f9Speter klausler if constexpr (WHICH == WhichLocation::Findloc) { 387cc1d13f9Speter klausler if (const Constant<T> *p{folder.Folding(arg_[1])}) { 388cc1d13f9Speter klausler value.emplace(*p); 389cc1d13f9Speter klausler } else { 390cc1d13f9Speter klausler return std::nullopt; 391cc1d13f9Speter klausler } 392cc1d13f9Speter klausler } 39382568675SPeter Klausler std::optional<int> dim; 39482568675SPeter Klausler Constant<LogicalResult> *mask{ 395cc1d13f9Speter klausler GetReductionMASK(arg_[maskArg], array->shape(), context_)}; 396cc1d13f9Speter klausler if ((!mask && arg_[maskArg]) || 397cc1d13f9Speter klausler !CheckReductionDIM(dim, context_, arg_, dimArg, array->Rank())) { 39882568675SPeter Klausler return std::nullopt; 39982568675SPeter Klausler } 40082568675SPeter Klausler bool back{false}; 401cc1d13f9Speter klausler if (arg_[backArg]) { 402cc1d13f9Speter klausler const auto *backConst{ 4036f7e715eSPeter Klausler Folder<LogicalResult>{context_, /*forOptionalArgument=*/true}.Folding( 4046f7e715eSPeter Klausler arg_[backArg])}; 40582568675SPeter Klausler if (backConst) { 40682568675SPeter Klausler back = backConst->GetScalarValue().value().IsTrue(); 40782568675SPeter Klausler } else { 40882568675SPeter Klausler return std::nullopt; 40982568675SPeter Klausler } 41082568675SPeter Klausler } 411cc1d13f9Speter klausler const RelationalOperator relation{WHICH == WhichLocation::Findloc 412cc1d13f9Speter klausler ? RelationalOperator::EQ 413cc1d13f9Speter klausler : WHICH == WhichLocation::Maxloc 414cc1d13f9Speter klausler ? (back ? RelationalOperator::GE : RelationalOperator::GT) 415cc1d13f9Speter klausler : back ? RelationalOperator::LE 416cc1d13f9Speter klausler : RelationalOperator::LT}; 41782568675SPeter Klausler // Use lower bounds of 1 exclusively. 41882568675SPeter Klausler array->SetLowerBoundsToOne(); 41982568675SPeter Klausler ConstantSubscripts at{array->lbounds()}, maskAt, resultIndices, resultShape; 42082568675SPeter Klausler if (mask) { 42135cc2ec4SMike Kashkarov if (auto scalarMask{mask->GetScalarValue()}) { 42235cc2ec4SMike Kashkarov // Convert into array in case of scalar MASK= (for 423ca0525faSPeter Klausler // MAXLOC/MINLOC/FINDLOC mask should be conformable) 42435cc2ec4SMike Kashkarov ConstantSubscript n{GetSize(array->shape())}; 42535cc2ec4SMike Kashkarov std::vector<Scalar<LogicalResult>> mask_elements( 42635cc2ec4SMike Kashkarov n, Scalar<LogicalResult>{scalarMask.value()}); 42735cc2ec4SMike Kashkarov *mask = Constant<LogicalResult>{ 428ca0525faSPeter Klausler std::move(mask_elements), ConstantSubscripts{array->shape()}}; 42935cc2ec4SMike Kashkarov } 43082568675SPeter Klausler mask->SetLowerBoundsToOne(); 43182568675SPeter Klausler maskAt = mask->lbounds(); 43282568675SPeter Klausler } 43382568675SPeter Klausler if (dim) { // DIM= 43482568675SPeter Klausler if (*dim < 1 || *dim > array->Rank()) { 435f70343d9Speter klausler context_.messages().Say("DIM=%d is out of range"_err_en_US, *dim); 43682568675SPeter Klausler return std::nullopt; 43782568675SPeter Klausler } 43882568675SPeter Klausler int zbDim{*dim - 1}; 43982568675SPeter Klausler resultShape = array->shape(); 44082568675SPeter Klausler resultShape.erase( 44182568675SPeter Klausler resultShape.begin() + zbDim); // scalar if array is vector 44282568675SPeter Klausler ConstantSubscript dimLength{array->shape()[zbDim]}; 44382568675SPeter Klausler ConstantSubscript n{GetSize(resultShape)}; 44482568675SPeter Klausler for (ConstantSubscript j{0}; j < n; ++j) { 445f70343d9Speter klausler ConstantSubscript hit{0}; 446f70343d9Speter klausler if constexpr (WHICH == WhichLocation::Maxloc || 447f70343d9Speter klausler WHICH == WhichLocation::Minloc) { 448cc1d13f9Speter klausler value.reset(); 449f70343d9Speter klausler } 45082568675SPeter Klausler for (ConstantSubscript k{0}; k < dimLength; 45182568675SPeter Klausler ++k, ++at[zbDim], mask && ++maskAt[zbDim]) { 45282568675SPeter Klausler if ((!mask || mask->At(maskAt).IsTrue()) && 45382e1e412SPeter Klausler IsHit(array->At(at), value, relation, back)) { 45482568675SPeter Klausler hit = at[zbDim]; 455f70343d9Speter klausler if constexpr (WHICH == WhichLocation::Findloc) { 45682568675SPeter Klausler if (!back) { 45782568675SPeter Klausler break; 45882568675SPeter Klausler } 45982568675SPeter Klausler } 46082568675SPeter Klausler } 461f70343d9Speter klausler } 46282568675SPeter Klausler resultIndices.emplace_back(hit); 4634133a85cSPeter Klausler at[zbDim] = std::max<ConstantSubscript>(dimLength, 1); 46482568675SPeter Klausler array->IncrementSubscripts(at); 465f70343d9Speter klausler at[zbDim] = 1; 46682568675SPeter Klausler if (mask) { 4674133a85cSPeter Klausler maskAt[zbDim] = mask->lbounds()[zbDim] + 4684133a85cSPeter Klausler std::max<ConstantSubscript>(dimLength, 1) - 1; 46982568675SPeter Klausler mask->IncrementSubscripts(maskAt); 47082568675SPeter Klausler maskAt[zbDim] = mask->lbounds()[zbDim]; 47182568675SPeter Klausler } 47282568675SPeter Klausler } 47382568675SPeter Klausler } else { // no DIM= 47482568675SPeter Klausler resultShape = ConstantSubscripts{array->Rank()}; // always a vector 47582568675SPeter Klausler ConstantSubscript n{GetSize(array->shape())}; 47682568675SPeter Klausler resultIndices = ConstantSubscripts(array->Rank(), 0); 47782568675SPeter Klausler for (ConstantSubscript j{0}; j < n; ++j, array->IncrementSubscripts(at), 47882568675SPeter Klausler mask && mask->IncrementSubscripts(maskAt)) { 47982568675SPeter Klausler if ((!mask || mask->At(maskAt).IsTrue()) && 48082e1e412SPeter Klausler IsHit(array->At(at), value, relation, back)) { 48182568675SPeter Klausler resultIndices = at; 482f70343d9Speter klausler if constexpr (WHICH == WhichLocation::Findloc) { 48382568675SPeter Klausler if (!back) { 48482568675SPeter Klausler break; 48582568675SPeter Klausler } 48682568675SPeter Klausler } 48782568675SPeter Klausler } 48882568675SPeter Klausler } 489f70343d9Speter klausler } 49082568675SPeter Klausler std::vector<Scalar<SubscriptInteger>> resultElements; 49182568675SPeter Klausler for (ConstantSubscript j : resultIndices) { 49282568675SPeter Klausler resultElements.emplace_back(j); 49382568675SPeter Klausler } 49482568675SPeter Klausler return Constant<SubscriptInteger>{ 49582568675SPeter Klausler std::move(resultElements), std::move(resultShape)}; 49682568675SPeter Klausler } 49782568675SPeter Klausler 49882568675SPeter Klausler private: 49982568675SPeter Klausler template <typename T> 500cc1d13f9Speter klausler bool IsHit(typename Constant<T>::Element element, 501cc1d13f9Speter klausler std::optional<Constant<T>> &value, 50282e1e412SPeter Klausler [[maybe_unused]] RelationalOperator relation, 50382e1e412SPeter Klausler [[maybe_unused]] bool back) const { 50482568675SPeter Klausler std::optional<Expr<LogicalResult>> cmp; 505f70343d9Speter klausler bool result{true}; 506cc1d13f9Speter klausler if (value) { 50782568675SPeter Klausler if constexpr (T::category == TypeCategory::Logical) { 50882568675SPeter Klausler // array(at) .EQV. value? 509cc1d13f9Speter klausler static_assert(WHICH == WhichLocation::Findloc); 510ec13942eSJean Perier cmp.emplace(ConvertToType<LogicalResult>( 511ec13942eSJean Perier Expr<T>{LogicalOperation<T::kind>{LogicalOperator::Eqv, 512ec13942eSJean Perier Expr<T>{Constant<T>{element}}, Expr<T>{Constant<T>{*value}}}})); 513cc1d13f9Speter klausler } else { // compare array(at) to value 51482e1e412SPeter Klausler if constexpr (T::category == TypeCategory::Real && 51582e1e412SPeter Klausler (WHICH == WhichLocation::Maxloc || 51682e1e412SPeter Klausler WHICH == WhichLocation::Minloc)) { 51782e1e412SPeter Klausler if (value && value->GetScalarValue().value().IsNotANumber() && 51882e1e412SPeter Klausler (back || !element.IsNotANumber())) { 51982e1e412SPeter Klausler // Replace NaN 52082e1e412SPeter Klausler cmp.emplace(Constant<LogicalResult>{Scalar<LogicalResult>{true}}); 52182e1e412SPeter Klausler } 52282e1e412SPeter Klausler } 52382e1e412SPeter Klausler if (!cmp) { 524ec13942eSJean Perier cmp.emplace(PackageRelation(relation, Expr<T>{Constant<T>{element}}, 525cc1d13f9Speter klausler Expr<T>{Constant<T>{*value}})); 52682568675SPeter Klausler } 52782e1e412SPeter Klausler } 52882568675SPeter Klausler Expr<LogicalResult> folded{Fold(context_, std::move(*cmp))}; 529f70343d9Speter klausler result = GetScalarConstantValue<LogicalResult>(folded).value().IsTrue(); 530f70343d9Speter klausler } else { 531f70343d9Speter klausler // first unmasked element for MAXLOC/MINLOC - always take it 53282568675SPeter Klausler } 533f70343d9Speter klausler if constexpr (WHICH == WhichLocation::Maxloc || 534f70343d9Speter klausler WHICH == WhichLocation::Minloc) { 535f70343d9Speter klausler if (result) { 536f70343d9Speter klausler value.emplace(std::move(element)); 537f70343d9Speter klausler } 538f70343d9Speter klausler } 539f70343d9Speter klausler return result; 540cc1d13f9Speter klausler } 541cc1d13f9Speter klausler 542cc1d13f9Speter klausler static constexpr int dimArg{WHICH == WhichLocation::Findloc ? 2 : 1}; 543cc1d13f9Speter klausler static constexpr int maskArg{dimArg + 1}; 544cc1d13f9Speter klausler static constexpr int backArg{maskArg + 2}; 54582568675SPeter Klausler 54682568675SPeter Klausler DynamicType type_; 54782568675SPeter Klausler ActualArguments &arg_; 54882568675SPeter Klausler FoldingContext &context_; 54982568675SPeter Klausler }; 55082568675SPeter Klausler 551cc1d13f9Speter klausler template <WhichLocation which> 552cc1d13f9Speter klausler static std::optional<Constant<SubscriptInteger>> FoldLocationCall( 55382568675SPeter Klausler ActualArguments &arg, FoldingContext &context) { 55482568675SPeter Klausler if (arg[0]) { 55582568675SPeter Klausler if (auto type{arg[0]->GetType()}) { 556ed71a0b4SPeter Klausler if constexpr (which == WhichLocation::Findloc) { 557ed71a0b4SPeter Klausler // Both ARRAY and VALUE are susceptible to conversion to a common 558ed71a0b4SPeter Klausler // comparison type. 559ed71a0b4SPeter Klausler if (arg[1]) { 560ed71a0b4SPeter Klausler if (auto valType{arg[1]->GetType()}) { 561ed71a0b4SPeter Klausler if (auto compareType{ComparisonType(*type, *valType)}) { 562ed71a0b4SPeter Klausler type = compareType; 563ed71a0b4SPeter Klausler } 564ed71a0b4SPeter Klausler } 565ed71a0b4SPeter Klausler } 566ed71a0b4SPeter Klausler } 567cc1d13f9Speter klausler return common::SearchTypes( 568cc1d13f9Speter klausler LocationHelper<which>{std::move(*type), arg, context}); 56982568675SPeter Klausler } 57082568675SPeter Klausler } 57182568675SPeter Klausler return std::nullopt; 57282568675SPeter Klausler } 57382568675SPeter Klausler 574cc1d13f9Speter klausler template <WhichLocation which, typename T> 575cc1d13f9Speter klausler static Expr<T> FoldLocation(FoldingContext &context, FunctionRef<T> &&ref) { 57682568675SPeter Klausler static_assert(T::category == TypeCategory::Integer); 57782568675SPeter Klausler if (std::optional<Constant<SubscriptInteger>> found{ 578cc1d13f9Speter klausler FoldLocationCall<which>(ref.arguments(), context)}) { 57982568675SPeter Klausler return Expr<T>{Fold( 58082568675SPeter Klausler context, ConvertToType<T>(Expr<SubscriptInteger>{std::move(*found)}))}; 58182568675SPeter Klausler } else { 58282568675SPeter Klausler return Expr<T>{std::move(ref)}; 58382568675SPeter Klausler } 58482568675SPeter Klausler } 58582568675SPeter Klausler 586503c085eSpeter klausler // for IALL, IANY, & IPARITY 587503c085eSpeter klausler template <typename T> 588503c085eSpeter klausler static Expr<T> FoldBitReduction(FoldingContext &context, FunctionRef<T> &&ref, 589503c085eSpeter klausler Scalar<T> (Scalar<T>::*operation)(const Scalar<T> &) const, 590503c085eSpeter klausler Scalar<T> identity) { 591fc97d2e6SPeter Klausler static_assert(T::category == TypeCategory::Integer || 592fc97d2e6SPeter Klausler T::category == TypeCategory::Unsigned); 59382568675SPeter Klausler std::optional<int> dim; 59482e1e412SPeter Klausler if (std::optional<ArrayAndMask<T>> arrayAndMask{ 59582e1e412SPeter Klausler ProcessReductionArgs<T>(context, ref.arguments(), dim, 596503c085eSpeter klausler /*ARRAY=*/0, /*DIM=*/1, /*MASK=*/2)}) { 59782e1e412SPeter Klausler OperationAccumulator<T> accumulator{arrayAndMask->array, operation}; 59882e1e412SPeter Klausler return Expr<T>{DoReduction<T>( 59982e1e412SPeter Klausler arrayAndMask->array, arrayAndMask->mask, dim, identity, accumulator)}; 600503c085eSpeter klausler } 601503c085eSpeter klausler return Expr<T>{std::move(ref)}; 602503c085eSpeter klausler } 603503c085eSpeter klausler 604fc97d2e6SPeter Klausler // Common cases for INTEGER and UNSIGNED 605fc97d2e6SPeter Klausler template <typename T> 606fc97d2e6SPeter Klausler std::optional<Expr<T>> FoldIntrinsicFunctionCommon( 607fc97d2e6SPeter Klausler FoldingContext &context, FunctionRef<T> &funcRef) { 60864ab3302SCarolineConcatto ActualArguments &args{funcRef.arguments()}; 60964ab3302SCarolineConcatto auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}; 61064ab3302SCarolineConcatto CHECK(intrinsic); 61164ab3302SCarolineConcatto std::string name{intrinsic->name}; 612fc97d2e6SPeter Klausler using Int4 = Type<TypeCategory::Integer, 4>; 613fc97d2e6SPeter Klausler if (name == "bit_size") { 61464ab3302SCarolineConcatto return Expr<T>{Scalar<T>::bits}; 61564ab3302SCarolineConcatto } else if (name == "digits") { 61664ab3302SCarolineConcatto if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) { 617cd03e96fSPeter Klausler return Expr<T>{common::visit( 61864ab3302SCarolineConcatto [](const auto &kx) { 61964ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::DIGITS; 62064ab3302SCarolineConcatto }, 62164ab3302SCarolineConcatto cx->u)}; 622fc97d2e6SPeter Klausler } else if (const auto *cx{UnwrapExpr<Expr<SomeUnsigned>>(args[0])}) { 623fc97d2e6SPeter Klausler return Expr<T>{common::visit( 624fc97d2e6SPeter Klausler [](const auto &kx) { 625fc97d2e6SPeter Klausler return Scalar<ResultType<decltype(kx)>>::DIGITS + 1; 626fc97d2e6SPeter Klausler }, 627fc97d2e6SPeter Klausler cx->u)}; 62864ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 629cd03e96fSPeter Klausler return Expr<T>{common::visit( 63064ab3302SCarolineConcatto [](const auto &kx) { 63164ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::DIGITS; 63264ab3302SCarolineConcatto }, 63364ab3302SCarolineConcatto cx->u)}; 63464ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 635cd03e96fSPeter Klausler return Expr<T>{common::visit( 63664ab3302SCarolineConcatto [](const auto &kx) { 63764ab3302SCarolineConcatto return Scalar<typename ResultType<decltype(kx)>::Part>::DIGITS; 63864ab3302SCarolineConcatto }, 63964ab3302SCarolineConcatto cx->u)}; 64064ab3302SCarolineConcatto } 641e723c69bSPeter Klausler } else if (name == "dot_product") { 642e723c69bSPeter Klausler return FoldDotProduct<T>(context, std::move(funcRef)); 64364ab3302SCarolineConcatto } else if (name == "dshiftl" || name == "dshiftr") { 64464ab3302SCarolineConcatto const auto fptr{ 64564ab3302SCarolineConcatto name == "dshiftl" ? &Scalar<T>::DSHIFTL : &Scalar<T>::DSHIFTR}; 64664ab3302SCarolineConcatto // Third argument can be of any kind. However, it must be smaller or equal 64764ab3302SCarolineConcatto // than BIT_SIZE. It can be converted to Int4 to simplify. 648e0daa2ebSPeter Klausler if (const auto *argCon{Folder<T>(context).Folding(args[0])}; 649e0daa2ebSPeter Klausler argCon && argCon->empty()) { 650e0daa2ebSPeter Klausler } else if (const auto *shiftCon{Folder<Int4>(context).Folding(args[2])}) { 6514244cab2SPeter Klausler for (const auto &scalar : shiftCon->values()) { 6524244cab2SPeter Klausler std::int64_t shiftVal{scalar.ToInt64()}; 6534244cab2SPeter Klausler if (shiftVal < 0) { 6544244cab2SPeter Klausler context.messages().Say("SHIFT=%jd count for %s is negative"_err_en_US, 6554244cab2SPeter Klausler std::intmax_t{shiftVal}, name); 6564244cab2SPeter Klausler break; 6574244cab2SPeter Klausler } else if (shiftVal > T::Scalar::bits) { 6584244cab2SPeter Klausler context.messages().Say( 6594244cab2SPeter Klausler "SHIFT=%jd count for %s is greater than %d"_err_en_US, 6604244cab2SPeter Klausler std::intmax_t{shiftVal}, name, T::Scalar::bits); 6614244cab2SPeter Klausler break; 6624244cab2SPeter Klausler } 6634244cab2SPeter Klausler } 6644244cab2SPeter Klausler } 66564ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T, Int4>(context, std::move(funcRef), 66664ab3302SCarolineConcatto ScalarFunc<T, T, T, Int4>( 66764ab3302SCarolineConcatto [&fptr](const Scalar<T> &i, const Scalar<T> &j, 66864ab3302SCarolineConcatto const Scalar<Int4> &shift) -> Scalar<T> { 66964ab3302SCarolineConcatto return std::invoke(fptr, i, j, static_cast<int>(shift.ToInt64())); 67064ab3302SCarolineConcatto })); 67164ab3302SCarolineConcatto } else if (name == "iand" || name == "ior" || name == "ieor") { 67264ab3302SCarolineConcatto auto fptr{&Scalar<T>::IAND}; 67364ab3302SCarolineConcatto if (name == "iand") { // done in fptr declaration 67464ab3302SCarolineConcatto } else if (name == "ior") { 67564ab3302SCarolineConcatto fptr = &Scalar<T>::IOR; 67664ab3302SCarolineConcatto } else if (name == "ieor") { 67764ab3302SCarolineConcatto fptr = &Scalar<T>::IEOR; 67864ab3302SCarolineConcatto } else { 67964ab3302SCarolineConcatto common::die("missing case to fold intrinsic function %s", name.c_str()); 68064ab3302SCarolineConcatto } 68164ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T>( 68264ab3302SCarolineConcatto context, std::move(funcRef), ScalarFunc<T, T, T>(fptr)); 683503c085eSpeter klausler } else if (name == "iall") { 684503c085eSpeter klausler return FoldBitReduction( 685503c085eSpeter klausler context, std::move(funcRef), &Scalar<T>::IAND, Scalar<T>{}.NOT()); 686503c085eSpeter klausler } else if (name == "iany") { 687503c085eSpeter klausler return FoldBitReduction( 688503c085eSpeter klausler context, std::move(funcRef), &Scalar<T>::IOR, Scalar<T>{}); 689f4a5fb0cSpeter klausler } else if (name == "ibclr" || name == "ibset") { 690f4a5fb0cSpeter klausler // Second argument can be of any kind. However, it must be smaller 691f4a5fb0cSpeter klausler // than BIT_SIZE. It can be converted to Int4 to simplify. 69264ab3302SCarolineConcatto auto fptr{&Scalar<T>::IBCLR}; 693f4a5fb0cSpeter klausler if (name == "ibclr") { // done in fptr definition 69464ab3302SCarolineConcatto } else if (name == "ibset") { 69564ab3302SCarolineConcatto fptr = &Scalar<T>::IBSET; 69664ab3302SCarolineConcatto } else { 69764ab3302SCarolineConcatto common::die("missing case to fold intrinsic function %s", name.c_str()); 69864ab3302SCarolineConcatto } 699e0daa2ebSPeter Klausler if (const auto *argCon{Folder<T>(context).Folding(args[0])}; 700e0daa2ebSPeter Klausler argCon && argCon->empty()) { 701e0daa2ebSPeter Klausler } else if (const auto *posCon{Folder<Int4>(context).Folding(args[1])}) { 7024244cab2SPeter Klausler for (const auto &scalar : posCon->values()) { 7034244cab2SPeter Klausler std::int64_t posVal{scalar.ToInt64()}; 704f4a5fb0cSpeter klausler if (posVal < 0) { 705f4a5fb0cSpeter klausler context.messages().Say( 7064244cab2SPeter Klausler "bit position for %s (%jd) is negative"_err_en_US, name, 7074244cab2SPeter Klausler std::intmax_t{posVal}); 7084244cab2SPeter Klausler break; 7094244cab2SPeter Klausler } else if (posVal >= T::Scalar::bits) { 710f4a5fb0cSpeter klausler context.messages().Say( 7114244cab2SPeter Klausler "bit position for %s (%jd) is not less than %d"_err_en_US, name, 7124244cab2SPeter Klausler std::intmax_t{posVal}, T::Scalar::bits); 7134244cab2SPeter Klausler break; 714f4a5fb0cSpeter klausler } 7154244cab2SPeter Klausler } 7164244cab2SPeter Klausler } 7174244cab2SPeter Klausler return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef), 7184244cab2SPeter Klausler ScalarFunc<T, T, Int4>( 7194244cab2SPeter Klausler [&](const Scalar<T> &i, const Scalar<Int4> &pos) -> Scalar<T> { 7204244cab2SPeter Klausler return std::invoke(fptr, i, static_cast<int>(pos.ToInt64())); 72164ab3302SCarolineConcatto })); 72234cc706bSPeter Klausler } else if (name == "ibits") { 7234244cab2SPeter Klausler const auto *posCon{Folder<Int4>(context).Folding(args[1])}; 7244244cab2SPeter Klausler const auto *lenCon{Folder<Int4>(context).Folding(args[2])}; 725e0daa2ebSPeter Klausler if (const auto *argCon{Folder<T>(context).Folding(args[0])}; 726e0daa2ebSPeter Klausler argCon && argCon->empty()) { 7276c5ba7cdSPeter Klausler } else { 7286c5ba7cdSPeter Klausler std::size_t posCt{posCon ? posCon->size() : 0}; 7296c5ba7cdSPeter Klausler std::size_t lenCt{lenCon ? lenCon->size() : 0}; 7306c5ba7cdSPeter Klausler std::size_t n{std::max(posCt, lenCt)}; 7316c5ba7cdSPeter Klausler for (std::size_t j{0}; j < n; ++j) { 7326c5ba7cdSPeter Klausler int posVal{j < posCt || posCt == 1 7336c5ba7cdSPeter Klausler ? static_cast<int>(posCon->values()[j % posCt].ToInt64()) 7346c5ba7cdSPeter Klausler : 0}; 7356c5ba7cdSPeter Klausler int lenVal{j < lenCt || lenCt == 1 7366c5ba7cdSPeter Klausler ? static_cast<int>(lenCon->values()[j % lenCt].ToInt64()) 7376c5ba7cdSPeter Klausler : 0}; 73834cc706bSPeter Klausler if (posVal < 0) { 73934cc706bSPeter Klausler context.messages().Say( 7406c5ba7cdSPeter Klausler "bit position for IBITS(POS=%jd) is negative"_err_en_US, 7416c5ba7cdSPeter Klausler std::intmax_t{posVal}); 7424244cab2SPeter Klausler break; 74334cc706bSPeter Klausler } else if (lenVal < 0) { 74434cc706bSPeter Klausler context.messages().Say( 7456c5ba7cdSPeter Klausler "bit length for IBITS(LEN=%jd) is negative"_err_en_US, 7466c5ba7cdSPeter Klausler std::intmax_t{lenVal}); 7474244cab2SPeter Klausler break; 7484244cab2SPeter Klausler } else if (posVal + lenVal > T::Scalar::bits) { 74934cc706bSPeter Klausler context.messages().Say( 7506c5ba7cdSPeter Klausler "IBITS() must have POS+LEN (>=%jd) no greater than %d"_err_en_US, 7516c5ba7cdSPeter Klausler std::intmax_t{posVal + lenVal}, T::Scalar::bits); 7524244cab2SPeter Klausler break; 75334cc706bSPeter Klausler } 7544244cab2SPeter Klausler } 7554244cab2SPeter Klausler } 7564244cab2SPeter Klausler return FoldElementalIntrinsic<T, T, Int4, Int4>(context, std::move(funcRef), 7574244cab2SPeter Klausler ScalarFunc<T, T, Int4, Int4>( 7584244cab2SPeter Klausler [&](const Scalar<T> &i, const Scalar<Int4> &pos, 7594244cab2SPeter Klausler const Scalar<Int4> &len) -> Scalar<T> { 7604244cab2SPeter Klausler return i.IBITS(static_cast<int>(pos.ToInt64()), 7614244cab2SPeter Klausler static_cast<int>(len.ToInt64())); 76234cc706bSPeter Klausler })); 763fc97d2e6SPeter Klausler } else if (name == "int" || name == "int2" || name == "int8" || 764fc97d2e6SPeter Klausler name == "uint") { 76564ab3302SCarolineConcatto if (auto *expr{UnwrapExpr<Expr<SomeType>>(args[0])}) { 766cd03e96fSPeter Klausler return common::visit( 76764ab3302SCarolineConcatto [&](auto &&x) -> Expr<T> { 76864ab3302SCarolineConcatto using From = std::decay_t<decltype(x)>; 76964ab3302SCarolineConcatto if constexpr (std::is_same_v<From, BOZLiteralConstant> || 77064ab3302SCarolineConcatto IsNumericCategoryExpr<From>()) { 77164ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(x))); 77264ab3302SCarolineConcatto } 77364ab3302SCarolineConcatto DIE("int() argument type not valid"); 77464ab3302SCarolineConcatto }, 77564ab3302SCarolineConcatto std::move(expr->u)); 77664ab3302SCarolineConcatto } 777503c085eSpeter klausler } else if (name == "iparity") { 778503c085eSpeter klausler return FoldBitReduction( 779503c085eSpeter klausler context, std::move(funcRef), &Scalar<T>::IEOR, Scalar<T>{}); 7804244cab2SPeter Klausler } else if (name == "ishft" || name == "ishftc") { 781e0daa2ebSPeter Klausler const auto *argCon{Folder<T>(context).Folding(args[0])}; 7824244cab2SPeter Klausler const auto *shiftCon{Folder<Int4>(context).Folding(args[1])}; 783e0daa2ebSPeter Klausler const auto *shiftVals{shiftCon ? &shiftCon->values() : nullptr}; 7846f7e715eSPeter Klausler const auto *sizeCon{args.size() == 3 7856f7e715eSPeter Klausler ? Folder<Int4>{context, /*forOptionalArgument=*/true}.Folding( 7866f7e715eSPeter Klausler args[2]) 7876f7e715eSPeter Klausler : nullptr}; 788e0daa2ebSPeter Klausler const auto *sizeVals{sizeCon ? &sizeCon->values() : nullptr}; 789e0daa2ebSPeter Klausler if ((argCon && argCon->empty()) || !shiftVals || shiftVals->empty() || 790e0daa2ebSPeter Klausler (sizeVals && sizeVals->empty())) { 791e0daa2ebSPeter Klausler // size= and shift= values don't need to be checked 792e0daa2ebSPeter Klausler } else { 793e0daa2ebSPeter Klausler for (const auto &scalar : *shiftVals) { 7944244cab2SPeter Klausler std::int64_t shiftVal{scalar.ToInt64()}; 7954244cab2SPeter Klausler if (shiftVal < -T::Scalar::bits) { 7964244cab2SPeter Klausler context.messages().Say( 7974244cab2SPeter Klausler "SHIFT=%jd count for %s is less than %d"_err_en_US, 7984244cab2SPeter Klausler std::intmax_t{shiftVal}, name, -T::Scalar::bits); 7994244cab2SPeter Klausler break; 8004244cab2SPeter Klausler } else if (shiftVal > T::Scalar::bits) { 8014244cab2SPeter Klausler context.messages().Say( 8024244cab2SPeter Klausler "SHIFT=%jd count for %s is greater than %d"_err_en_US, 8034244cab2SPeter Klausler std::intmax_t{shiftVal}, name, T::Scalar::bits); 8044244cab2SPeter Klausler break; 8054244cab2SPeter Klausler } 8064244cab2SPeter Klausler } 807e0daa2ebSPeter Klausler if (sizeVals) { 808e0daa2ebSPeter Klausler for (const auto &scalar : *sizeVals) { 8094244cab2SPeter Klausler std::int64_t sizeVal{scalar.ToInt64()}; 8104244cab2SPeter Klausler if (sizeVal <= 0) { 8114244cab2SPeter Klausler context.messages().Say( 8124244cab2SPeter Klausler "SIZE=%jd count for ishftc is not positive"_err_en_US, 8134244cab2SPeter Klausler std::intmax_t{sizeVal}, name); 8144244cab2SPeter Klausler break; 8154244cab2SPeter Klausler } else if (sizeVal > T::Scalar::bits) { 8164244cab2SPeter Klausler context.messages().Say( 8174244cab2SPeter Klausler "SIZE=%jd count for ishftc is greater than %d"_err_en_US, 8184244cab2SPeter Klausler std::intmax_t{sizeVal}, T::Scalar::bits); 8194244cab2SPeter Klausler break; 8204244cab2SPeter Klausler } 8214244cab2SPeter Klausler } 822e0daa2ebSPeter Klausler if (shiftVals->size() == 1 || sizeVals->size() == 1 || 823e0daa2ebSPeter Klausler shiftVals->size() == sizeVals->size()) { 824e0daa2ebSPeter Klausler auto iters{std::max(shiftVals->size(), sizeVals->size())}; 825e0daa2ebSPeter Klausler for (std::size_t j{0}; j < iters; ++j) { 826e0daa2ebSPeter Klausler auto shiftVal{static_cast<int>( 827e0daa2ebSPeter Klausler (*shiftVals)[j % shiftVals->size()].ToInt64())}; 828e0daa2ebSPeter Klausler auto sizeVal{ 829e0daa2ebSPeter Klausler static_cast<int>((*sizeVals)[j % sizeVals->size()].ToInt64())}; 8304244cab2SPeter Klausler if (sizeVal > 0 && std::abs(shiftVal) > sizeVal) { 8314244cab2SPeter Klausler context.messages().Say( 8324244cab2SPeter Klausler "SHIFT=%jd count for ishftc is greater in magnitude than SIZE=%jd"_err_en_US, 8334244cab2SPeter Klausler std::intmax_t{shiftVal}, std::intmax_t{sizeVal}); 8344244cab2SPeter Klausler break; 8354244cab2SPeter Klausler } 8364244cab2SPeter Klausler } 8374244cab2SPeter Klausler } 8384244cab2SPeter Klausler } 8394244cab2SPeter Klausler } 8404244cab2SPeter Klausler if (name == "ishft") { 841f4a5fb0cSpeter klausler return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef), 84274d5c3c0SPeter Steinfeld ScalarFunc<T, T, Int4>( 8434244cab2SPeter Klausler [&](const Scalar<T> &i, const Scalar<Int4> &shift) -> Scalar<T> { 8444244cab2SPeter Klausler return i.ISHFT(static_cast<int>(shift.ToInt64())); 845f4a5fb0cSpeter klausler })); 8464244cab2SPeter Klausler } else if (!args.at(2)) { // ISHFTC(no SIZE=) 8474244cab2SPeter Klausler return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef), 8484244cab2SPeter Klausler ScalarFunc<T, T, Int4>( 8494244cab2SPeter Klausler [&](const Scalar<T> &i, const Scalar<Int4> &shift) -> Scalar<T> { 8504244cab2SPeter Klausler return i.ISHFTC(static_cast<int>(shift.ToInt64())); 8514244cab2SPeter Klausler })); 8524244cab2SPeter Klausler } else { // ISHFTC(with SIZE=) 853baec06a9SPeter Klausler return FoldElementalIntrinsic<T, T, Int4, Int4>(context, 854baec06a9SPeter Klausler std::move(funcRef), 855baec06a9SPeter Klausler ScalarFunc<T, T, Int4, Int4>( 856baec06a9SPeter Klausler [&](const Scalar<T> &i, const Scalar<Int4> &shift, 857baec06a9SPeter Klausler const Scalar<Int4> &size) -> Scalar<T> { 858baec06a9SPeter Klausler auto shiftVal{static_cast<int>(shift.ToInt64())}; 859baec06a9SPeter Klausler auto sizeVal{static_cast<int>(size.ToInt64())}; 860baec06a9SPeter Klausler return i.ISHFTC(shiftVal, sizeVal); 8616f7e715eSPeter Klausler }), 8626f7e715eSPeter Klausler /*hasOptionalArgument=*/true); 863baec06a9SPeter Klausler } 8646d279f40SPeter Klausler } else if (name == "izext" || name == "jzext") { 8656d279f40SPeter Klausler if (args.size() == 1) { 866fc97d2e6SPeter Klausler if (auto *expr{UnwrapExpr<Expr<SomeKind<T::category>>>(args[0])}) { 8676d279f40SPeter Klausler // Rewrite to IAND(INT(n,k),255_k) for k=KIND(T) 8686d279f40SPeter Klausler intrinsic->name = "iand"; 8696d279f40SPeter Klausler auto converted{ConvertToType<T>(std::move(*expr))}; 870fc97d2e6SPeter Klausler *expr = 871fc97d2e6SPeter Klausler Fold(context, Expr<SomeKind<T::category>>{std::move(converted)}); 8726d279f40SPeter Klausler args.emplace_back(AsGenericExpr(Expr<T>{Scalar<T>{255}})); 8736d279f40SPeter Klausler return FoldIntrinsicFunction(context, std::move(funcRef)); 8746d279f40SPeter Klausler } 8756d279f40SPeter Klausler } 876fc97d2e6SPeter Klausler } else if (name == "maskl" || name == "maskr" || name == "umaskl" || 877fc97d2e6SPeter Klausler name == "umaskr") { 878fc97d2e6SPeter Klausler // Argument can be of any kind but value has to be smaller than BIT_SIZE. 879fc97d2e6SPeter Klausler // It can be safely converted to Int4 to simplify. 880fc97d2e6SPeter Klausler const auto fptr{name == "maskl" || name == "umaskl" ? &Scalar<T>::MASKL 881fc97d2e6SPeter Klausler : &Scalar<T>::MASKR}; 882fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, Int4>(context, std::move(funcRef), 883fc97d2e6SPeter Klausler ScalarFunc<T, Int4>([&fptr](const Scalar<Int4> &places) -> Scalar<T> { 884fc97d2e6SPeter Klausler return fptr(static_cast<int>(places.ToInt64())); 885fc97d2e6SPeter Klausler })); 886fc97d2e6SPeter Klausler } else if (name == "matmul") { 887fc97d2e6SPeter Klausler return FoldMatmul(context, std::move(funcRef)); 888fc97d2e6SPeter Klausler } else if (name == "max") { 889fc97d2e6SPeter Klausler return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater); 890fc97d2e6SPeter Klausler } else if (name == "maxval") { 891fc97d2e6SPeter Klausler return FoldMaxvalMinval<T>(context, std::move(funcRef), 892fc97d2e6SPeter Klausler RelationalOperator::GT, 893fc97d2e6SPeter Klausler T::category == TypeCategory::Unsigned ? typename T::Scalar{} 894fc97d2e6SPeter Klausler : T::Scalar::Least()); 895fc97d2e6SPeter Klausler } else if (name == "merge_bits") { 896fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, T, T, T>( 897fc97d2e6SPeter Klausler context, std::move(funcRef), &Scalar<T>::MERGE_BITS); 898fc97d2e6SPeter Klausler } else if (name == "min") { 899fc97d2e6SPeter Klausler return FoldMINorMAX(context, std::move(funcRef), Ordering::Less); 900fc97d2e6SPeter Klausler } else if (name == "minval") { 901fc97d2e6SPeter Klausler return FoldMaxvalMinval<T>(context, std::move(funcRef), 902fc97d2e6SPeter Klausler RelationalOperator::LT, 903fc97d2e6SPeter Klausler T::category == TypeCategory::Unsigned ? typename T::Scalar{}.NOT() 904fc97d2e6SPeter Klausler : T::Scalar::HUGE()); 905fc97d2e6SPeter Klausler } else if (name == "not") { 906fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, T>( 907fc97d2e6SPeter Klausler context, std::move(funcRef), &Scalar<T>::NOT); 908fc97d2e6SPeter Klausler } else if (name == "product") { 909fc97d2e6SPeter Klausler return FoldProduct<T>(context, std::move(funcRef), Scalar<T>{1}); 910fc97d2e6SPeter Klausler } else if (name == "radix") { 911fc97d2e6SPeter Klausler return Expr<T>{2}; 912fc97d2e6SPeter Klausler } else if (name == "shifta" || name == "shiftr" || name == "shiftl") { 913fc97d2e6SPeter Klausler // Second argument can be of any kind. However, it must be smaller or 914fc97d2e6SPeter Klausler // equal than BIT_SIZE. It can be converted to Int4 to simplify. 915fc97d2e6SPeter Klausler auto fptr{&Scalar<T>::SHIFTA}; 916fc97d2e6SPeter Klausler if (name == "shifta") { // done in fptr definition 917fc97d2e6SPeter Klausler } else if (name == "shiftr") { 918fc97d2e6SPeter Klausler fptr = &Scalar<T>::SHIFTR; 919fc97d2e6SPeter Klausler } else if (name == "shiftl") { 920fc97d2e6SPeter Klausler fptr = &Scalar<T>::SHIFTL; 921fc97d2e6SPeter Klausler } else { 922fc97d2e6SPeter Klausler common::die("missing case to fold intrinsic function %s", name.c_str()); 923fc97d2e6SPeter Klausler } 924fc97d2e6SPeter Klausler if (const auto *argCon{Folder<T>(context).Folding(args[0])}; 925fc97d2e6SPeter Klausler argCon && argCon->empty()) { 926fc97d2e6SPeter Klausler } else if (const auto *shiftCon{Folder<Int4>(context).Folding(args[1])}) { 927fc97d2e6SPeter Klausler for (const auto &scalar : shiftCon->values()) { 928fc97d2e6SPeter Klausler std::int64_t shiftVal{scalar.ToInt64()}; 929fc97d2e6SPeter Klausler if (shiftVal < 0) { 930fc97d2e6SPeter Klausler context.messages().Say("SHIFT=%jd count for %s is negative"_err_en_US, 931fc97d2e6SPeter Klausler std::intmax_t{shiftVal}, name, -T::Scalar::bits); 932fc97d2e6SPeter Klausler break; 933fc97d2e6SPeter Klausler } else if (shiftVal > T::Scalar::bits) { 934fc97d2e6SPeter Klausler context.messages().Say( 935fc97d2e6SPeter Klausler "SHIFT=%jd count for %s is greater than %d"_err_en_US, 936fc97d2e6SPeter Klausler std::intmax_t{shiftVal}, name, T::Scalar::bits); 937fc97d2e6SPeter Klausler break; 938fc97d2e6SPeter Klausler } 939fc97d2e6SPeter Klausler } 940fc97d2e6SPeter Klausler } 941fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef), 942fc97d2e6SPeter Klausler ScalarFunc<T, T, Int4>( 943fc97d2e6SPeter Klausler [&](const Scalar<T> &i, const Scalar<Int4> &shift) -> Scalar<T> { 944fc97d2e6SPeter Klausler return std::invoke(fptr, i, static_cast<int>(shift.ToInt64())); 945fc97d2e6SPeter Klausler })); 946fc97d2e6SPeter Klausler } else if (name == "sum") { 947fc97d2e6SPeter Klausler return FoldSum<T>(context, std::move(funcRef)); 948fc97d2e6SPeter Klausler } 949fc97d2e6SPeter Klausler return std::nullopt; 950fc97d2e6SPeter Klausler } 951fc97d2e6SPeter Klausler 952fc97d2e6SPeter Klausler template <int KIND> 953fc97d2e6SPeter Klausler Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction( 954fc97d2e6SPeter Klausler FoldingContext &context, 955fc97d2e6SPeter Klausler FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) { 956fc97d2e6SPeter Klausler if (auto foldedCommon{FoldIntrinsicFunctionCommon(context, funcRef)}) { 957fc97d2e6SPeter Klausler return std::move(*foldedCommon); 958fc97d2e6SPeter Klausler } 959fc97d2e6SPeter Klausler 960fc97d2e6SPeter Klausler using T = Type<TypeCategory::Integer, KIND>; 961fc97d2e6SPeter Klausler ActualArguments &args{funcRef.arguments()}; 962fc97d2e6SPeter Klausler auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}; 963fc97d2e6SPeter Klausler CHECK(intrinsic); 964fc97d2e6SPeter Klausler std::string name{intrinsic->name}; 965fc97d2e6SPeter Klausler 966fc97d2e6SPeter Klausler auto FromInt64{[&name, &context](std::int64_t n) { 967fc97d2e6SPeter Klausler Scalar<T> result{n}; 968fc97d2e6SPeter Klausler if (result.ToInt64() != n && 969fc97d2e6SPeter Klausler context.languageFeatures().ShouldWarn( 970fc97d2e6SPeter Klausler common::UsageWarning::FoldingException)) { 971fc97d2e6SPeter Klausler context.messages().Say(common::UsageWarning::FoldingException, 972fc97d2e6SPeter Klausler "Result of intrinsic function '%s' (%jd) overflows its result type"_warn_en_US, 973fc97d2e6SPeter Klausler name, std::intmax_t{n}); 974fc97d2e6SPeter Klausler } 975fc97d2e6SPeter Klausler return result; 976fc97d2e6SPeter Klausler }}; 977fc97d2e6SPeter Klausler 978fc97d2e6SPeter Klausler if (name == "abs") { // incl. babs, iiabs, jiaabs, & kiabs 979fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, T>(context, std::move(funcRef), 980fc97d2e6SPeter Klausler ScalarFunc<T, T>([&context](const Scalar<T> &i) -> Scalar<T> { 981fc97d2e6SPeter Klausler typename Scalar<T>::ValueWithOverflow j{i.ABS()}; 982fc97d2e6SPeter Klausler if (j.overflow && 983fc97d2e6SPeter Klausler context.languageFeatures().ShouldWarn( 984fc97d2e6SPeter Klausler common::UsageWarning::FoldingException)) { 985fc97d2e6SPeter Klausler context.messages().Say(common::UsageWarning::FoldingException, 986fc97d2e6SPeter Klausler "abs(integer(kind=%d)) folding overflowed"_warn_en_US, KIND); 987fc97d2e6SPeter Klausler } 988fc97d2e6SPeter Klausler return j.value; 989fc97d2e6SPeter Klausler })); 990fc97d2e6SPeter Klausler } else if (name == "ceiling" || name == "floor" || name == "nint") { 991fc97d2e6SPeter Klausler if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 992fc97d2e6SPeter Klausler // NINT rounds ties away from zero, not to even 993fc97d2e6SPeter Klausler common::RoundingMode mode{name == "ceiling" ? common::RoundingMode::Up 994fc97d2e6SPeter Klausler : name == "floor" ? common::RoundingMode::Down 995fc97d2e6SPeter Klausler : common::RoundingMode::TiesAwayFromZero}; 996fc97d2e6SPeter Klausler return common::visit( 997fc97d2e6SPeter Klausler [&](const auto &kx) { 998fc97d2e6SPeter Klausler using TR = ResultType<decltype(kx)>; 999fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef), 1000fc97d2e6SPeter Klausler ScalarFunc<T, TR>([&](const Scalar<TR> &x) { 1001fc97d2e6SPeter Klausler auto y{x.template ToInteger<Scalar<T>>(mode)}; 1002fc97d2e6SPeter Klausler if (y.flags.test(RealFlag::Overflow) && 1003fc97d2e6SPeter Klausler context.languageFeatures().ShouldWarn( 1004fc97d2e6SPeter Klausler common::UsageWarning::FoldingException)) { 1005fc97d2e6SPeter Klausler context.messages().Say( 1006fc97d2e6SPeter Klausler common::UsageWarning::FoldingException, 1007fc97d2e6SPeter Klausler "%s intrinsic folding overflow"_warn_en_US, name); 1008fc97d2e6SPeter Klausler } 1009fc97d2e6SPeter Klausler return y.value; 1010fc97d2e6SPeter Klausler })); 1011fc97d2e6SPeter Klausler }, 1012fc97d2e6SPeter Klausler cx->u); 1013fc97d2e6SPeter Klausler } 1014fc97d2e6SPeter Klausler } else if (name == "count") { 1015fc97d2e6SPeter Klausler int maskKind = args[0]->GetType()->kind(); 1016fc97d2e6SPeter Klausler switch (maskKind) { 1017fc97d2e6SPeter Klausler SWITCH_COVERS_ALL_CASES 1018fc97d2e6SPeter Klausler case 1: 1019fc97d2e6SPeter Klausler return FoldCount<T, 1>(context, std::move(funcRef)); 1020fc97d2e6SPeter Klausler case 2: 1021fc97d2e6SPeter Klausler return FoldCount<T, 2>(context, std::move(funcRef)); 1022fc97d2e6SPeter Klausler case 4: 1023fc97d2e6SPeter Klausler return FoldCount<T, 4>(context, std::move(funcRef)); 1024fc97d2e6SPeter Klausler case 8: 1025fc97d2e6SPeter Klausler return FoldCount<T, 8>(context, std::move(funcRef)); 1026fc97d2e6SPeter Klausler } 1027fc97d2e6SPeter Klausler } else if (name == "dim") { 1028fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 1029fc97d2e6SPeter Klausler ScalarFunc<T, T, T>( 1030fc97d2e6SPeter Klausler [&context](const Scalar<T> &x, const Scalar<T> &y) -> Scalar<T> { 1031fc97d2e6SPeter Klausler auto result{x.DIM(y)}; 1032fc97d2e6SPeter Klausler if (result.overflow && 1033fc97d2e6SPeter Klausler context.languageFeatures().ShouldWarn( 1034fc97d2e6SPeter Klausler common::UsageWarning::FoldingException)) { 1035fc97d2e6SPeter Klausler context.messages().Say(common::UsageWarning::FoldingException, 1036fc97d2e6SPeter Klausler "DIM intrinsic folding overflow"_warn_en_US); 1037fc97d2e6SPeter Klausler } 1038fc97d2e6SPeter Klausler return result.value; 1039fc97d2e6SPeter Klausler })); 1040fc97d2e6SPeter Klausler } else if (name == "exponent") { 1041fc97d2e6SPeter Klausler if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 1042fc97d2e6SPeter Klausler return common::visit( 1043fc97d2e6SPeter Klausler [&funcRef, &context](const auto &x) -> Expr<T> { 1044fc97d2e6SPeter Klausler using TR = typename std::decay_t<decltype(x)>::Result; 1045fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef), 1046fc97d2e6SPeter Klausler &Scalar<TR>::template EXPONENT<Scalar<T>>); 1047fc97d2e6SPeter Klausler }, 1048fc97d2e6SPeter Klausler sx->u); 1049fc97d2e6SPeter Klausler } else { 1050fc97d2e6SPeter Klausler DIE("exponent argument must be real"); 1051fc97d2e6SPeter Klausler } 1052fc97d2e6SPeter Klausler } else if (name == "findloc") { 1053fc97d2e6SPeter Klausler return FoldLocation<WhichLocation::Findloc, T>(context, std::move(funcRef)); 1054fc97d2e6SPeter Klausler } else if (name == "huge") { 1055fc97d2e6SPeter Klausler return Expr<T>{Scalar<T>::HUGE()}; 1056fc97d2e6SPeter Klausler } else if (name == "iachar" || name == "ichar") { 1057fc97d2e6SPeter Klausler auto *someChar{UnwrapExpr<Expr<SomeCharacter>>(args[0])}; 1058fc97d2e6SPeter Klausler CHECK(someChar); 1059fc97d2e6SPeter Klausler if (auto len{ToInt64(someChar->LEN())}) { 1060fc97d2e6SPeter Klausler if (len.value() < 1) { 1061fc97d2e6SPeter Klausler context.messages().Say( 1062fc97d2e6SPeter Klausler "Character in intrinsic function %s must have length one"_err_en_US, 1063fc97d2e6SPeter Klausler name); 1064fc97d2e6SPeter Klausler } else if (len.value() > 1 && 1065fc97d2e6SPeter Klausler context.languageFeatures().ShouldWarn( 1066fc97d2e6SPeter Klausler common::UsageWarning::Portability)) { 1067fc97d2e6SPeter Klausler // Do not die, this was not checked before 1068fc97d2e6SPeter Klausler context.messages().Say(common::UsageWarning::Portability, 1069fc97d2e6SPeter Klausler "Character in intrinsic function %s should have length one"_port_en_US, 1070fc97d2e6SPeter Klausler name); 1071fc97d2e6SPeter Klausler } else { 1072fc97d2e6SPeter Klausler return common::visit( 1073fc97d2e6SPeter Klausler [&funcRef, &context, &FromInt64](const auto &str) -> Expr<T> { 1074fc97d2e6SPeter Klausler using Char = typename std::decay_t<decltype(str)>::Result; 1075fc97d2e6SPeter Klausler (void)FromInt64; 1076fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, Char>(context, 1077fc97d2e6SPeter Klausler std::move(funcRef), 1078fc97d2e6SPeter Klausler ScalarFunc<T, Char>( 1079fc97d2e6SPeter Klausler #ifndef _MSC_VER 1080fc97d2e6SPeter Klausler [&FromInt64](const Scalar<Char> &c) { 1081fc97d2e6SPeter Klausler return FromInt64(CharacterUtils<Char::kind>::ICHAR( 1082fc97d2e6SPeter Klausler CharacterUtils<Char::kind>::Resize(c, 1))); 1083fc97d2e6SPeter Klausler })); 1084fc97d2e6SPeter Klausler #else // _MSC_VER 1085fc97d2e6SPeter Klausler // MSVC 14 get confused by the original code above and 1086fc97d2e6SPeter Klausler // ends up emitting an error about passing a std::string 1087fc97d2e6SPeter Klausler // to the std::u16string instantiation of 1088fc97d2e6SPeter Klausler // CharacterUtils<2>::ICHAR(). Can't find a work-around, 1089fc97d2e6SPeter Klausler // so remove the FromInt64 error checking lambda that 1090fc97d2e6SPeter Klausler // seems to have caused the proble. 1091fc97d2e6SPeter Klausler [](const Scalar<Char> &c) { 1092fc97d2e6SPeter Klausler return CharacterUtils<Char::kind>::ICHAR( 1093fc97d2e6SPeter Klausler CharacterUtils<Char::kind>::Resize(c, 1)); 1094fc97d2e6SPeter Klausler })); 1095fc97d2e6SPeter Klausler #endif // _MSC_VER 1096fc97d2e6SPeter Klausler }, 1097fc97d2e6SPeter Klausler someChar->u); 1098fc97d2e6SPeter Klausler } 1099fc97d2e6SPeter Klausler } 1100fc97d2e6SPeter Klausler } else if (name == "index" || name == "scan" || name == "verify") { 1101fc97d2e6SPeter Klausler if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) { 1102fc97d2e6SPeter Klausler return common::visit( 1103fc97d2e6SPeter Klausler [&](const auto &kch) -> Expr<T> { 1104fc97d2e6SPeter Klausler using TC = typename std::decay_t<decltype(kch)>::Result; 1105fc97d2e6SPeter Klausler if (UnwrapExpr<Expr<SomeLogical>>(args[2])) { // BACK= 1106fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, TC, TC, LogicalResult>(context, 1107fc97d2e6SPeter Klausler std::move(funcRef), 1108fc97d2e6SPeter Klausler ScalarFunc<T, TC, TC, LogicalResult>{ 1109fc97d2e6SPeter Klausler [&name, &FromInt64](const Scalar<TC> &str, 1110fc97d2e6SPeter Klausler const Scalar<TC> &other, 1111fc97d2e6SPeter Klausler const Scalar<LogicalResult> &back) { 1112fc97d2e6SPeter Klausler return FromInt64(name == "index" 1113fc97d2e6SPeter Klausler ? CharacterUtils<TC::kind>::INDEX( 1114fc97d2e6SPeter Klausler str, other, back.IsTrue()) 1115fc97d2e6SPeter Klausler : name == "scan" 1116fc97d2e6SPeter Klausler ? CharacterUtils<TC::kind>::SCAN( 1117fc97d2e6SPeter Klausler str, other, back.IsTrue()) 1118fc97d2e6SPeter Klausler : CharacterUtils<TC::kind>::VERIFY( 1119fc97d2e6SPeter Klausler str, other, back.IsTrue())); 1120fc97d2e6SPeter Klausler }}); 1121fc97d2e6SPeter Klausler } else { 1122fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, TC, TC>(context, 1123fc97d2e6SPeter Klausler std::move(funcRef), 1124fc97d2e6SPeter Klausler ScalarFunc<T, TC, TC>{ 1125fc97d2e6SPeter Klausler [&name, &FromInt64]( 1126fc97d2e6SPeter Klausler const Scalar<TC> &str, const Scalar<TC> &other) { 1127fc97d2e6SPeter Klausler return FromInt64(name == "index" 1128fc97d2e6SPeter Klausler ? CharacterUtils<TC::kind>::INDEX(str, other) 1129fc97d2e6SPeter Klausler : name == "scan" 1130fc97d2e6SPeter Klausler ? CharacterUtils<TC::kind>::SCAN(str, other) 1131fc97d2e6SPeter Klausler : CharacterUtils<TC::kind>::VERIFY(str, other)); 1132fc97d2e6SPeter Klausler }}); 1133fc97d2e6SPeter Klausler } 1134fc97d2e6SPeter Klausler }, 1135fc97d2e6SPeter Klausler charExpr->u); 1136fc97d2e6SPeter Klausler } else { 1137fc97d2e6SPeter Klausler DIE("first argument must be CHARACTER"); 1138fc97d2e6SPeter Klausler } 1139fc97d2e6SPeter Klausler } else if (name == "int_ptr_kind") { 1140fc97d2e6SPeter Klausler return Expr<T>{8}; 1141fc97d2e6SPeter Klausler } else if (name == "kind") { 1142fc97d2e6SPeter Klausler // FoldOperation(FunctionRef &&) in fold-implementation.h will not 1143fc97d2e6SPeter Klausler // have folded the argument; in the case of TypeParamInquiry, 1144fc97d2e6SPeter Klausler // try to get the type of the parameter itself. 1145fc97d2e6SPeter Klausler if (const auto *expr{args[0] ? args[0]->UnwrapExpr() : nullptr}) { 1146fc97d2e6SPeter Klausler if (const auto *inquiry{UnwrapExpr<TypeParamInquiry>(*expr)}) { 1147fc97d2e6SPeter Klausler if (const auto *typeSpec{inquiry->parameter().GetType()}) { 1148fc97d2e6SPeter Klausler if (const auto *intrinType{typeSpec->AsIntrinsic()}) { 1149fc97d2e6SPeter Klausler if (auto k{ToInt64(Fold( 1150fc97d2e6SPeter Klausler context, Expr<SubscriptInteger>{intrinType->kind()}))}) { 1151fc97d2e6SPeter Klausler return Expr<T>{*k}; 1152fc97d2e6SPeter Klausler } 1153fc97d2e6SPeter Klausler } 1154fc97d2e6SPeter Klausler } 1155fc97d2e6SPeter Klausler } else if (auto dyType{expr->GetType()}) { 1156fc97d2e6SPeter Klausler return Expr<T>{dyType->kind()}; 1157fc97d2e6SPeter Klausler } 1158fc97d2e6SPeter Klausler } 115964ab3302SCarolineConcatto } else if (name == "lbound") { 116064ab3302SCarolineConcatto return LBOUND(context, std::move(funcRef)); 1161*94963919SPeter Klausler } else if (name == "lcobound") { 1162*94963919SPeter Klausler return COBOUND(context, std::move(funcRef), /*isUCOBOUND=*/false); 116364ab3302SCarolineConcatto } else if (name == "leadz" || name == "trailz" || name == "poppar" || 116464ab3302SCarolineConcatto name == "popcnt") { 1165fc97d2e6SPeter Klausler if (auto *sn{UnwrapExpr<Expr<SomeKind<T::category>>>(args[0])}) { 1166cd03e96fSPeter Klausler return common::visit( 116764ab3302SCarolineConcatto [&funcRef, &context, &name](const auto &n) -> Expr<T> { 116864ab3302SCarolineConcatto using TI = typename std::decay_t<decltype(n)>::Result; 116964ab3302SCarolineConcatto if (name == "poppar") { 117064ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef), 117164ab3302SCarolineConcatto ScalarFunc<T, TI>([](const Scalar<TI> &i) -> Scalar<T> { 117264ab3302SCarolineConcatto return Scalar<T>{i.POPPAR() ? 1 : 0}; 117364ab3302SCarolineConcatto })); 117464ab3302SCarolineConcatto } 117564ab3302SCarolineConcatto auto fptr{&Scalar<TI>::LEADZ}; 117664ab3302SCarolineConcatto if (name == "leadz") { // done in fptr definition 117764ab3302SCarolineConcatto } else if (name == "trailz") { 117864ab3302SCarolineConcatto fptr = &Scalar<TI>::TRAILZ; 117964ab3302SCarolineConcatto } else if (name == "popcnt") { 118064ab3302SCarolineConcatto fptr = &Scalar<TI>::POPCNT; 118164ab3302SCarolineConcatto } else { 118264ab3302SCarolineConcatto common::die( 118364ab3302SCarolineConcatto "missing case to fold intrinsic function %s", name.c_str()); 118464ab3302SCarolineConcatto } 118564ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef), 118671d80cd6SMichael Kruse // `i` should be declared as `const Scalar<TI>&`. 118771d80cd6SMichael Kruse // We declare it as `auto` to workaround an msvc bug: 118871d80cd6SMichael Kruse // https://developercommunity.visualstudio.com/t/Regression:-nested-closure-assumes-wrong/10130223 118971d80cd6SMichael Kruse ScalarFunc<T, TI>([&fptr](const auto &i) -> Scalar<T> { 119064ab3302SCarolineConcatto return Scalar<T>{std::invoke(fptr, i)}; 119164ab3302SCarolineConcatto })); 119264ab3302SCarolineConcatto }, 119364ab3302SCarolineConcatto sn->u); 119464ab3302SCarolineConcatto } else { 119564ab3302SCarolineConcatto DIE("leadz argument must be integer"); 119664ab3302SCarolineConcatto } 119764ab3302SCarolineConcatto } else if (name == "len") { 119864ab3302SCarolineConcatto if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) { 1199cd03e96fSPeter Klausler return common::visit( 120064ab3302SCarolineConcatto [&](auto &kx) { 120164ab3302SCarolineConcatto if (auto len{kx.LEN()}) { 120239157895SEric Schweitz if (IsScopeInvariantExpr(*len)) { 120364ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(*std::move(len))); 120464ab3302SCarolineConcatto } else { 120564ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 120664ab3302SCarolineConcatto } 120739157895SEric Schweitz } else { 120839157895SEric Schweitz return Expr<T>{std::move(funcRef)}; 120939157895SEric Schweitz } 121064ab3302SCarolineConcatto }, 121164ab3302SCarolineConcatto charExpr->u); 121264ab3302SCarolineConcatto } else { 121364ab3302SCarolineConcatto DIE("len() argument must be of character type"); 121464ab3302SCarolineConcatto } 121564ab3302SCarolineConcatto } else if (name == "len_trim") { 121664ab3302SCarolineConcatto if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) { 1217cd03e96fSPeter Klausler return common::visit( 121864ab3302SCarolineConcatto [&](const auto &kch) -> Expr<T> { 121964ab3302SCarolineConcatto using TC = typename std::decay_t<decltype(kch)>::Result; 122064ab3302SCarolineConcatto return FoldElementalIntrinsic<T, TC>(context, std::move(funcRef), 1221a280d300SPeter Klausler ScalarFunc<T, TC>{[&FromInt64](const Scalar<TC> &str) { 1222a280d300SPeter Klausler return FromInt64(CharacterUtils<TC::kind>::LEN_TRIM(str)); 122364ab3302SCarolineConcatto }}); 122464ab3302SCarolineConcatto }, 122564ab3302SCarolineConcatto charExpr->u); 122664ab3302SCarolineConcatto } else { 122764ab3302SCarolineConcatto DIE("len_trim() argument must be of character type"); 122864ab3302SCarolineConcatto } 1229f1fa3b7fSJean Perier } else if (name == "max0" || name == "max1") { 1230f1fa3b7fSJean Perier return RewriteSpecificMINorMAX(context, std::move(funcRef)); 123164ab3302SCarolineConcatto } else if (name == "maxexponent") { 123264ab3302SCarolineConcatto if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 1233cd03e96fSPeter Klausler return common::visit( 123464ab3302SCarolineConcatto [](const auto &x) { 123564ab3302SCarolineConcatto using TR = typename std::decay_t<decltype(x)>::Result; 123664ab3302SCarolineConcatto return Expr<T>{Scalar<TR>::MAXEXPONENT}; 123764ab3302SCarolineConcatto }, 123864ab3302SCarolineConcatto sx->u); 123964ab3302SCarolineConcatto } 1240cc1d13f9Speter klausler } else if (name == "maxloc") { 1241cc1d13f9Speter klausler return FoldLocation<WhichLocation::Maxloc, T>(context, std::move(funcRef)); 1242cc1d13f9Speter klausler } else if (name == "min0" || name == "min1") { 1243cc1d13f9Speter klausler return RewriteSpecificMINorMAX(context, std::move(funcRef)); 124464ab3302SCarolineConcatto } else if (name == "minexponent") { 124564ab3302SCarolineConcatto if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 1246cd03e96fSPeter Klausler return common::visit( 124764ab3302SCarolineConcatto [](const auto &x) { 124864ab3302SCarolineConcatto using TR = typename std::decay_t<decltype(x)>::Result; 124964ab3302SCarolineConcatto return Expr<T>{Scalar<TR>::MINEXPONENT}; 125064ab3302SCarolineConcatto }, 125164ab3302SCarolineConcatto sx->u); 125264ab3302SCarolineConcatto } 1253cc1d13f9Speter klausler } else if (name == "minloc") { 1254cc1d13f9Speter klausler return FoldLocation<WhichLocation::Minloc, T>(context, std::move(funcRef)); 125564ab3302SCarolineConcatto } else if (name == "mod") { 1256317277e4SPeter Klausler bool badPConst{false}; 1257317277e4SPeter Klausler if (auto *pExpr{UnwrapExpr<Expr<T>>(args[1])}) { 1258317277e4SPeter Klausler *pExpr = Fold(context, std::move(*pExpr)); 1259317277e4SPeter Klausler if (auto pConst{GetScalarConstantValue<T>(*pExpr)}; pConst && 1260317277e4SPeter Klausler pConst->IsZero() && 1261317277e4SPeter Klausler context.languageFeatures().ShouldWarn( 1262317277e4SPeter Klausler common::UsageWarning::FoldingAvoidsRuntimeCrash)) { 12630f973ac7SPeter Klausler context.messages().Say(common::UsageWarning::FoldingAvoidsRuntimeCrash, 12640f973ac7SPeter Klausler "MOD: P argument is zero"_warn_en_US); 1265317277e4SPeter Klausler badPConst = true; 1266317277e4SPeter Klausler } 1267317277e4SPeter Klausler } 126864ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 126964ab3302SCarolineConcatto ScalarFuncWithContext<T, T, T>( 1270317277e4SPeter Klausler [badPConst](FoldingContext &context, const Scalar<T> &x, 127164ab3302SCarolineConcatto const Scalar<T> &y) -> Scalar<T> { 127264ab3302SCarolineConcatto auto quotRem{x.DivideSigned(y)}; 1273505f6da1SPeter Klausler if (context.languageFeatures().ShouldWarn( 1274505f6da1SPeter Klausler common::UsageWarning::FoldingAvoidsRuntimeCrash)) { 1275317277e4SPeter Klausler if (!badPConst && quotRem.divisionByZero) { 12760f973ac7SPeter Klausler context.messages().Say( 12770f973ac7SPeter Klausler common::UsageWarning::FoldingAvoidsRuntimeCrash, 12780f973ac7SPeter Klausler "mod() by zero"_warn_en_US); 127964ab3302SCarolineConcatto } else if (quotRem.overflow) { 12800f973ac7SPeter Klausler context.messages().Say( 12810f973ac7SPeter Klausler common::UsageWarning::FoldingAvoidsRuntimeCrash, 12820f973ac7SPeter Klausler "mod() folding overflowed"_warn_en_US); 128364ab3302SCarolineConcatto } 1284505f6da1SPeter Klausler } 128564ab3302SCarolineConcatto return quotRem.remainder; 128664ab3302SCarolineConcatto })); 128764ab3302SCarolineConcatto } else if (name == "modulo") { 1288317277e4SPeter Klausler bool badPConst{false}; 1289317277e4SPeter Klausler if (auto *pExpr{UnwrapExpr<Expr<T>>(args[1])}) { 1290317277e4SPeter Klausler *pExpr = Fold(context, std::move(*pExpr)); 1291317277e4SPeter Klausler if (auto pConst{GetScalarConstantValue<T>(*pExpr)}; pConst && 1292317277e4SPeter Klausler pConst->IsZero() && 1293317277e4SPeter Klausler context.languageFeatures().ShouldWarn( 1294317277e4SPeter Klausler common::UsageWarning::FoldingAvoidsRuntimeCrash)) { 12950f973ac7SPeter Klausler context.messages().Say(common::UsageWarning::FoldingAvoidsRuntimeCrash, 12960f973ac7SPeter Klausler "MODULO: P argument is zero"_warn_en_US); 1297317277e4SPeter Klausler badPConst = true; 1298317277e4SPeter Klausler } 1299317277e4SPeter Klausler } 130064ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 1301317277e4SPeter Klausler ScalarFuncWithContext<T, T, T>([badPConst](FoldingContext &context, 130274d5c3c0SPeter Steinfeld const Scalar<T> &x, 130364ab3302SCarolineConcatto const Scalar<T> &y) -> Scalar<T> { 130464ab3302SCarolineConcatto auto result{x.MODULO(y)}; 1305317277e4SPeter Klausler if (!badPConst && result.overflow && 1306505f6da1SPeter Klausler context.languageFeatures().ShouldWarn( 1307505f6da1SPeter Klausler common::UsageWarning::FoldingException)) { 13080f973ac7SPeter Klausler context.messages().Say(common::UsageWarning::FoldingException, 13090f973ac7SPeter Klausler "modulo() folding overflowed"_warn_en_US); 131064ab3302SCarolineConcatto } 131164ab3302SCarolineConcatto return result.value; 131264ab3302SCarolineConcatto })); 131364ab3302SCarolineConcatto } else if (name == "precision") { 131464ab3302SCarolineConcatto if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 1315cd03e96fSPeter Klausler return Expr<T>{common::visit( 131664ab3302SCarolineConcatto [](const auto &kx) { 131764ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::PRECISION; 131864ab3302SCarolineConcatto }, 131964ab3302SCarolineConcatto cx->u)}; 132064ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 1321cd03e96fSPeter Klausler return Expr<T>{common::visit( 132264ab3302SCarolineConcatto [](const auto &kx) { 132364ab3302SCarolineConcatto return Scalar<typename ResultType<decltype(kx)>::Part>::PRECISION; 132464ab3302SCarolineConcatto }, 132564ab3302SCarolineConcatto cx->u)}; 132664ab3302SCarolineConcatto } 132764ab3302SCarolineConcatto } else if (name == "range") { 132864ab3302SCarolineConcatto if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) { 1329cd03e96fSPeter Klausler return Expr<T>{common::visit( 133064ab3302SCarolineConcatto [](const auto &kx) { 133164ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::RANGE; 133264ab3302SCarolineConcatto }, 133364ab3302SCarolineConcatto cx->u)}; 1334fc97d2e6SPeter Klausler } else if (const auto *cx{UnwrapExpr<Expr<SomeUnsigned>>(args[0])}) { 1335fc97d2e6SPeter Klausler return Expr<T>{common::visit( 1336fc97d2e6SPeter Klausler [](const auto &kx) { 1337fc97d2e6SPeter Klausler return Scalar<ResultType<decltype(kx)>>::UnsignedRANGE; 1338fc97d2e6SPeter Klausler }, 1339fc97d2e6SPeter Klausler cx->u)}; 134064ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 1341cd03e96fSPeter Klausler return Expr<T>{common::visit( 134264ab3302SCarolineConcatto [](const auto &kx) { 134364ab3302SCarolineConcatto return Scalar<ResultType<decltype(kx)>>::RANGE; 134464ab3302SCarolineConcatto }, 134564ab3302SCarolineConcatto cx->u)}; 134664ab3302SCarolineConcatto } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 1347cd03e96fSPeter Klausler return Expr<T>{common::visit( 134864ab3302SCarolineConcatto [](const auto &kx) { 134964ab3302SCarolineConcatto return Scalar<typename ResultType<decltype(kx)>::Part>::RANGE; 135064ab3302SCarolineConcatto }, 135164ab3302SCarolineConcatto cx->u)}; 135264ab3302SCarolineConcatto } 135364ab3302SCarolineConcatto } else if (name == "rank") { 1354ff567a4eSPeter Klausler if (args[0]) { 1355ff567a4eSPeter Klausler const Symbol *symbol{nullptr}; 1356ff567a4eSPeter Klausler if (auto dataRef{ExtractDataRef(args[0])}) { 1357ff567a4eSPeter Klausler symbol = &dataRef->GetLastSymbol(); 1358ff567a4eSPeter Klausler } else { 1359ff567a4eSPeter Klausler symbol = args[0]->GetAssumedTypeDummy(); 1360ff567a4eSPeter Klausler } 1361ff567a4eSPeter Klausler if (symbol && IsAssumedRank(*symbol)) { 1362e0ca7b44SSteve Scalpone // DescriptorInquiry can only be placed in expression of kind 1363e0ca7b44SSteve Scalpone // DescriptorInquiry::Result::kind. 1364ff567a4eSPeter Klausler return ConvertToType<T>( 1365ff567a4eSPeter Klausler Expr<Type<TypeCategory::Integer, DescriptorInquiry::Result::kind>>{ 1366ff567a4eSPeter Klausler DescriptorInquiry{ 1367ff567a4eSPeter Klausler NamedEntity{*symbol}, DescriptorInquiry::Field::Rank}}); 1368e0ca7b44SSteve Scalpone } 1369ff567a4eSPeter Klausler return Expr<T>{args[0]->Rank()}; 1370e0ca7b44SSteve Scalpone } 137164ab3302SCarolineConcatto } else if (name == "selected_char_kind") { 137264ab3302SCarolineConcatto if (const auto *chCon{UnwrapExpr<Constant<TypeOf<std::string>>>(args[0])}) { 137364ab3302SCarolineConcatto if (std::optional<std::string> value{chCon->GetScalarValue()}) { 137464ab3302SCarolineConcatto int defaultKind{ 137564ab3302SCarolineConcatto context.defaults().GetDefaultKind(TypeCategory::Character)}; 137664ab3302SCarolineConcatto return Expr<T>{SelectedCharKind(*value, defaultKind)}; 137764ab3302SCarolineConcatto } 137864ab3302SCarolineConcatto } 1379fc97d2e6SPeter Klausler } else if (name == "selected_int_kind" || name == "selected_unsigned_kind") { 1380af54b676SPeter Klausler if (auto p{ToInt64(args[0])}) { 138123c2bedfSPeter Klausler return Expr<T>{context.targetCharacteristics().SelectedIntKind(*p)}; 138264ab3302SCarolineConcatto } 13838383d768SPeter Klausler } else if (name == "selected_logical_kind") { 13848383d768SPeter Klausler if (auto p{ToInt64(args[0])}) { 13858383d768SPeter Klausler return Expr<T>{context.targetCharacteristics().SelectedLogicalKind(*p)}; 13868383d768SPeter Klausler } 13878f16101cSpeter klausler } else if (name == "selected_real_kind" || 13888f16101cSpeter klausler name == "__builtin_ieee_selected_real_kind") { 138964ab3302SCarolineConcatto if (auto p{GetInt64ArgOr(args[0], 0)}) { 139064ab3302SCarolineConcatto if (auto r{GetInt64ArgOr(args[1], 0)}) { 139164ab3302SCarolineConcatto if (auto radix{GetInt64ArgOr(args[2], 2)}) { 139223c2bedfSPeter Klausler return Expr<T>{ 139323c2bedfSPeter Klausler context.targetCharacteristics().SelectedRealKind(*p, *r, *radix)}; 139464ab3302SCarolineConcatto } 139564ab3302SCarolineConcatto } 139664ab3302SCarolineConcatto } 139764ab3302SCarolineConcatto } else if (name == "shape") { 1398fb3faa8bSJean Perier if (auto shape{GetContextFreeShape(context, args[0])}) { 139964ab3302SCarolineConcatto if (auto shapeExpr{AsExtentArrayExpr(*shape)}) { 140064ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(*shapeExpr))); 140164ab3302SCarolineConcatto } 140264ab3302SCarolineConcatto } 140364ab3302SCarolineConcatto } else if (name == "sign") { 140464ab3302SCarolineConcatto return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 140574d5c3c0SPeter Steinfeld ScalarFunc<T, T, T>([&context](const Scalar<T> &j, 140674d5c3c0SPeter Steinfeld const Scalar<T> &k) -> Scalar<T> { 140764ab3302SCarolineConcatto typename Scalar<T>::ValueWithOverflow result{j.SIGN(k)}; 1408505f6da1SPeter Klausler if (result.overflow && 1409505f6da1SPeter Klausler context.languageFeatures().ShouldWarn( 1410505f6da1SPeter Klausler common::UsageWarning::FoldingException)) { 14110f973ac7SPeter Klausler context.messages().Say(common::UsageWarning::FoldingException, 141274d5c3c0SPeter Steinfeld "sign(integer(kind=%d)) folding overflowed"_warn_en_US, KIND); 141364ab3302SCarolineConcatto } 141464ab3302SCarolineConcatto return result.value; 141564ab3302SCarolineConcatto })); 141664ab3302SCarolineConcatto } else if (name == "size") { 1417fb3faa8bSJean Perier if (auto shape{GetContextFreeShape(context, args[0])}) { 1418221ba64eSPeter Klausler if (args[1]) { // DIM= is present, get one extent 1419221ba64eSPeter Klausler std::optional<int> dim; 1420221ba64eSPeter Klausler if (const auto *array{args[0].value().UnwrapExpr()}; array && 1421221ba64eSPeter Klausler !CheckDimArg(args[1], *array, context.messages(), false, dim)) { 14229245f355Speter klausler return MakeInvalidIntrinsic<T>(std::move(funcRef)); 1423221ba64eSPeter Klausler } else if (dim) { 1424221ba64eSPeter Klausler if (auto &extent{shape->at(*dim)}) { 142564ab3302SCarolineConcatto return Fold(context, ConvertToType<T>(std::move(*extent))); 142664ab3302SCarolineConcatto } 142764ab3302SCarolineConcatto } 142864ab3302SCarolineConcatto } else if (auto extents{common::AllElementsPresent(std::move(*shape))}) { 142964ab3302SCarolineConcatto // DIM= is absent; compute PRODUCT(SHAPE()) 143064ab3302SCarolineConcatto ExtentExpr product{1}; 143164ab3302SCarolineConcatto for (auto &&extent : std::move(*extents)) { 143264ab3302SCarolineConcatto product = std::move(product) * std::move(extent); 143364ab3302SCarolineConcatto } 143464ab3302SCarolineConcatto return Expr<T>{ConvertToType<T>(Fold(context, std::move(product)))}; 143564ab3302SCarolineConcatto } 143664ab3302SCarolineConcatto } 14376aa3591eSpeter klausler } else if (name == "sizeof") { // in bytes; extension 14386aa3591eSpeter klausler if (auto info{ 14396aa3591eSpeter klausler characteristics::TypeAndShape::Characterize(args[0], context)}) { 14406aa3591eSpeter klausler if (auto bytes{info->MeasureSizeInBytes(context)}) { 14416aa3591eSpeter klausler return Expr<T>{Fold(context, ConvertToType<T>(std::move(*bytes)))}; 14426aa3591eSpeter klausler } 14436aa3591eSpeter klausler } 14446aa3591eSpeter klausler } else if (name == "storage_size") { // in bits 1445efc5926cSpeter klausler if (auto info{ 1446efc5926cSpeter klausler characteristics::TypeAndShape::Characterize(args[0], context)}) { 1447efc5926cSpeter klausler if (auto bytes{info->MeasureElementSizeInBytes(context, true)}) { 14486aa3591eSpeter klausler return Expr<T>{ 14496aa3591eSpeter klausler Fold(context, Expr<T>{8} * ConvertToType<T>(std::move(*bytes)))}; 14506aa3591eSpeter klausler } 14516aa3591eSpeter klausler } 145264ab3302SCarolineConcatto } else if (name == "ubound") { 145364ab3302SCarolineConcatto return UBOUND(context, std::move(funcRef)); 1454*94963919SPeter Klausler } else if (name == "ucobound") { 1455*94963919SPeter Klausler return COBOUND(context, std::move(funcRef), /*isUCOBOUND=*/true); 1456e1ad2735SPeter Klausler } else if (name == "__builtin_numeric_storage_size") { 1457e1ad2735SPeter Klausler if (!context.moduleFileName()) { 1458e1ad2735SPeter Klausler // Don't fold this reference until it appears in the module file 1459e1ad2735SPeter Klausler // for ISO_FORTRAN_ENV -- the value depends on the compiler options 1460e1ad2735SPeter Klausler // that might be in force. 1461e1ad2735SPeter Klausler } else { 1462e1ad2735SPeter Klausler auto intBytes{ 1463e1ad2735SPeter Klausler context.targetCharacteristics().GetByteSize(TypeCategory::Integer, 1464e1ad2735SPeter Klausler context.defaults().GetDefaultKind(TypeCategory::Integer))}; 1465e1ad2735SPeter Klausler auto realBytes{ 1466e1ad2735SPeter Klausler context.targetCharacteristics().GetByteSize(TypeCategory::Real, 1467e1ad2735SPeter Klausler context.defaults().GetDefaultKind(TypeCategory::Real))}; 1468505f6da1SPeter Klausler if (intBytes != realBytes && 1469505f6da1SPeter Klausler context.languageFeatures().ShouldWarn( 1470505f6da1SPeter Klausler common::UsageWarning::FoldingValueChecks)) { 14710f973ac7SPeter Klausler context.messages().Say(common::UsageWarning::FoldingValueChecks, 14720f973ac7SPeter Klausler *context.moduleFileName(), 1473e1ad2735SPeter Klausler "NUMERIC_STORAGE_SIZE from ISO_FORTRAN_ENV is not well-defined when default INTEGER and REAL are not consistent due to compiler options"_warn_en_US); 1474e1ad2735SPeter Klausler } 1475e1ad2735SPeter Klausler return Expr<T>{8 * std::min(intBytes, realBytes)}; 1476e1ad2735SPeter Klausler } 147764ab3302SCarolineConcatto } 147864ab3302SCarolineConcatto return Expr<T>{std::move(funcRef)}; 147964ab3302SCarolineConcatto } 148064ab3302SCarolineConcatto 1481fc97d2e6SPeter Klausler template <int KIND> 1482fc97d2e6SPeter Klausler Expr<Type<TypeCategory::Unsigned, KIND>> FoldIntrinsicFunction( 1483fc97d2e6SPeter Klausler FoldingContext &context, 1484fc97d2e6SPeter Klausler FunctionRef<Type<TypeCategory::Unsigned, KIND>> &&funcRef) { 1485fc97d2e6SPeter Klausler if (auto foldedCommon{FoldIntrinsicFunctionCommon(context, funcRef)}) { 1486fc97d2e6SPeter Klausler return std::move(*foldedCommon); 1487fc97d2e6SPeter Klausler } 1488fc97d2e6SPeter Klausler using T = Type<TypeCategory::Unsigned, KIND>; 1489fc97d2e6SPeter Klausler ActualArguments &args{funcRef.arguments()}; 1490fc97d2e6SPeter Klausler auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}; 1491fc97d2e6SPeter Klausler CHECK(intrinsic); 1492fc97d2e6SPeter Klausler std::string name{intrinsic->name}; 1493fc97d2e6SPeter Klausler if (name == "huge") { 1494fc97d2e6SPeter Klausler return Expr<T>{Scalar<T>{}.NOT()}; 1495fc97d2e6SPeter Klausler } else if (name == "mod" || name == "modulo") { 1496fc97d2e6SPeter Klausler bool badPConst{false}; 1497fc97d2e6SPeter Klausler if (auto *pExpr{UnwrapExpr<Expr<T>>(args[1])}) { 1498fc97d2e6SPeter Klausler *pExpr = Fold(context, std::move(*pExpr)); 1499fc97d2e6SPeter Klausler if (auto pConst{GetScalarConstantValue<T>(*pExpr)}; pConst && 1500fc97d2e6SPeter Klausler pConst->IsZero() && 1501fc97d2e6SPeter Klausler context.languageFeatures().ShouldWarn( 1502fc97d2e6SPeter Klausler common::UsageWarning::FoldingAvoidsRuntimeCrash)) { 1503fc97d2e6SPeter Klausler context.messages().Say(common::UsageWarning::FoldingAvoidsRuntimeCrash, 1504fc97d2e6SPeter Klausler "%s: P argument is zero"_warn_en_US, name); 1505fc97d2e6SPeter Klausler badPConst = true; 1506fc97d2e6SPeter Klausler } 1507fc97d2e6SPeter Klausler } 1508fc97d2e6SPeter Klausler return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 1509fc97d2e6SPeter Klausler ScalarFuncWithContext<T, T, T>( 1510fc97d2e6SPeter Klausler [badPConst, &name](FoldingContext &context, const Scalar<T> &x, 1511fc97d2e6SPeter Klausler const Scalar<T> &y) -> Scalar<T> { 1512fc97d2e6SPeter Klausler auto quotRem{x.DivideUnsigned(y)}; 1513fc97d2e6SPeter Klausler if (context.languageFeatures().ShouldWarn( 1514fc97d2e6SPeter Klausler common::UsageWarning::FoldingAvoidsRuntimeCrash)) { 1515fc97d2e6SPeter Klausler if (!badPConst && quotRem.divisionByZero) { 1516fc97d2e6SPeter Klausler context.messages().Say( 1517fc97d2e6SPeter Klausler common::UsageWarning::FoldingAvoidsRuntimeCrash, 1518fc97d2e6SPeter Klausler "%s() by zero"_warn_en_US, name); 1519fc97d2e6SPeter Klausler } 1520fc97d2e6SPeter Klausler } 1521fc97d2e6SPeter Klausler return quotRem.remainder; 1522fc97d2e6SPeter Klausler })); 1523fc97d2e6SPeter Klausler } 1524fc97d2e6SPeter Klausler return Expr<T>{std::move(funcRef)}; 1525fc97d2e6SPeter Klausler } 1526fc97d2e6SPeter Klausler 1527803f1e46Speter klausler // Substitutes a bare type parameter reference with its value if it has one now 1528803f1e46Speter klausler // in an instantiation. Bare LEN type parameters are substituted only when 1529803f1e46Speter klausler // the known value is constant. 15304cbfd93aSpeter klausler Expr<TypeParamInquiry::Result> FoldOperation( 15314cbfd93aSpeter klausler FoldingContext &context, TypeParamInquiry &&inquiry) { 153252cc9df1SPeter Steinfeld std::optional<NamedEntity> base{inquiry.base()}; 153352cc9df1SPeter Steinfeld parser::CharBlock parameterName{inquiry.parameter().name()}; 153452cc9df1SPeter Steinfeld if (base) { 153552cc9df1SPeter Steinfeld // Handling "designator%typeParam". Get the value of the type parameter 153652cc9df1SPeter Steinfeld // from the instantiation of the base 153752cc9df1SPeter Steinfeld if (const semantics::DeclTypeSpec * 153852cc9df1SPeter Steinfeld declType{base->GetLastSymbol().GetType()}) { 153952cc9df1SPeter Steinfeld if (const semantics::ParamValue * 154052cc9df1SPeter Steinfeld paramValue{ 154152cc9df1SPeter Steinfeld declType->derivedTypeSpec().FindParameter(parameterName)}) { 154252cc9df1SPeter Steinfeld const semantics::MaybeIntExpr ¶mExpr{paramValue->GetExplicit()}; 154352cc9df1SPeter Steinfeld if (paramExpr && IsConstantExpr(*paramExpr)) { 154452cc9df1SPeter Steinfeld Expr<SomeInteger> intExpr{*paramExpr}; 154552cc9df1SPeter Steinfeld return Fold(context, 154652cc9df1SPeter Steinfeld ConvertToType<TypeParamInquiry::Result>(std::move(intExpr))); 154752cc9df1SPeter Steinfeld } 154852cc9df1SPeter Steinfeld } 154952cc9df1SPeter Steinfeld } 155052cc9df1SPeter Steinfeld } else { 1551803f1e46Speter klausler // A "bare" type parameter: replace with its value, if that's now known 155273a0ae02SPeter Klausler // in a current derived type instantiation. 155364ab3302SCarolineConcatto if (const auto *pdt{context.pdtInstance()}) { 155473a0ae02SPeter Klausler auto restorer{context.WithoutPDTInstance()}; // don't loop 1555803f1e46Speter klausler bool isLen{false}; 155673a0ae02SPeter Klausler if (const semantics::Scope * scope{pdt->scope()}) { 155752cc9df1SPeter Steinfeld auto iter{scope->find(parameterName)}; 155864ab3302SCarolineConcatto if (iter != scope->end()) { 155964ab3302SCarolineConcatto const Symbol &symbol{*iter->second}; 156064ab3302SCarolineConcatto const auto *details{symbol.detailsIf<semantics::TypeParamDetails>()}; 156152cc9df1SPeter Steinfeld if (details) { 1562803f1e46Speter klausler isLen = details->attr() == common::TypeParamAttr::Len; 156352cc9df1SPeter Steinfeld const semantics::MaybeIntExpr &initExpr{details->init()}; 1564803f1e46Speter klausler if (initExpr && IsConstantExpr(*initExpr) && 1565803f1e46Speter klausler (!isLen || ToInt64(*initExpr))) { 156652cc9df1SPeter Steinfeld Expr<SomeInteger> expr{*initExpr}; 156764ab3302SCarolineConcatto return Fold(context, 15684cbfd93aSpeter klausler ConvertToType<TypeParamInquiry::Result>(std::move(expr))); 156964ab3302SCarolineConcatto } 157064ab3302SCarolineConcatto } 157164ab3302SCarolineConcatto } 157252cc9df1SPeter Steinfeld } 157352cc9df1SPeter Steinfeld if (const auto *value{pdt->FindParameter(parameterName)}) { 157464ab3302SCarolineConcatto if (value->isExplicit()) { 1575803f1e46Speter klausler auto folded{Fold(context, 15764cbfd93aSpeter klausler AsExpr(ConvertToType<TypeParamInquiry::Result>( 1577803f1e46Speter klausler Expr<SomeInteger>{value->GetExplicit().value()})))}; 1578803f1e46Speter klausler if (!isLen || ToInt64(folded)) { 1579803f1e46Speter klausler return folded; 1580803f1e46Speter klausler } 158164ab3302SCarolineConcatto } 158264ab3302SCarolineConcatto } 158364ab3302SCarolineConcatto } 158464ab3302SCarolineConcatto } 15854cbfd93aSpeter klausler return AsExpr(std::move(inquiry)); 158664ab3302SCarolineConcatto } 158764ab3302SCarolineConcatto 158864ab3302SCarolineConcatto std::optional<std::int64_t> ToInt64(const Expr<SomeInteger> &expr) { 1589cd03e96fSPeter Klausler return common::visit( 159064ab3302SCarolineConcatto [](const auto &kindExpr) { return ToInt64(kindExpr); }, expr.u); 159164ab3302SCarolineConcatto } 159264ab3302SCarolineConcatto 1593fc97d2e6SPeter Klausler std::optional<std::int64_t> ToInt64(const Expr<SomeUnsigned> &expr) { 1594fc97d2e6SPeter Klausler return common::visit( 1595fc97d2e6SPeter Klausler [](const auto &kindExpr) { return ToInt64(kindExpr); }, expr.u); 1596fc97d2e6SPeter Klausler } 1597fc97d2e6SPeter Klausler 159864ab3302SCarolineConcatto std::optional<std::int64_t> ToInt64(const Expr<SomeType> &expr) { 1599fc97d2e6SPeter Klausler if (const auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(expr)}) { 1600fc97d2e6SPeter Klausler return ToInt64(*intExpr); 1601fc97d2e6SPeter Klausler } else if (const auto *unsignedExpr{UnwrapExpr<Expr<SomeUnsigned>>(expr)}) { 1602fc97d2e6SPeter Klausler return ToInt64(*unsignedExpr); 1603fc97d2e6SPeter Klausler } else { 1604fc97d2e6SPeter Klausler return std::nullopt; 1605fc97d2e6SPeter Klausler } 160664ab3302SCarolineConcatto } 1607af54b676SPeter Klausler 1608af54b676SPeter Klausler std::optional<std::int64_t> ToInt64(const ActualArgument &arg) { 1609af54b676SPeter Klausler return ToInt64(arg.UnwrapExpr()); 161064ab3302SCarolineConcatto } 161164ab3302SCarolineConcatto 16125c5bde1bSPeter Klausler #ifdef _MSC_VER // disable bogus warning about missing definitions 16135c5bde1bSPeter Klausler #pragma warning(disable : 4661) 16145c5bde1bSPeter Klausler #endif 161564ab3302SCarolineConcatto FOR_EACH_INTEGER_KIND(template class ExpressionBase, ) 1616fc97d2e6SPeter Klausler FOR_EACH_UNSIGNED_KIND(template class ExpressionBase, ) 161764ab3302SCarolineConcatto template class ExpressionBase<SomeInteger>; 1618fc97d2e6SPeter Klausler template class ExpressionBase<SomeUnsigned>; 16191f879005STim Keith } // namespace Fortran::evaluate 1620