xref: /llvm-project/flang/lib/Evaluate/fold-logical.cpp (revision c28a7c1efd89d3dbee5f7212313f836855dd08fd)
164ab3302SCarolineConcatto //===-- lib/Evaluate/fold-logical.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"
11503c085eSpeter klausler #include "fold-reduction.h"
1264ab3302SCarolineConcatto #include "flang/Evaluate/check-expression.h"
1367f15e76SPeter Klausler #include "flang/Runtime/magic-numbers.h"
1464ab3302SCarolineConcatto 
1564ab3302SCarolineConcatto namespace Fortran::evaluate {
1664ab3302SCarolineConcatto 
177709f12eSTarun Prabhu template <typename T>
187709f12eSTarun Prabhu static std::optional<Expr<SomeType>> ZeroExtend(const Constant<T> &c) {
197709f12eSTarun Prabhu   std::vector<Scalar<LargestInt>> exts;
207709f12eSTarun Prabhu   for (const auto &v : c.values()) {
217709f12eSTarun Prabhu     exts.push_back(Scalar<LargestInt>::ConvertUnsigned(v).value);
227709f12eSTarun Prabhu   }
237709f12eSTarun Prabhu   return AsGenericExpr(
247709f12eSTarun Prabhu       Constant<LargestInt>(std::move(exts), ConstantSubscripts(c.shape())));
257709f12eSTarun Prabhu }
267709f12eSTarun Prabhu 
272f2fce8eSTarun Prabhu // for ALL, ANY & PARITY
28503c085eSpeter klausler template <typename T>
292f2fce8eSTarun Prabhu static Expr<T> FoldAllAnyParity(FoldingContext &context, FunctionRef<T> &&ref,
30503c085eSpeter klausler     Scalar<T> (Scalar<T>::*operation)(const Scalar<T> &) const,
31503c085eSpeter klausler     Scalar<T> identity) {
32503c085eSpeter klausler   static_assert(T::category == TypeCategory::Logical);
3382568675SPeter Klausler   std::optional<int> dim;
3482e1e412SPeter Klausler   if (std::optional<ArrayAndMask<T>> arrayAndMask{
3582e1e412SPeter Klausler           ProcessReductionArgs<T>(context, ref.arguments(), dim,
36503c085eSpeter klausler               /*ARRAY(MASK)=*/0, /*DIM=*/1)}) {
3782e1e412SPeter Klausler     OperationAccumulator accumulator{arrayAndMask->array, operation};
3882e1e412SPeter Klausler     return Expr<T>{DoReduction<T>(
3982e1e412SPeter Klausler         arrayAndMask->array, arrayAndMask->mask, dim, identity, accumulator)};
40503c085eSpeter klausler   }
41503c085eSpeter klausler   return Expr<T>{std::move(ref)};
42503c085eSpeter klausler }
43503c085eSpeter klausler 
441444e5acSPeter Klausler // OUT_OF_RANGE(x,mold[,round]) references are entirely rewritten here into
451444e5acSPeter Klausler // expressions, which are then folded into constants when 'x' and 'round'
461444e5acSPeter Klausler // are constant.  It is guaranteed that 'x' is evaluated at most once.
47fc97d2e6SPeter Klausler // TODO: unsigned
481444e5acSPeter Klausler 
491444e5acSPeter Klausler template <int X_RKIND, int MOLD_IKIND>
501444e5acSPeter Klausler Expr<SomeReal> RealToIntBoundHelper(bool round, bool negate) {
511444e5acSPeter Klausler   using RType = Type<TypeCategory::Real, X_RKIND>;
521444e5acSPeter Klausler   using RealType = Scalar<RType>;
531444e5acSPeter Klausler   using IntType = Scalar<Type<TypeCategory::Integer, MOLD_IKIND>>;
541444e5acSPeter Klausler   RealType result{}; // 0.
551444e5acSPeter Klausler   common::RoundingMode roundingMode{round
561444e5acSPeter Klausler           ? common::RoundingMode::TiesAwayFromZero
571444e5acSPeter Klausler           : common::RoundingMode::ToZero};
581444e5acSPeter Klausler   // Add decreasing powers of two to the result to find the largest magnitude
591444e5acSPeter Klausler   // value that can be converted to the integer type without overflow.
601444e5acSPeter Klausler   RealType at{RealType::FromInteger(IntType{negate ? -1 : 1}).value};
611444e5acSPeter Klausler   bool decrement{true};
621444e5acSPeter Klausler   while (!at.template ToInteger<IntType>(roundingMode)
631444e5acSPeter Klausler               .flags.test(RealFlag::Overflow)) {
641444e5acSPeter Klausler     auto tmp{at.SCALE(IntType{1})};
651444e5acSPeter Klausler     if (tmp.flags.test(RealFlag::Overflow)) {
661444e5acSPeter Klausler       decrement = false;
671444e5acSPeter Klausler       break;
681444e5acSPeter Klausler     }
691444e5acSPeter Klausler     at = tmp.value;
701444e5acSPeter Klausler   }
711444e5acSPeter Klausler   while (true) {
721444e5acSPeter Klausler     if (decrement) {
731444e5acSPeter Klausler       at = at.SCALE(IntType{-1}).value;
741444e5acSPeter Klausler     } else {
751444e5acSPeter Klausler       decrement = true;
761444e5acSPeter Klausler     }
771444e5acSPeter Klausler     auto tmp{at.Add(result)};
781444e5acSPeter Klausler     if (tmp.flags.test(RealFlag::Inexact)) {
791444e5acSPeter Klausler       break;
801444e5acSPeter Klausler     } else if (!tmp.value.template ToInteger<IntType>(roundingMode)
811444e5acSPeter Klausler                     .flags.test(RealFlag::Overflow)) {
821444e5acSPeter Klausler       result = tmp.value;
831444e5acSPeter Klausler     }
841444e5acSPeter Klausler   }
851444e5acSPeter Klausler   return AsCategoryExpr(Constant<RType>{std::move(result)});
861444e5acSPeter Klausler }
871444e5acSPeter Klausler 
881444e5acSPeter Klausler static Expr<SomeReal> RealToIntBound(
891444e5acSPeter Klausler     int xRKind, int moldIKind, bool round, bool negate) {
901444e5acSPeter Klausler   switch (xRKind) {
911444e5acSPeter Klausler #define ICASES(RK) \
921444e5acSPeter Klausler   switch (moldIKind) { \
931444e5acSPeter Klausler   case 1: \
941444e5acSPeter Klausler     return RealToIntBoundHelper<RK, 1>(round, negate); \
951444e5acSPeter Klausler     break; \
961444e5acSPeter Klausler   case 2: \
971444e5acSPeter Klausler     return RealToIntBoundHelper<RK, 2>(round, negate); \
981444e5acSPeter Klausler     break; \
991444e5acSPeter Klausler   case 4: \
1001444e5acSPeter Klausler     return RealToIntBoundHelper<RK, 4>(round, negate); \
1011444e5acSPeter Klausler     break; \
1021444e5acSPeter Klausler   case 8: \
1031444e5acSPeter Klausler     return RealToIntBoundHelper<RK, 8>(round, negate); \
1041444e5acSPeter Klausler     break; \
1051444e5acSPeter Klausler   case 16: \
1061444e5acSPeter Klausler     return RealToIntBoundHelper<RK, 16>(round, negate); \
1071444e5acSPeter Klausler     break; \
1081444e5acSPeter Klausler   } \
1091444e5acSPeter Klausler   break
1101444e5acSPeter Klausler   case 2:
1111444e5acSPeter Klausler     ICASES(2);
1121444e5acSPeter Klausler     break;
1131444e5acSPeter Klausler   case 3:
1141444e5acSPeter Klausler     ICASES(3);
1151444e5acSPeter Klausler     break;
1161444e5acSPeter Klausler   case 4:
1171444e5acSPeter Klausler     ICASES(4);
1181444e5acSPeter Klausler     break;
1191444e5acSPeter Klausler   case 8:
1201444e5acSPeter Klausler     ICASES(8);
1211444e5acSPeter Klausler     break;
1221444e5acSPeter Klausler   case 10:
1231444e5acSPeter Klausler     ICASES(10);
1241444e5acSPeter Klausler     break;
1251444e5acSPeter Klausler   case 16:
1261444e5acSPeter Klausler     ICASES(16);
1271444e5acSPeter Klausler     break;
1281444e5acSPeter Klausler   }
1291444e5acSPeter Klausler   DIE("RealToIntBound: no case");
1301444e5acSPeter Klausler #undef ICASES
1311444e5acSPeter Klausler }
1321444e5acSPeter Klausler 
1331444e5acSPeter Klausler class RealToIntLimitHelper {
1341444e5acSPeter Klausler public:
1351444e5acSPeter Klausler   using Result = std::optional<Expr<SomeReal>>;
1361444e5acSPeter Klausler   using Types = RealTypes;
1371444e5acSPeter Klausler   RealToIntLimitHelper(
1381444e5acSPeter Klausler       FoldingContext &context, Expr<SomeReal> &&hi, Expr<SomeReal> &lo)
1391444e5acSPeter Klausler       : context_{context}, hi_{std::move(hi)}, lo_{lo} {}
1401444e5acSPeter Klausler   template <typename T> Result Test() {
1411444e5acSPeter Klausler     if (UnwrapExpr<Expr<T>>(hi_)) {
1421444e5acSPeter Klausler       bool promote{T::kind < 16};
1431444e5acSPeter Klausler       Result constResult;
1441444e5acSPeter Klausler       if (auto hiV{GetScalarConstantValue<T>(hi_)}) {
1451444e5acSPeter Klausler         auto loV{GetScalarConstantValue<T>(lo_)};
1461444e5acSPeter Klausler         CHECK(loV.has_value());
1471444e5acSPeter Klausler         auto diff{hiV->Subtract(*loV, Rounding{common::RoundingMode::ToZero})};
1481444e5acSPeter Klausler         promote = promote &&
1491444e5acSPeter Klausler             (diff.flags.test(RealFlag::Overflow) ||
1501444e5acSPeter Klausler                 diff.flags.test(RealFlag::Inexact));
1511444e5acSPeter Klausler         constResult = AsCategoryExpr(Constant<T>{std::move(diff.value)});
1521444e5acSPeter Klausler       }
1531444e5acSPeter Klausler       if (promote) {
1541444e5acSPeter Klausler         constexpr int nextKind{T::kind < 4 ? 4 : T::kind == 4 ? 8 : 16};
1551444e5acSPeter Klausler         using T2 = Type<TypeCategory::Real, nextKind>;
1561444e5acSPeter Klausler         hi_ = Expr<SomeReal>{Fold(context_, ConvertToType<T2>(std::move(hi_)))};
1571444e5acSPeter Klausler         lo_ = Expr<SomeReal>{Fold(context_, ConvertToType<T2>(std::move(lo_)))};
1581444e5acSPeter Klausler         if (constResult) {
1591444e5acSPeter Klausler           // Use promoted constants on next iteration of SearchTypes
1601444e5acSPeter Klausler           return std::nullopt;
1611444e5acSPeter Klausler         }
1621444e5acSPeter Klausler       }
1631444e5acSPeter Klausler       if (constResult) {
1641444e5acSPeter Klausler         return constResult;
1651444e5acSPeter Klausler       } else {
1661444e5acSPeter Klausler         return AsCategoryExpr(std::move(hi_) - Expr<SomeReal>{lo_});
1671444e5acSPeter Klausler       }
1681444e5acSPeter Klausler     } else {
1691444e5acSPeter Klausler       return std::nullopt;
1701444e5acSPeter Klausler     }
1711444e5acSPeter Klausler   }
1721444e5acSPeter Klausler 
1731444e5acSPeter Klausler private:
1741444e5acSPeter Klausler   FoldingContext &context_;
1751444e5acSPeter Klausler   Expr<SomeReal> hi_;
1761444e5acSPeter Klausler   Expr<SomeReal> &lo_;
1771444e5acSPeter Klausler };
1781444e5acSPeter Klausler 
1791444e5acSPeter Klausler static std::optional<Expr<SomeReal>> RealToIntLimit(
1801444e5acSPeter Klausler     FoldingContext &context, Expr<SomeReal> &&hi, Expr<SomeReal> &lo) {
1811444e5acSPeter Klausler   return common::SearchTypes(RealToIntLimitHelper{context, std::move(hi), lo});
1821444e5acSPeter Klausler }
1831444e5acSPeter Klausler 
1841444e5acSPeter Klausler // RealToRealBounds() returns a pair (HUGE(x),REAL(HUGE(mold),KIND(x)))
1851444e5acSPeter Klausler // when REAL(HUGE(x),KIND(mold)) overflows, and std::nullopt otherwise.
1861444e5acSPeter Klausler template <int X_RKIND, int MOLD_RKIND>
1871444e5acSPeter Klausler std::optional<std::pair<Expr<SomeReal>, Expr<SomeReal>>>
1881444e5acSPeter Klausler RealToRealBoundsHelper() {
1891444e5acSPeter Klausler   using RType = Type<TypeCategory::Real, X_RKIND>;
1901444e5acSPeter Klausler   using RealType = Scalar<RType>;
1911444e5acSPeter Klausler   using MoldRealType = Scalar<Type<TypeCategory::Real, MOLD_RKIND>>;
1921444e5acSPeter Klausler   if (!MoldRealType::Convert(RealType::HUGE()).flags.test(RealFlag::Overflow)) {
1931444e5acSPeter Klausler     return std::nullopt;
1941444e5acSPeter Klausler   } else {
1951444e5acSPeter Klausler     return std::make_pair(AsCategoryExpr(Constant<RType>{
1961444e5acSPeter Klausler                               RealType::Convert(MoldRealType::HUGE()).value}),
1971444e5acSPeter Klausler         AsCategoryExpr(Constant<RType>{RealType::HUGE()}));
1981444e5acSPeter Klausler   }
1991444e5acSPeter Klausler }
2001444e5acSPeter Klausler 
2011444e5acSPeter Klausler static std::optional<std::pair<Expr<SomeReal>, Expr<SomeReal>>>
2021444e5acSPeter Klausler RealToRealBounds(int xRKind, int moldRKind) {
2031444e5acSPeter Klausler   switch (xRKind) {
2041444e5acSPeter Klausler #define RCASES(RK) \
2051444e5acSPeter Klausler   switch (moldRKind) { \
2061444e5acSPeter Klausler   case 2: \
2071444e5acSPeter Klausler     return RealToRealBoundsHelper<RK, 2>(); \
2081444e5acSPeter Klausler     break; \
2091444e5acSPeter Klausler   case 3: \
2101444e5acSPeter Klausler     return RealToRealBoundsHelper<RK, 3>(); \
2111444e5acSPeter Klausler     break; \
2121444e5acSPeter Klausler   case 4: \
2131444e5acSPeter Klausler     return RealToRealBoundsHelper<RK, 4>(); \
2141444e5acSPeter Klausler     break; \
2151444e5acSPeter Klausler   case 8: \
2161444e5acSPeter Klausler     return RealToRealBoundsHelper<RK, 8>(); \
2171444e5acSPeter Klausler     break; \
2181444e5acSPeter Klausler   case 10: \
2191444e5acSPeter Klausler     return RealToRealBoundsHelper<RK, 10>(); \
2201444e5acSPeter Klausler     break; \
2211444e5acSPeter Klausler   case 16: \
2221444e5acSPeter Klausler     return RealToRealBoundsHelper<RK, 16>(); \
2231444e5acSPeter Klausler     break; \
2241444e5acSPeter Klausler   } \
2251444e5acSPeter Klausler   break
2261444e5acSPeter Klausler   case 2:
2271444e5acSPeter Klausler     RCASES(2);
2281444e5acSPeter Klausler     break;
2291444e5acSPeter Klausler   case 3:
2301444e5acSPeter Klausler     RCASES(3);
2311444e5acSPeter Klausler     break;
2321444e5acSPeter Klausler   case 4:
2331444e5acSPeter Klausler     RCASES(4);
2341444e5acSPeter Klausler     break;
2351444e5acSPeter Klausler   case 8:
2361444e5acSPeter Klausler     RCASES(8);
2371444e5acSPeter Klausler     break;
2381444e5acSPeter Klausler   case 10:
2391444e5acSPeter Klausler     RCASES(10);
2401444e5acSPeter Klausler     break;
2411444e5acSPeter Klausler   case 16:
2421444e5acSPeter Klausler     RCASES(16);
2431444e5acSPeter Klausler     break;
2441444e5acSPeter Klausler   }
2451444e5acSPeter Klausler   DIE("RealToRealBounds: no case");
2461444e5acSPeter Klausler #undef RCASES
2471444e5acSPeter Klausler }
2481444e5acSPeter Klausler 
2491444e5acSPeter Klausler template <int X_IKIND, int MOLD_RKIND>
2501444e5acSPeter Klausler std::optional<Expr<SomeInteger>> IntToRealBoundHelper(bool negate) {
2511444e5acSPeter Klausler   using IType = Type<TypeCategory::Integer, X_IKIND>;
2521444e5acSPeter Klausler   using IntType = Scalar<IType>;
2531444e5acSPeter Klausler   using RealType = Scalar<Type<TypeCategory::Real, MOLD_RKIND>>;
2541444e5acSPeter Klausler   IntType result{}; // 0
2551444e5acSPeter Klausler   while (true) {
2561444e5acSPeter Klausler     std::optional<IntType> next;
2571444e5acSPeter Klausler     for (int bit{0}; bit < IntType::bits; ++bit) {
2581444e5acSPeter Klausler       IntType power{IntType{}.IBSET(bit)};
2591444e5acSPeter Klausler       if (power.IsNegative()) {
2601444e5acSPeter Klausler         if (!negate) {
2611444e5acSPeter Klausler           break;
2621444e5acSPeter Klausler         }
2631444e5acSPeter Klausler       } else if (negate) {
2641444e5acSPeter Klausler         power = power.Negate().value;
2651444e5acSPeter Klausler       }
2661444e5acSPeter Klausler       auto tmp{power.AddSigned(result)};
2671444e5acSPeter Klausler       if (tmp.overflow ||
2681444e5acSPeter Klausler           RealType::FromInteger(tmp.value).flags.test(RealFlag::Overflow)) {
2691444e5acSPeter Klausler         break;
2701444e5acSPeter Klausler       }
2711444e5acSPeter Klausler       next = tmp.value;
2721444e5acSPeter Klausler     }
2731444e5acSPeter Klausler     if (next) {
2741444e5acSPeter Klausler       CHECK(result.CompareSigned(*next) != Ordering::Equal);
2751444e5acSPeter Klausler       result = *next;
2761444e5acSPeter Klausler     } else {
2771444e5acSPeter Klausler       break;
2781444e5acSPeter Klausler     }
2791444e5acSPeter Klausler   }
2801444e5acSPeter Klausler   if (result.CompareSigned(IntType::HUGE()) == Ordering::Equal) {
2811444e5acSPeter Klausler     return std::nullopt;
2821444e5acSPeter Klausler   } else {
2831444e5acSPeter Klausler     return AsCategoryExpr(Constant<IType>{std::move(result)});
2841444e5acSPeter Klausler   }
2851444e5acSPeter Klausler }
2861444e5acSPeter Klausler 
2871444e5acSPeter Klausler static std::optional<Expr<SomeInteger>> IntToRealBound(
2881444e5acSPeter Klausler     int xIKind, int moldRKind, bool negate) {
2891444e5acSPeter Klausler   switch (xIKind) {
2901444e5acSPeter Klausler #define RCASES(IK) \
2911444e5acSPeter Klausler   switch (moldRKind) { \
2921444e5acSPeter Klausler   case 2: \
2931444e5acSPeter Klausler     return IntToRealBoundHelper<IK, 2>(negate); \
2941444e5acSPeter Klausler     break; \
2951444e5acSPeter Klausler   case 3: \
2961444e5acSPeter Klausler     return IntToRealBoundHelper<IK, 3>(negate); \
2971444e5acSPeter Klausler     break; \
2981444e5acSPeter Klausler   case 4: \
2991444e5acSPeter Klausler     return IntToRealBoundHelper<IK, 4>(negate); \
3001444e5acSPeter Klausler     break; \
3011444e5acSPeter Klausler   case 8: \
3021444e5acSPeter Klausler     return IntToRealBoundHelper<IK, 8>(negate); \
3031444e5acSPeter Klausler     break; \
3041444e5acSPeter Klausler   case 10: \
3051444e5acSPeter Klausler     return IntToRealBoundHelper<IK, 10>(negate); \
3061444e5acSPeter Klausler     break; \
3071444e5acSPeter Klausler   case 16: \
3081444e5acSPeter Klausler     return IntToRealBoundHelper<IK, 16>(negate); \
3091444e5acSPeter Klausler     break; \
3101444e5acSPeter Klausler   } \
3111444e5acSPeter Klausler   break
3121444e5acSPeter Klausler   case 1:
3131444e5acSPeter Klausler     RCASES(1);
3141444e5acSPeter Klausler     break;
3151444e5acSPeter Klausler   case 2:
3161444e5acSPeter Klausler     RCASES(2);
3171444e5acSPeter Klausler     break;
3181444e5acSPeter Klausler   case 4:
3191444e5acSPeter Klausler     RCASES(4);
3201444e5acSPeter Klausler     break;
3211444e5acSPeter Klausler   case 8:
3221444e5acSPeter Klausler     RCASES(8);
3231444e5acSPeter Klausler     break;
3241444e5acSPeter Klausler   case 16:
3251444e5acSPeter Klausler     RCASES(16);
3261444e5acSPeter Klausler     break;
3271444e5acSPeter Klausler   }
3281444e5acSPeter Klausler   DIE("IntToRealBound: no case");
3291444e5acSPeter Klausler #undef RCASES
3301444e5acSPeter Klausler }
3311444e5acSPeter Klausler 
3321444e5acSPeter Klausler template <int X_IKIND, int MOLD_IKIND>
3331444e5acSPeter Klausler std::optional<Expr<SomeInteger>> IntToIntBoundHelper() {
3341444e5acSPeter Klausler   if constexpr (X_IKIND <= MOLD_IKIND) {
3351444e5acSPeter Klausler     return std::nullopt;
3361444e5acSPeter Klausler   } else {
3371444e5acSPeter Klausler     using XIType = Type<TypeCategory::Integer, X_IKIND>;
3381444e5acSPeter Klausler     using IntegerType = Scalar<XIType>;
3391444e5acSPeter Klausler     using MoldIType = Type<TypeCategory::Integer, MOLD_IKIND>;
3401444e5acSPeter Klausler     using MoldIntegerType = Scalar<MoldIType>;
3411444e5acSPeter Klausler     return AsCategoryExpr(Constant<XIType>{
3421444e5acSPeter Klausler         IntegerType::ConvertSigned(MoldIntegerType::HUGE()).value});
3431444e5acSPeter Klausler   }
3441444e5acSPeter Klausler }
3451444e5acSPeter Klausler 
3461444e5acSPeter Klausler static std::optional<Expr<SomeInteger>> IntToIntBound(
3471444e5acSPeter Klausler     int xIKind, int moldIKind) {
3481444e5acSPeter Klausler   switch (xIKind) {
3491444e5acSPeter Klausler #define ICASES(IK) \
3501444e5acSPeter Klausler   switch (moldIKind) { \
3511444e5acSPeter Klausler   case 1: \
3521444e5acSPeter Klausler     return IntToIntBoundHelper<IK, 1>(); \
3531444e5acSPeter Klausler     break; \
3541444e5acSPeter Klausler   case 2: \
3551444e5acSPeter Klausler     return IntToIntBoundHelper<IK, 2>(); \
3561444e5acSPeter Klausler     break; \
3571444e5acSPeter Klausler   case 4: \
3581444e5acSPeter Klausler     return IntToIntBoundHelper<IK, 4>(); \
3591444e5acSPeter Klausler     break; \
3601444e5acSPeter Klausler   case 8: \
3611444e5acSPeter Klausler     return IntToIntBoundHelper<IK, 8>(); \
3621444e5acSPeter Klausler     break; \
3631444e5acSPeter Klausler   case 16: \
3641444e5acSPeter Klausler     return IntToIntBoundHelper<IK, 16>(); \
3651444e5acSPeter Klausler     break; \
3661444e5acSPeter Klausler   } \
3671444e5acSPeter Klausler   break
3681444e5acSPeter Klausler   case 1:
3691444e5acSPeter Klausler     ICASES(1);
3701444e5acSPeter Klausler     break;
3711444e5acSPeter Klausler   case 2:
3721444e5acSPeter Klausler     ICASES(2);
3731444e5acSPeter Klausler     break;
3741444e5acSPeter Klausler   case 4:
3751444e5acSPeter Klausler     ICASES(4);
3761444e5acSPeter Klausler     break;
3771444e5acSPeter Klausler   case 8:
3781444e5acSPeter Klausler     ICASES(8);
3791444e5acSPeter Klausler     break;
3801444e5acSPeter Klausler   case 16:
3811444e5acSPeter Klausler     ICASES(16);
3821444e5acSPeter Klausler     break;
3831444e5acSPeter Klausler   }
3841444e5acSPeter Klausler   DIE("IntToIntBound: no case");
3851444e5acSPeter Klausler #undef ICASES
3861444e5acSPeter Klausler }
3871444e5acSPeter Klausler 
3881444e5acSPeter Klausler // ApplyIntrinsic() constructs the typed expression representation
3891444e5acSPeter Klausler // for a specific intrinsic function reference.
3901444e5acSPeter Klausler // TODO: maybe move into tools.h?
3911444e5acSPeter Klausler class IntrinsicCallHelper {
3921444e5acSPeter Klausler public:
3931444e5acSPeter Klausler   explicit IntrinsicCallHelper(SpecificCall &&call) : call_{call} {
3941444e5acSPeter Klausler     CHECK(proc_.IsFunction());
3951444e5acSPeter Klausler     typeAndShape_ = proc_.functionResult->GetTypeAndShape();
3961444e5acSPeter Klausler     CHECK(typeAndShape_ != nullptr);
3971444e5acSPeter Klausler   }
3981444e5acSPeter Klausler   using Result = std::optional<Expr<SomeType>>;
3991444e5acSPeter Klausler   using Types = LengthlessIntrinsicTypes;
4001444e5acSPeter Klausler   template <typename T> Result Test() {
4011444e5acSPeter Klausler     if (T::category == typeAndShape_->type().category() &&
4021444e5acSPeter Klausler         T::kind == typeAndShape_->type().kind()) {
4031444e5acSPeter Klausler       return AsGenericExpr(FunctionRef<T>{
4041444e5acSPeter Klausler           ProcedureDesignator{std::move(call_.specificIntrinsic)},
4051444e5acSPeter Klausler           std::move(call_.arguments)});
4061444e5acSPeter Klausler     } else {
4071444e5acSPeter Klausler       return std::nullopt;
4081444e5acSPeter Klausler     }
4091444e5acSPeter Klausler   }
4101444e5acSPeter Klausler 
4111444e5acSPeter Klausler private:
4121444e5acSPeter Klausler   SpecificCall call_;
4131444e5acSPeter Klausler   const characteristics::Procedure &proc_{
4141444e5acSPeter Klausler       call_.specificIntrinsic.characteristics.value()};
4151444e5acSPeter Klausler   const characteristics::TypeAndShape *typeAndShape_{nullptr};
4161444e5acSPeter Klausler };
4171444e5acSPeter Klausler 
4181444e5acSPeter Klausler static Expr<SomeType> ApplyIntrinsic(
4191444e5acSPeter Klausler     FoldingContext &context, const std::string &func, ActualArguments &&args) {
4201444e5acSPeter Klausler   auto found{
4211444e5acSPeter Klausler       context.intrinsics().Probe(CallCharacteristics{func}, args, context)};
4221444e5acSPeter Klausler   CHECK(found.has_value());
4231444e5acSPeter Klausler   auto result{common::SearchTypes(IntrinsicCallHelper{std::move(*found)})};
4241444e5acSPeter Klausler   CHECK(result.has_value());
4251444e5acSPeter Klausler   return *result;
4261444e5acSPeter Klausler }
4271444e5acSPeter Klausler 
4281444e5acSPeter Klausler static Expr<LogicalResult> CompareUnsigned(FoldingContext &context,
4291444e5acSPeter Klausler     const char *intrin, Expr<SomeType> &&x, Expr<SomeType> &&y) {
4301444e5acSPeter Klausler   Expr<SomeType> result{ApplyIntrinsic(context, intrin,
4311444e5acSPeter Klausler       ActualArguments{
4321444e5acSPeter Klausler           ActualArgument{std::move(x)}, ActualArgument{std::move(y)}})};
4331444e5acSPeter Klausler   return DEREF(UnwrapExpr<Expr<LogicalResult>>(result));
4341444e5acSPeter Klausler }
4351444e5acSPeter Klausler 
4361444e5acSPeter Klausler // Determines the right kind of INTEGER to hold the bits of a REAL type.
4371444e5acSPeter Klausler static Expr<SomeType> IntTransferMold(
4381444e5acSPeter Klausler     const TargetCharacteristics &target, DynamicType realType, bool asVector) {
4391444e5acSPeter Klausler   CHECK(realType.category() == TypeCategory::Real);
4401444e5acSPeter Klausler   int rKind{realType.kind()};
4411444e5acSPeter Klausler   int iKind{std::max<int>(target.GetAlignment(TypeCategory::Real, rKind),
4421444e5acSPeter Klausler       target.GetByteSize(TypeCategory::Real, rKind))};
4431444e5acSPeter Klausler   CHECK(target.CanSupportType(TypeCategory::Integer, iKind));
4441444e5acSPeter Klausler   DynamicType iType{TypeCategory::Integer, iKind};
4451444e5acSPeter Klausler   ConstantSubscripts shape;
4461444e5acSPeter Klausler   if (asVector) {
4471444e5acSPeter Klausler     shape = ConstantSubscripts{1};
4481444e5acSPeter Klausler   }
4491444e5acSPeter Klausler   Constant<SubscriptInteger> value{
4501444e5acSPeter Klausler       std::vector<Scalar<SubscriptInteger>>{0}, std::move(shape)};
4511444e5acSPeter Klausler   auto expr{ConvertToType(iType, AsGenericExpr(std::move(value)))};
4521444e5acSPeter Klausler   CHECK(expr.has_value());
4531444e5acSPeter Klausler   return std::move(*expr);
4541444e5acSPeter Klausler }
4551444e5acSPeter Klausler 
4561444e5acSPeter Klausler static Expr<SomeType> GetRealBits(FoldingContext &context, Expr<SomeReal> &&x) {
4571444e5acSPeter Klausler   auto xType{x.GetType()};
4581444e5acSPeter Klausler   CHECK(xType.has_value());
4591444e5acSPeter Klausler   bool asVector{x.Rank() > 0};
4601444e5acSPeter Klausler   return ApplyIntrinsic(context, "transfer",
4611444e5acSPeter Klausler       ActualArguments{ActualArgument{AsGenericExpr(std::move(x))},
4621444e5acSPeter Klausler           ActualArgument{IntTransferMold(
4631444e5acSPeter Klausler               context.targetCharacteristics(), *xType, asVector)}});
4641444e5acSPeter Klausler }
4651444e5acSPeter Klausler 
4661444e5acSPeter Klausler template <int KIND>
4671444e5acSPeter Klausler static Expr<Type<TypeCategory::Logical, KIND>> RewriteOutOfRange(
4681444e5acSPeter Klausler     FoldingContext &context,
4691444e5acSPeter Klausler     FunctionRef<Type<TypeCategory::Logical, KIND>> &&funcRef) {
4701444e5acSPeter Klausler   using ResultType = Type<TypeCategory::Logical, KIND>;
4711444e5acSPeter Klausler   ActualArguments &args{funcRef.arguments()};
4721444e5acSPeter Klausler   // Fold x= and round= unconditionally
4731444e5acSPeter Klausler   if (auto *x{UnwrapExpr<Expr<SomeType>>(args[0])}) {
4741444e5acSPeter Klausler     *args[0] = Fold(context, std::move(*x));
4751444e5acSPeter Klausler   }
4761444e5acSPeter Klausler   if (args.size() >= 3) {
4771444e5acSPeter Klausler     if (auto *round{UnwrapExpr<Expr<SomeType>>(args[2])}) {
4781444e5acSPeter Klausler       *args[2] = Fold(context, std::move(*round));
4791444e5acSPeter Klausler     }
4801444e5acSPeter Klausler   }
4811444e5acSPeter Klausler   if (auto *x{UnwrapExpr<Expr<SomeType>>(args[0])}) {
4821444e5acSPeter Klausler     x = UnwrapExpr<Expr<SomeType>>(args[0]);
4831444e5acSPeter Klausler     CHECK(x != nullptr);
4841444e5acSPeter Klausler     if (const auto *mold{UnwrapExpr<Expr<SomeType>>(args[1])}) {
4851444e5acSPeter Klausler       DynamicType xType{x->GetType().value()};
4861444e5acSPeter Klausler       std::optional<Expr<LogicalResult>> result;
4871444e5acSPeter Klausler       bool alwaysFalse{false};
4881444e5acSPeter Klausler       if (auto *iXExpr{UnwrapExpr<Expr<SomeInteger>>(*x)}) {
4891444e5acSPeter Klausler         int iXKind{iXExpr->GetType().value().kind()};
4901444e5acSPeter Klausler         if (auto *iMoldExpr{UnwrapExpr<Expr<SomeInteger>>(*mold)}) {
4911444e5acSPeter Klausler           // INTEGER -> INTEGER
4921444e5acSPeter Klausler           int iMoldKind{iMoldExpr->GetType().value().kind()};
4931444e5acSPeter Klausler           if (auto hi{IntToIntBound(iXKind, iMoldKind)}) {
4941444e5acSPeter Klausler             // 'hi' is INT(HUGE(mold), KIND(x))
4951444e5acSPeter Klausler             // OUT_OF_RANGE(x,mold) = (x + (hi + 1)) .UGT. (2*hi + 1)
4961444e5acSPeter Klausler             auto one{DEREF(UnwrapExpr<Expr<SomeInteger>>(ConvertToType(
4971444e5acSPeter Klausler                 xType, AsGenericExpr(Constant<SubscriptInteger>{1}))))};
4981444e5acSPeter Klausler             auto lhs{std::move(*iXExpr) +
4991444e5acSPeter Klausler                 (Expr<SomeInteger>{*hi} + Expr<SomeInteger>{one})};
5001444e5acSPeter Klausler             auto two{DEREF(UnwrapExpr<Expr<SomeInteger>>(ConvertToType(
5011444e5acSPeter Klausler                 xType, AsGenericExpr(Constant<SubscriptInteger>{2}))))};
5021444e5acSPeter Klausler             auto rhs{std::move(two) * std::move(*hi) + std::move(one)};
5031444e5acSPeter Klausler             result = CompareUnsigned(context, "bgt",
5041444e5acSPeter Klausler                 Expr<SomeType>{std::move(lhs)}, Expr<SomeType>{std::move(rhs)});
5051444e5acSPeter Klausler           } else {
5061444e5acSPeter Klausler             alwaysFalse = true;
5071444e5acSPeter Klausler           }
5081444e5acSPeter Klausler         } else if (auto *rMoldExpr{UnwrapExpr<Expr<SomeReal>>(*mold)}) {
5091444e5acSPeter Klausler           // INTEGER -> REAL
5101444e5acSPeter Klausler           int rMoldKind{rMoldExpr->GetType().value().kind()};
5111444e5acSPeter Klausler           if (auto hi{IntToRealBound(iXKind, rMoldKind, /*negate=*/false)}) {
5121444e5acSPeter Klausler             // OUT_OF_RANGE(x,mold) = (x - lo) .UGT. (hi - lo)
5131444e5acSPeter Klausler             auto lo{IntToRealBound(iXKind, rMoldKind, /*negate=*/true)};
5141444e5acSPeter Klausler             CHECK(lo.has_value());
5151444e5acSPeter Klausler             auto lhs{std::move(*iXExpr) - Expr<SomeInteger>{*lo}};
5161444e5acSPeter Klausler             auto rhs{std::move(*hi) - std::move(*lo)};
5171444e5acSPeter Klausler             result = CompareUnsigned(context, "bgt",
5181444e5acSPeter Klausler                 Expr<SomeType>{std::move(lhs)}, Expr<SomeType>{std::move(rhs)});
5191444e5acSPeter Klausler           } else {
5201444e5acSPeter Klausler             alwaysFalse = true;
5211444e5acSPeter Klausler           }
5221444e5acSPeter Klausler         }
5231444e5acSPeter Klausler       } else if (auto *rXExpr{UnwrapExpr<Expr<SomeReal>>(*x)}) {
5241444e5acSPeter Klausler         int rXKind{rXExpr->GetType().value().kind()};
5251444e5acSPeter Klausler         if (auto *iMoldExpr{UnwrapExpr<Expr<SomeInteger>>(*mold)}) {
5261444e5acSPeter Klausler           // REAL -> INTEGER
5271444e5acSPeter Klausler           int iMoldKind{iMoldExpr->GetType().value().kind()};
5281444e5acSPeter Klausler           auto hi{RealToIntBound(rXKind, iMoldKind, false, false)};
5291444e5acSPeter Klausler           auto lo{RealToIntBound(rXKind, iMoldKind, false, true)};
5301444e5acSPeter Klausler           if (args.size() >= 3) {
5311444e5acSPeter Klausler             // Bounds depend on round= value
5321444e5acSPeter Klausler             if (auto *round{UnwrapExpr<Expr<SomeType>>(args[2])}) {
5331444e5acSPeter Klausler               if (const Symbol * whole{UnwrapWholeSymbolDataRef(*round)};
534505f6da1SPeter Klausler                   whole && semantics::IsOptional(whole->GetUltimate()) &&
535505f6da1SPeter Klausler                   context.languageFeatures().ShouldWarn(
536505f6da1SPeter Klausler                       common::UsageWarning::OptionalMustBePresent)) {
5371444e5acSPeter Klausler                 if (auto source{args[2]->sourceLocation()}) {
5380f973ac7SPeter Klausler                   context.messages().Say(
5390f973ac7SPeter Klausler                       common::UsageWarning::OptionalMustBePresent, *source,
5401444e5acSPeter Klausler                       "ROUND= argument to OUT_OF_RANGE() is an optional dummy argument that must be present at execution"_warn_en_US);
5411444e5acSPeter Klausler                 }
5421444e5acSPeter Klausler               }
5431444e5acSPeter Klausler               auto rlo{RealToIntBound(rXKind, iMoldKind, true, true)};
5441444e5acSPeter Klausler               auto rhi{RealToIntBound(rXKind, iMoldKind, true, false)};
5451444e5acSPeter Klausler               auto mlo{Fold(context,
5461444e5acSPeter Klausler                   ApplyIntrinsic(context, "merge",
5471444e5acSPeter Klausler                       ActualArguments{
5481444e5acSPeter Klausler                           ActualArgument{Expr<SomeType>{std::move(rlo)}},
5491444e5acSPeter Klausler                           ActualArgument{Expr<SomeType>{std::move(lo)}},
5501444e5acSPeter Klausler                           ActualArgument{Expr<SomeType>{*round}}}))};
5511444e5acSPeter Klausler               auto mhi{Fold(context,
5521444e5acSPeter Klausler                   ApplyIntrinsic(context, "merge",
5531444e5acSPeter Klausler                       ActualArguments{
5541444e5acSPeter Klausler                           ActualArgument{Expr<SomeType>{std::move(rhi)}},
5551444e5acSPeter Klausler                           ActualArgument{Expr<SomeType>{std::move(hi)}},
5561444e5acSPeter Klausler                           ActualArgument{std::move(*round)}}))};
5571444e5acSPeter Klausler               lo = std::move(DEREF(UnwrapExpr<Expr<SomeReal>>(mlo)));
5581444e5acSPeter Klausler               hi = std::move(DEREF(UnwrapExpr<Expr<SomeReal>>(mhi)));
5591444e5acSPeter Klausler             }
5601444e5acSPeter Klausler           }
5611444e5acSPeter Klausler           // OUT_OF_RANGE(x,mold[,round]) =
5621444e5acSPeter Klausler           //   TRANSFER(x - lo, int) .UGT. TRANSFER(hi - lo, int)
5631444e5acSPeter Klausler           hi = Fold(context, std::move(hi));
5641444e5acSPeter Klausler           lo = Fold(context, std::move(lo));
5651444e5acSPeter Klausler           if (auto rhs{RealToIntLimit(context, std::move(hi), lo)}) {
5661444e5acSPeter Klausler             Expr<SomeReal> lhs{std::move(*rXExpr) - std::move(lo)};
5671444e5acSPeter Klausler             result = CompareUnsigned(context, "bgt",
5681444e5acSPeter Klausler                 GetRealBits(context, std::move(lhs)),
5691444e5acSPeter Klausler                 GetRealBits(context, std::move(*rhs)));
5701444e5acSPeter Klausler           }
5711444e5acSPeter Klausler         } else if (auto *rMoldExpr{UnwrapExpr<Expr<SomeReal>>(*mold)}) {
5721444e5acSPeter Klausler           // REAL -> REAL
5731444e5acSPeter Klausler           // Only finite arguments with ABS(x) > HUGE(mold) are .TRUE.
5741444e5acSPeter Klausler           // OUT_OF_RANGE(x,mold) =
5751444e5acSPeter Klausler           //   TRANSFER(ABS(x) - HUGE(mold), int) - 1 .ULT.
5761444e5acSPeter Klausler           //   TRANSFER(HUGE(mold), int)
5771444e5acSPeter Klausler           // Note that OUT_OF_RANGE(+/-Inf or NaN,mold) =
5781444e5acSPeter Klausler           //   TRANSFER(+Inf or Nan, int) - 1 .ULT. TRANSFER(HUGE(mold), int)
5791444e5acSPeter Klausler           int rMoldKind{rMoldExpr->GetType().value().kind()};
5801444e5acSPeter Klausler           if (auto bounds{RealToRealBounds(rXKind, rMoldKind)}) {
5811444e5acSPeter Klausler             auto &[moldHuge, xHuge]{*bounds};
5821444e5acSPeter Klausler             Expr<SomeType> abs{ApplyIntrinsic(context, "abs",
5831444e5acSPeter Klausler                 ActualArguments{
5841444e5acSPeter Klausler                     ActualArgument{Expr<SomeType>{std::move(*rXExpr)}}})};
5851444e5acSPeter Klausler             auto &absR{DEREF(UnwrapExpr<Expr<SomeReal>>(abs))};
5861444e5acSPeter Klausler             Expr<SomeType> diffBits{
5871444e5acSPeter Klausler                 GetRealBits(context, std::move(absR) - std::move(moldHuge))};
5881444e5acSPeter Klausler             auto &diffBitsI{DEREF(UnwrapExpr<Expr<SomeInteger>>(diffBits))};
5891444e5acSPeter Klausler             Expr<SomeType> decr{std::move(diffBitsI) -
5901444e5acSPeter Klausler                 Expr<SomeInteger>{Expr<SubscriptInteger>{1}}};
5911444e5acSPeter Klausler             result = CompareUnsigned(context, "blt", std::move(decr),
5921444e5acSPeter Klausler                 GetRealBits(context, std::move(xHuge)));
5931444e5acSPeter Klausler           } else {
5941444e5acSPeter Klausler             alwaysFalse = true;
5951444e5acSPeter Klausler           }
5961444e5acSPeter Klausler         }
5971444e5acSPeter Klausler       }
5981444e5acSPeter Klausler       if (alwaysFalse) {
5991444e5acSPeter Klausler         // xType can never overflow moldType, so
6001444e5acSPeter Klausler         //   OUT_OF_RANGE(x) = (x /= 0) .AND. .FALSE.
6011444e5acSPeter Klausler         // which has the same shape as x.
6021444e5acSPeter Klausler         Expr<LogicalResult> scalarFalse{
6031444e5acSPeter Klausler             Constant<LogicalResult>{Scalar<LogicalResult>{false}}};
6041444e5acSPeter Klausler         if (x->Rank() > 0) {
6051444e5acSPeter Klausler           if (auto nez{Relate(context.messages(), RelationalOperator::NE,
6061444e5acSPeter Klausler                   std::move(*x),
6071444e5acSPeter Klausler                   AsGenericExpr(Constant<SubscriptInteger>{0}))}) {
6081444e5acSPeter Klausler             result = Expr<LogicalResult>{LogicalOperation<LogicalResult::kind>{
6091444e5acSPeter Klausler                 LogicalOperator::And, std::move(*nez), std::move(scalarFalse)}};
6101444e5acSPeter Klausler           }
6111444e5acSPeter Klausler         } else {
6121444e5acSPeter Klausler           result = std::move(scalarFalse);
6131444e5acSPeter Klausler         }
6141444e5acSPeter Klausler       }
6151444e5acSPeter Klausler       if (result) {
6161444e5acSPeter Klausler         auto restorer{context.messages().DiscardMessages()};
6171444e5acSPeter Klausler         return Fold(
6181444e5acSPeter Klausler             context, AsExpr(ConvertToType<ResultType>(std::move(*result))));
6191444e5acSPeter Klausler       }
6201444e5acSPeter Klausler     }
6211444e5acSPeter Klausler   }
6221444e5acSPeter Klausler   return AsExpr(std::move(funcRef));
6231444e5acSPeter Klausler }
6241444e5acSPeter Klausler 
6254b57fe65SPeter Klausler static std::optional<common::RoundingMode> GetRoundingMode(
6264b57fe65SPeter Klausler     const std::optional<ActualArgument> &arg) {
6274b57fe65SPeter Klausler   if (arg) {
6284b57fe65SPeter Klausler     if (const auto *cst{UnwrapExpr<Constant<SomeDerived>>(*arg)}) {
6294b57fe65SPeter Klausler       if (auto constr{cst->GetScalarValue()}) {
6304b57fe65SPeter Klausler         if (StructureConstructorValues & values{constr->values()};
6314b57fe65SPeter Klausler             values.size() == 1) {
6324b57fe65SPeter Klausler           const Expr<SomeType> &value{values.begin()->second.value()};
6334b57fe65SPeter Klausler           if (auto code{ToInt64(value)}) {
6344b57fe65SPeter Klausler             return static_cast<common::RoundingMode>(*code);
6354b57fe65SPeter Klausler           }
6364b57fe65SPeter Klausler         }
6374b57fe65SPeter Klausler       }
6384b57fe65SPeter Klausler     }
6394b57fe65SPeter Klausler   }
6404b57fe65SPeter Klausler   return std::nullopt;
6414b57fe65SPeter Klausler }
6424b57fe65SPeter Klausler 
64364ab3302SCarolineConcatto template <int KIND>
64464ab3302SCarolineConcatto Expr<Type<TypeCategory::Logical, KIND>> FoldIntrinsicFunction(
64564ab3302SCarolineConcatto     FoldingContext &context,
64664ab3302SCarolineConcatto     FunctionRef<Type<TypeCategory::Logical, KIND>> &&funcRef) {
64764ab3302SCarolineConcatto   using T = Type<TypeCategory::Logical, KIND>;
64864ab3302SCarolineConcatto   ActualArguments &args{funcRef.arguments()};
64964ab3302SCarolineConcatto   auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
65064ab3302SCarolineConcatto   CHECK(intrinsic);
65164ab3302SCarolineConcatto   std::string name{intrinsic->name};
65264ab3302SCarolineConcatto   if (name == "all") {
6532f2fce8eSTarun Prabhu     return FoldAllAnyParity(
654503c085eSpeter klausler         context, std::move(funcRef), &Scalar<T>::AND, Scalar<T>{true});
65564ab3302SCarolineConcatto   } else if (name == "any") {
6562f2fce8eSTarun Prabhu     return FoldAllAnyParity(
657503c085eSpeter klausler         context, std::move(funcRef), &Scalar<T>::OR, Scalar<T>{false});
658c7574188SPeter Steinfeld   } else if (name == "associated") {
659c7574188SPeter Steinfeld     bool gotConstant{true};
660c7574188SPeter Steinfeld     const Expr<SomeType> *firstArgExpr{args[0]->UnwrapExpr()};
661c7574188SPeter Steinfeld     if (!firstArgExpr || !IsNullPointer(*firstArgExpr)) {
662c7574188SPeter Steinfeld       gotConstant = false;
663c7574188SPeter Steinfeld     } else if (args[1]) { // There's a second argument
664c7574188SPeter Steinfeld       const Expr<SomeType> *secondArgExpr{args[1]->UnwrapExpr()};
665c7574188SPeter Steinfeld       if (!secondArgExpr || !IsNullPointer(*secondArgExpr)) {
666c7574188SPeter Steinfeld         gotConstant = false;
667c7574188SPeter Steinfeld       }
668c7574188SPeter Steinfeld     }
669c7574188SPeter Steinfeld     return gotConstant ? Expr<T>{false} : Expr<T>{std::move(funcRef)};
67064ab3302SCarolineConcatto   } else if (name == "bge" || name == "bgt" || name == "ble" || name == "blt") {
67164ab3302SCarolineConcatto     static_assert(std::is_same_v<Scalar<LargestInt>, BOZLiteralConstant>);
6727709f12eSTarun Prabhu 
6737709f12eSTarun Prabhu     // The arguments to these intrinsics can be of different types. In that
6747709f12eSTarun Prabhu     // case, the shorter of the two would need to be zero-extended to match
6757709f12eSTarun Prabhu     // the size of the other. If at least one of the operands is not a constant,
6767709f12eSTarun Prabhu     // the zero-extending will be done during lowering. Otherwise, the folding
6777709f12eSTarun Prabhu     // must be done here.
6787709f12eSTarun Prabhu     std::optional<Expr<SomeType>> constArgs[2];
6797709f12eSTarun Prabhu     for (int i{0}; i <= 1; i++) {
6807709f12eSTarun Prabhu       if (BOZLiteralConstant * x{UnwrapExpr<BOZLiteralConstant>(args[i])}) {
6817709f12eSTarun Prabhu         constArgs[i] = AsGenericExpr(Constant<LargestInt>{std::move(*x)});
6827709f12eSTarun Prabhu       } else if (auto *x{UnwrapExpr<Expr<SomeInteger>>(args[i])}) {
6837709f12eSTarun Prabhu         common::visit(
6847709f12eSTarun Prabhu             [&](const auto &ix) {
6857709f12eSTarun Prabhu               using IntT = typename std::decay_t<decltype(ix)>::Result;
6867709f12eSTarun Prabhu               if (auto *c{UnwrapConstantValue<IntT>(ix)}) {
6877709f12eSTarun Prabhu                 constArgs[i] = ZeroExtend(*c);
6887709f12eSTarun Prabhu               }
6897709f12eSTarun Prabhu             },
6907709f12eSTarun Prabhu             x->u);
69164ab3302SCarolineConcatto       }
69264ab3302SCarolineConcatto     }
6937709f12eSTarun Prabhu 
6947709f12eSTarun Prabhu     if (constArgs[0] && constArgs[1]) {
69564ab3302SCarolineConcatto       auto fptr{&Scalar<LargestInt>::BGE};
69664ab3302SCarolineConcatto       if (name == "bge") { // done in fptr declaration
69764ab3302SCarolineConcatto       } else if (name == "bgt") {
69864ab3302SCarolineConcatto         fptr = &Scalar<LargestInt>::BGT;
69964ab3302SCarolineConcatto       } else if (name == "ble") {
70064ab3302SCarolineConcatto         fptr = &Scalar<LargestInt>::BLE;
70164ab3302SCarolineConcatto       } else if (name == "blt") {
70264ab3302SCarolineConcatto         fptr = &Scalar<LargestInt>::BLT;
70364ab3302SCarolineConcatto       } else {
70464ab3302SCarolineConcatto         common::die("missing case to fold intrinsic function %s", name.c_str());
70564ab3302SCarolineConcatto       }
7067709f12eSTarun Prabhu 
7077709f12eSTarun Prabhu       for (int i{0}; i <= 1; i++) {
7087709f12eSTarun Prabhu         *args[i] = std::move(constArgs[i].value());
7097709f12eSTarun Prabhu       }
7107709f12eSTarun Prabhu 
71164ab3302SCarolineConcatto       return FoldElementalIntrinsic<T, LargestInt, LargestInt>(context,
71264ab3302SCarolineConcatto           std::move(funcRef),
71364ab3302SCarolineConcatto           ScalarFunc<T, LargestInt, LargestInt>(
7147709f12eSTarun Prabhu               [&fptr](
7157709f12eSTarun Prabhu                   const Scalar<LargestInt> &i, const Scalar<LargestInt> &j) {
71664ab3302SCarolineConcatto                 return Scalar<T>{std::invoke(fptr, i, j)};
71764ab3302SCarolineConcatto               }));
7187709f12eSTarun Prabhu     } else {
7197709f12eSTarun Prabhu       return Expr<T>{std::move(funcRef)};
7207709f12eSTarun Prabhu     }
7212f80b73eSpeter klausler   } else if (name == "btest") {
722fc97d2e6SPeter Klausler     using SameInt = Type<TypeCategory::Integer, KIND>;
7232f80b73eSpeter klausler     if (const auto *ix{UnwrapExpr<Expr<SomeInteger>>(args[0])}) {
724cd03e96fSPeter Klausler       return common::visit(
7252f80b73eSpeter klausler           [&](const auto &x) {
7262f80b73eSpeter klausler             using IT = ResultType<decltype(x)>;
7272f80b73eSpeter klausler             return FoldElementalIntrinsic<T, IT, SameInt>(context,
7282f80b73eSpeter klausler                 std::move(funcRef),
7292f80b73eSpeter klausler                 ScalarFunc<T, IT, SameInt>(
7302f80b73eSpeter klausler                     [&](const Scalar<IT> &x, const Scalar<SameInt> &pos) {
7312f80b73eSpeter klausler                       auto posVal{pos.ToInt64()};
7322f80b73eSpeter klausler                       if (posVal < 0 || posVal >= x.bits) {
7332f80b73eSpeter klausler                         context.messages().Say(
7342f80b73eSpeter klausler                             "POS=%jd out of range for BTEST"_err_en_US,
7352f80b73eSpeter klausler                             static_cast<std::intmax_t>(posVal));
7362f80b73eSpeter klausler                       }
7372f80b73eSpeter klausler                       return Scalar<T>{x.BTEST(posVal)};
7382f80b73eSpeter klausler                     }));
7392f80b73eSpeter klausler           },
7402f80b73eSpeter klausler           ix->u);
741fc97d2e6SPeter Klausler     } else if (const auto *ux{UnwrapExpr<Expr<SomeUnsigned>>(args[0])}) {
742fc97d2e6SPeter Klausler       return common::visit(
743fc97d2e6SPeter Klausler           [&](const auto &x) {
744fc97d2e6SPeter Klausler             using UT = ResultType<decltype(x)>;
745fc97d2e6SPeter Klausler             return FoldElementalIntrinsic<T, UT, SameInt>(context,
746fc97d2e6SPeter Klausler                 std::move(funcRef),
747fc97d2e6SPeter Klausler                 ScalarFunc<T, UT, SameInt>(
748fc97d2e6SPeter Klausler                     [&](const Scalar<UT> &x, const Scalar<SameInt> &pos) {
749fc97d2e6SPeter Klausler                       auto posVal{pos.ToInt64()};
750fc97d2e6SPeter Klausler                       if (posVal < 0 || posVal >= x.bits) {
751fc97d2e6SPeter Klausler                         context.messages().Say(
752fc97d2e6SPeter Klausler                             "POS=%jd out of range for BTEST"_err_en_US,
753fc97d2e6SPeter Klausler                             static_cast<std::intmax_t>(posVal));
754fc97d2e6SPeter Klausler                       }
755fc97d2e6SPeter Klausler                       return Scalar<T>{x.BTEST(posVal)};
756fc97d2e6SPeter Klausler                     }));
757fc97d2e6SPeter Klausler           },
758fc97d2e6SPeter Klausler           ux->u);
7592f80b73eSpeter klausler     }
760e723c69bSPeter Klausler   } else if (name == "dot_product") {
761e723c69bSPeter Klausler     return FoldDotProduct<T>(context, std::move(funcRef));
762460fc79aSPeter Klausler   } else if (name == "extends_type_of") {
763460fc79aSPeter Klausler     // Type extension testing with EXTENDS_TYPE_OF() ignores any type
764460fc79aSPeter Klausler     // parameters. Returns a constant truth value when the result is known now.
765460fc79aSPeter Klausler     if (args[0] && args[1]) {
766460fc79aSPeter Klausler       auto t0{args[0]->GetType()};
767460fc79aSPeter Klausler       auto t1{args[1]->GetType()};
768460fc79aSPeter Klausler       if (t0 && t1) {
769460fc79aSPeter Klausler         if (auto result{t0->ExtendsTypeOf(*t1)}) {
770460fc79aSPeter Klausler           return Expr<T>{*result};
771460fc79aSPeter Klausler         }
772460fc79aSPeter Klausler       }
773460fc79aSPeter Klausler     }
77471d868cfSpeter klausler   } else if (name == "isnan" || name == "__builtin_ieee_is_nan") {
7754ee8aef4SDavid Truby     // Only replace the type of the function if we can do the fold
7764ee8aef4SDavid Truby     if (args[0] && args[0]->UnwrapExpr() &&
7774ee8aef4SDavid Truby         IsActuallyConstant(*args[0]->UnwrapExpr())) {
77808bfc002SPeter Klausler       auto restorer{context.messages().DiscardMessages()};
77908bfc002SPeter Klausler       using DefaultReal = Type<TypeCategory::Real, 4>;
78029fa4518Speter klausler       return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef),
78129fa4518Speter klausler           ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) {
78229fa4518Speter klausler             return Scalar<T>{x.IsNotANumber()};
78329fa4518Speter klausler           }));
7844ee8aef4SDavid Truby     }
785d3f5ef24SYury Gribov   } else if (name == "__builtin_ieee_is_negative") {
786d3f5ef24SYury Gribov     auto restorer{context.messages().DiscardMessages()};
787d3f5ef24SYury Gribov     using DefaultReal = Type<TypeCategory::Real, 4>;
78809ea692dSV Donaldson     if (args[0] && args[0]->UnwrapExpr() &&
78909ea692dSV Donaldson         IsActuallyConstant(*args[0]->UnwrapExpr())) {
790d3f5ef24SYury Gribov       return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef),
791d3f5ef24SYury Gribov           ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) {
792d3f5ef24SYury Gribov             return Scalar<T>{x.IsNegative()};
793d3f5ef24SYury Gribov           }));
79409ea692dSV Donaldson     }
795d3f5ef24SYury Gribov   } else if (name == "__builtin_ieee_is_normal") {
796d3f5ef24SYury Gribov     auto restorer{context.messages().DiscardMessages()};
797d3f5ef24SYury Gribov     using DefaultReal = Type<TypeCategory::Real, 4>;
79875ddabd5SDavid Truby     if (args[0] && args[0]->UnwrapExpr() &&
79975ddabd5SDavid Truby         IsActuallyConstant(*args[0]->UnwrapExpr())) {
800d3f5ef24SYury Gribov       return FoldElementalIntrinsic<T, DefaultReal>(context, std::move(funcRef),
801d3f5ef24SYury Gribov           ScalarFunc<T, DefaultReal>([](const Scalar<DefaultReal> &x) {
802d3f5ef24SYury Gribov             return Scalar<T>{x.IsNormal()};
803d3f5ef24SYury Gribov           }));
80475ddabd5SDavid Truby     }
80564ab3302SCarolineConcatto   } else if (name == "is_contiguous") {
80664ab3302SCarolineConcatto     if (args.at(0)) {
80764ab3302SCarolineConcatto       if (auto *expr{args[0]->UnwrapExpr()}) {
80894896994SPeter Klausler         if (auto contiguous{IsContiguous(*expr, context)}) {
80994896994SPeter Klausler           return Expr<T>{*contiguous};
81064ab3302SCarolineConcatto         }
8116f5df419SJean Perier       } else if (auto *assumedType{args[0]->GetAssumedTypeDummy()}) {
8126f5df419SJean Perier         if (auto contiguous{IsContiguous(*assumedType, context)}) {
8136f5df419SJean Perier           return Expr<T>{*contiguous};
8146f5df419SJean Perier         }
81564ab3302SCarolineConcatto       }
81664ab3302SCarolineConcatto     }
81767f15e76SPeter Klausler   } else if (name == "is_iostat_end") {
81867f15e76SPeter Klausler     if (args[0] && args[0]->UnwrapExpr() &&
81967f15e76SPeter Klausler         IsActuallyConstant(*args[0]->UnwrapExpr())) {
82067f15e76SPeter Klausler       using Int64 = Type<TypeCategory::Integer, 8>;
82167f15e76SPeter Klausler       return FoldElementalIntrinsic<T, Int64>(context, std::move(funcRef),
82267f15e76SPeter Klausler           ScalarFunc<T, Int64>([](const Scalar<Int64> &x) {
82367f15e76SPeter Klausler             return Scalar<T>{x.ToInt64() == FORTRAN_RUNTIME_IOSTAT_END};
82467f15e76SPeter Klausler           }));
82567f15e76SPeter Klausler     }
82667f15e76SPeter Klausler   } else if (name == "is_iostat_eor") {
82767f15e76SPeter Klausler     if (args[0] && args[0]->UnwrapExpr() &&
82867f15e76SPeter Klausler         IsActuallyConstant(*args[0]->UnwrapExpr())) {
82967f15e76SPeter Klausler       using Int64 = Type<TypeCategory::Integer, 8>;
83067f15e76SPeter Klausler       return FoldElementalIntrinsic<T, Int64>(context, std::move(funcRef),
83167f15e76SPeter Klausler           ScalarFunc<T, Int64>([](const Scalar<Int64> &x) {
83267f15e76SPeter Klausler             return Scalar<T>{x.ToInt64() == FORTRAN_RUNTIME_IOSTAT_EOR};
83367f15e76SPeter Klausler           }));
83467f15e76SPeter Klausler     }
8356965a776Speter klausler   } else if (name == "lge" || name == "lgt" || name == "lle" || name == "llt") {
8366965a776Speter klausler     // Rewrite LGE/LGT/LLE/LLT into ASCII character relations
8376965a776Speter klausler     auto *cx0{UnwrapExpr<Expr<SomeCharacter>>(args[0])};
8386965a776Speter klausler     auto *cx1{UnwrapExpr<Expr<SomeCharacter>>(args[1])};
8396965a776Speter klausler     if (cx0 && cx1) {
8406965a776Speter klausler       return Fold(context,
8416965a776Speter klausler           ConvertToType<T>(
8426965a776Speter klausler               PackageRelation(name == "lge" ? RelationalOperator::GE
8436965a776Speter klausler                       : name == "lgt"       ? RelationalOperator::GT
8446965a776Speter klausler                       : name == "lle"       ? RelationalOperator::LE
8456965a776Speter klausler                                             : RelationalOperator::LT,
8466965a776Speter klausler                   ConvertToType<Ascii>(std::move(*cx0)),
8476965a776Speter klausler                   ConvertToType<Ascii>(std::move(*cx1)))));
8486965a776Speter klausler     }
84927899112SJean Perier   } else if (name == "logical") {
85027899112SJean Perier     if (auto *expr{UnwrapExpr<Expr<SomeLogical>>(args[0])}) {
85127899112SJean Perier       return Fold(context, ConvertToType<T>(std::move(*expr)));
85227899112SJean Perier     }
8530fdf9123SPeter Klausler   } else if (name == "matmul") {
8540fdf9123SPeter Klausler     return FoldMatmul(context, std::move(funcRef));
85554784b18SPeter Klausler   } else if (name == "out_of_range") {
8561444e5acSPeter Klausler     return RewriteOutOfRange<KIND>(context, std::move(funcRef));
8572f2fce8eSTarun Prabhu   } else if (name == "parity") {
8582f2fce8eSTarun Prabhu     return FoldAllAnyParity(
8592f2fce8eSTarun Prabhu         context, std::move(funcRef), &Scalar<T>::NEQV, Scalar<T>{false});
860460fc79aSPeter Klausler   } else if (name == "same_type_as") {
861460fc79aSPeter Klausler     // Type equality testing with SAME_TYPE_AS() ignores any type parameters.
862460fc79aSPeter Klausler     // Returns a constant truth value when the result is known now.
863460fc79aSPeter Klausler     if (args[0] && args[1]) {
864460fc79aSPeter Klausler       auto t0{args[0]->GetType()};
865460fc79aSPeter Klausler       auto t1{args[1]->GetType()};
866460fc79aSPeter Klausler       if (t0 && t1) {
867460fc79aSPeter Klausler         if (auto result{t0->SameTypeAs(*t1)}) {
868460fc79aSPeter Klausler           return Expr<T>{*result};
869460fc79aSPeter Klausler         }
870460fc79aSPeter Klausler       }
871460fc79aSPeter Klausler     }
8724b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_datatype") {
873aa39ddd0Speter klausler     return Expr<T>{true};
8744b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_denormal") {
8754b57fe65SPeter Klausler     return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
8764b57fe65SPeter Klausler         IeeeFeature::Denormal)};
8774b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_divide") {
8784b57fe65SPeter Klausler     return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
8794b57fe65SPeter Klausler         IeeeFeature::Divide)};
8804b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_flag") {
8814b57fe65SPeter Klausler     return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
8824b57fe65SPeter Klausler         IeeeFeature::Flags)};
8834b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_halting") {
884*c28a7c1eSvdonaldson     if (!context.targetCharacteristics()
885*c28a7c1eSvdonaldson              .haltingSupportIsUnknownAtCompileTime()) {
8864b57fe65SPeter Klausler       return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
8874b57fe65SPeter Klausler           IeeeFeature::Halting)};
888*c28a7c1eSvdonaldson     }
8894b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_inf") {
8904b57fe65SPeter Klausler     return Expr<T>{
8914b57fe65SPeter Klausler         context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Inf)};
8924b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_io") {
8934b57fe65SPeter Klausler     return Expr<T>{
8944b57fe65SPeter Klausler         context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Io)};
8954b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_nan") {
8964b57fe65SPeter Klausler     return Expr<T>{
8974b57fe65SPeter Klausler         context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::NaN)};
8984b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_rounding") {
8994b57fe65SPeter Klausler     if (context.targetCharacteristics().ieeeFeatures().test(
9004b57fe65SPeter Klausler             IeeeFeature::Rounding)) {
9014b57fe65SPeter Klausler       if (auto mode{GetRoundingMode(args[0])}) {
9024b57fe65SPeter Klausler         return Expr<T>{mode != common::RoundingMode::TiesAwayFromZero};
9034b57fe65SPeter Klausler       }
9044b57fe65SPeter Klausler     }
9054b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_sqrt") {
9064b57fe65SPeter Klausler     return Expr<T>{
9074b57fe65SPeter Klausler         context.targetCharacteristics().ieeeFeatures().test(IeeeFeature::Sqrt)};
9084b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_standard") {
9094b57fe65SPeter Klausler     return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
9104b57fe65SPeter Klausler         IeeeFeature::Standard)};
9114b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_subnormal") {
9124b57fe65SPeter Klausler     return Expr<T>{context.targetCharacteristics().ieeeFeatures().test(
9134b57fe65SPeter Klausler         IeeeFeature::Subnormal)};
9144b57fe65SPeter Klausler   } else if (name == "__builtin_ieee_support_underflow_control") {
9156003be7eSvdonaldson     // Setting kind=0 checks subnormal flushing control across all type kinds.
9166003be7eSvdonaldson     if (args[0]) {
9176003be7eSvdonaldson       return Expr<T>{
9186003be7eSvdonaldson           context.targetCharacteristics().hasSubnormalFlushingControl(
9196003be7eSvdonaldson               args[0]->GetType().value().kind())};
9206003be7eSvdonaldson     } else {
9216003be7eSvdonaldson       return Expr<T>{
9226003be7eSvdonaldson           context.targetCharacteristics().hasSubnormalFlushingControl(
9236003be7eSvdonaldson               /*any=*/false)};
9246003be7eSvdonaldson     }
92564ab3302SCarolineConcatto   }
92664ab3302SCarolineConcatto   return Expr<T>{std::move(funcRef)};
92764ab3302SCarolineConcatto }
92864ab3302SCarolineConcatto 
92964ab3302SCarolineConcatto template <typename T>
93064ab3302SCarolineConcatto Expr<LogicalResult> FoldOperation(
93164ab3302SCarolineConcatto     FoldingContext &context, Relational<T> &&relation) {
93264ab3302SCarolineConcatto   if (auto array{ApplyElementwise(context, relation,
93364ab3302SCarolineConcatto           std::function<Expr<LogicalResult>(Expr<T> &&, Expr<T> &&)>{
93464ab3302SCarolineConcatto               [=](Expr<T> &&x, Expr<T> &&y) {
93564ab3302SCarolineConcatto                 return Expr<LogicalResult>{Relational<SomeType>{
93664ab3302SCarolineConcatto                     Relational<T>{relation.opr, std::move(x), std::move(y)}}};
93764ab3302SCarolineConcatto               }})}) {
93864ab3302SCarolineConcatto     return *array;
93964ab3302SCarolineConcatto   }
94064ab3302SCarolineConcatto   if (auto folded{OperandsAreConstants(relation)}) {
94164ab3302SCarolineConcatto     bool result{};
94264ab3302SCarolineConcatto     if constexpr (T::category == TypeCategory::Integer) {
94364ab3302SCarolineConcatto       result =
94464ab3302SCarolineConcatto           Satisfies(relation.opr, folded->first.CompareSigned(folded->second));
945fc97d2e6SPeter Klausler     } else if constexpr (T::category == TypeCategory::Unsigned) {
946fc97d2e6SPeter Klausler       result = Satisfies(
947fc97d2e6SPeter Klausler           relation.opr, folded->first.CompareUnsigned(folded->second));
94864ab3302SCarolineConcatto     } else if constexpr (T::category == TypeCategory::Real) {
94964ab3302SCarolineConcatto       result = Satisfies(relation.opr, folded->first.Compare(folded->second));
950df62afd5Speter klausler     } else if constexpr (T::category == TypeCategory::Complex) {
951df62afd5Speter klausler       result = (relation.opr == RelationalOperator::EQ) ==
952df62afd5Speter klausler           folded->first.Equals(folded->second);
95364ab3302SCarolineConcatto     } else if constexpr (T::category == TypeCategory::Character) {
95464ab3302SCarolineConcatto       result = Satisfies(relation.opr, Compare(folded->first, folded->second));
95564ab3302SCarolineConcatto     } else {
956df62afd5Speter klausler       static_assert(T::category != TypeCategory::Logical);
95764ab3302SCarolineConcatto     }
95864ab3302SCarolineConcatto     return Expr<LogicalResult>{Constant<LogicalResult>{result}};
95964ab3302SCarolineConcatto   }
96064ab3302SCarolineConcatto   return Expr<LogicalResult>{Relational<SomeType>{std::move(relation)}};
96164ab3302SCarolineConcatto }
96264ab3302SCarolineConcatto 
96364ab3302SCarolineConcatto Expr<LogicalResult> FoldOperation(
96464ab3302SCarolineConcatto     FoldingContext &context, Relational<SomeType> &&relation) {
965cd03e96fSPeter Klausler   return common::visit(
96664ab3302SCarolineConcatto       [&](auto &&x) {
96764ab3302SCarolineConcatto         return Expr<LogicalResult>{FoldOperation(context, std::move(x))};
96864ab3302SCarolineConcatto       },
96964ab3302SCarolineConcatto       std::move(relation.u));
97064ab3302SCarolineConcatto }
97164ab3302SCarolineConcatto 
97264ab3302SCarolineConcatto template <int KIND>
97364ab3302SCarolineConcatto Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(
97464ab3302SCarolineConcatto     FoldingContext &context, Not<KIND> &&x) {
97564ab3302SCarolineConcatto   if (auto array{ApplyElementwise(context, x)}) {
97664ab3302SCarolineConcatto     return *array;
97764ab3302SCarolineConcatto   }
97864ab3302SCarolineConcatto   using Ty = Type<TypeCategory::Logical, KIND>;
97964ab3302SCarolineConcatto   auto &operand{x.left()};
98064ab3302SCarolineConcatto   if (auto value{GetScalarConstantValue<Ty>(operand)}) {
98164ab3302SCarolineConcatto     return Expr<Ty>{Constant<Ty>{!value->IsTrue()}};
98264ab3302SCarolineConcatto   }
98364ab3302SCarolineConcatto   return Expr<Ty>{x};
98464ab3302SCarolineConcatto }
98564ab3302SCarolineConcatto 
98664ab3302SCarolineConcatto template <int KIND>
98764ab3302SCarolineConcatto Expr<Type<TypeCategory::Logical, KIND>> FoldOperation(
98864ab3302SCarolineConcatto     FoldingContext &context, LogicalOperation<KIND> &&operation) {
98964ab3302SCarolineConcatto   using LOGICAL = Type<TypeCategory::Logical, KIND>;
99064ab3302SCarolineConcatto   if (auto array{ApplyElementwise(context, operation,
99164ab3302SCarolineConcatto           std::function<Expr<LOGICAL>(Expr<LOGICAL> &&, Expr<LOGICAL> &&)>{
99264ab3302SCarolineConcatto               [=](Expr<LOGICAL> &&x, Expr<LOGICAL> &&y) {
99364ab3302SCarolineConcatto                 return Expr<LOGICAL>{LogicalOperation<KIND>{
99464ab3302SCarolineConcatto                     operation.logicalOperator, std::move(x), std::move(y)}};
99564ab3302SCarolineConcatto               }})}) {
99664ab3302SCarolineConcatto     return *array;
99764ab3302SCarolineConcatto   }
99864ab3302SCarolineConcatto   if (auto folded{OperandsAreConstants(operation)}) {
99964ab3302SCarolineConcatto     bool xt{folded->first.IsTrue()}, yt{folded->second.IsTrue()}, result{};
100064ab3302SCarolineConcatto     switch (operation.logicalOperator) {
10011f879005STim Keith     case LogicalOperator::And:
10021f879005STim Keith       result = xt && yt;
10031f879005STim Keith       break;
10041f879005STim Keith     case LogicalOperator::Or:
10051f879005STim Keith       result = xt || yt;
10061f879005STim Keith       break;
10071f879005STim Keith     case LogicalOperator::Eqv:
10081f879005STim Keith       result = xt == yt;
10091f879005STim Keith       break;
10101f879005STim Keith     case LogicalOperator::Neqv:
10111f879005STim Keith       result = xt != yt;
10121f879005STim Keith       break;
10131f879005STim Keith     case LogicalOperator::Not:
10141f879005STim Keith       DIE("not a binary operator");
101564ab3302SCarolineConcatto     }
101664ab3302SCarolineConcatto     return Expr<LOGICAL>{Constant<LOGICAL>{result}};
101764ab3302SCarolineConcatto   }
101864ab3302SCarolineConcatto   return Expr<LOGICAL>{std::move(operation)};
101964ab3302SCarolineConcatto }
102064ab3302SCarolineConcatto 
10215c5bde1bSPeter Klausler #ifdef _MSC_VER // disable bogus warning about missing definitions
10225c5bde1bSPeter Klausler #pragma warning(disable : 4661)
10235c5bde1bSPeter Klausler #endif
102464ab3302SCarolineConcatto FOR_EACH_LOGICAL_KIND(template class ExpressionBase, )
102564ab3302SCarolineConcatto template class ExpressionBase<SomeLogical>;
10261f879005STim Keith } // namespace Fortran::evaluate
1027