xref: /llvm-project/flang/lib/Evaluate/fold-integer.cpp (revision e0ca7b447b5424bfecd8f6541d987108ca81a856)
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"
1064ab3302SCarolineConcatto 
1164ab3302SCarolineConcatto namespace Fortran::evaluate {
1264ab3302SCarolineConcatto 
1364ab3302SCarolineConcatto template<int KIND>
1464ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> LBOUND(FoldingContext &context,
1564ab3302SCarolineConcatto     FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) {
1664ab3302SCarolineConcatto   using T = Type<TypeCategory::Integer, KIND>;
1764ab3302SCarolineConcatto   ActualArguments &args{funcRef.arguments()};
1864ab3302SCarolineConcatto   if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
1964ab3302SCarolineConcatto     if (int rank{array->Rank()}; rank > 0) {
2064ab3302SCarolineConcatto       std::optional<int> dim;
2164ab3302SCarolineConcatto       if (funcRef.Rank() == 0) {
2264ab3302SCarolineConcatto         // Optional DIM= argument is present: result is scalar.
2364ab3302SCarolineConcatto         if (auto dim64{GetInt64Arg(args[1])}) {
2464ab3302SCarolineConcatto           if (*dim64 < 1 || *dim64 > rank) {
2564ab3302SCarolineConcatto             context.messages().Say("DIM=%jd dimension is out of range for "
2664ab3302SCarolineConcatto                                    "rank-%d array"_en_US,
2764ab3302SCarolineConcatto                 static_cast<std::intmax_t>(*dim64), rank);
2864ab3302SCarolineConcatto             return MakeInvalidIntrinsic<T>(std::move(funcRef));
2964ab3302SCarolineConcatto           } else {
3064ab3302SCarolineConcatto             dim = *dim64 - 1;  // 1-based to 0-based
3164ab3302SCarolineConcatto           }
3264ab3302SCarolineConcatto         } else {
3364ab3302SCarolineConcatto           // DIM= is present but not constant
3464ab3302SCarolineConcatto           return Expr<T>{std::move(funcRef)};
3564ab3302SCarolineConcatto         }
3664ab3302SCarolineConcatto       }
3764ab3302SCarolineConcatto       bool lowerBoundsAreOne{true};
3864ab3302SCarolineConcatto       if (auto named{ExtractNamedEntity(*array)}) {
3964ab3302SCarolineConcatto         const Symbol &symbol{named->GetLastSymbol()};
4064ab3302SCarolineConcatto         if (symbol.Rank() == rank) {
4164ab3302SCarolineConcatto           lowerBoundsAreOne = false;
4264ab3302SCarolineConcatto           if (dim) {
4364ab3302SCarolineConcatto             return Fold(context,
4464ab3302SCarolineConcatto                 ConvertToType<T>(GetLowerBound(context, *named, *dim)));
4564ab3302SCarolineConcatto           } else if (auto extents{
4664ab3302SCarolineConcatto                          AsExtentArrayExpr(GetLowerBounds(context, *named))}) {
4764ab3302SCarolineConcatto             return Fold(context,
4864ab3302SCarolineConcatto                 ConvertToType<T>(Expr<ExtentType>{std::move(*extents)}));
4964ab3302SCarolineConcatto           }
5064ab3302SCarolineConcatto         } else {
5164ab3302SCarolineConcatto           lowerBoundsAreOne = symbol.Rank() == 0;  // LBOUND(array%component)
5264ab3302SCarolineConcatto         }
5364ab3302SCarolineConcatto       }
5464ab3302SCarolineConcatto       if (lowerBoundsAreOne) {
5564ab3302SCarolineConcatto         if (dim) {
5664ab3302SCarolineConcatto           return Expr<T>{1};
5764ab3302SCarolineConcatto         } else {
5864ab3302SCarolineConcatto           std::vector<Scalar<T>> ones(rank, Scalar<T>{1});
5964ab3302SCarolineConcatto           return Expr<T>{
6064ab3302SCarolineConcatto               Constant<T>{std::move(ones), ConstantSubscripts{rank}}};
6164ab3302SCarolineConcatto         }
6264ab3302SCarolineConcatto       }
6364ab3302SCarolineConcatto     }
6464ab3302SCarolineConcatto   }
6564ab3302SCarolineConcatto   return Expr<T>{std::move(funcRef)};
6664ab3302SCarolineConcatto }
6764ab3302SCarolineConcatto 
6864ab3302SCarolineConcatto template<int KIND>
6964ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context,
7064ab3302SCarolineConcatto     FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) {
7164ab3302SCarolineConcatto   using T = Type<TypeCategory::Integer, KIND>;
7264ab3302SCarolineConcatto   ActualArguments &args{funcRef.arguments()};
7364ab3302SCarolineConcatto   if (auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
7464ab3302SCarolineConcatto     if (int rank{array->Rank()}; rank > 0) {
7564ab3302SCarolineConcatto       std::optional<int> dim;
7664ab3302SCarolineConcatto       if (funcRef.Rank() == 0) {
7764ab3302SCarolineConcatto         // Optional DIM= argument is present: result is scalar.
7864ab3302SCarolineConcatto         if (auto dim64{GetInt64Arg(args[1])}) {
7964ab3302SCarolineConcatto           if (*dim64 < 1 || *dim64 > rank) {
8064ab3302SCarolineConcatto             context.messages().Say("DIM=%jd dimension is out of range for "
8164ab3302SCarolineConcatto                                    "rank-%d array"_en_US,
8264ab3302SCarolineConcatto                 static_cast<std::intmax_t>(*dim64), rank);
8364ab3302SCarolineConcatto             return MakeInvalidIntrinsic<T>(std::move(funcRef));
8464ab3302SCarolineConcatto           } else {
8564ab3302SCarolineConcatto             dim = *dim64 - 1;  // 1-based to 0-based
8664ab3302SCarolineConcatto           }
8764ab3302SCarolineConcatto         } else {
8864ab3302SCarolineConcatto           // DIM= is present but not constant
8964ab3302SCarolineConcatto           return Expr<T>{std::move(funcRef)};
9064ab3302SCarolineConcatto         }
9164ab3302SCarolineConcatto       }
9264ab3302SCarolineConcatto       bool takeBoundsFromShape{true};
9364ab3302SCarolineConcatto       if (auto named{ExtractNamedEntity(*array)}) {
9464ab3302SCarolineConcatto         const Symbol &symbol{named->GetLastSymbol()};
9564ab3302SCarolineConcatto         if (symbol.Rank() == rank) {
9664ab3302SCarolineConcatto           takeBoundsFromShape = false;
9764ab3302SCarolineConcatto           if (dim) {
9864ab3302SCarolineConcatto             if (semantics::IsAssumedSizeArray(symbol) && *dim == rank) {
9964ab3302SCarolineConcatto               return Expr<T>{-1};
10064ab3302SCarolineConcatto             } else if (auto ub{GetUpperBound(context, *named, *dim)}) {
10164ab3302SCarolineConcatto               return Fold(context, ConvertToType<T>(std::move(*ub)));
10264ab3302SCarolineConcatto             }
10364ab3302SCarolineConcatto           } else {
10464ab3302SCarolineConcatto             Shape ubounds{GetUpperBounds(context, *named)};
10564ab3302SCarolineConcatto             if (semantics::IsAssumedSizeArray(symbol)) {
10664ab3302SCarolineConcatto               CHECK(!ubounds.back());
10764ab3302SCarolineConcatto               ubounds.back() = ExtentExpr{-1};
10864ab3302SCarolineConcatto             }
10964ab3302SCarolineConcatto             if (auto extents{AsExtentArrayExpr(ubounds)}) {
11064ab3302SCarolineConcatto               return Fold(context,
11164ab3302SCarolineConcatto                   ConvertToType<T>(Expr<ExtentType>{std::move(*extents)}));
11264ab3302SCarolineConcatto             }
11364ab3302SCarolineConcatto           }
11464ab3302SCarolineConcatto         } else {
11564ab3302SCarolineConcatto           takeBoundsFromShape = symbol.Rank() == 0;  // UBOUND(array%component)
11664ab3302SCarolineConcatto         }
11764ab3302SCarolineConcatto       }
11864ab3302SCarolineConcatto       if (takeBoundsFromShape) {
11964ab3302SCarolineConcatto         if (auto shape{GetShape(context, *array)}) {
12064ab3302SCarolineConcatto           if (dim) {
12164ab3302SCarolineConcatto             if (auto &dimSize{shape->at(*dim)}) {
12264ab3302SCarolineConcatto               return Fold(context,
12364ab3302SCarolineConcatto                   ConvertToType<T>(Expr<ExtentType>{std::move(*dimSize)}));
12464ab3302SCarolineConcatto             }
12564ab3302SCarolineConcatto           } else if (auto shapeExpr{AsExtentArrayExpr(*shape)}) {
12664ab3302SCarolineConcatto             return Fold(context, ConvertToType<T>(std::move(*shapeExpr)));
12764ab3302SCarolineConcatto           }
12864ab3302SCarolineConcatto         }
12964ab3302SCarolineConcatto       }
13064ab3302SCarolineConcatto     }
13164ab3302SCarolineConcatto   }
13264ab3302SCarolineConcatto   return Expr<T>{std::move(funcRef)};
13364ab3302SCarolineConcatto }
13464ab3302SCarolineConcatto 
13564ab3302SCarolineConcatto template<int KIND>
13664ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction(
13764ab3302SCarolineConcatto     FoldingContext &context,
13864ab3302SCarolineConcatto     FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) {
13964ab3302SCarolineConcatto   using T = Type<TypeCategory::Integer, KIND>;
14064ab3302SCarolineConcatto   using Int4 = Type<TypeCategory::Integer, 4>;
14164ab3302SCarolineConcatto   ActualArguments &args{funcRef.arguments()};
14264ab3302SCarolineConcatto   auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
14364ab3302SCarolineConcatto   CHECK(intrinsic);
14464ab3302SCarolineConcatto   std::string name{intrinsic->name};
14564ab3302SCarolineConcatto   if (name == "abs") {
14664ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T>(context, std::move(funcRef),
14764ab3302SCarolineConcatto         ScalarFunc<T, T>([&context](const Scalar<T> &i) -> Scalar<T> {
14864ab3302SCarolineConcatto           typename Scalar<T>::ValueWithOverflow j{i.ABS()};
14964ab3302SCarolineConcatto           if (j.overflow) {
15064ab3302SCarolineConcatto             context.messages().Say(
15164ab3302SCarolineConcatto                 "abs(integer(kind=%d)) folding overflowed"_en_US, KIND);
15264ab3302SCarolineConcatto           }
15364ab3302SCarolineConcatto           return j.value;
15464ab3302SCarolineConcatto         }));
15564ab3302SCarolineConcatto   } else if (name == "bit_size") {
15664ab3302SCarolineConcatto     return Expr<T>{Scalar<T>::bits};
15764ab3302SCarolineConcatto   } else if (name == "ceiling" || name == "floor" || name == "nint") {
15864ab3302SCarolineConcatto     if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
15964ab3302SCarolineConcatto       // NINT rounds ties away from zero, not to even
16064ab3302SCarolineConcatto       common::RoundingMode mode{name == "ceiling"
16164ab3302SCarolineConcatto               ? common::RoundingMode::Up
16264ab3302SCarolineConcatto               : name == "floor" ? common::RoundingMode::Down
16364ab3302SCarolineConcatto                                 : common::RoundingMode::TiesAwayFromZero};
16464ab3302SCarolineConcatto       return std::visit(
16564ab3302SCarolineConcatto           [&](const auto &kx) {
16664ab3302SCarolineConcatto             using TR = ResultType<decltype(kx)>;
16764ab3302SCarolineConcatto             return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef),
16864ab3302SCarolineConcatto                 ScalarFunc<T, TR>([&](const Scalar<TR> &x) {
16964ab3302SCarolineConcatto                   auto y{x.template ToInteger<Scalar<T>>(mode)};
17064ab3302SCarolineConcatto                   if (y.flags.test(RealFlag::Overflow)) {
17164ab3302SCarolineConcatto                     context.messages().Say(
17264ab3302SCarolineConcatto                         "%s intrinsic folding overflow"_en_US, name);
17364ab3302SCarolineConcatto                   }
17464ab3302SCarolineConcatto                   return y.value;
17564ab3302SCarolineConcatto                 }));
17664ab3302SCarolineConcatto           },
17764ab3302SCarolineConcatto           cx->u);
17864ab3302SCarolineConcatto     }
17964ab3302SCarolineConcatto   } else if (name == "count") {
18064ab3302SCarolineConcatto     if (!args[1]) {  // TODO: COUNT(x,DIM=d)
18164ab3302SCarolineConcatto       if (const auto *constant{UnwrapConstantValue<LogicalResult>(args[0])}) {
18264ab3302SCarolineConcatto         std::int64_t result{0};
18364ab3302SCarolineConcatto         for (const auto &element : constant->values()) {
18464ab3302SCarolineConcatto           if (element.IsTrue()) {
18564ab3302SCarolineConcatto             ++result;
18664ab3302SCarolineConcatto           }
18764ab3302SCarolineConcatto         }
18864ab3302SCarolineConcatto         return Expr<T>{result};
18964ab3302SCarolineConcatto       }
19064ab3302SCarolineConcatto     }
19164ab3302SCarolineConcatto   } else if (name == "digits") {
19264ab3302SCarolineConcatto     if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
19364ab3302SCarolineConcatto       return Expr<T>{std::visit(
19464ab3302SCarolineConcatto           [](const auto &kx) {
19564ab3302SCarolineConcatto             return Scalar<ResultType<decltype(kx)>>::DIGITS;
19664ab3302SCarolineConcatto           },
19764ab3302SCarolineConcatto           cx->u)};
19864ab3302SCarolineConcatto     } else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
19964ab3302SCarolineConcatto       return Expr<T>{std::visit(
20064ab3302SCarolineConcatto           [](const auto &kx) {
20164ab3302SCarolineConcatto             return Scalar<ResultType<decltype(kx)>>::DIGITS;
20264ab3302SCarolineConcatto           },
20364ab3302SCarolineConcatto           cx->u)};
20464ab3302SCarolineConcatto     } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
20564ab3302SCarolineConcatto       return Expr<T>{std::visit(
20664ab3302SCarolineConcatto           [](const auto &kx) {
20764ab3302SCarolineConcatto             return Scalar<typename ResultType<decltype(kx)>::Part>::DIGITS;
20864ab3302SCarolineConcatto           },
20964ab3302SCarolineConcatto           cx->u)};
21064ab3302SCarolineConcatto     }
21164ab3302SCarolineConcatto   } else if (name == "dim") {
21264ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T, T>(
21364ab3302SCarolineConcatto         context, std::move(funcRef), &Scalar<T>::DIM);
21464ab3302SCarolineConcatto   } else if (name == "dshiftl" || name == "dshiftr") {
21564ab3302SCarolineConcatto     const auto fptr{
21664ab3302SCarolineConcatto         name == "dshiftl" ? &Scalar<T>::DSHIFTL : &Scalar<T>::DSHIFTR};
21764ab3302SCarolineConcatto     // Third argument can be of any kind. However, it must be smaller or equal
21864ab3302SCarolineConcatto     // than BIT_SIZE. It can be converted to Int4 to simplify.
21964ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T, T, Int4>(context, std::move(funcRef),
22064ab3302SCarolineConcatto         ScalarFunc<T, T, T, Int4>(
22164ab3302SCarolineConcatto             [&fptr](const Scalar<T> &i, const Scalar<T> &j,
22264ab3302SCarolineConcatto                 const Scalar<Int4> &shift) -> Scalar<T> {
22364ab3302SCarolineConcatto               return std::invoke(fptr, i, j, static_cast<int>(shift.ToInt64()));
22464ab3302SCarolineConcatto             }));
22564ab3302SCarolineConcatto   } else if (name == "exponent") {
22664ab3302SCarolineConcatto     if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
22764ab3302SCarolineConcatto       return std::visit(
22864ab3302SCarolineConcatto           [&funcRef, &context](const auto &x) -> Expr<T> {
22964ab3302SCarolineConcatto             using TR = typename std::decay_t<decltype(x)>::Result;
23064ab3302SCarolineConcatto             return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef),
23164ab3302SCarolineConcatto                 &Scalar<TR>::template EXPONENT<Scalar<T>>);
23264ab3302SCarolineConcatto           },
23364ab3302SCarolineConcatto           sx->u);
23464ab3302SCarolineConcatto     } else {
23564ab3302SCarolineConcatto       DIE("exponent argument must be real");
23664ab3302SCarolineConcatto     }
23764ab3302SCarolineConcatto   } else if (name == "huge") {
23864ab3302SCarolineConcatto     return Expr<T>{Scalar<T>::HUGE()};
23964ab3302SCarolineConcatto   } else if (name == "iachar" || name == "ichar") {
24064ab3302SCarolineConcatto     auto *someChar{UnwrapExpr<Expr<SomeCharacter>>(args[0])};
24164ab3302SCarolineConcatto     CHECK(someChar);
24264ab3302SCarolineConcatto     if (auto len{ToInt64(someChar->LEN())}) {
24364ab3302SCarolineConcatto       if (len.value() != 1) {
24464ab3302SCarolineConcatto         // Do not die, this was not checked before
24564ab3302SCarolineConcatto         context.messages().Say(
24664ab3302SCarolineConcatto             "Character in intrinsic function %s must have length one"_en_US,
24764ab3302SCarolineConcatto             name);
24864ab3302SCarolineConcatto       } else {
24964ab3302SCarolineConcatto         return std::visit(
25064ab3302SCarolineConcatto             [&funcRef, &context](const auto &str) -> Expr<T> {
25164ab3302SCarolineConcatto               using Char = typename std::decay_t<decltype(str)>::Result;
25264ab3302SCarolineConcatto               return FoldElementalIntrinsic<T, Char>(context,
25364ab3302SCarolineConcatto                   std::move(funcRef),
25464ab3302SCarolineConcatto                   ScalarFunc<T, Char>([](const Scalar<Char> &c) {
25564ab3302SCarolineConcatto                     return Scalar<T>{CharacterUtils<Char::kind>::ICHAR(c)};
25664ab3302SCarolineConcatto                   }));
25764ab3302SCarolineConcatto             },
25864ab3302SCarolineConcatto             someChar->u);
25964ab3302SCarolineConcatto       }
26064ab3302SCarolineConcatto     }
26164ab3302SCarolineConcatto   } else if (name == "iand" || name == "ior" || name == "ieor") {
26264ab3302SCarolineConcatto     auto fptr{&Scalar<T>::IAND};
26364ab3302SCarolineConcatto     if (name == "iand") {  // done in fptr declaration
26464ab3302SCarolineConcatto     } else if (name == "ior") {
26564ab3302SCarolineConcatto       fptr = &Scalar<T>::IOR;
26664ab3302SCarolineConcatto     } else if (name == "ieor") {
26764ab3302SCarolineConcatto       fptr = &Scalar<T>::IEOR;
26864ab3302SCarolineConcatto     } else {
26964ab3302SCarolineConcatto       common::die("missing case to fold intrinsic function %s", name.c_str());
27064ab3302SCarolineConcatto     }
27164ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T, T>(
27264ab3302SCarolineConcatto         context, std::move(funcRef), ScalarFunc<T, T, T>(fptr));
27364ab3302SCarolineConcatto   } else if (name == "ibclr" || name == "ibset" || name == "ishft" ||
27464ab3302SCarolineConcatto       name == "shifta" || name == "shiftr" || name == "shiftl") {
27564ab3302SCarolineConcatto     // Second argument can be of any kind. However, it must be smaller or
27664ab3302SCarolineConcatto     // equal than BIT_SIZE. It can be converted to Int4 to simplify.
27764ab3302SCarolineConcatto     auto fptr{&Scalar<T>::IBCLR};
27864ab3302SCarolineConcatto     if (name == "ibclr") {  // done in fprt definition
27964ab3302SCarolineConcatto     } else if (name == "ibset") {
28064ab3302SCarolineConcatto       fptr = &Scalar<T>::IBSET;
28164ab3302SCarolineConcatto     } else if (name == "ishft") {
28264ab3302SCarolineConcatto       fptr = &Scalar<T>::ISHFT;
28364ab3302SCarolineConcatto     } else if (name == "shifta") {
28464ab3302SCarolineConcatto       fptr = &Scalar<T>::SHIFTA;
28564ab3302SCarolineConcatto     } else if (name == "shiftr") {
28664ab3302SCarolineConcatto       fptr = &Scalar<T>::SHIFTR;
28764ab3302SCarolineConcatto     } else if (name == "shiftl") {
28864ab3302SCarolineConcatto       fptr = &Scalar<T>::SHIFTL;
28964ab3302SCarolineConcatto     } else {
29064ab3302SCarolineConcatto       common::die("missing case to fold intrinsic function %s", name.c_str());
29164ab3302SCarolineConcatto     }
29264ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef),
29364ab3302SCarolineConcatto         ScalarFunc<T, T, Int4>(
29464ab3302SCarolineConcatto             [&fptr](const Scalar<T> &i, const Scalar<Int4> &pos) -> Scalar<T> {
29564ab3302SCarolineConcatto               return std::invoke(fptr, i, static_cast<int>(pos.ToInt64()));
29664ab3302SCarolineConcatto             }));
29764ab3302SCarolineConcatto   } else if (name == "index" || name == "scan" || name == "verify") {
29864ab3302SCarolineConcatto     if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) {
29964ab3302SCarolineConcatto       return std::visit(
30064ab3302SCarolineConcatto           [&](const auto &kch) -> Expr<T> {
30164ab3302SCarolineConcatto             using TC = typename std::decay_t<decltype(kch)>::Result;
30264ab3302SCarolineConcatto             if (UnwrapExpr<Expr<SomeLogical>>(args[2])) {  // BACK=
30364ab3302SCarolineConcatto               return FoldElementalIntrinsic<T, TC, TC, LogicalResult>(context,
30464ab3302SCarolineConcatto                   std::move(funcRef),
30564ab3302SCarolineConcatto                   ScalarFunc<T, TC, TC, LogicalResult>{
30664ab3302SCarolineConcatto                       [&name](const Scalar<TC> &str, const Scalar<TC> &other,
30764ab3302SCarolineConcatto                           const Scalar<LogicalResult> &back) -> Scalar<T> {
30864ab3302SCarolineConcatto                         return name == "index"
30964ab3302SCarolineConcatto                             ? CharacterUtils<TC::kind>::INDEX(
31064ab3302SCarolineConcatto                                   str, other, back.IsTrue())
31164ab3302SCarolineConcatto                             : name == "scan" ? CharacterUtils<TC::kind>::SCAN(
31264ab3302SCarolineConcatto                                                    str, other, back.IsTrue())
31364ab3302SCarolineConcatto                                              : CharacterUtils<TC::kind>::VERIFY(
31464ab3302SCarolineConcatto                                                    str, other, back.IsTrue());
31564ab3302SCarolineConcatto                       }});
31664ab3302SCarolineConcatto             } else {
31764ab3302SCarolineConcatto               return FoldElementalIntrinsic<T, TC, TC>(context,
31864ab3302SCarolineConcatto                   std::move(funcRef),
31964ab3302SCarolineConcatto                   ScalarFunc<T, TC, TC>{
32064ab3302SCarolineConcatto                       [&name](const Scalar<TC> &str,
32164ab3302SCarolineConcatto                           const Scalar<TC> &other) -> Scalar<T> {
32264ab3302SCarolineConcatto                         return name == "index"
32364ab3302SCarolineConcatto                             ? CharacterUtils<TC::kind>::INDEX(str, other)
32464ab3302SCarolineConcatto                             : name == "scan"
32564ab3302SCarolineConcatto                                 ? CharacterUtils<TC::kind>::SCAN(str, other)
32664ab3302SCarolineConcatto                                 : CharacterUtils<TC::kind>::VERIFY(str, other);
32764ab3302SCarolineConcatto                       }});
32864ab3302SCarolineConcatto             }
32964ab3302SCarolineConcatto           },
33064ab3302SCarolineConcatto           charExpr->u);
33164ab3302SCarolineConcatto     } else {
33264ab3302SCarolineConcatto       DIE("first argument must be CHARACTER");
33364ab3302SCarolineConcatto     }
33464ab3302SCarolineConcatto   } else if (name == "int") {
33564ab3302SCarolineConcatto     if (auto *expr{UnwrapExpr<Expr<SomeType>>(args[0])}) {
33664ab3302SCarolineConcatto       return std::visit(
33764ab3302SCarolineConcatto           [&](auto &&x) -> Expr<T> {
33864ab3302SCarolineConcatto             using From = std::decay_t<decltype(x)>;
33964ab3302SCarolineConcatto             if constexpr (std::is_same_v<From, BOZLiteralConstant> ||
34064ab3302SCarolineConcatto                 IsNumericCategoryExpr<From>()) {
34164ab3302SCarolineConcatto               return Fold(context, ConvertToType<T>(std::move(x)));
34264ab3302SCarolineConcatto             }
34364ab3302SCarolineConcatto             DIE("int() argument type not valid");
34464ab3302SCarolineConcatto           },
34564ab3302SCarolineConcatto           std::move(expr->u));
34664ab3302SCarolineConcatto     }
34764ab3302SCarolineConcatto   } else if (name == "int_ptr_kind") {
34864ab3302SCarolineConcatto     return Expr<T>{8};
34964ab3302SCarolineConcatto   } else if (name == "kind") {
35064ab3302SCarolineConcatto     if constexpr (common::HasMember<T, IntegerTypes>) {
35164ab3302SCarolineConcatto       return Expr<T>{args[0].value().GetType()->kind()};
35264ab3302SCarolineConcatto     } else {
35364ab3302SCarolineConcatto       DIE("kind() result not integral");
35464ab3302SCarolineConcatto     }
35564ab3302SCarolineConcatto   } else if (name == "lbound") {
35664ab3302SCarolineConcatto     return LBOUND(context, std::move(funcRef));
35764ab3302SCarolineConcatto   } else if (name == "leadz" || name == "trailz" || name == "poppar" ||
35864ab3302SCarolineConcatto       name == "popcnt") {
35964ab3302SCarolineConcatto     if (auto *sn{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
36064ab3302SCarolineConcatto       return std::visit(
36164ab3302SCarolineConcatto           [&funcRef, &context, &name](const auto &n) -> Expr<T> {
36264ab3302SCarolineConcatto             using TI = typename std::decay_t<decltype(n)>::Result;
36364ab3302SCarolineConcatto             if (name == "poppar") {
36464ab3302SCarolineConcatto               return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef),
36564ab3302SCarolineConcatto                   ScalarFunc<T, TI>([](const Scalar<TI> &i) -> Scalar<T> {
36664ab3302SCarolineConcatto                     return Scalar<T>{i.POPPAR() ? 1 : 0};
36764ab3302SCarolineConcatto                   }));
36864ab3302SCarolineConcatto             }
36964ab3302SCarolineConcatto             auto fptr{&Scalar<TI>::LEADZ};
37064ab3302SCarolineConcatto             if (name == "leadz") {  // done in fptr definition
37164ab3302SCarolineConcatto             } else if (name == "trailz") {
37264ab3302SCarolineConcatto               fptr = &Scalar<TI>::TRAILZ;
37364ab3302SCarolineConcatto             } else if (name == "popcnt") {
37464ab3302SCarolineConcatto               fptr = &Scalar<TI>::POPCNT;
37564ab3302SCarolineConcatto             } else {
37664ab3302SCarolineConcatto               common::die(
37764ab3302SCarolineConcatto                   "missing case to fold intrinsic function %s", name.c_str());
37864ab3302SCarolineConcatto             }
37964ab3302SCarolineConcatto             return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef),
38064ab3302SCarolineConcatto                 ScalarFunc<T, TI>([&fptr](const Scalar<TI> &i) -> Scalar<T> {
38164ab3302SCarolineConcatto                   return Scalar<T>{std::invoke(fptr, i)};
38264ab3302SCarolineConcatto                 }));
38364ab3302SCarolineConcatto           },
38464ab3302SCarolineConcatto           sn->u);
38564ab3302SCarolineConcatto     } else {
38664ab3302SCarolineConcatto       DIE("leadz argument must be integer");
38764ab3302SCarolineConcatto     }
38864ab3302SCarolineConcatto   } else if (name == "len") {
38964ab3302SCarolineConcatto     if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) {
39064ab3302SCarolineConcatto       return std::visit(
39164ab3302SCarolineConcatto           [&](auto &kx) {
39264ab3302SCarolineConcatto             if (auto len{kx.LEN()}) {
39364ab3302SCarolineConcatto               return Fold(context, ConvertToType<T>(*std::move(len)));
39464ab3302SCarolineConcatto             } else {
39564ab3302SCarolineConcatto               return Expr<T>{std::move(funcRef)};
39664ab3302SCarolineConcatto             }
39764ab3302SCarolineConcatto           },
39864ab3302SCarolineConcatto           charExpr->u);
39964ab3302SCarolineConcatto     } else {
40064ab3302SCarolineConcatto       DIE("len() argument must be of character type");
40164ab3302SCarolineConcatto     }
40264ab3302SCarolineConcatto   } else if (name == "len_trim") {
40364ab3302SCarolineConcatto     if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) {
40464ab3302SCarolineConcatto       return std::visit(
40564ab3302SCarolineConcatto           [&](const auto &kch) -> Expr<T> {
40664ab3302SCarolineConcatto             using TC = typename std::decay_t<decltype(kch)>::Result;
40764ab3302SCarolineConcatto             return FoldElementalIntrinsic<T, TC>(context, std::move(funcRef),
40864ab3302SCarolineConcatto                 ScalarFunc<T, TC>{[](const Scalar<TC> &str) -> Scalar<T> {
40964ab3302SCarolineConcatto                   return CharacterUtils<TC::kind>::LEN_TRIM(str);
41064ab3302SCarolineConcatto                 }});
41164ab3302SCarolineConcatto           },
41264ab3302SCarolineConcatto           charExpr->u);
41364ab3302SCarolineConcatto     } else {
41464ab3302SCarolineConcatto       DIE("len_trim() argument must be of character type");
41564ab3302SCarolineConcatto     }
41664ab3302SCarolineConcatto   } else if (name == "maskl" || name == "maskr") {
41764ab3302SCarolineConcatto     // Argument can be of any kind but value has to be smaller than BIT_SIZE.
41864ab3302SCarolineConcatto     // It can be safely converted to Int4 to simplify.
41964ab3302SCarolineConcatto     const auto fptr{name == "maskl" ? &Scalar<T>::MASKL : &Scalar<T>::MASKR};
42064ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, Int4>(context, std::move(funcRef),
42164ab3302SCarolineConcatto         ScalarFunc<T, Int4>([&fptr](const Scalar<Int4> &places) -> Scalar<T> {
42264ab3302SCarolineConcatto           return fptr(static_cast<int>(places.ToInt64()));
42364ab3302SCarolineConcatto         }));
42464ab3302SCarolineConcatto   } else if (name == "max") {
42564ab3302SCarolineConcatto     return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater);
42664ab3302SCarolineConcatto   } else if (name == "maxexponent") {
42764ab3302SCarolineConcatto     if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
42864ab3302SCarolineConcatto       return std::visit(
42964ab3302SCarolineConcatto           [](const auto &x) {
43064ab3302SCarolineConcatto             using TR = typename std::decay_t<decltype(x)>::Result;
43164ab3302SCarolineConcatto             return Expr<T>{Scalar<TR>::MAXEXPONENT};
43264ab3302SCarolineConcatto           },
43364ab3302SCarolineConcatto           sx->u);
43464ab3302SCarolineConcatto     }
43564ab3302SCarolineConcatto   } else if (name == "merge") {
43664ab3302SCarolineConcatto     return FoldMerge<T>(context, std::move(funcRef));
43764ab3302SCarolineConcatto   } else if (name == "merge_bits") {
43864ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T, T, T>(
43964ab3302SCarolineConcatto         context, std::move(funcRef), &Scalar<T>::MERGE_BITS);
44064ab3302SCarolineConcatto   } else if (name == "minexponent") {
44164ab3302SCarolineConcatto     if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
44264ab3302SCarolineConcatto       return std::visit(
44364ab3302SCarolineConcatto           [](const auto &x) {
44464ab3302SCarolineConcatto             using TR = typename std::decay_t<decltype(x)>::Result;
44564ab3302SCarolineConcatto             return Expr<T>{Scalar<TR>::MINEXPONENT};
44664ab3302SCarolineConcatto           },
44764ab3302SCarolineConcatto           sx->u);
44864ab3302SCarolineConcatto     }
44964ab3302SCarolineConcatto   } else if (name == "min") {
45064ab3302SCarolineConcatto     return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
45164ab3302SCarolineConcatto   } else if (name == "mod") {
45264ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
45364ab3302SCarolineConcatto         ScalarFuncWithContext<T, T, T>(
45464ab3302SCarolineConcatto             [](FoldingContext &context, const Scalar<T> &x,
45564ab3302SCarolineConcatto                 const Scalar<T> &y) -> Scalar<T> {
45664ab3302SCarolineConcatto               auto quotRem{x.DivideSigned(y)};
45764ab3302SCarolineConcatto               if (quotRem.divisionByZero) {
45864ab3302SCarolineConcatto                 context.messages().Say("mod() by zero"_en_US);
45964ab3302SCarolineConcatto               } else if (quotRem.overflow) {
46064ab3302SCarolineConcatto                 context.messages().Say("mod() folding overflowed"_en_US);
46164ab3302SCarolineConcatto               }
46264ab3302SCarolineConcatto               return quotRem.remainder;
46364ab3302SCarolineConcatto             }));
46464ab3302SCarolineConcatto   } else if (name == "modulo") {
46564ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
46664ab3302SCarolineConcatto         ScalarFuncWithContext<T, T, T>(
46764ab3302SCarolineConcatto             [](FoldingContext &context, const Scalar<T> &x,
46864ab3302SCarolineConcatto                 const Scalar<T> &y) -> Scalar<T> {
46964ab3302SCarolineConcatto               auto result{x.MODULO(y)};
47064ab3302SCarolineConcatto               if (result.overflow) {
47164ab3302SCarolineConcatto                 context.messages().Say("modulo() folding overflowed"_en_US);
47264ab3302SCarolineConcatto               }
47364ab3302SCarolineConcatto               return result.value;
47464ab3302SCarolineConcatto             }));
47564ab3302SCarolineConcatto   } else if (name == "precision") {
47664ab3302SCarolineConcatto     if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
47764ab3302SCarolineConcatto       return Expr<T>{std::visit(
47864ab3302SCarolineConcatto           [](const auto &kx) {
47964ab3302SCarolineConcatto             return Scalar<ResultType<decltype(kx)>>::PRECISION;
48064ab3302SCarolineConcatto           },
48164ab3302SCarolineConcatto           cx->u)};
48264ab3302SCarolineConcatto     } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
48364ab3302SCarolineConcatto       return Expr<T>{std::visit(
48464ab3302SCarolineConcatto           [](const auto &kx) {
48564ab3302SCarolineConcatto             return Scalar<typename ResultType<decltype(kx)>::Part>::PRECISION;
48664ab3302SCarolineConcatto           },
48764ab3302SCarolineConcatto           cx->u)};
48864ab3302SCarolineConcatto     }
48964ab3302SCarolineConcatto   } else if (name == "radix") {
49064ab3302SCarolineConcatto     return Expr<T>{2};
49164ab3302SCarolineConcatto   } else if (name == "range") {
49264ab3302SCarolineConcatto     if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
49364ab3302SCarolineConcatto       return Expr<T>{std::visit(
49464ab3302SCarolineConcatto           [](const auto &kx) {
49564ab3302SCarolineConcatto             return Scalar<ResultType<decltype(kx)>>::RANGE;
49664ab3302SCarolineConcatto           },
49764ab3302SCarolineConcatto           cx->u)};
49864ab3302SCarolineConcatto     } else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) {
49964ab3302SCarolineConcatto       return Expr<T>{std::visit(
50064ab3302SCarolineConcatto           [](const auto &kx) {
50164ab3302SCarolineConcatto             return Scalar<ResultType<decltype(kx)>>::RANGE;
50264ab3302SCarolineConcatto           },
50364ab3302SCarolineConcatto           cx->u)};
50464ab3302SCarolineConcatto     } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) {
50564ab3302SCarolineConcatto       return Expr<T>{std::visit(
50664ab3302SCarolineConcatto           [](const auto &kx) {
50764ab3302SCarolineConcatto             return Scalar<typename ResultType<decltype(kx)>::Part>::RANGE;
50864ab3302SCarolineConcatto           },
50964ab3302SCarolineConcatto           cx->u)};
51064ab3302SCarolineConcatto     }
51164ab3302SCarolineConcatto   } else if (name == "rank") {
512*e0ca7b44SSteve Scalpone     if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
513*e0ca7b44SSteve Scalpone       if (auto named{ExtractNamedEntity(*array)}) {
514*e0ca7b44SSteve Scalpone         const Symbol &symbol{named->GetLastSymbol()};
515*e0ca7b44SSteve Scalpone         if (semantics::IsAssumedRankArray(symbol)) {
516*e0ca7b44SSteve Scalpone           // DescriptorInquiry can only be placed in expression of kind
517*e0ca7b44SSteve Scalpone           // DescriptorInquiry::Result::kind.
518*e0ca7b44SSteve Scalpone           return ConvertToType<T>(Expr<
519*e0ca7b44SSteve Scalpone               Type<TypeCategory::Integer, DescriptorInquiry::Result::kind>>{
520*e0ca7b44SSteve Scalpone               DescriptorInquiry{*named, DescriptorInquiry::Field::Rank}});
521*e0ca7b44SSteve Scalpone         }
522*e0ca7b44SSteve Scalpone       }
523*e0ca7b44SSteve Scalpone       return Expr<T>{args[0].value().Rank()};
524*e0ca7b44SSteve Scalpone     }
52564ab3302SCarolineConcatto     return Expr<T>{args[0].value().Rank()};
52664ab3302SCarolineConcatto   } else if (name == "selected_char_kind") {
52764ab3302SCarolineConcatto     if (const auto *chCon{UnwrapExpr<Constant<TypeOf<std::string>>>(args[0])}) {
52864ab3302SCarolineConcatto       if (std::optional<std::string> value{chCon->GetScalarValue()}) {
52964ab3302SCarolineConcatto         int defaultKind{
53064ab3302SCarolineConcatto             context.defaults().GetDefaultKind(TypeCategory::Character)};
53164ab3302SCarolineConcatto         return Expr<T>{SelectedCharKind(*value, defaultKind)};
53264ab3302SCarolineConcatto       }
53364ab3302SCarolineConcatto     }
53464ab3302SCarolineConcatto   } else if (name == "selected_int_kind") {
53564ab3302SCarolineConcatto     if (auto p{GetInt64Arg(args[0])}) {
53664ab3302SCarolineConcatto       return Expr<T>{SelectedIntKind(*p)};
53764ab3302SCarolineConcatto     }
53864ab3302SCarolineConcatto   } else if (name == "selected_real_kind") {
53964ab3302SCarolineConcatto     if (auto p{GetInt64ArgOr(args[0], 0)}) {
54064ab3302SCarolineConcatto       if (auto r{GetInt64ArgOr(args[1], 0)}) {
54164ab3302SCarolineConcatto         if (auto radix{GetInt64ArgOr(args[2], 2)}) {
54264ab3302SCarolineConcatto           return Expr<T>{SelectedRealKind(*p, *r, *radix)};
54364ab3302SCarolineConcatto         }
54464ab3302SCarolineConcatto       }
54564ab3302SCarolineConcatto     }
54664ab3302SCarolineConcatto   } else if (name == "shape") {
54764ab3302SCarolineConcatto     if (auto shape{GetShape(context, args[0])}) {
54864ab3302SCarolineConcatto       if (auto shapeExpr{AsExtentArrayExpr(*shape)}) {
54964ab3302SCarolineConcatto         return Fold(context, ConvertToType<T>(std::move(*shapeExpr)));
55064ab3302SCarolineConcatto       }
55164ab3302SCarolineConcatto     }
55264ab3302SCarolineConcatto   } else if (name == "sign") {
55364ab3302SCarolineConcatto     return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef),
55464ab3302SCarolineConcatto         ScalarFunc<T, T, T>(
55564ab3302SCarolineConcatto             [&context](const Scalar<T> &j, const Scalar<T> &k) -> Scalar<T> {
55664ab3302SCarolineConcatto               typename Scalar<T>::ValueWithOverflow result{j.SIGN(k)};
55764ab3302SCarolineConcatto               if (result.overflow) {
55864ab3302SCarolineConcatto                 context.messages().Say(
55964ab3302SCarolineConcatto                     "sign(integer(kind=%d)) folding overflowed"_en_US, KIND);
56064ab3302SCarolineConcatto               }
56164ab3302SCarolineConcatto               return result.value;
56264ab3302SCarolineConcatto             }));
56364ab3302SCarolineConcatto   } else if (name == "size") {
56464ab3302SCarolineConcatto     if (auto shape{GetShape(context, args[0])}) {
56564ab3302SCarolineConcatto       if (auto &dimArg{args[1]}) {  // DIM= is present, get one extent
56664ab3302SCarolineConcatto         if (auto dim{GetInt64Arg(args[1])}) {
56764ab3302SCarolineConcatto           int rank{GetRank(*shape)};
56864ab3302SCarolineConcatto           if (*dim >= 1 && *dim <= rank) {
56964ab3302SCarolineConcatto             if (auto &extent{shape->at(*dim - 1)}) {
57064ab3302SCarolineConcatto               return Fold(context, ConvertToType<T>(std::move(*extent)));
57164ab3302SCarolineConcatto             }
57264ab3302SCarolineConcatto           } else {
57364ab3302SCarolineConcatto             context.messages().Say(
57464ab3302SCarolineConcatto                 "size(array,dim=%jd) dimension is out of range for rank-%d array"_en_US,
57564ab3302SCarolineConcatto                 static_cast<std::intmax_t>(*dim), static_cast<int>(rank));
57664ab3302SCarolineConcatto           }
57764ab3302SCarolineConcatto         }
57864ab3302SCarolineConcatto       } else if (auto extents{common::AllElementsPresent(std::move(*shape))}) {
57964ab3302SCarolineConcatto         // DIM= is absent; compute PRODUCT(SHAPE())
58064ab3302SCarolineConcatto         ExtentExpr product{1};
58164ab3302SCarolineConcatto         for (auto &&extent : std::move(*extents)) {
58264ab3302SCarolineConcatto           product = std::move(product) * std::move(extent);
58364ab3302SCarolineConcatto         }
58464ab3302SCarolineConcatto         return Expr<T>{ConvertToType<T>(Fold(context, std::move(product)))};
58564ab3302SCarolineConcatto       }
58664ab3302SCarolineConcatto     }
58764ab3302SCarolineConcatto   } else if (name == "ubound") {
58864ab3302SCarolineConcatto     return UBOUND(context, std::move(funcRef));
58964ab3302SCarolineConcatto   }
59064ab3302SCarolineConcatto   // TODO:
59164ab3302SCarolineConcatto   // cshift, dot_product, eoshift,
59264ab3302SCarolineConcatto   // findloc, iall, iany, iparity, ibits, image_status, ishftc,
59364ab3302SCarolineConcatto   // matmul, maxloc, maxval,
59464ab3302SCarolineConcatto   // minloc, minval, not, pack, product, reduce,
59564ab3302SCarolineConcatto   // sign, spread, sum, transfer, transpose, unpack
59664ab3302SCarolineConcatto   return Expr<T>{std::move(funcRef)};
59764ab3302SCarolineConcatto }
59864ab3302SCarolineConcatto 
59964ab3302SCarolineConcatto // Substitute a bare type parameter reference with its value if it has one now
60064ab3302SCarolineConcatto template<int KIND>
60164ab3302SCarolineConcatto Expr<Type<TypeCategory::Integer, KIND>> FoldOperation(
60264ab3302SCarolineConcatto     FoldingContext &context, TypeParamInquiry<KIND> &&inquiry) {
60364ab3302SCarolineConcatto   using IntKIND = Type<TypeCategory::Integer, KIND>;
60464ab3302SCarolineConcatto   if (!inquiry.base()) {
60564ab3302SCarolineConcatto     // A "bare" type parameter: replace with its value, if that's now known.
60664ab3302SCarolineConcatto     if (const auto *pdt{context.pdtInstance()}) {
60764ab3302SCarolineConcatto       if (const semantics::Scope * scope{context.pdtInstance()->scope()}) {
60864ab3302SCarolineConcatto         auto iter{scope->find(inquiry.parameter().name())};
60964ab3302SCarolineConcatto         if (iter != scope->end()) {
61064ab3302SCarolineConcatto           const Symbol &symbol{*iter->second};
61164ab3302SCarolineConcatto           const auto *details{symbol.detailsIf<semantics::TypeParamDetails>()};
61264ab3302SCarolineConcatto           if (details && details->init()) {
61364ab3302SCarolineConcatto             Expr<SomeInteger> expr{*details->init()};
61464ab3302SCarolineConcatto             return Fold(context,
61564ab3302SCarolineConcatto                 Expr<IntKIND>{
61664ab3302SCarolineConcatto                     Convert<IntKIND, TypeCategory::Integer>(std::move(expr))});
61764ab3302SCarolineConcatto           }
61864ab3302SCarolineConcatto         }
61964ab3302SCarolineConcatto       }
62064ab3302SCarolineConcatto       if (const auto *value{pdt->FindParameter(inquiry.parameter().name())}) {
62164ab3302SCarolineConcatto         if (value->isExplicit()) {
62264ab3302SCarolineConcatto           return Fold(context,
62364ab3302SCarolineConcatto               Expr<IntKIND>{Convert<IntKIND, TypeCategory::Integer>(
62464ab3302SCarolineConcatto                   Expr<SomeInteger>{value->GetExplicit().value()})});
62564ab3302SCarolineConcatto         }
62664ab3302SCarolineConcatto       }
62764ab3302SCarolineConcatto     }
62864ab3302SCarolineConcatto   }
62964ab3302SCarolineConcatto   return Expr<IntKIND>{std::move(inquiry)};
63064ab3302SCarolineConcatto }
63164ab3302SCarolineConcatto 
63264ab3302SCarolineConcatto std::optional<std::int64_t> ToInt64(const Expr<SomeInteger> &expr) {
63364ab3302SCarolineConcatto   return std::visit(
63464ab3302SCarolineConcatto       [](const auto &kindExpr) { return ToInt64(kindExpr); }, expr.u);
63564ab3302SCarolineConcatto }
63664ab3302SCarolineConcatto 
63764ab3302SCarolineConcatto std::optional<std::int64_t> ToInt64(const Expr<SomeType> &expr) {
63864ab3302SCarolineConcatto   if (const auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(expr)}) {
63964ab3302SCarolineConcatto     return ToInt64(*intExpr);
64064ab3302SCarolineConcatto   } else {
64164ab3302SCarolineConcatto     return std::nullopt;
64264ab3302SCarolineConcatto   }
64364ab3302SCarolineConcatto }
64464ab3302SCarolineConcatto 
64564ab3302SCarolineConcatto FOR_EACH_INTEGER_KIND(template class ExpressionBase, )
64664ab3302SCarolineConcatto template class ExpressionBase<SomeInteger>;
64764ab3302SCarolineConcatto }
648