1 //===-- lib/Evaluate/fold-integer.cpp -------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "fold-implementation.h" 10 #include "fold-reduction.h" 11 #include "flang/Evaluate/check-expression.h" 12 13 namespace Fortran::evaluate { 14 15 // Given a collection of ConstantSubscripts values, package them as a Constant. 16 // Return scalar value if asScalar == true and shape-dim array otherwise. 17 template <typename T> 18 Expr<T> PackageConstantBounds( 19 const ConstantSubscripts &&bounds, bool asScalar = false) { 20 if (asScalar) { 21 return Expr<T>{Constant<T>{bounds.at(0)}}; 22 } else { 23 // As rank-dim array 24 const int rank{GetRank(bounds)}; 25 std::vector<Scalar<T>> packed(rank); 26 std::transform(bounds.begin(), bounds.end(), packed.begin(), 27 [](ConstantSubscript x) { return Scalar<T>(x); }); 28 return Expr<T>{Constant<T>{std::move(packed), ConstantSubscripts{rank}}}; 29 } 30 } 31 32 // Class to retrieve the constant bound of an expression which is an 33 // array that devolves to a type of Constant<T> 34 class GetConstantArrayBoundHelper { 35 public: 36 template <typename T> 37 static Expr<T> GetLbound( 38 const Expr<SomeType> &array, std::optional<int> dim) { 39 return PackageConstantBounds<T>( 40 GetConstantArrayBoundHelper(dim, /*getLbound=*/true).Get(array), 41 dim.has_value()); 42 } 43 44 template <typename T> 45 static Expr<T> GetUbound( 46 const Expr<SomeType> &array, std::optional<int> dim) { 47 return PackageConstantBounds<T>( 48 GetConstantArrayBoundHelper(dim, /*getLbound=*/false).Get(array), 49 dim.has_value()); 50 } 51 52 private: 53 GetConstantArrayBoundHelper( 54 std::optional<ConstantSubscript> dim, bool getLbound) 55 : dim_{dim}, getLbound_{getLbound} {} 56 57 template <typename T> ConstantSubscripts Get(const T &) { 58 // The method is needed for template expansion, but we should never get 59 // here in practice. 60 CHECK(false); 61 return {0}; 62 } 63 64 template <typename T> ConstantSubscripts Get(const Constant<T> &x) { 65 if (getLbound_) { 66 // Return the lower bound 67 if (dim_) { 68 return {x.lbounds().at(*dim_)}; 69 } else { 70 return x.lbounds(); 71 } 72 } else { 73 // Return the upper bound 74 if (arrayFromParenthesesExpr) { 75 // Underlying array comes from (x) expression - return shapes 76 if (dim_) { 77 return {x.shape().at(*dim_)}; 78 } else { 79 return x.shape(); 80 } 81 } else { 82 return x.ComputeUbounds(dim_); 83 } 84 } 85 } 86 87 template <typename T> ConstantSubscripts Get(const Parentheses<T> &x) { 88 // Cause of temp variable inside parentheses - return [1, ... 1] for lower 89 // bounds and shape for upper bounds 90 if (getLbound_) { 91 return ConstantSubscripts(x.Rank(), ConstantSubscript{1}); 92 } else { 93 // Indicate that underlying array comes from parentheses expression. 94 // Continue to unwrap expression until we hit a constant 95 arrayFromParenthesesExpr = true; 96 return Get(x.left()); 97 } 98 } 99 100 template <typename T> ConstantSubscripts Get(const Expr<T> &x) { 101 // recurse through Expr<T>'a until we hit a constant 102 return common::visit([&](const auto &inner) { return Get(inner); }, 103 // [&](const auto &) { return 0; }, 104 x.u); 105 } 106 107 const std::optional<ConstantSubscript> dim_; 108 const bool getLbound_; 109 bool arrayFromParenthesesExpr{false}; 110 }; 111 112 template <int KIND> 113 Expr<Type<TypeCategory::Integer, KIND>> LBOUND(FoldingContext &context, 114 FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) { 115 using T = Type<TypeCategory::Integer, KIND>; 116 ActualArguments &args{funcRef.arguments()}; 117 if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) { 118 if (int rank{array->Rank()}; rank > 0) { 119 std::optional<int> dim; 120 if (funcRef.Rank() == 0) { 121 // Optional DIM= argument is present: result is scalar. 122 if (auto dim64{GetInt64Arg(args[1])}) { 123 if (*dim64 < 1 || *dim64 > rank) { 124 context.messages().Say("DIM=%jd dimension is out of range for " 125 "rank-%d array"_err_en_US, 126 *dim64, rank); 127 return MakeInvalidIntrinsic<T>(std::move(funcRef)); 128 } else { 129 dim = *dim64 - 1; // 1-based to 0-based 130 } 131 } else { 132 // DIM= is present but not constant 133 return Expr<T>{std::move(funcRef)}; 134 } 135 } 136 bool lowerBoundsAreOne{true}; 137 if (auto named{ExtractNamedEntity(*array)}) { 138 const Symbol &symbol{named->GetLastSymbol()}; 139 if (symbol.Rank() == rank) { 140 lowerBoundsAreOne = false; 141 if (dim) { 142 if (auto lb{GetLBOUND(context, *named, *dim)}) { 143 return Fold(context, ConvertToType<T>(std::move(*lb))); 144 } 145 } else if (auto extents{ 146 AsExtentArrayExpr(GetLBOUNDs(context, *named))}) { 147 return Fold(context, 148 ConvertToType<T>(Expr<ExtentType>{std::move(*extents)})); 149 } 150 } else { 151 lowerBoundsAreOne = symbol.Rank() == 0; // LBOUND(array%component) 152 } 153 } 154 if (IsActuallyConstant(*array)) { 155 return GetConstantArrayBoundHelper::GetLbound<T>(*array, dim); 156 } 157 if (lowerBoundsAreOne) { 158 ConstantSubscripts ones(rank, ConstantSubscript{1}); 159 return PackageConstantBounds<T>(std::move(ones), dim.has_value()); 160 } 161 } 162 } 163 return Expr<T>{std::move(funcRef)}; 164 } 165 166 template <int KIND> 167 Expr<Type<TypeCategory::Integer, KIND>> UBOUND(FoldingContext &context, 168 FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) { 169 using T = Type<TypeCategory::Integer, KIND>; 170 ActualArguments &args{funcRef.arguments()}; 171 if (auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) { 172 if (int rank{array->Rank()}; rank > 0) { 173 std::optional<int> dim; 174 if (funcRef.Rank() == 0) { 175 // Optional DIM= argument is present: result is scalar. 176 if (auto dim64{GetInt64Arg(args[1])}) { 177 if (*dim64 < 1 || *dim64 > rank) { 178 context.messages().Say("DIM=%jd dimension is out of range for " 179 "rank-%d array"_err_en_US, 180 *dim64, rank); 181 return MakeInvalidIntrinsic<T>(std::move(funcRef)); 182 } else { 183 dim = *dim64 - 1; // 1-based to 0-based 184 } 185 } else { 186 // DIM= is present but not constant 187 return Expr<T>{std::move(funcRef)}; 188 } 189 } 190 bool takeBoundsFromShape{true}; 191 if (auto named{ExtractNamedEntity(*array)}) { 192 const Symbol &symbol{named->GetLastSymbol()}; 193 if (symbol.Rank() == rank) { 194 takeBoundsFromShape = false; 195 if (dim) { 196 if (semantics::IsAssumedSizeArray(symbol) && *dim == rank - 1) { 197 context.messages().Say("DIM=%jd dimension is out of range for " 198 "rank-%d assumed-size array"_err_en_US, 199 rank, rank); 200 return MakeInvalidIntrinsic<T>(std::move(funcRef)); 201 } else if (auto ub{GetUBOUND(context, *named, *dim)}) { 202 return Fold(context, ConvertToType<T>(std::move(*ub))); 203 } 204 } else { 205 Shape ubounds{GetUBOUNDs(context, *named)}; 206 if (semantics::IsAssumedSizeArray(symbol)) { 207 CHECK(!ubounds.back()); 208 ubounds.back() = ExtentExpr{-1}; 209 } 210 if (auto extents{AsExtentArrayExpr(ubounds)}) { 211 return Fold(context, 212 ConvertToType<T>(Expr<ExtentType>{std::move(*extents)})); 213 } 214 } 215 } else { 216 takeBoundsFromShape = symbol.Rank() == 0; // UBOUND(array%component) 217 } 218 } 219 if (IsActuallyConstant(*array)) { 220 return GetConstantArrayBoundHelper::GetUbound<T>(*array, dim); 221 } 222 if (takeBoundsFromShape) { 223 if (auto shape{GetContextFreeShape(context, *array)}) { 224 if (dim) { 225 if (auto &dimSize{shape->at(*dim)}) { 226 return Fold(context, 227 ConvertToType<T>(Expr<ExtentType>{std::move(*dimSize)})); 228 } 229 } else if (auto shapeExpr{AsExtentArrayExpr(*shape)}) { 230 return Fold(context, ConvertToType<T>(std::move(*shapeExpr))); 231 } 232 } 233 } 234 } 235 } 236 return Expr<T>{std::move(funcRef)}; 237 } 238 239 // COUNT() 240 template <typename T> 241 static Expr<T> FoldCount(FoldingContext &context, FunctionRef<T> &&ref) { 242 static_assert(T::category == TypeCategory::Integer); 243 ActualArguments &arg{ref.arguments()}; 244 if (const Constant<LogicalResult> *mask{arg.empty() 245 ? nullptr 246 : Folder<LogicalResult>{context}.Folding(arg[0])}) { 247 std::optional<int> dim; 248 if (CheckReductionDIM(dim, context, arg, 1, mask->Rank())) { 249 auto accumulator{[&](Scalar<T> &element, const ConstantSubscripts &at) { 250 if (mask->At(at).IsTrue()) { 251 element = element.AddSigned(Scalar<T>{1}).value; 252 } 253 }}; 254 return Expr<T>{DoReduction<T>(*mask, dim, Scalar<T>{}, accumulator)}; 255 } 256 } 257 return Expr<T>{std::move(ref)}; 258 } 259 260 // FINDLOC(), MAXLOC(), & MINLOC() 261 enum class WhichLocation { Findloc, Maxloc, Minloc }; 262 template <WhichLocation WHICH> class LocationHelper { 263 public: 264 LocationHelper( 265 DynamicType &&type, ActualArguments &arg, FoldingContext &context) 266 : type_{type}, arg_{arg}, context_{context} {} 267 using Result = std::optional<Constant<SubscriptInteger>>; 268 using Types = std::conditional_t<WHICH == WhichLocation::Findloc, 269 AllIntrinsicTypes, RelationalTypes>; 270 271 template <typename T> Result Test() const { 272 if (T::category != type_.category() || T::kind != type_.kind()) { 273 return std::nullopt; 274 } 275 CHECK(arg_.size() == (WHICH == WhichLocation::Findloc ? 6 : 5)); 276 Folder<T> folder{context_}; 277 Constant<T> *array{folder.Folding(arg_[0])}; 278 if (!array) { 279 return std::nullopt; 280 } 281 std::optional<Constant<T>> value; 282 if constexpr (WHICH == WhichLocation::Findloc) { 283 if (const Constant<T> *p{folder.Folding(arg_[1])}) { 284 value.emplace(*p); 285 } else { 286 return std::nullopt; 287 } 288 } 289 std::optional<int> dim; 290 Constant<LogicalResult> *mask{ 291 GetReductionMASK(arg_[maskArg], array->shape(), context_)}; 292 if ((!mask && arg_[maskArg]) || 293 !CheckReductionDIM(dim, context_, arg_, dimArg, array->Rank())) { 294 return std::nullopt; 295 } 296 bool back{false}; 297 if (arg_[backArg]) { 298 const auto *backConst{ 299 Folder<LogicalResult>{context_}.Folding(arg_[backArg])}; 300 if (backConst) { 301 back = backConst->GetScalarValue().value().IsTrue(); 302 } else { 303 return std::nullopt; 304 } 305 } 306 const RelationalOperator relation{WHICH == WhichLocation::Findloc 307 ? RelationalOperator::EQ 308 : WHICH == WhichLocation::Maxloc 309 ? (back ? RelationalOperator::GE : RelationalOperator::GT) 310 : back ? RelationalOperator::LE 311 : RelationalOperator::LT}; 312 // Use lower bounds of 1 exclusively. 313 array->SetLowerBoundsToOne(); 314 ConstantSubscripts at{array->lbounds()}, maskAt, resultIndices, resultShape; 315 if (mask) { 316 if (auto scalarMask{mask->GetScalarValue()}) { 317 // Convert into array in case of scalar MASK= (for 318 // MAXLOC/MINLOC/FINDLOC mask should be be conformable) 319 ConstantSubscript n{GetSize(array->shape())}; 320 std::vector<Scalar<LogicalResult>> mask_elements( 321 n, Scalar<LogicalResult>{scalarMask.value()}); 322 *mask = Constant<LogicalResult>{ 323 std::move(mask_elements), ConstantSubscripts{n}}; 324 } 325 mask->SetLowerBoundsToOne(); 326 maskAt = mask->lbounds(); 327 } 328 if (dim) { // DIM= 329 if (*dim < 1 || *dim > array->Rank()) { 330 context_.messages().Say("DIM=%d is out of range"_err_en_US, *dim); 331 return std::nullopt; 332 } 333 int zbDim{*dim - 1}; 334 resultShape = array->shape(); 335 resultShape.erase( 336 resultShape.begin() + zbDim); // scalar if array is vector 337 ConstantSubscript dimLength{array->shape()[zbDim]}; 338 ConstantSubscript n{GetSize(resultShape)}; 339 for (ConstantSubscript j{0}; j < n; ++j) { 340 ConstantSubscript hit{0}; 341 if constexpr (WHICH == WhichLocation::Maxloc || 342 WHICH == WhichLocation::Minloc) { 343 value.reset(); 344 } 345 for (ConstantSubscript k{0}; k < dimLength; 346 ++k, ++at[zbDim], mask && ++maskAt[zbDim]) { 347 if ((!mask || mask->At(maskAt).IsTrue()) && 348 IsHit(array->At(at), value, relation)) { 349 hit = at[zbDim]; 350 if constexpr (WHICH == WhichLocation::Findloc) { 351 if (!back) { 352 break; 353 } 354 } 355 } 356 } 357 resultIndices.emplace_back(hit); 358 at[zbDim] = std::max<ConstantSubscript>(dimLength, 1); 359 array->IncrementSubscripts(at); 360 at[zbDim] = 1; 361 if (mask) { 362 maskAt[zbDim] = mask->lbounds()[zbDim] + 363 std::max<ConstantSubscript>(dimLength, 1) - 1; 364 mask->IncrementSubscripts(maskAt); 365 maskAt[zbDim] = mask->lbounds()[zbDim]; 366 } 367 } 368 } else { // no DIM= 369 resultShape = ConstantSubscripts{array->Rank()}; // always a vector 370 ConstantSubscript n{GetSize(array->shape())}; 371 resultIndices = ConstantSubscripts(array->Rank(), 0); 372 for (ConstantSubscript j{0}; j < n; ++j, array->IncrementSubscripts(at), 373 mask && mask->IncrementSubscripts(maskAt)) { 374 if ((!mask || mask->At(maskAt).IsTrue()) && 375 IsHit(array->At(at), value, relation)) { 376 resultIndices = at; 377 if constexpr (WHICH == WhichLocation::Findloc) { 378 if (!back) { 379 break; 380 } 381 } 382 } 383 } 384 } 385 std::vector<Scalar<SubscriptInteger>> resultElements; 386 for (ConstantSubscript j : resultIndices) { 387 resultElements.emplace_back(j); 388 } 389 return Constant<SubscriptInteger>{ 390 std::move(resultElements), std::move(resultShape)}; 391 } 392 393 private: 394 template <typename T> 395 bool IsHit(typename Constant<T>::Element element, 396 std::optional<Constant<T>> &value, 397 [[maybe_unused]] RelationalOperator relation) const { 398 std::optional<Expr<LogicalResult>> cmp; 399 bool result{true}; 400 if (value) { 401 if constexpr (T::category == TypeCategory::Logical) { 402 // array(at) .EQV. value? 403 static_assert(WHICH == WhichLocation::Findloc); 404 cmp.emplace(ConvertToType<LogicalResult>( 405 Expr<T>{LogicalOperation<T::kind>{LogicalOperator::Eqv, 406 Expr<T>{Constant<T>{element}}, Expr<T>{Constant<T>{*value}}}})); 407 } else { // compare array(at) to value 408 cmp.emplace(PackageRelation(relation, Expr<T>{Constant<T>{element}}, 409 Expr<T>{Constant<T>{*value}})); 410 } 411 Expr<LogicalResult> folded{Fold(context_, std::move(*cmp))}; 412 result = GetScalarConstantValue<LogicalResult>(folded).value().IsTrue(); 413 } else { 414 // first unmasked element for MAXLOC/MINLOC - always take it 415 } 416 if constexpr (WHICH == WhichLocation::Maxloc || 417 WHICH == WhichLocation::Minloc) { 418 if (result) { 419 value.emplace(std::move(element)); 420 } 421 } 422 return result; 423 } 424 425 static constexpr int dimArg{WHICH == WhichLocation::Findloc ? 2 : 1}; 426 static constexpr int maskArg{dimArg + 1}; 427 static constexpr int backArg{maskArg + 2}; 428 429 DynamicType type_; 430 ActualArguments &arg_; 431 FoldingContext &context_; 432 }; 433 434 template <WhichLocation which> 435 static std::optional<Constant<SubscriptInteger>> FoldLocationCall( 436 ActualArguments &arg, FoldingContext &context) { 437 if (arg[0]) { 438 if (auto type{arg[0]->GetType()}) { 439 return common::SearchTypes( 440 LocationHelper<which>{std::move(*type), arg, context}); 441 } 442 } 443 return std::nullopt; 444 } 445 446 template <WhichLocation which, typename T> 447 static Expr<T> FoldLocation(FoldingContext &context, FunctionRef<T> &&ref) { 448 static_assert(T::category == TypeCategory::Integer); 449 if (std::optional<Constant<SubscriptInteger>> found{ 450 FoldLocationCall<which>(ref.arguments(), context)}) { 451 return Expr<T>{Fold( 452 context, ConvertToType<T>(Expr<SubscriptInteger>{std::move(*found)}))}; 453 } else { 454 return Expr<T>{std::move(ref)}; 455 } 456 } 457 458 // for IALL, IANY, & IPARITY 459 template <typename T> 460 static Expr<T> FoldBitReduction(FoldingContext &context, FunctionRef<T> &&ref, 461 Scalar<T> (Scalar<T>::*operation)(const Scalar<T> &) const, 462 Scalar<T> identity) { 463 static_assert(T::category == TypeCategory::Integer); 464 std::optional<int> dim; 465 if (std::optional<Constant<T>> array{ 466 ProcessReductionArgs<T>(context, ref.arguments(), dim, identity, 467 /*ARRAY=*/0, /*DIM=*/1, /*MASK=*/2)}) { 468 auto accumulator{[&](Scalar<T> &element, const ConstantSubscripts &at) { 469 element = (element.*operation)(array->At(at)); 470 }}; 471 return Expr<T>{DoReduction<T>(*array, dim, identity, accumulator)}; 472 } 473 return Expr<T>{std::move(ref)}; 474 } 475 476 template <int KIND> 477 Expr<Type<TypeCategory::Integer, KIND>> FoldIntrinsicFunction( 478 FoldingContext &context, 479 FunctionRef<Type<TypeCategory::Integer, KIND>> &&funcRef) { 480 using T = Type<TypeCategory::Integer, KIND>; 481 using Int4 = Type<TypeCategory::Integer, 4>; 482 ActualArguments &args{funcRef.arguments()}; 483 auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)}; 484 CHECK(intrinsic); 485 std::string name{intrinsic->name}; 486 if (name == "abs") { // incl. babs, iiabs, jiaabs, & kiabs 487 return FoldElementalIntrinsic<T, T>(context, std::move(funcRef), 488 ScalarFunc<T, T>([&context](const Scalar<T> &i) -> Scalar<T> { 489 typename Scalar<T>::ValueWithOverflow j{i.ABS()}; 490 if (j.overflow) { 491 context.messages().Say( 492 "abs(integer(kind=%d)) folding overflowed"_warn_en_US, KIND); 493 } 494 return j.value; 495 })); 496 } else if (name == "bit_size") { 497 return Expr<T>{Scalar<T>::bits}; 498 } else if (name == "ceiling" || name == "floor" || name == "nint") { 499 if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 500 // NINT rounds ties away from zero, not to even 501 common::RoundingMode mode{name == "ceiling" ? common::RoundingMode::Up 502 : name == "floor" ? common::RoundingMode::Down 503 : common::RoundingMode::TiesAwayFromZero}; 504 return common::visit( 505 [&](const auto &kx) { 506 using TR = ResultType<decltype(kx)>; 507 return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef), 508 ScalarFunc<T, TR>([&](const Scalar<TR> &x) { 509 auto y{x.template ToInteger<Scalar<T>>(mode)}; 510 if (y.flags.test(RealFlag::Overflow)) { 511 context.messages().Say( 512 "%s intrinsic folding overflow"_warn_en_US, name); 513 } 514 return y.value; 515 })); 516 }, 517 cx->u); 518 } 519 } else if (name == "count") { 520 return FoldCount<T>(context, std::move(funcRef)); 521 } else if (name == "digits") { 522 if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) { 523 return Expr<T>{common::visit( 524 [](const auto &kx) { 525 return Scalar<ResultType<decltype(kx)>>::DIGITS; 526 }, 527 cx->u)}; 528 } else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 529 return Expr<T>{common::visit( 530 [](const auto &kx) { 531 return Scalar<ResultType<decltype(kx)>>::DIGITS; 532 }, 533 cx->u)}; 534 } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 535 return Expr<T>{common::visit( 536 [](const auto &kx) { 537 return Scalar<typename ResultType<decltype(kx)>::Part>::DIGITS; 538 }, 539 cx->u)}; 540 } 541 } else if (name == "dim") { 542 return FoldElementalIntrinsic<T, T, T>( 543 context, std::move(funcRef), &Scalar<T>::DIM); 544 } else if (name == "dshiftl" || name == "dshiftr") { 545 const auto fptr{ 546 name == "dshiftl" ? &Scalar<T>::DSHIFTL : &Scalar<T>::DSHIFTR}; 547 // Third argument can be of any kind. However, it must be smaller or equal 548 // than BIT_SIZE. It can be converted to Int4 to simplify. 549 return FoldElementalIntrinsic<T, T, T, Int4>(context, std::move(funcRef), 550 ScalarFunc<T, T, T, Int4>( 551 [&fptr](const Scalar<T> &i, const Scalar<T> &j, 552 const Scalar<Int4> &shift) -> Scalar<T> { 553 return std::invoke(fptr, i, j, static_cast<int>(shift.ToInt64())); 554 })); 555 } else if (name == "exponent") { 556 if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 557 return common::visit( 558 [&funcRef, &context](const auto &x) -> Expr<T> { 559 using TR = typename std::decay_t<decltype(x)>::Result; 560 return FoldElementalIntrinsic<T, TR>(context, std::move(funcRef), 561 &Scalar<TR>::template EXPONENT<Scalar<T>>); 562 }, 563 sx->u); 564 } else { 565 DIE("exponent argument must be real"); 566 } 567 } else if (name == "findloc") { 568 return FoldLocation<WhichLocation::Findloc, T>(context, std::move(funcRef)); 569 } else if (name == "huge") { 570 return Expr<T>{Scalar<T>::HUGE()}; 571 } else if (name == "iachar" || name == "ichar") { 572 auto *someChar{UnwrapExpr<Expr<SomeCharacter>>(args[0])}; 573 CHECK(someChar); 574 if (auto len{ToInt64(someChar->LEN())}) { 575 if (len.value() != 1) { 576 // Do not die, this was not checked before 577 context.messages().Say( 578 "Character in intrinsic function %s must have length one"_warn_en_US, 579 name); 580 } else { 581 return common::visit( 582 [&funcRef, &context](const auto &str) -> Expr<T> { 583 using Char = typename std::decay_t<decltype(str)>::Result; 584 return FoldElementalIntrinsic<T, Char>(context, 585 std::move(funcRef), 586 ScalarFunc<T, Char>([](const Scalar<Char> &c) { 587 return Scalar<T>{CharacterUtils<Char::kind>::ICHAR(c)}; 588 })); 589 }, 590 someChar->u); 591 } 592 } 593 } else if (name == "iand" || name == "ior" || name == "ieor") { 594 auto fptr{&Scalar<T>::IAND}; 595 if (name == "iand") { // done in fptr declaration 596 } else if (name == "ior") { 597 fptr = &Scalar<T>::IOR; 598 } else if (name == "ieor") { 599 fptr = &Scalar<T>::IEOR; 600 } else { 601 common::die("missing case to fold intrinsic function %s", name.c_str()); 602 } 603 return FoldElementalIntrinsic<T, T, T>( 604 context, std::move(funcRef), ScalarFunc<T, T, T>(fptr)); 605 } else if (name == "iall") { 606 return FoldBitReduction( 607 context, std::move(funcRef), &Scalar<T>::IAND, Scalar<T>{}.NOT()); 608 } else if (name == "iany") { 609 return FoldBitReduction( 610 context, std::move(funcRef), &Scalar<T>::IOR, Scalar<T>{}); 611 } else if (name == "ibclr" || name == "ibset") { 612 // Second argument can be of any kind. However, it must be smaller 613 // than BIT_SIZE. It can be converted to Int4 to simplify. 614 auto fptr{&Scalar<T>::IBCLR}; 615 if (name == "ibclr") { // done in fptr definition 616 } else if (name == "ibset") { 617 fptr = &Scalar<T>::IBSET; 618 } else { 619 common::die("missing case to fold intrinsic function %s", name.c_str()); 620 } 621 return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef), 622 ScalarFunc<T, T, Int4>([&](const Scalar<T> &i, 623 const Scalar<Int4> &pos) -> Scalar<T> { 624 auto posVal{static_cast<int>(pos.ToInt64())}; 625 if (posVal < 0) { 626 context.messages().Say( 627 "bit position for %s (%d) is negative"_err_en_US, name, posVal); 628 } else if (posVal >= i.bits) { 629 context.messages().Say( 630 "bit position for %s (%d) is not less than %d"_err_en_US, name, 631 posVal, i.bits); 632 } 633 return std::invoke(fptr, i, posVal); 634 })); 635 } else if (name == "ibits") { 636 return FoldElementalIntrinsic<T, T, Int4, Int4>(context, std::move(funcRef), 637 ScalarFunc<T, T, Int4, Int4>([&](const Scalar<T> &i, 638 const Scalar<Int4> &pos, 639 const Scalar<Int4> &len) -> Scalar<T> { 640 auto posVal{static_cast<int>(pos.ToInt64())}; 641 auto lenVal{static_cast<int>(len.ToInt64())}; 642 if (posVal < 0) { 643 context.messages().Say( 644 "bit position for IBITS(POS=%d,LEN=%d) is negative"_err_en_US, 645 posVal, lenVal); 646 } else if (lenVal < 0) { 647 context.messages().Say( 648 "bit length for IBITS(POS=%d,LEN=%d) is negative"_err_en_US, 649 posVal, lenVal); 650 } else if (posVal + lenVal > i.bits) { 651 context.messages().Say( 652 "IBITS(POS=%d,LEN=%d) must have POS+LEN no greater than %d"_err_en_US, 653 posVal + lenVal, i.bits); 654 } 655 return i.IBITS(posVal, lenVal); 656 })); 657 } else if (name == "index" || name == "scan" || name == "verify") { 658 if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) { 659 return common::visit( 660 [&](const auto &kch) -> Expr<T> { 661 using TC = typename std::decay_t<decltype(kch)>::Result; 662 if (UnwrapExpr<Expr<SomeLogical>>(args[2])) { // BACK= 663 return FoldElementalIntrinsic<T, TC, TC, LogicalResult>(context, 664 std::move(funcRef), 665 ScalarFunc<T, TC, TC, LogicalResult>{ 666 [&name](const Scalar<TC> &str, const Scalar<TC> &other, 667 const Scalar<LogicalResult> &back) -> Scalar<T> { 668 return name == "index" 669 ? CharacterUtils<TC::kind>::INDEX( 670 str, other, back.IsTrue()) 671 : name == "scan" ? CharacterUtils<TC::kind>::SCAN( 672 str, other, back.IsTrue()) 673 : CharacterUtils<TC::kind>::VERIFY( 674 str, other, back.IsTrue()); 675 }}); 676 } else { 677 return FoldElementalIntrinsic<T, TC, TC>(context, 678 std::move(funcRef), 679 ScalarFunc<T, TC, TC>{ 680 [&name](const Scalar<TC> &str, 681 const Scalar<TC> &other) -> Scalar<T> { 682 return name == "index" 683 ? CharacterUtils<TC::kind>::INDEX(str, other) 684 : name == "scan" 685 ? CharacterUtils<TC::kind>::SCAN(str, other) 686 : CharacterUtils<TC::kind>::VERIFY(str, other); 687 }}); 688 } 689 }, 690 charExpr->u); 691 } else { 692 DIE("first argument must be CHARACTER"); 693 } 694 } else if (name == "int") { 695 if (auto *expr{UnwrapExpr<Expr<SomeType>>(args[0])}) { 696 return common::visit( 697 [&](auto &&x) -> Expr<T> { 698 using From = std::decay_t<decltype(x)>; 699 if constexpr (std::is_same_v<From, BOZLiteralConstant> || 700 IsNumericCategoryExpr<From>()) { 701 return Fold(context, ConvertToType<T>(std::move(x))); 702 } 703 DIE("int() argument type not valid"); 704 }, 705 std::move(expr->u)); 706 } 707 } else if (name == "int_ptr_kind") { 708 return Expr<T>{8}; 709 } else if (name == "kind") { 710 if constexpr (common::HasMember<T, IntegerTypes>) { 711 return Expr<T>{args[0].value().GetType()->kind()}; 712 } else { 713 DIE("kind() result not integral"); 714 } 715 } else if (name == "iparity") { 716 return FoldBitReduction( 717 context, std::move(funcRef), &Scalar<T>::IEOR, Scalar<T>{}); 718 } else if (name == "ishft") { 719 return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef), 720 ScalarFunc<T, T, Int4>([&](const Scalar<T> &i, 721 const Scalar<Int4> &pos) -> Scalar<T> { 722 auto posVal{static_cast<int>(pos.ToInt64())}; 723 if (posVal < -i.bits) { 724 context.messages().Say( 725 "SHIFT=%d count for ishft is less than %d"_err_en_US, posVal, 726 -i.bits); 727 } else if (posVal > i.bits) { 728 context.messages().Say( 729 "SHIFT=%d count for ishft is greater than %d"_err_en_US, posVal, 730 i.bits); 731 } 732 return i.ISHFT(posVal); 733 })); 734 } else if (name == "lbound") { 735 return LBOUND(context, std::move(funcRef)); 736 } else if (name == "leadz" || name == "trailz" || name == "poppar" || 737 name == "popcnt") { 738 if (auto *sn{UnwrapExpr<Expr<SomeInteger>>(args[0])}) { 739 return common::visit( 740 [&funcRef, &context, &name](const auto &n) -> Expr<T> { 741 using TI = typename std::decay_t<decltype(n)>::Result; 742 if (name == "poppar") { 743 return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef), 744 ScalarFunc<T, TI>([](const Scalar<TI> &i) -> Scalar<T> { 745 return Scalar<T>{i.POPPAR() ? 1 : 0}; 746 })); 747 } 748 auto fptr{&Scalar<TI>::LEADZ}; 749 if (name == "leadz") { // done in fptr definition 750 } else if (name == "trailz") { 751 fptr = &Scalar<TI>::TRAILZ; 752 } else if (name == "popcnt") { 753 fptr = &Scalar<TI>::POPCNT; 754 } else { 755 common::die( 756 "missing case to fold intrinsic function %s", name.c_str()); 757 } 758 return FoldElementalIntrinsic<T, TI>(context, std::move(funcRef), 759 ScalarFunc<T, TI>([&fptr](const Scalar<TI> &i) -> Scalar<T> { 760 return Scalar<T>{std::invoke(fptr, i)}; 761 })); 762 }, 763 sn->u); 764 } else { 765 DIE("leadz argument must be integer"); 766 } 767 } else if (name == "len") { 768 if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) { 769 return common::visit( 770 [&](auto &kx) { 771 if (auto len{kx.LEN()}) { 772 if (IsScopeInvariantExpr(*len)) { 773 return Fold(context, ConvertToType<T>(*std::move(len))); 774 } else { 775 return Expr<T>{std::move(funcRef)}; 776 } 777 } else { 778 return Expr<T>{std::move(funcRef)}; 779 } 780 }, 781 charExpr->u); 782 } else { 783 DIE("len() argument must be of character type"); 784 } 785 } else if (name == "len_trim") { 786 if (auto *charExpr{UnwrapExpr<Expr<SomeCharacter>>(args[0])}) { 787 return common::visit( 788 [&](const auto &kch) -> Expr<T> { 789 using TC = typename std::decay_t<decltype(kch)>::Result; 790 return FoldElementalIntrinsic<T, TC>(context, std::move(funcRef), 791 ScalarFunc<T, TC>{[](const Scalar<TC> &str) -> Scalar<T> { 792 return CharacterUtils<TC::kind>::LEN_TRIM(str); 793 }}); 794 }, 795 charExpr->u); 796 } else { 797 DIE("len_trim() argument must be of character type"); 798 } 799 } else if (name == "maskl" || name == "maskr") { 800 // Argument can be of any kind but value has to be smaller than BIT_SIZE. 801 // It can be safely converted to Int4 to simplify. 802 const auto fptr{name == "maskl" ? &Scalar<T>::MASKL : &Scalar<T>::MASKR}; 803 return FoldElementalIntrinsic<T, Int4>(context, std::move(funcRef), 804 ScalarFunc<T, Int4>([&fptr](const Scalar<Int4> &places) -> Scalar<T> { 805 return fptr(static_cast<int>(places.ToInt64())); 806 })); 807 } else if (name == "max") { 808 return FoldMINorMAX(context, std::move(funcRef), Ordering::Greater); 809 } else if (name == "max0" || name == "max1") { 810 return RewriteSpecificMINorMAX(context, std::move(funcRef)); 811 } else if (name == "maxexponent") { 812 if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 813 return common::visit( 814 [](const auto &x) { 815 using TR = typename std::decay_t<decltype(x)>::Result; 816 return Expr<T>{Scalar<TR>::MAXEXPONENT}; 817 }, 818 sx->u); 819 } 820 } else if (name == "maxloc") { 821 return FoldLocation<WhichLocation::Maxloc, T>(context, std::move(funcRef)); 822 } else if (name == "maxval") { 823 return FoldMaxvalMinval<T>(context, std::move(funcRef), 824 RelationalOperator::GT, T::Scalar::Least()); 825 } else if (name == "merge") { 826 return FoldMerge<T>(context, std::move(funcRef)); 827 } else if (name == "merge_bits") { 828 return FoldElementalIntrinsic<T, T, T, T>( 829 context, std::move(funcRef), &Scalar<T>::MERGE_BITS); 830 } else if (name == "min") { 831 return FoldMINorMAX(context, std::move(funcRef), Ordering::Less); 832 } else if (name == "min0" || name == "min1") { 833 return RewriteSpecificMINorMAX(context, std::move(funcRef)); 834 } else if (name == "minexponent") { 835 if (auto *sx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 836 return common::visit( 837 [](const auto &x) { 838 using TR = typename std::decay_t<decltype(x)>::Result; 839 return Expr<T>{Scalar<TR>::MINEXPONENT}; 840 }, 841 sx->u); 842 } 843 } else if (name == "minloc") { 844 return FoldLocation<WhichLocation::Minloc, T>(context, std::move(funcRef)); 845 } else if (name == "minval") { 846 return FoldMaxvalMinval<T>( 847 context, std::move(funcRef), RelationalOperator::LT, T::Scalar::HUGE()); 848 } else if (name == "mod") { 849 return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 850 ScalarFuncWithContext<T, T, T>( 851 [](FoldingContext &context, const Scalar<T> &x, 852 const Scalar<T> &y) -> Scalar<T> { 853 auto quotRem{x.DivideSigned(y)}; 854 if (quotRem.divisionByZero) { 855 context.messages().Say("mod() by zero"_warn_en_US); 856 } else if (quotRem.overflow) { 857 context.messages().Say("mod() folding overflowed"_warn_en_US); 858 } 859 return quotRem.remainder; 860 })); 861 } else if (name == "modulo") { 862 return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 863 ScalarFuncWithContext<T, T, T>( 864 [](FoldingContext &context, const Scalar<T> &x, 865 const Scalar<T> &y) -> Scalar<T> { 866 auto result{x.MODULO(y)}; 867 if (result.overflow) { 868 context.messages().Say( 869 "modulo() folding overflowed"_warn_en_US); 870 } 871 return result.value; 872 })); 873 } else if (name == "not") { 874 return FoldElementalIntrinsic<T, T>( 875 context, std::move(funcRef), &Scalar<T>::NOT); 876 } else if (name == "precision") { 877 if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 878 return Expr<T>{common::visit( 879 [](const auto &kx) { 880 return Scalar<ResultType<decltype(kx)>>::PRECISION; 881 }, 882 cx->u)}; 883 } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 884 return Expr<T>{common::visit( 885 [](const auto &kx) { 886 return Scalar<typename ResultType<decltype(kx)>::Part>::PRECISION; 887 }, 888 cx->u)}; 889 } 890 } else if (name == "product") { 891 return FoldProduct<T>(context, std::move(funcRef), Scalar<T>{1}); 892 } else if (name == "radix") { 893 return Expr<T>{2}; 894 } else if (name == "range") { 895 if (const auto *cx{UnwrapExpr<Expr<SomeInteger>>(args[0])}) { 896 return Expr<T>{common::visit( 897 [](const auto &kx) { 898 return Scalar<ResultType<decltype(kx)>>::RANGE; 899 }, 900 cx->u)}; 901 } else if (const auto *cx{UnwrapExpr<Expr<SomeReal>>(args[0])}) { 902 return Expr<T>{common::visit( 903 [](const auto &kx) { 904 return Scalar<ResultType<decltype(kx)>>::RANGE; 905 }, 906 cx->u)}; 907 } else if (const auto *cx{UnwrapExpr<Expr<SomeComplex>>(args[0])}) { 908 return Expr<T>{common::visit( 909 [](const auto &kx) { 910 return Scalar<typename ResultType<decltype(kx)>::Part>::RANGE; 911 }, 912 cx->u)}; 913 } 914 } else if (name == "rank") { 915 if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) { 916 if (auto named{ExtractNamedEntity(*array)}) { 917 const Symbol &symbol{named->GetLastSymbol()}; 918 if (IsAssumedRank(symbol)) { 919 // DescriptorInquiry can only be placed in expression of kind 920 // DescriptorInquiry::Result::kind. 921 return ConvertToType<T>(Expr< 922 Type<TypeCategory::Integer, DescriptorInquiry::Result::kind>>{ 923 DescriptorInquiry{*named, DescriptorInquiry::Field::Rank}}); 924 } 925 } 926 return Expr<T>{args[0].value().Rank()}; 927 } 928 return Expr<T>{args[0].value().Rank()}; 929 } else if (name == "selected_char_kind") { 930 if (const auto *chCon{UnwrapExpr<Constant<TypeOf<std::string>>>(args[0])}) { 931 if (std::optional<std::string> value{chCon->GetScalarValue()}) { 932 int defaultKind{ 933 context.defaults().GetDefaultKind(TypeCategory::Character)}; 934 return Expr<T>{SelectedCharKind(*value, defaultKind)}; 935 } 936 } 937 } else if (name == "selected_int_kind") { 938 if (auto p{GetInt64Arg(args[0])}) { 939 return Expr<T>{SelectedIntKind(*p)}; 940 } 941 } else if (name == "selected_real_kind" || 942 name == "__builtin_ieee_selected_real_kind") { 943 if (auto p{GetInt64ArgOr(args[0], 0)}) { 944 if (auto r{GetInt64ArgOr(args[1], 0)}) { 945 if (auto radix{GetInt64ArgOr(args[2], 2)}) { 946 return Expr<T>{SelectedRealKind(*p, *r, *radix)}; 947 } 948 } 949 } 950 } else if (name == "shape") { 951 if (auto shape{GetContextFreeShape(context, args[0])}) { 952 if (auto shapeExpr{AsExtentArrayExpr(*shape)}) { 953 return Fold(context, ConvertToType<T>(std::move(*shapeExpr))); 954 } 955 } 956 } else if (name == "shifta" || name == "shiftr" || name == "shiftl") { 957 // Second argument can be of any kind. However, it must be smaller or 958 // equal than BIT_SIZE. It can be converted to Int4 to simplify. 959 auto fptr{&Scalar<T>::SHIFTA}; 960 if (name == "shifta") { // done in fptr definition 961 } else if (name == "shiftr") { 962 fptr = &Scalar<T>::SHIFTR; 963 } else if (name == "shiftl") { 964 fptr = &Scalar<T>::SHIFTL; 965 } else { 966 common::die("missing case to fold intrinsic function %s", name.c_str()); 967 } 968 return FoldElementalIntrinsic<T, T, Int4>(context, std::move(funcRef), 969 ScalarFunc<T, T, Int4>([&](const Scalar<T> &i, 970 const Scalar<Int4> &pos) -> Scalar<T> { 971 auto posVal{static_cast<int>(pos.ToInt64())}; 972 if (posVal < 0) { 973 context.messages().Say( 974 "SHIFT=%d count for %s is negative"_err_en_US, posVal, name); 975 } else if (posVal > i.bits) { 976 context.messages().Say( 977 "SHIFT=%d count for %s is greater than %d"_err_en_US, posVal, 978 name, i.bits); 979 } 980 return std::invoke(fptr, i, posVal); 981 })); 982 } else if (name == "sign") { 983 return FoldElementalIntrinsic<T, T, T>(context, std::move(funcRef), 984 ScalarFunc<T, T, T>( 985 [&context](const Scalar<T> &j, const Scalar<T> &k) -> Scalar<T> { 986 typename Scalar<T>::ValueWithOverflow result{j.SIGN(k)}; 987 if (result.overflow) { 988 context.messages().Say( 989 "sign(integer(kind=%d)) folding overflowed"_warn_en_US, 990 KIND); 991 } 992 return result.value; 993 })); 994 } else if (name == "size") { 995 if (auto shape{GetContextFreeShape(context, args[0])}) { 996 if (auto &dimArg{args[1]}) { // DIM= is present, get one extent 997 if (auto dim{GetInt64Arg(args[1])}) { 998 int rank{GetRank(*shape)}; 999 if (*dim >= 1 && *dim <= rank) { 1000 const Symbol *symbol{UnwrapWholeSymbolDataRef(args[0])}; 1001 if (symbol && IsAssumedSizeArray(*symbol) && *dim == rank) { 1002 context.messages().Say( 1003 "size(array,dim=%jd) of last dimension is not available for rank-%d assumed-size array dummy argument"_err_en_US, 1004 *dim, rank); 1005 return MakeInvalidIntrinsic<T>(std::move(funcRef)); 1006 } else if (auto &extent{shape->at(*dim - 1)}) { 1007 return Fold(context, ConvertToType<T>(std::move(*extent))); 1008 } 1009 } else { 1010 context.messages().Say( 1011 "size(array,dim=%jd) dimension is out of range for rank-%d array"_warn_en_US, 1012 *dim, rank); 1013 } 1014 } 1015 } else if (auto extents{common::AllElementsPresent(std::move(*shape))}) { 1016 // DIM= is absent; compute PRODUCT(SHAPE()) 1017 ExtentExpr product{1}; 1018 for (auto &&extent : std::move(*extents)) { 1019 product = std::move(product) * std::move(extent); 1020 } 1021 return Expr<T>{ConvertToType<T>(Fold(context, std::move(product)))}; 1022 } 1023 } 1024 } else if (name == "sizeof") { // in bytes; extension 1025 if (auto info{ 1026 characteristics::TypeAndShape::Characterize(args[0], context)}) { 1027 if (auto bytes{info->MeasureSizeInBytes(context)}) { 1028 return Expr<T>{Fold(context, ConvertToType<T>(std::move(*bytes)))}; 1029 } 1030 } 1031 } else if (name == "storage_size") { // in bits 1032 if (auto info{ 1033 characteristics::TypeAndShape::Characterize(args[0], context)}) { 1034 if (auto bytes{info->MeasureElementSizeInBytes(context, true)}) { 1035 return Expr<T>{ 1036 Fold(context, Expr<T>{8} * ConvertToType<T>(std::move(*bytes)))}; 1037 } 1038 } 1039 } else if (name == "sum") { 1040 return FoldSum<T>(context, std::move(funcRef)); 1041 } else if (name == "ubound") { 1042 return UBOUND(context, std::move(funcRef)); 1043 } 1044 // TODO: dot_product, ishftc, matmul, sign, transfer 1045 return Expr<T>{std::move(funcRef)}; 1046 } 1047 1048 // Substitutes a bare type parameter reference with its value if it has one now 1049 // in an instantiation. Bare LEN type parameters are substituted only when 1050 // the known value is constant. 1051 Expr<TypeParamInquiry::Result> FoldOperation( 1052 FoldingContext &context, TypeParamInquiry &&inquiry) { 1053 std::optional<NamedEntity> base{inquiry.base()}; 1054 parser::CharBlock parameterName{inquiry.parameter().name()}; 1055 if (base) { 1056 // Handling "designator%typeParam". Get the value of the type parameter 1057 // from the instantiation of the base 1058 if (const semantics::DeclTypeSpec * 1059 declType{base->GetLastSymbol().GetType()}) { 1060 if (const semantics::ParamValue * 1061 paramValue{ 1062 declType->derivedTypeSpec().FindParameter(parameterName)}) { 1063 const semantics::MaybeIntExpr ¶mExpr{paramValue->GetExplicit()}; 1064 if (paramExpr && IsConstantExpr(*paramExpr)) { 1065 Expr<SomeInteger> intExpr{*paramExpr}; 1066 return Fold(context, 1067 ConvertToType<TypeParamInquiry::Result>(std::move(intExpr))); 1068 } 1069 } 1070 } 1071 } else { 1072 // A "bare" type parameter: replace with its value, if that's now known 1073 // in a current derived type instantiation, for KIND type parameters. 1074 if (const auto *pdt{context.pdtInstance()}) { 1075 bool isLen{false}; 1076 if (const semantics::Scope * scope{context.pdtInstance()->scope()}) { 1077 auto iter{scope->find(parameterName)}; 1078 if (iter != scope->end()) { 1079 const Symbol &symbol{*iter->second}; 1080 const auto *details{symbol.detailsIf<semantics::TypeParamDetails>()}; 1081 if (details) { 1082 isLen = details->attr() == common::TypeParamAttr::Len; 1083 const semantics::MaybeIntExpr &initExpr{details->init()}; 1084 if (initExpr && IsConstantExpr(*initExpr) && 1085 (!isLen || ToInt64(*initExpr))) { 1086 Expr<SomeInteger> expr{*initExpr}; 1087 return Fold(context, 1088 ConvertToType<TypeParamInquiry::Result>(std::move(expr))); 1089 } 1090 } 1091 } 1092 } 1093 if (const auto *value{pdt->FindParameter(parameterName)}) { 1094 if (value->isExplicit()) { 1095 auto folded{Fold(context, 1096 AsExpr(ConvertToType<TypeParamInquiry::Result>( 1097 Expr<SomeInteger>{value->GetExplicit().value()})))}; 1098 if (!isLen || ToInt64(folded)) { 1099 return folded; 1100 } 1101 } 1102 } 1103 } 1104 } 1105 return AsExpr(std::move(inquiry)); 1106 } 1107 1108 std::optional<std::int64_t> ToInt64(const Expr<SomeInteger> &expr) { 1109 return common::visit( 1110 [](const auto &kindExpr) { return ToInt64(kindExpr); }, expr.u); 1111 } 1112 1113 std::optional<std::int64_t> ToInt64(const Expr<SomeType> &expr) { 1114 if (const auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(expr)}) { 1115 return ToInt64(*intExpr); 1116 } else { 1117 return std::nullopt; 1118 } 1119 } 1120 1121 #ifdef _MSC_VER // disable bogus warning about missing definitions 1122 #pragma warning(disable : 4661) 1123 #endif 1124 FOR_EACH_INTEGER_KIND(template class ExpressionBase, ) 1125 template class ExpressionBase<SomeInteger>; 1126 } // namespace Fortran::evaluate 1127