xref: /llvm-project/flang/lib/Evaluate/fold-integer.cpp (revision 94963919011d77c2f3f9d867cb73067a4f50e87c)
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 &paramExpr{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