9#ifndef FORTRAN_EVALUATE_FOLD_IMPLEMENTATION_H_
10#define FORTRAN_EVALUATE_FOLD_IMPLEMENTATION_H_
15#include "flang/Common/indirection.h"
16#include "flang/Common/template.h"
17#include "flang/Common/unwrap.h"
18#include "flang/Evaluate/characteristics.h"
19#include "flang/Evaluate/check-expression.h"
20#include "flang/Evaluate/common.h"
21#include "flang/Evaluate/constant.h"
22#include "flang/Evaluate/expression.h"
23#include "flang/Evaluate/fold.h"
24#include "flang/Evaluate/intrinsics-library.h"
25#include "flang/Evaluate/intrinsics.h"
26#include "flang/Evaluate/shape.h"
27#include "flang/Evaluate/tools.h"
28#include "flang/Evaluate/traverse.h"
29#include "flang/Evaluate/type.h"
30#include "flang/Parser/message.h"
31#include "flang/Semantics/scope.h"
32#include "flang/Semantics/symbol.h"
33#include "flang/Semantics/tools.h"
51static constexpr bool useKahanSummation{
false};
54template <
typename T>
class Folder {
56 explicit Folder(
FoldingContext &c,
bool forOptionalArgument =
false)
57 : context_{c}, forOptionalArgument_{forOptionalArgument} {}
58 std::optional<Constant<T>> GetNamedConstant(
const Symbol &);
59 std::optional<Constant<T>> ApplySubscripts(
const Constant<T> &array,
64 std::optional<Constant<T>> GetConstantComponent(
66 std::optional<Constant<T>> Folding(
ArrayRef &);
67 std::optional<Constant<T>> Folding(
DataRef &);
69 Constant<T> *Folding(std::optional<ActualArgument> &);
84 bool forOptionalArgument_{
false};
87std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
91template <
typename TR,
typename... TA>
92std::optional<std::function<Scalar<TR>(
FoldingContext &, Scalar<TA>...)>>
93GetHostRuntimeWrapper(
const std::string &name) {
94 std::vector<DynamicType> argTypes{TA{}.GetType()...};
95 if (
auto hostWrapper{GetHostRuntimeWrapper(name, TR{}.GetType(), argTypes)}) {
98 std::vector<Expr<SomeType>> genericArgs{
100 return GetScalarConstantValue<TR>(
101 (*hostWrapper)(context, std::move(genericArgs)))
116common::IfNoLvalue<Expr<ResultType<A>>, A> FoldOperation(
118 static_assert(!std::is_same_v<A, Expr<ResultType<A>>>,
119 "call Fold() instead for Expr<>");
136 return Folder<T>{context}.Folding(std::move(designator));
151std::optional<Constant<T>> Folder<T>::GetNamedConstant(
const Symbol &symbol0) {
152 const Symbol &symbol{ResolveAssociations(symbol0)};
153 if (IsNamedConstant(symbol)) {
154 if (
const auto *
object{
155 symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
156 if (
const auto *constant{UnwrapConstantValue<T>(object->init())}) {
165std::optional<Constant<T>> Folder<T>::Folding(
ArrayRef &aRef) {
166 std::vector<Constant<SubscriptInteger>> subscripts;
169 if (
auto constant{GetConstantSubscript(context_, ss, aRef.base(), dim++)}) {
170 subscripts.emplace_back(std::move(*constant));
175 if (
Component * component{aRef.base().UnwrapComponent()}) {
176 return GetConstantComponent(*component, &subscripts);
178 GetNamedConstant(aRef.base().GetLastSymbol())}) {
179 return ApplySubscripts(*array, subscripts);
186std::optional<Constant<T>> Folder<T>::Folding(
DataRef &ref) {
187 return common::visit(
189 [
this](SymbolRef &sym) {
return GetNamedConstant(*sym); },
191 comp = FoldOperation(context_, std::move(comp));
192 return GetConstantComponent(comp);
195 aRef = FoldOperation(context_, std::move(aRef));
196 return Folding(aRef);
198 [](
CoarrayRef &) {
return std::optional<Constant<T>>{}; },
205std::optional<Constant<T>> Folder<T>::ApplySubscripts(
const Constant<T> &array,
207 const auto &shape{array.shape()};
208 const auto &lbounds{array.lbounds()};
209 int rank{GetRank(shape)};
210 CHECK(rank ==
static_cast<int>(subscripts.size()));
211 std::size_t elements{1};
212 ConstantSubscripts resultShape;
213 ConstantSubscripts ssLB;
214 for (
const auto &ss : subscripts) {
215 if (ss.Rank() == 1) {
216 resultShape.push_back(
static_cast<ConstantSubscript
>(ss.size()));
217 elements *= ss.size();
218 ssLB.push_back(ss.lbounds().front());
219 }
else if (ss.Rank() > 1) {
223 ConstantSubscripts ssAt(rank, 0), at(rank, 0), tmp(1, 0);
224 std::vector<Scalar<T>> values;
225 while (elements-- > 0) {
226 bool increment{
true};
228 for (
int j{0}; j < rank; ++j) {
229 if (subscripts[j].Rank() == 0) {
230 at[j] = subscripts[j].GetScalarValue().value().ToInt64();
232 CHECK(k < GetRank(resultShape));
233 tmp[0] = ssLB.at(k) + ssAt.at(k);
234 at[j] = subscripts[j].At(tmp).ToInt64();
236 if (++ssAt[k] == resultShape[k]) {
244 if (at[j] < lbounds[j] || at[j] >= lbounds[j] + shape[j]) {
245 context_.messages().Say(
246 "Subscript value (%jd) is out of range on dimension %d in reference to a constant array value"_err_en_US,
251 values.emplace_back(array.At(at));
252 CHECK(!increment || elements == 0);
253 CHECK(k == GetRank(resultShape));
255 if constexpr (T::category == TypeCategory::Character) {
256 return Constant<T>{array.LEN(), std::move(values), std::move(resultShape)};
257 }
else if constexpr (std::is_same_v<T, SomeDerived>) {
258 return Constant<T>{array.result().derivedTypeSpec(), std::move(values),
259 std::move(resultShape)};
261 return Constant<T>{std::move(values), std::move(resultShape)};
266std::optional<Constant<T>> Folder<T>::ApplyComponent(
269 if (
auto scalar{structures.GetScalarValue()}) {
270 if (std::optional<
Expr<SomeType>> expr{scalar->Find(component)}) {
271 if (
const Constant<T> *value{UnwrapConstantValue<T>(*expr)}) {
273 return ApplySubscripts(*value, *subscripts);
281 std::unique_ptr<ArrayConstructor<T>> array;
282 if (structures.empty()) {
285 ConstantSubscripts at{structures.lbounds()};
289 if (
const Constant<T> *value{UnwrapConstantValue<T>(expr.value())}) {
293 auto *typedExpr{UnwrapExpr<Expr<T>>(expr.value())};
295 array = std::make_unique<ArrayConstructor<T>>(*typedExpr);
296 if constexpr (T::category == TypeCategory::Character) {
301 if (
auto element{ApplySubscripts(*value, *subscripts)}) {
302 CHECK(element->Rank() == 0);
303 array->Push(
Expr<T>{std::move(*element)});
308 CHECK(value->Rank() == 0);
315 }
while (structures.IncrementSubscripts(at));
319 if (
auto *constant{UnwrapConstantValue<T>(result)}) {
320 return constant->Reshape(common::Clone(structures.shape()));
327std::optional<Constant<T>> Folder<T>::GetConstantComponent(
Component &component,
331 [&](
const Symbol &symbol) {
341 return std::optional<Constant<SomeDerived>>{};
344 component.base().u)}) {
345 return ApplyComponent(
346 std::move(*structures), component.GetLastSymbol(), subscripts);
353 if constexpr (T::category == TypeCategory::Character) {
354 if (
auto *substring{common::Unwrap<Substring>(designator.u)}) {
356 substring->Fold(context_)}) {
357 if (
const auto *specific{std::get_if<Expr<T>>(&folded->u)}) {
358 return std::move(*specific);
365 }
else if constexpr (T::category == TypeCategory::Real) {
366 if (
auto *zPart{std::get_if<ComplexPart>(&designator.u)}) {
367 *zPart = FoldOperation(context_, std::move(*zPart));
370 return Fold(context_,
372 zPart->part() == ComplexPart::Part::IM,
379 return common::visit(
381 [&](SymbolRef &&symbol) {
382 if (
auto constant{GetNamedConstant(*symbol)}) {
383 return Expr<T>{std::move(*constant)};
385 return Expr<T>{std::move(designator)};
388 aRef = FoldOperation(context_, std::move(aRef));
389 if (
auto c{Folding(aRef)}) {
396 component = FoldOperation(context_, std::move(component));
397 if (
auto c{GetConstantComponent(component)}) {
408 std::move(designator.u));
414Constant<T> *Folder<T>::Folding(std::optional<ActualArgument> &arg) {
415 if (
auto *expr{UnwrapExpr<Expr<SomeType>>(arg)}) {
416 *expr = Fold(context_, std::move(*expr));
417 if constexpr (T::category != TypeCategory::Derived) {
418 if (!UnwrapExpr<
Expr<T>>(*expr)) {
420 var{forOptionalArgument_
421 ? UnwrapWholeSymbolOrComponentDataRef(*expr)
423 var && (IsOptional(*var) || IsAllocatableOrObjectPointer(var))) {
425 }
else if (
auto converted{
426 ConvertToType(T::GetType(), std::move(*expr))}) {
427 *expr = Fold(context_, std::move(*converted));
431 return UnwrapConstantValue<T>(*expr);
436template <
typename... A, std::size_t... I>
437std::optional<std::tuple<const Constant<A> *...>> GetConstantArgumentsHelper(
439 bool hasOptionalArgument, std::index_sequence<I...>) {
440 static_assert(
sizeof...(A) > 0);
441 std::tuple<const Constant<A> *...> args{
442 Folder<A>{context, hasOptionalArgument}.Folding(arguments.at(I))...};
443 if ((... && (std::get<I>(args)))) {
450template <
typename... A>
451std::optional<std::tuple<const Constant<A> *...>> GetConstantArguments(
452 FoldingContext &context, ActualArguments &args,
bool hasOptionalArgument) {
453 return GetConstantArgumentsHelper<A...>(
454 context, args, hasOptionalArgument, std::index_sequence_for<A...>{});
457template <
typename... A, std::size_t... I>
458std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArgumentsHelper(
459 FoldingContext &context, ActualArguments &args,
bool hasOptionalArgument,
460 std::index_sequence<I...>) {
462 GetConstantArguments<A...>(context, args, hasOptionalArgument)}) {
463 return std::tuple<Scalar<A>...>{
464 std::get<I>(*constArgs)->GetScalarValue().value()...};
470template <
typename... A>
471std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArguments(
472 FoldingContext &context, ActualArguments &args,
bool hasOptionalArgument) {
473 return GetScalarConstantArgumentsHelper<A...>(
474 context, args, hasOptionalArgument, std::index_sequence_for<A...>{});
481template <
typename TR,
typename... TArgs>
482using ScalarFunc = std::function<Scalar<TR>(
const Scalar<TArgs> &...)>;
483template <
typename TR,
typename... TArgs>
484using ScalarFuncWithContext =
485 std::function<Scalar<TR>(
FoldingContext &,
const Scalar<TArgs> &...)>;
487template <
template <
typename,
typename...>
typename WrapperType,
typename TR,
488 typename... TA, std::size_t... I>
491 bool hasOptionalArgument, std::index_sequence<I...>) {
492 if (std::optional<std::tuple<
const Constant<TA> *...>> args{
493 GetConstantArguments<TA...>(
494 context, funcRef.arguments(), hasOptionalArgument)}) {
496 ConstantSubscripts shape;
498 const ConstantSubscripts *shapes[]{&std::get<I>(*args)->shape()...};
499 const int ranks[]{std::get<I>(*args)->Rank()...};
500 for (
unsigned int i{0}; i <
sizeof...(TA); ++i) {
506 if (shape != *shapes[i]) {
511 context.messages().Say(
512 "Arguments in elemental intrinsic function are not conformable"_err_en_US);
513 return Expr<TR>{std::move(funcRef)};
518 CHECK(rank == GetRank(shape));
520 std::vector<Scalar<TR>> results;
521 std::optional<uint64_t> n{TotalElementCount(shape)};
523 context.messages().Say(
524 "Too many elements in elemental intrinsic function result"_err_en_US);
525 return Expr<TR>{std::move(funcRef)};
529 ConstantSubscripts resultIndex(rank, 1);
530 ConstantSubscripts argIndex[]{std::get<I>(*args)->lbounds()...};
532 if constexpr (std::is_same_v<WrapperType<TR, TA...>,
533 ScalarFuncWithContext<TR, TA...>>) {
534 results.emplace_back(
535 func(context, std::get<I>(*args)->At(argIndex[I])...));
536 }
else if constexpr (std::is_same_v<WrapperType<TR, TA...>,
537 ScalarFunc<TR, TA...>>) {
538 results.emplace_back(func(std::get<I>(*args)->At(argIndex[I])...));
540 (std::get<I>(*args)->IncrementSubscripts(argIndex[I]), ...);
541 }
while (bounds.IncrementSubscripts(resultIndex));
544 if constexpr (TR::category == TypeCategory::Character) {
545 auto len{
static_cast<ConstantSubscript
>(
546 results.empty() ? 0 : results[0].length())};
548 }
else if constexpr (TR::category == TypeCategory::Derived) {
549 if (!results.empty()) {
553 std::move(results), std::move(shape)}};
559 return Expr<TR>{std::move(funcRef)};
562template <
typename TR,
typename... TA>
565 bool hasOptionalArgument =
false) {
566 return FoldElementalIntrinsicHelper<ScalarFunc, TR, TA...>(context,
567 std::move(funcRef), func, hasOptionalArgument,
568 std::index_sequence_for<TA...>{});
570template <
typename TR,
typename... TA>
573 bool hasOptionalArgument =
false) {
574 return FoldElementalIntrinsicHelper<ScalarFuncWithContext, TR, TA...>(context,
575 std::move(funcRef), func, hasOptionalArgument,
576 std::index_sequence_for<TA...>{});
579std::optional<std::int64_t> GetInt64ArgOr(
580 const std::optional<ActualArgument> &, std::int64_t defaultValue);
582template <
typename A,
typename B>
583std::optional<std::vector<A>> GetIntegerVector(
const B &x) {
584 static_assert(common::is_integral_v<A>);
585 if (
const auto *someInteger{UnwrapExpr<Expr<SomeInteger>>(x)}) {
586 return common::visit(
587 [](
const auto &typedExpr) -> std::optional<std::vector<A>> {
588 using T = ResultType<
decltype(typedExpr)>;
589 if (
const auto *constant{UnwrapConstantValue<T>(typedExpr)}) {
590 if (constant->Rank() == 1) {
591 std::vector<A> result;
592 for (
const auto &value : constant->values()) {
593 result.push_back(
static_cast<A
>(value.ToInt64()));
611 invalid.name = IntrinsicProcTable::InvalidName;
613 ActualArguments{std::move(funcRef.arguments())}}};
617 auto args{funcRef.arguments()};
618 CHECK(args.size() == 3);
619 const auto *array{UnwrapConstantValue<T>(args[0])};
620 const auto *shiftExpr{UnwrapExpr<Expr<SomeInteger>>(args[1])};
621 auto dim{GetInt64ArgOr(args[2], 1)};
622 if (!array || !shiftExpr || !dim) {
623 return Expr<T>{std::move(funcRef)};
625 auto convertedShift{Fold(context_,
627 const auto *shift{UnwrapConstantValue<SubscriptInteger>(convertedShift)};
629 return Expr<T>{std::move(funcRef)};
632 if (*dim < 1 || *dim > array->Rank()) {
633 context_.messages().Say(
"Invalid 'dim=' argument (%jd) in CSHIFT"_err_en_US,
634 static_cast<std::intmax_t
>(*dim));
635 }
else if (shift->Rank() > 0 && shift->Rank() != array->Rank() - 1) {
638 int rank{array->Rank()};
639 int zbDim{
static_cast<int>(*dim) - 1};
641 if (shift->Rank() > 0) {
643 for (
int j{0}; j < rank; ++j) {
645 if (array->shape()[j] != shift->shape()[k]) {
646 context_.messages().Say(
647 "Invalid 'shift=' argument in CSHIFT: extent on dimension %d is %jd but must be %jd"_err_en_US,
648 k + 1,
static_cast<std::intmax_t
>(shift->shape()[k]),
649 static_cast<std::intmax_t
>(array->shape()[j]));
657 std::vector<Scalar<T>> resultElements;
658 ConstantSubscripts arrayLB{array->lbounds()};
659 ConstantSubscripts arrayAt{arrayLB};
660 ConstantSubscript &dimIndex{arrayAt[zbDim]};
661 ConstantSubscript dimLB{dimIndex};
662 ConstantSubscript dimExtent{array->shape()[zbDim]};
663 ConstantSubscripts shiftLB{shift->lbounds()};
664 for (
auto n{GetSize(array->shape())}; n > 0; --n) {
665 ConstantSubscript origDimIndex{dimIndex};
666 ConstantSubscripts shiftAt;
667 if (shift->Rank() > 0) {
669 for (
int j{0}; j < rank; ++j) {
671 shiftAt.emplace_back(shiftLB[k++] + arrayAt[j] - arrayLB[j]);
675 ConstantSubscript shiftCount{shift->At(shiftAt).ToInt64()};
676 dimIndex = dimLB + ((dimIndex - dimLB + shiftCount) % dimExtent);
677 if (dimIndex < dimLB) {
678 dimIndex += dimExtent;
679 }
else if (dimIndex >= dimLB + dimExtent) {
680 dimIndex -= dimExtent;
682 resultElements.push_back(array->At(arrayAt));
683 dimIndex = origDimIndex;
684 array->IncrementSubscripts(arrayAt);
686 return Expr<T>{PackageConstant<T>(
687 std::move(resultElements), *array, array->shape())};
691 return MakeInvalidIntrinsic(std::move(funcRef));
695 auto args{funcRef.arguments()};
696 CHECK(args.size() == 4);
697 const auto *array{UnwrapConstantValue<T>(args[0])};
698 const auto *shiftExpr{UnwrapExpr<Expr<SomeInteger>>(args[1])};
699 auto dim{GetInt64ArgOr(args[3], 1)};
700 if (!array || !shiftExpr || !dim) {
701 return Expr<T>{std::move(funcRef)};
704 auto convertedShift{Fold(context_,
706 const auto *shift{UnwrapConstantValue<SubscriptInteger>(convertedShift)};
708 return Expr<T>{std::move(funcRef)};
711 std::optional<Expr<SomeType>> convertedBoundary;
712 if (
const auto *boundaryExpr{UnwrapExpr<Expr<SomeType>>(args[2])}) {
713 convertedBoundary = Fold(context_,
715 boundary = UnwrapExpr<Constant<T>>(convertedBoundary);
717 return Expr<T>{std::move(funcRef)};
721 if (*dim < 1 || *dim > array->Rank()) {
722 context_.messages().Say(
723 "Invalid 'dim=' argument (%jd) in EOSHIFT"_err_en_US,
724 static_cast<std::intmax_t
>(*dim));
725 }
else if (shift->Rank() > 0 && shift->Rank() != array->Rank() - 1) {
727 }
else if (boundary && boundary->Rank() > 0 &&
728 boundary->Rank() != array->Rank() - 1) {
731 int rank{array->Rank()};
732 int zbDim{
static_cast<int>(*dim) - 1};
734 if (shift->Rank() > 0) {
736 for (
int j{0}; j < rank; ++j) {
738 if (array->shape()[j] != shift->shape()[k]) {
739 context_.messages().Say(
740 "Invalid 'shift=' argument in EOSHIFT: extent on dimension %d is %jd but must be %jd"_err_en_US,
741 k + 1,
static_cast<std::intmax_t
>(shift->shape()[k]),
742 static_cast<std::intmax_t
>(array->shape()[j]));
749 if (boundary && boundary->Rank() > 0) {
751 for (
int j{0}; j < rank; ++j) {
753 if (array->shape()[j] != boundary->shape()[k]) {
754 context_.messages().Say(
755 "Invalid 'boundary=' argument in EOSHIFT: extent on dimension %d is %jd but must be %jd"_err_en_US,
756 k + 1,
static_cast<std::intmax_t
>(boundary->shape()[k]),
757 static_cast<std::intmax_t
>(array->shape()[j]));
765 std::vector<Scalar<T>> resultElements;
766 ConstantSubscripts arrayLB{array->lbounds()};
767 ConstantSubscripts arrayAt{arrayLB};
768 ConstantSubscript &dimIndex{arrayAt[zbDim]};
769 ConstantSubscript dimLB{dimIndex};
770 ConstantSubscript dimExtent{array->shape()[zbDim]};
771 ConstantSubscripts shiftLB{shift->lbounds()};
772 ConstantSubscripts boundaryLB;
774 boundaryLB = boundary->lbounds();
776 for (
auto n{GetSize(array->shape())}; n > 0; --n) {
777 ConstantSubscript origDimIndex{dimIndex};
778 ConstantSubscripts shiftAt;
779 if (shift->Rank() > 0) {
781 for (
int j{0}; j < rank; ++j) {
783 shiftAt.emplace_back(shiftLB[k++] + arrayAt[j] - arrayLB[j]);
787 ConstantSubscript shiftCount{shift->At(shiftAt).ToInt64()};
788 dimIndex += shiftCount;
789 if (dimIndex >= dimLB && dimIndex < dimLB + dimExtent) {
790 resultElements.push_back(array->At(arrayAt));
791 }
else if (boundary) {
792 ConstantSubscripts boundaryAt;
793 if (boundary->Rank() > 0) {
794 for (
int j{0}; j < rank; ++j) {
797 boundaryAt.emplace_back(
798 boundaryLB[k++] + arrayAt[j] - arrayLB[j]);
802 resultElements.push_back(boundary->At(boundaryAt));
803 }
else if constexpr (T::category == TypeCategory::Integer ||
804 T::category == TypeCategory::Unsigned ||
805 T::category == TypeCategory::Real ||
806 T::category == TypeCategory::Complex ||
807 T::category == TypeCategory::Logical) {
808 resultElements.emplace_back();
809 }
else if constexpr (T::category == TypeCategory::Character) {
810 auto len{
static_cast<std::size_t
>(array->LEN())};
811 typename Scalar<T>::value_type space{
' '};
812 resultElements.emplace_back(len, space);
814 DIE(
"no derived type boundary");
816 dimIndex = origDimIndex;
817 array->IncrementSubscripts(arrayAt);
819 return Expr<T>{PackageConstant<T>(
820 std::move(resultElements), *array, array->shape())};
824 return MakeInvalidIntrinsic(std::move(funcRef));
828 return FoldElementalIntrinsic<T, T, T, LogicalResult>(context_,
830 ScalarFunc<T, T, T, LogicalResult>(
831 [](
const Scalar<T> &ifTrue,
const Scalar<T> &ifFalse,
832 const Scalar<LogicalResult> &predicate) -> Scalar<T> {
833 return predicate.IsTrue() ? ifTrue : ifFalse;
838 auto args{funcRef.arguments()};
839 CHECK(args.size() == 3);
840 const auto *array{UnwrapConstantValue<T>(args[0])};
841 const auto *vector{UnwrapConstantValue<T>(args[2])};
842 auto convertedMask{Fold(context_,
843 ConvertToType<LogicalResult>(
845 const auto *mask{UnwrapConstantValue<LogicalResult>(convertedMask)};
846 if (!array || !mask || (args[2] && !vector)) {
847 return Expr<T>{std::move(funcRef)};
850 ConstantSubscript arrayElements{GetSize(array->shape())};
851 ConstantSubscript truths{0};
852 ConstantSubscripts maskAt{mask->lbounds()};
853 if (mask->Rank() == 0) {
854 if (mask->At(maskAt).IsTrue()) {
855 truths = arrayElements;
857 }
else if (array->shape() != mask->shape()) {
859 return MakeInvalidIntrinsic(std::move(funcRef));
861 for (ConstantSubscript j{0}; j < arrayElements;
862 ++j, mask->IncrementSubscripts(maskAt)) {
863 if (mask->At(maskAt).IsTrue()) {
868 std::vector<Scalar<T>> resultElements;
869 ConstantSubscripts arrayAt{array->lbounds()};
870 ConstantSubscript resultSize{truths};
872 resultSize = vector->shape().at(0);
873 if (resultSize < truths) {
874 context_.messages().Say(
875 "Invalid 'vector=' argument in PACK: the 'mask=' argument has %jd true elements, but the vector has only %jd elements"_err_en_US,
876 static_cast<std::intmax_t
>(truths),
877 static_cast<std::intmax_t
>(resultSize));
878 return MakeInvalidIntrinsic(std::move(funcRef));
881 for (ConstantSubscript j{0}; j < truths;) {
882 if (mask->At(maskAt).IsTrue()) {
883 resultElements.push_back(array->At(arrayAt));
886 array->IncrementSubscripts(arrayAt);
887 mask->IncrementSubscripts(maskAt);
890 ConstantSubscripts vectorAt{vector->lbounds()};
891 vectorAt.at(0) += truths;
892 for (ConstantSubscript j{truths}; j < resultSize; ++j) {
893 resultElements.push_back(vector->At(vectorAt));
897 return Expr<T>{PackageConstant<T>(std::move(resultElements), *array,
898 ConstantSubscripts{
static_cast<ConstantSubscript
>(resultSize)})};
902 auto args{funcRef.arguments()};
903 CHECK(args.size() == 4);
904 const auto *source{UnwrapConstantValue<T>(args[0])};
905 const auto *pad{UnwrapConstantValue<T>(args[2])};
906 std::optional<std::vector<ConstantSubscript>> shape{
907 GetIntegerVector<ConstantSubscript>(args[1])};
908 std::optional<std::vector<int>> order{GetIntegerVector<int>(args[3])};
909 std::optional<uint64_t> optResultElement;
910 std::optional<std::vector<int>> dimOrder;
913 if (shape->size() > common::maxRank) {
914 context_.messages().Say(
915 "Size of 'shape=' argument (%zd) must not be greater than %d"_err_en_US,
916 shape->size(), common::maxRank);
918 }
else if (HasNegativeExtent(*shape)) {
919 context_.messages().Say(
920 "'shape=' argument (%s) must not have a negative extent"_err_en_US,
921 DEREF(args[1]->UnwrapExpr()).AsFortran());
924 optResultElement = TotalElementCount(*shape);
925 if (!optResultElement) {
926 context_.messages().Say(
927 "'shape=' argument (%s) specifies an array with too many elements"_err_en_US,
928 DEREF(args[1]->UnwrapExpr()).AsFortran());
933 dimOrder = ValidateDimensionOrder(GetRank(*shape), *order);
935 context_.messages().Say(
936 "Invalid 'order=' argument (%s) in RESHAPE"_err_en_US,
937 DEREF(args[3]->UnwrapExpr()).AsFortran());
944 }
else if (!source || !shape || (args[2] && !pad) || (args[3] && !order)) {
945 return Expr<T>{std::move(funcRef)};
947 uint64_t resultElements{*optResultElement};
948 std::vector<int> *dimOrderPtr{dimOrder ? &dimOrder.value() :
nullptr};
949 if (resultElements > source->size() && (!pad || pad->empty())) {
950 context_.messages().Say(
951 "Too few elements in 'source=' argument and 'pad=' "
952 "argument is not present or has null size"_err_en_US);
956 ? source->Reshape(std::move(shape.value()))
957 : pad->Reshape(std::move(shape.value()))};
958 ConstantSubscripts subscripts{result.lbounds()};
959 auto copied{result.CopyFrom(*source,
960 std::min(
static_cast<uint64_t
>(source->size()), resultElements),
961 subscripts, dimOrderPtr)};
962 if (copied < resultElements) {
964 copied += result.CopyFrom(
965 *pad, resultElements - copied, subscripts, dimOrderPtr);
967 CHECK(copied == resultElements);
968 return Expr<T>{std::move(result)};
972 return MakeInvalidIntrinsic(std::move(funcRef));
976 auto args{funcRef.arguments()};
977 CHECK(args.size() == 3);
978 const Constant<T> *source{UnwrapConstantValue<T>(args[0])};
979 auto dim{ToInt64(args[1])};
980 auto ncopies{ToInt64(args[2])};
981 if (!source || !dim) {
982 return Expr<T>{std::move(funcRef)};
984 int sourceRank{source->Rank()};
985 if (sourceRank >= common::maxRank) {
986 context_.messages().Say(
987 "SOURCE= argument to SPREAD has rank %d but must have rank less than %d"_err_en_US,
988 sourceRank, common::maxRank);
989 }
else if (*dim < 1 || *dim > sourceRank + 1) {
990 context_.messages().Say(
991 "DIM=%d argument to SPREAD must be between 1 and %d"_err_en_US, *dim,
993 }
else if (!ncopies) {
994 return Expr<T>{std::move(funcRef)};
1004 ConstantSubscripts shape{source->shape()};
1005 shape.insert(shape.begin() + *dim - 1, *ncopies);
1006 Constant<T> spread{source->Reshape(std::move(shape))};
1007 std::optional<uint64_t> n{TotalElementCount(spread.shape())};
1009 context_.messages().Say(
"Too many elements in SPREAD result"_err_en_US);
1011 std::vector<int> dimOrder;
1012 for (
int j{0}; j < sourceRank; ++j) {
1013 dimOrder.push_back(j < *dim - 1 ? j : j + 1);
1015 dimOrder.push_back(*dim - 1);
1016 ConstantSubscripts at{spread.lbounds()};
1017 spread.CopyFrom(*source, *n, at, &dimOrder);
1018 return Expr<T>{std::move(spread)};
1022 return MakeInvalidIntrinsic(std::move(funcRef));
1026 auto args{funcRef.arguments()};
1027 CHECK(args.size() == 1);
1028 const auto *matrix{UnwrapConstantValue<T>(args[0])};
1030 return Expr<T>{std::move(funcRef)};
1033 std::vector<Scalar<T>> resultElements;
1034 ConstantSubscripts at(2);
1035 for (ConstantSubscript j{0}; j < matrix->shape()[0]; ++j) {
1036 at[0] = matrix->lbounds()[0] + j;
1037 for (ConstantSubscript k{0}; k < matrix->shape()[1]; ++k) {
1038 at[1] = matrix->lbounds()[1] + k;
1039 resultElements.push_back(matrix->At(at));
1042 at = matrix->shape();
1043 std::swap(at[0], at[1]);
1044 return Expr<T>{PackageConstant<T>(std::move(resultElements), *matrix, at)};
1048 auto args{funcRef.arguments()};
1049 CHECK(args.size() == 3);
1050 const auto *vector{UnwrapConstantValue<T>(args[0])};
1051 auto convertedMask{Fold(context_,
1052 ConvertToType<LogicalResult>(
1054 const auto *mask{UnwrapConstantValue<LogicalResult>(convertedMask)};
1055 const auto *field{UnwrapConstantValue<T>(args[2])};
1056 if (!vector || !mask || !field) {
1057 return Expr<T>{std::move(funcRef)};
1060 if (field->Rank() > 0 && field->shape() != mask->shape()) {
1062 return MakeInvalidIntrinsic(std::move(funcRef));
1064 ConstantSubscript maskElements{GetSize(mask->shape())};
1065 ConstantSubscript truths{0};
1066 ConstantSubscripts maskAt{mask->lbounds()};
1067 for (ConstantSubscript j{0}; j < maskElements;
1068 ++j, mask->IncrementSubscripts(maskAt)) {
1069 if (mask->At(maskAt).IsTrue()) {
1073 if (truths > GetSize(vector->shape())) {
1074 context_.messages().Say(
1075 "Invalid 'vector=' argument in UNPACK: the 'mask=' argument has %jd true elements, but the vector has only %jd elements"_err_en_US,
1076 static_cast<std::intmax_t
>(truths),
1077 static_cast<std::intmax_t
>(GetSize(vector->shape())));
1078 return MakeInvalidIntrinsic(std::move(funcRef));
1080 std::vector<Scalar<T>> resultElements;
1081 ConstantSubscripts vectorAt{vector->lbounds()};
1082 ConstantSubscripts fieldAt{field->lbounds()};
1083 for (ConstantSubscript j{0}; j < maskElements; ++j) {
1084 if (mask->At(maskAt).IsTrue()) {
1085 resultElements.push_back(vector->At(vectorAt));
1086 vector->IncrementSubscripts(vectorAt);
1088 resultElements.push_back(field->At(fieldAt));
1090 mask->IncrementSubscripts(maskAt);
1091 field->IncrementSubscripts(fieldAt);
1094 PackageConstant<T>(std::move(resultElements), *vector, mask->shape())};
1097std::optional<Expr<SomeType>> FoldTransfer(
1101 if (
auto folded{FoldTransfer(context_, funcRef.arguments())}) {
1102 return DEREF(UnwrapExpr<
Expr<T>>(*folded));
1104 return Expr<T>{std::move(funcRef)};
1110template <
typename T>
1113 static_assert(T::category == TypeCategory::Integer ||
1114 T::category == TypeCategory::Unsigned ||
1115 T::category == TypeCategory::Real ||
1116 T::category == TypeCategory::Character);
1139 auto &args{funcRef.arguments()};
1140 std::size_t nargs{args.size()};
1141 bool allArgsConstant{
true};
1142 bool extremumAnyway{nargs == 2 && T::category != TypeCategory::Character};
1146 if (!folder.Folding(args[0])) {
1147 allArgsConstant =
false;
1149 if (!folder.Folding(args[1])) {
1150 allArgsConstant =
false;
1156 for (std::size_t i{2}; i < nargs; ++i) {
1158 if (!folder.Folding(args[i])) {
1159 allArgsConstant =
false;
1167 if (allArgsConstant || extremumAnyway) {
1170 if (
const auto *resultp{UnwrapExpr<Expr<T>>(args[0])}) {
1172 for (std::size_t i{1}; i < nargs; ++i) {
1173 if (
const auto *tExpr{UnwrapExpr<Expr<T>>(args[i])}) {
1174 result = FoldOperation(
1175 context,
Extremum<T>{order, std::move(result), *tExpr});
1178 return Expr<T>{std::move(funcRef)};
1186 return Expr<T>{std::move(funcRef)};
1196template <
typename T>
1197Expr<T> RewriteSpecificMINorMAX(
1199 ActualArguments &args{funcRef.arguments()};
1200 auto &intrinsic{DEREF(std::get_if<SpecificIntrinsic>(&funcRef.proc().u))};
1203 std::optional<DynamicType> resultType;
1205 for (
auto j{args.size()}; j-- > 0;) {
1212 (type.category() == resultType->category() &&
1213 type.kind() > resultType->kind()) ||
1214 resultType->category() == TypeCategory::Integer) {
1216 resultTypeArg = &*args[j];
1221 return Expr<T>{std::move(funcRef)};
1224 intrinsic.name.find(
"max") != std::string::npos ?
"max"s :
"min"s;
1225 intrinsic.characteristics.value().functionResult.value().SetType(*resultType);
1226 auto insertConversion{[&](
const auto &x) ->
Expr<T> {
1227 using TR = ResultType<
decltype(x)>;
1230 return Fold(context, ConvertToType<T>(AsCategoryExpr(std::move(maxRef))));
1232 if (
auto *sx{UnwrapExpr<Expr<SomeReal>>(*resultTypeArg)}) {
1233 return common::visit(insertConversion, sx->u);
1234 }
else if (
auto *sx{UnwrapExpr<Expr<SomeInteger>>(*resultTypeArg)}) {
1235 return common::visit(insertConversion, sx->u);
1237 return Expr<T>{std::move(funcRef)};
1265 ActualArguments &args{funcRef.arguments()};
1267 if (args.size() >= 2 && args[1]) {
1270 auto *expr{args.size() >= 1 && args[0]
1271 ? UnwrapExpr<Expr<SomeDerived>>(args[0])
1276 const auto *derived{GetEnumerationTypeSpec(expr->GetType())};
1277 const semantics::Scope *scope{derived ? derived->GetScope() :
nullptr};
1281 auto ordIter{scope->find(
1282 semantics::SourceName{semantics::DerivedTypeDetails::ordinalComponentName,
1283 sizeof(semantics::DerivedTypeDetails::ordinalComponentName) - 1})};
1284 if (ordIter == scope->end()) {
1287 const semantics::Symbol &ordSym{*ordIter->second};
1288 int count{derived->typeSymbol()
1290 .get<semantics::DerivedTypeDetails>()
1291 .enumeratorCount()};
1292 auto *constant{UnwrapConstantValue<SomeDerived>(*expr)};
1302 if (context.inConstantContext()) {
1303 context.messages().Say(isNext
1304 ?
"NEXT() of the last enumerator is out of range"_err_en_US
1305 :
"PREVIOUS() of the first enumerator is out of range"_err_en_US);
1306 return MakeInvalidIntrinsic<SomeDerived>(std::move(funcRef));
1310 if (
auto sc{constant->GetScalarValue()}) {
1311 if (
auto ordExpr{sc->Find(ordSym)}) {
1312 if (
auto ordVal{ToInt64(*ordExpr)}) {
1313 if (isNext ? *ordVal >= count : *ordVal <= 1) {
1314 return handleBoundary();
1316 int newOrd{isNext ?
static_cast<int>(*ordVal + 1)
1317 : static_cast<int>(*ordVal - 1)};
1324 }
else if (constant->Rank() > 0) {
1330 std::vector<StructureConstructor> elements;
1331 elements.reserve(constant->values().size());
1332 for (
const StructureConstructorValues &scv : constant->values()) {
1333 auto ordVal{ToInt64(scv.find(ordSym)->second.value())};
1334 if (isNext ? *ordVal >= count : *ordVal <= 1) {
1335 return handleBoundary();
1337 int newOrd{isNext ?
static_cast<int>(*ordVal + 1)
1338 : static_cast<int>(*ordVal - 1)};
1342 elements.emplace_back(std::move(ctor));
1345 *derived, std::move(elements), ConstantSubscripts{constant->shape()}}};
1356static inline bool IsTypeOnlyInquiryArg(
1358 const auto &dummies{intrinsic.characteristics.value().dummyArguments};
1359 if (j < dummies.size()) {
1360 if (
const auto *
object{
1361 std::get_if<characteristics::DummyDataObject>(&dummies[j].u)}) {
1362 return object->attrs.test(
1363 characteristics::DummyDataObject::Attr::OnlyIntrinsicInquiry);
1369template <
typename T>
1371 ActualArguments &args{funcRef.arguments()};
1372 const auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
1373 if (!intrinsic || intrinsic->name !=
"kind") {
1376 for (std::optional<ActualArgument> &arg : args) {
1377 if (arg && arg->GetConditionalArg()) {
1378 FoldConditionalArg(context, arg);
1379 }
else if (
auto *expr{UnwrapExpr<Expr<SomeType>>(arg)}) {
1380 if (!intrinsic && IsNamedConstantDesignator(*expr)) {
1390 *expr = Fold(context, std::move(*expr));
1415 std::optional<FunctionRef<T>> viaRepresentative;
1416 for (std::size_t j{0}; j < args.size(); ++j) {
1417 if (args[j] && args[j]->isConditionalArg()) {
1419 DEREF(args[j]->GetConditionalArg())};
1423 if (condArg.HasNilConsequent() ||
1424 !IsTypeOnlyInquiryArg(*intrinsic, j)) {
1425 return Expr<T>{std::move(funcRef)};
1427 if (!viaRepresentative) {
1428 viaRepresentative.emplace(funcRef);
1431 *viaRepresentative->arguments()[j] =
1432 common::Clone(DEREF(condArg.FirstNonNilConsequent()));
1435 if (viaRepresentative) {
1436 Expr<T> folded{FoldOperation(context, std::move(*viaRepresentative))};
1440 return std::holds_alternative<Constant<T>>(folded.u)
1442 :
Expr<T>{std::move(funcRef)};
1444 const std::string name{intrinsic->name};
1445 if (name ==
"cshift") {
1446 return Folder<T>{context}.CSHIFT(std::move(funcRef));
1447 }
else if (name ==
"eoshift") {
1448 return Folder<T>{context}.EOSHIFT(std::move(funcRef));
1449 }
else if (name ==
"merge") {
1450 return Folder<T>{context}.MERGE(std::move(funcRef));
1451 }
else if (name ==
"pack") {
1452 return Folder<T>{context}.PACK(std::move(funcRef));
1453 }
else if (name ==
"reshape") {
1454 return Folder<T>{context}.RESHAPE(std::move(funcRef));
1455 }
else if (name ==
"spread") {
1456 return Folder<T>{context}.SPREAD(std::move(funcRef));
1457 }
else if (name ==
"transfer") {
1458 return Folder<T>{context}.TRANSFER(std::move(funcRef));
1459 }
else if (name ==
"transpose") {
1460 return Folder<T>{context}.TRANSPOSE(std::move(funcRef));
1461 }
else if (name ==
"unpack") {
1462 return Folder<T>{context}.UNPACK(std::move(funcRef));
1465 if constexpr (std::is_same_v<T, SomeDerived>) {
1468 if (name ==
"huge") {
1470 if (args.size() >= 1 && args[0]) {
1471 if (
auto *expr{UnwrapExpr<Expr<SomeDerived>>(args[0])}) {
1472 return std::move(*expr);
1475 }
else if (name ==
"next" || name ==
"previous") {
1476 return FoldEnumerationNextOrPrevious(
1477 context, std::move(funcRef), name ==
"next");
1480 return FoldIntrinsicFunction(context, std::move(funcRef));
1483 return Expr<T>{std::move(funcRef)};
1487template <
typename T>
class ArrayConstructorFolder {
1489 explicit ArrayConstructorFolder(
FoldingContext &c) : context_{c} {}
1492 if constexpr (T::category == TypeCategory::Character) {
1493 if (
const auto *len{array.LEN()}) {
1494 charLength_ = ToInt64(Fold(context_, common::Clone(*len)));
1495 knownCharLength_ = charLength_.has_value();
1499 if (FoldArray(array)) {
1500 auto n{
static_cast<ConstantSubscript
>(elements_.size())};
1501 if constexpr (std::is_same_v<T, SomeDerived>) {
1503 std::move(elements_), ConstantSubscripts{n}}};
1504 }
else if constexpr (T::category == TypeCategory::Character) {
1507 *charLength_, std::move(elements_), ConstantSubscripts{n}}};
1511 std::move(elements_), ConstantSubscripts{n}, resultInfo_}};
1514 return Expr<T>{std::move(array)};
1518 bool FoldArray(
const Expr<T> &expr) {
1519 Expr<T> folded{Fold(context_, common::Clone(expr))};
1520 if (
const auto *c{UnwrapConstantValue<T>(folded)}) {
1523 ConstantSubscripts index{c->lbounds()};
1525 elements_.emplace_back(c->At(index));
1526 }
while (c->IncrementSubscripts(index));
1528 if constexpr (T::category == TypeCategory::Character) {
1529 if (!knownCharLength_) {
1530 charLength_ = std::max(c->LEN(), charLength_.value_or(-1));
1532 }
else if constexpr (T::category == TypeCategory::Real ||
1533 T::category == TypeCategory::Complex) {
1534 if (c->result().isFromInexactLiteralConversion()) {
1535 resultInfo_.set_isFromInexactLiteralConversion();
1543 bool FoldArray(
const common::CopyableIndirection<
Expr<T>> &expr) {
1544 return FoldArray(expr.value());
1553 std::optional<ConstantSubscript> start{ToInt64(
lower)}, end{ToInt64(upper)},
1554 step{ToInt64(stride)};
1555 if (start && end && step && *step != 0) {
1557 ConstantSubscript &j{context_.StartImpliedDo(iDo.name(), *start)};
1559 for (; j <= *end; j += *step) {
1560 result &= FoldArray(iDo.values());
1563 for (; j >= *end; j += *step) {
1564 result &= FoldArray(iDo.values());
1567 context_.EndImpliedDo(iDo.name());
1574 return common::visit([&](
const auto &y) {
return FoldArray(y); }, x.u);
1577 for (
const auto &x : xs) {
1578 if (!FoldArray(x)) {
1586 std::vector<Scalar<T>> elements_;
1587 std::optional<ConstantSubscript> charLength_;
1588 bool knownCharLength_{
false};
1589 typename Constant<T>::Result resultInfo_;
1592template <
typename T>
1605template <
typename T>
1606bool ArrayConstructorIsFlat(
const ArrayConstructorValues<T> &values) {
1607 for (
const ArrayConstructorValue<T> &x : values) {
1608 if (!std::holds_alternative<Expr<T>>(x.u)) {
1615template <
typename T>
1616std::optional<Expr<T>> AsFlatArrayConstructor(
const Expr<T> &expr) {
1617 if (
const auto *c{UnwrapConstantValue<T>(expr)}) {
1620 ConstantSubscripts at{c->lbounds()};
1623 }
while (c->IncrementSubscripts(at));
1625 return std::make_optional<Expr<T>>(std::move(result));
1626 }
else if (
const auto *a{UnwrapExpr<ArrayConstructor<T>>(expr)}) {
1627 if (ArrayConstructorIsFlat(*a)) {
1628 return std::make_optional<Expr<T>>(expr);
1630 }
else if (
const auto *p{UnwrapExpr<Parentheses<T>>(expr)}) {
1631 return AsFlatArrayConstructor(
Expr<T>{p->left()});
1633 return std::nullopt;
1636template <TypeCategory CAT>
1637std::enable_if_t<CAT != TypeCategory::Derived,
1638 std::optional<Expr<SomeKind<CAT>>>>
1640 return common::visit(
1642 if (
auto flattened{AsFlatArrayConstructor(kindExpr)}) {
1645 return std::nullopt;
1655template <
typename T>
1656std::optional<Expr<T>> FromArrayConstructor(
1658 if (
auto constShape{AsConstantExtents(context, shape)};
1659 constShape && !HasNegativeExtent(*constShape)) {
1661 if (
auto *constant{UnwrapConstantValue<T>(result)}) {
1663 return Expr<T>{constant->Reshape(std::move(*constShape))};
1665 if (constShape->size() == 1) {
1666 if (
auto elements{GetShape(context, result)}) {
1667 if (
auto constElements{AsConstantExtents(context, *elements)}) {
1668 if (constElements->size() == 1 &&
1669 constElements->at(0) == constShape->at(0)) {
1672 return std::move(result);
1678 return std::nullopt;
1689template <
typename RESULT,
typename OPERAND>
1695 if constexpr (common::HasMember<OPERAND, AllIntrinsicCategoryTypes>) {
1697 [&](
auto &&kindExpr) {
1698 using kindType = ResultType<
decltype(kindExpr)>;
1699 auto &aConst{std::get<ArrayConstructor<kindType>>(kindExpr.u)};
1700 for (
auto &acValue : aConst) {
1701 auto &scalar{std::get<Expr<kindType>>(acValue.u)};
1702 result.Push(Fold(context, f(
Expr<OPERAND>{std::move(scalar)})));
1705 std::move(values.u));
1707 auto &aConst{std::get<ArrayConstructor<OPERAND>>(values.u)};
1708 for (
auto &acValue : aConst) {
1709 auto &scalar{std::get<Expr<OPERAND>>(acValue.u)};
1710 result.Push(Fold(context, f(std::move(scalar))));
1713 if constexpr (RESULT::category == TypeCategory::Character) {
1715 result.set_LEN(std::move(*length));
1718 return FromArrayConstructor(context, std::move(result), shape);
1721template <
typename RESULT,
typename A>
1725 if constexpr (RESULT::category == TypeCategory::Character) {
1727 result.set_LEN(std::move(*length));
1733template <
typename LEFT,
typename RIGHT>
1737 auto rightIter{rightArrConst.begin()};
1738 for (
auto &leftValue : leftArrConst) {
1739 CHECK(rightIter != rightArrConst.end());
1740 auto &leftExpr{std::get<Expr<LEFT>>(leftValue.u)};
1741 auto &rightExpr{std::get<Expr<RIGHT>>(rightIter->u)};
1742 if (leftExpr.Rank() != rightExpr.Rank()) {
1745 std::optional<Shape> leftShape{GetShape(context, leftExpr)};
1746 std::optional<Shape> rightShape{GetShape(context, rightExpr)};
1747 if (!leftShape || !rightShape || *leftShape != *rightShape) {
1756template <
typename RESULT,
typename LEFT,
typename RIGHT>
1761 -> std::optional<Expr<RESULT>> {
1762 auto result{ArrayConstructorFromMold<RESULT>(leftValues, std::move(length))};
1763 auto &leftArrConst{std::get<ArrayConstructor<LEFT>>(leftValues.u)};
1764 if constexpr (common::HasMember<RIGHT, AllIntrinsicCategoryTypes>) {
1765 bool mapped{common::visit(
1766 [&](
auto &&kindExpr) ->
bool {
1767 using kindType = ResultType<
decltype(kindExpr)>;
1769 auto &rightArrConst{std::get<ArrayConstructor<kindType>>(kindExpr.u)};
1770 if (!ShapesMatch(context, leftArrConst, rightArrConst)) {
1773 auto rightIter{rightArrConst.begin()};
1774 for (
auto &leftValue : leftArrConst) {
1775 CHECK(rightIter != rightArrConst.end());
1776 auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
1777 auto &rightScalar{std::get<Expr<kindType>>(rightIter->u)};
1778 result.Push(Fold(context,
1779 f(std::move(leftScalar),
Expr<RIGHT>{std::move(rightScalar)})));
1784 std::move(rightValues.u))};
1786 return std::nullopt;
1789 auto &rightArrConst{std::get<ArrayConstructor<RIGHT>>(rightValues.u)};
1790 if (!ShapesMatch(context, leftArrConst, rightArrConst)) {
1791 return std::nullopt;
1793 auto rightIter{rightArrConst.begin()};
1794 for (
auto &leftValue : leftArrConst) {
1795 CHECK(rightIter != rightArrConst.end());
1796 auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
1797 auto &rightScalar{std::get<Expr<RIGHT>>(rightIter->u)};
1799 Fold(context, f(std::move(leftScalar), std::move(rightScalar))));
1803 return FromArrayConstructor(context, std::move(result), shape);
1807template <
typename RESULT,
typename LEFT,
typename RIGHT>
1812 -> std::optional<Expr<RESULT>> {
1813 auto result{ArrayConstructorFromMold<RESULT>(leftValues, std::move(length))};
1814 auto &leftArrConst{std::get<ArrayConstructor<LEFT>>(leftValues.u)};
1815 for (
auto &leftValue : leftArrConst) {
1816 auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
1818 Fold(context, f(std::move(leftScalar),
Expr<RIGHT>{rightScalar})));
1820 return FromArrayConstructor(context, std::move(result), shape);
1824template <
typename RESULT,
typename LEFT,
typename RIGHT>
1829 -> std::optional<Expr<RESULT>> {
1830 auto result{ArrayConstructorFromMold<RESULT>(leftScalar, std::move(length))};
1831 if constexpr (common::HasMember<RIGHT, AllIntrinsicCategoryTypes>) {
1833 [&](
auto &&kindExpr) {
1834 using kindType = ResultType<
decltype(kindExpr)>;
1835 auto &rightArrConst{std::get<ArrayConstructor<kindType>>(kindExpr.u)};
1836 for (
auto &rightValue : rightArrConst) {
1837 auto &rightScalar{std::get<Expr<kindType>>(rightValue.u)};
1838 result.Push(Fold(context,
1843 std::move(rightValues.u));
1845 auto &rightArrConst{std::get<ArrayConstructor<RIGHT>>(rightValues.u)};
1846 for (
auto &rightValue : rightArrConst) {
1847 auto &rightScalar{std::get<Expr<RIGHT>>(rightValue.u)};
1849 Fold(context, f(
Expr<LEFT>{leftScalar}, std::move(rightScalar))));
1852 return FromArrayConstructor(context, std::move(result), shape);
1855template <
typename DERIVED,
typename RESULT,
typename... OPD>
1856std::optional<Expr<SubscriptInteger>> ComputeResultLength(
1858 if constexpr (RESULT::category == TypeCategory::Character) {
1861 return std::nullopt;
1868template <
typename DERIVED,
typename RESULT,
typename OPERAND>
1872 -> std::optional<Expr<RESULT>> {
1873 auto &expr{operation.left()};
1874 expr = Fold(context, std::move(expr));
1875 if (expr.Rank() > 0) {
1876 if (std::optional<Shape> shape{GetShape(context, expr)}) {
1877 if (
auto values{AsFlatArrayConstructor(expr)}) {
1878 return MapOperation(context, std::move(f), *shape,
1879 ComputeResultLength(operation), std::move(*values));
1883 return std::nullopt;
1886template <
typename DERIVED,
typename RESULT,
typename OPERAND>
1887auto ApplyElementwise(
1889 -> std::optional<Expr<RESULT>> {
1890 return ApplyElementwise(context, operation,
1897template <
typename DERIVED,
typename RESULT,
typename LEFT,
typename RIGHT>
1901 -> std::optional<Expr<RESULT>> {
1902 auto resultLength{ComputeResultLength(operation)};
1903 auto &leftExpr{operation.left()};
1904 auto &rightExpr{operation.right()};
1905 if (leftExpr.Rank() != rightExpr.Rank() && leftExpr.Rank() != 0 &&
1906 rightExpr.Rank() != 0) {
1907 return std::nullopt;
1909 leftExpr = Fold(context, std::move(leftExpr));
1910 rightExpr = Fold(context, std::move(rightExpr));
1911 if (leftExpr.Rank() > 0) {
1912 if (std::optional<Shape> leftShape{GetShape(context, leftExpr)}) {
1913 if (
auto left{AsFlatArrayConstructor(leftExpr)}) {
1914 if (rightExpr.Rank() > 0) {
1915 if (std::optional<Shape> rightShape{GetShape(context, rightExpr)}) {
1916 if (
auto right{AsFlatArrayConstructor(rightExpr)}) {
1917 if (CheckConformance(context.messages(), *leftShape, *rightShape,
1918 CheckConformanceFlags::EitherScalarExpandable)
1919 .value_or(
false )) {
1920 return MapOperation(context, std::move(f), *leftShape,
1921 std::move(resultLength), std::move(*left),
1924 return std::nullopt;
1926 return MapOperation(context, std::move(f), *leftShape,
1927 std::move(resultLength), std::move(*left), std::move(*right));
1930 }
else if (IsExpandableScalar(rightExpr, context, *leftShape)) {
1931 return MapOperation(context, std::move(f), *leftShape,
1932 std::move(resultLength), std::move(*left), rightExpr);
1936 }
else if (rightExpr.Rank() > 0) {
1937 if (std::optional<Shape> rightShape{GetShape(context, rightExpr)}) {
1938 if (IsExpandableScalar(leftExpr, context, *rightShape)) {
1939 if (
auto right{AsFlatArrayConstructor(rightExpr)}) {
1940 return MapOperation(context, std::move(f), *rightShape,
1941 std::move(resultLength), leftExpr, std::move(*right));
1946 return std::nullopt;
1949template <
typename DERIVED,
typename RESULT,
typename LEFT,
typename RIGHT>
1950auto ApplyElementwise(
1952 -> std::optional<Expr<RESULT>> {
1953 return ApplyElementwise(context, operation,
1956 return Expr<RESULT>{DERIVED{std::move(left), std::move(right)}};
1962template <
typename TO,
typename FROM>
1963common::IfNoLvalue<std::optional<TO>, FROM> ConvertString(FROM &&s) {
1964 if constexpr (std::is_same_v<TO, FROM>) {
1965 return std::make_optional<TO>(std::move(s));
1970 for (
auto iter{s.cbegin()}; iter != s.cend(); ++iter) {
1971 if (
static_cast<std::uint64_t
>(*iter) > 127) {
1972 return std::nullopt;
1974 str.push_back(
static_cast<typename TO::value_type
>(*iter));
1976 return std::make_optional<TO>(std::move(str));
1980template <
typename TO, TypeCategory FROMCAT>
1983 if (
auto array{ApplyElementwise(context, convert)}) {
1989 } msvcWorkaround{context, convert};
1990 return common::visit(
1991 [&msvcWorkaround](
auto &kindExpr) ->
Expr<TO> {
1992 using Operand = ResultType<
decltype(kindExpr)>;
1995 TypeCategory
constexpr FromCat{FROMCAT};
1996 static_assert(FromCat == Operand::category);
1997 auto &convert{msvcWorkaround.convert};
1998 if (
auto value{GetScalarConstantValue<Operand>(kindExpr)}) {
2000 if constexpr (TO::category == TypeCategory::Integer) {
2001 if constexpr (FromCat == TypeCategory::Integer) {
2002 auto converted{Scalar<TO>::ConvertSigned(*value)};
2003 if (converted.overflow) {
2004 ctx.Warn(common::UsageWarning::FoldingException,
2005 "conversion of %s_%d to INTEGER(%d) overflowed; result is %s"_warn_en_US,
2006 value->SignedDecimal(), Operand::kind, TO::kind,
2007 converted.value.SignedDecimal());
2009 return ScalarConstantToExpr(std::move(converted.value));
2010 }
else if constexpr (FromCat == TypeCategory::Unsigned) {
2011 auto converted{Scalar<TO>::ConvertUnsigned(*value)};
2012 if ((converted.overflow || converted.value.IsNegative())) {
2013 ctx.Warn(common::UsageWarning::FoldingException,
2014 "conversion of %s_U%d to INTEGER(%d) overflowed; result is %s"_warn_en_US,
2015 value->UnsignedDecimal(), Operand::kind, TO::kind,
2016 converted.value.SignedDecimal());
2018 return ScalarConstantToExpr(std::move(converted.value));
2019 }
else if constexpr (FromCat == TypeCategory::Real) {
2020 auto converted{value->template ToInteger<Scalar<TO>>()};
2021 if (converted.flags.test(RealFlag::InvalidArgument)) {
2022 ctx.Warn(common::UsageWarning::FoldingException,
2023 "REAL(%d) to INTEGER(%d) conversion: invalid argument"_warn_en_US,
2024 Operand::kind, TO::kind);
2025 }
else if (converted.flags.test(RealFlag::Overflow)) {
2026 ctx.Warn(common::UsageWarning::FoldingException,
2027 "REAL(%d) to INTEGER(%d) conversion overflowed"_warn_en_US,
2028 Operand::kind, TO::kind);
2030 return ScalarConstantToExpr(std::move(converted.value));
2032 }
else if constexpr (TO::category == TypeCategory::Unsigned) {
2033 if constexpr (FromCat == TypeCategory::Integer ||
2034 FromCat == TypeCategory::Unsigned) {
2036 Constant<TO>{Scalar<TO>::ConvertUnsigned(*value).value}};
2037 }
else if constexpr (FromCat == TypeCategory::Real) {
2039 Constant<TO>{value->template ToInteger<Scalar<TO>>().value}};
2041 }
else if constexpr (TO::category == TypeCategory::Real) {
2042 if constexpr (FromCat == TypeCategory::Integer ||
2043 FromCat == TypeCategory::Unsigned) {
2044 auto converted{Scalar<TO>::FromInteger(
2045 *value, FromCat == TypeCategory::Unsigned)};
2046 if (!converted.flags.empty()) {
2048 std::snprintf(buffer,
sizeof buffer,
2049 "INTEGER(%d) to REAL(%d) conversion", Operand::kind,
2051 ctx.RealFlagWarnings(converted.flags, buffer);
2053 return ScalarConstantToExpr(std::move(converted.value));
2054 }
else if constexpr (FromCat == TypeCategory::Real) {
2055 auto converted{Scalar<TO>::Convert(*value)};
2057 if (!converted.flags.empty()) {
2058 std::snprintf(buffer,
sizeof buffer,
2059 "REAL(%d) to REAL(%d) conversion", Operand::kind, TO::kind);
2060 ctx.RealFlagWarnings(converted.flags, buffer);
2062 if (ctx.targetCharacteristics().areSubnormalsFlushedToZero()) {
2063 converted.value = converted.value.FlushSubnormalToZero();
2065 return ScalarConstantToExpr(std::move(converted.value));
2067 }
else if constexpr (TO::category == TypeCategory::Complex) {
2068 if constexpr (FromCat == TypeCategory::Complex) {
2069 return FoldOperation(ctx,
2076 }
else if constexpr (TO::category == TypeCategory::Character &&
2077 FromCat == TypeCategory::Character) {
2078 if (
auto converted{ConvertString<Scalar<TO>>(std::move(*value))}) {
2079 return ScalarConstantToExpr(std::move(*converted));
2081 }
else if constexpr (TO::category == TypeCategory::Logical &&
2082 FromCat == TypeCategory::Logical) {
2085 }
else if constexpr (TO::category == FromCat &&
2086 FromCat != TypeCategory::Character) {
2088 if constexpr (std::is_same_v<Operand, TO>) {
2089 return std::move(kindExpr);
2090 }
else if constexpr (TO::category == TypeCategory::Logical ||
2091 TO::category == TypeCategory::Integer) {
2092 if (
auto *innerConv{
2093 std::get_if<Convert<Operand, TO::category>>(&kindExpr.u)}) {
2095 if (
auto *x{std::get_if<Expr<TO>>(&innerConv->left().u)}) {
2096 if constexpr (TO::category == TypeCategory::Logical ||
2097 TO::kind <= Operand::kind) {
2098 return std::move(*x);
2100 }
else if constexpr (std::is_same_v<TO,
2101 DescriptorInquiry::Result>) {
2102 if (std::holds_alternative<DescriptorInquiry>(x->u) ||
2103 std::holds_alternative<TypeParamInquiry>(x->u)) {
2105 return std::move(*x);
2112 return Expr<TO>{std::move(convert)};
2117template <
typename T>
2119 auto &operand{x.left()};
2120 operand = Fold(context, std::move(operand));
2121 if (
auto value{GetScalarConstantValue<T>(operand)}) {
2126 return std::move(operand);
2132template <
typename T>
2134 if (
auto array{ApplyElementwise(context, x)}) {
2137 auto &operand{x.left()};
2138 if (
auto *nn{std::get_if<Negate<T>>(&x.left().u)}) {
2140 if (IsVariable(nn->left())) {
2141 return FoldOperation(context,
Parentheses<T>{std::move(nn->left())});
2143 return std::move(nn->left());
2145 }
else if (
auto value{GetScalarConstantValue<T>(operand)}) {
2146 if constexpr (T::category == TypeCategory::Integer) {
2147 auto negated{value->Negate()};
2148 if (negated.overflow) {
2149 context.Warn(common::UsageWarning::FoldingException,
2150 "INTEGER(%d) negation overflowed"_warn_en_US, T::kind);
2153 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2165template <
typename LEFT,
typename RIGHT>
2166std::optional<std::pair<Scalar<LEFT>, Scalar<RIGHT>>> OperandsAreConstants(
2168 if (
auto xvalue{GetScalarConstantValue<LEFT>(x)}) {
2169 if (
auto yvalue{GetScalarConstantValue<RIGHT>(y)}) {
2170 return {std::make_pair(*xvalue, *yvalue)};
2173 return std::nullopt;
2176template <
typename DERIVED,
typename RESULT,
typename LEFT,
typename RIGHT>
2177std::optional<std::pair<Scalar<LEFT>, Scalar<RIGHT>>> OperandsAreConstants(
2179 return OperandsAreConstants(operation.left(), operation.right());
2182template <
typename T>
2184 if (
auto array{ApplyElementwise(context, x)}) {
2187 if (
auto folded{OperandsAreConstants(x)}) {
2188 if constexpr (T::category == TypeCategory::Integer) {
2189 auto sum{folded->first.AddSigned(folded->second)};
2191 context.Warn(common::UsageWarning::FoldingException,
2192 "INTEGER(%d) addition overflowed"_warn_en_US, T::kind);
2195 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2197 Constant<T>{folded->first.AddUnsigned(folded->second).value}};
2199 auto sum{folded->first.Add(
2200 folded->second, context.targetCharacteristics().roundingMode())};
2201 context.RealFlagWarnings(sum.flags,
"addition");
2202 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2203 sum.value = sum.value.FlushSubnormalToZero();
2207 }
else if constexpr (T::category == TypeCategory::Integer ||
2208 T::category == TypeCategory::Unsigned) {
2209 if (
auto c{GetScalarConstantValue<T>(x.right())}) {
2210 if (c->IsZero() && x.left().Rank() == 0) {
2211 if (IsVariable(x.left())) {
2212 return FoldOperation(context,
Parentheses<T>{std::move(x.left())});
2214 return std::move(x.left());
2217 }
else if (
auto c{GetScalarConstantValue<T>(x.left())}) {
2218 if (c->IsZero() && x.right().Rank() == 0) {
2219 if (IsVariable(x.right())) {
2220 return FoldOperation(context,
Parentheses<T>{std::move(x.right())});
2222 return std::move(x.right());
2230template <
typename T>
2232 if (
auto array{ApplyElementwise(context, x)}) {
2235 if (
auto folded{OperandsAreConstants(x)}) {
2236 if constexpr (T::category == TypeCategory::Integer) {
2237 auto difference{folded->first.SubtractSigned(folded->second)};
2238 if (difference.overflow) {
2239 context.Warn(common::UsageWarning::FoldingException,
2240 "INTEGER(%d) subtraction overflowed"_warn_en_US, T::kind);
2243 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2245 Constant<T>{folded->first.SubtractSigned(folded->second).value}};
2247 auto difference{folded->first.Subtract(
2248 folded->second, context.targetCharacteristics().roundingMode())};
2249 context.RealFlagWarnings(difference.flags,
"subtraction");
2250 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2251 difference.value = difference.value.FlushSubnormalToZero();
2255 }
else if constexpr (T::category == TypeCategory::Integer ||
2256 T::category == TypeCategory::Unsigned) {
2257 if (
auto c{GetScalarConstantValue<T>(x.right())}) {
2258 if (c->IsZero() && x.left().Rank() == 0) {
2259 if (IsVariable(x.left())) {
2260 return FoldOperation(context,
Parentheses<T>{std::move(x.left())});
2262 return std::move(x.left());
2270template <
typename T>
2272 if (
auto array{ApplyElementwise(context, x)}) {
2275 if (
auto folded{OperandsAreConstants(x)}) {
2276 if constexpr (T::category == TypeCategory::Integer) {
2277 auto product{folded->first.MultiplySigned(folded->second)};
2278 if (product.SignedMultiplicationOverflowed()) {
2279 context.Warn(common::UsageWarning::FoldingException,
2280 "INTEGER(%d) multiplication overflowed"_warn_en_US, T::kind);
2283 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2285 Constant<T>{folded->first.MultiplyUnsigned(folded->second).lower}};
2287 auto product{folded->first.Multiply(
2288 folded->second, context.targetCharacteristics().roundingMode())};
2289 context.RealFlagWarnings(product.flags,
"multiplication");
2290 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2291 product.value = product.value.FlushSubnormalToZero();
2295 }
else if constexpr (T::category == TypeCategory::Integer) {
2296 if (
auto c{GetScalarConstantValue<T>(x.right())}) {
2297 x.right() = std::move(x.left());
2298 x.left() =
Expr<T>{std::move(*c)};
2300 if (
auto c{GetScalarConstantValue<T>(x.left())}) {
2301 if (c->IsZero() && x.right().Rank() == 0) {
2302 return std::move(x.left());
2303 }
else if (c->CompareSigned(Scalar<T>{1}) == Ordering::Equal) {
2304 if (IsVariable(x.right())) {
2305 return FoldOperation(context,
Parentheses<T>{std::move(x.right())});
2307 return std::move(x.right());
2309 }
else if (c->CompareSigned(Scalar<T>{-1}) == Ordering::Equal) {
2310 return FoldOperation(context,
Negate<T>{std::move(x.right())});
2317template <
typename T>
2319 if (
auto array{ApplyElementwise(context, x)}) {
2322 if (
auto folded{OperandsAreConstants(x)}) {
2323 if constexpr (T::category == TypeCategory::Integer) {
2324 auto quotAndRem{folded->first.DivideSigned(folded->second)};
2325 if (quotAndRem.divisionByZero) {
2326 context.Warn(common::UsageWarning::FoldingException,
2327 "INTEGER(%d) division by zero"_warn_en_US, T::kind);
2330 if (quotAndRem.overflow) {
2331 context.Warn(common::UsageWarning::FoldingException,
2332 "INTEGER(%d) division overflowed"_warn_en_US, T::kind);
2335 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2336 auto quotAndRem{folded->first.DivideUnsigned(folded->second)};
2337 if (quotAndRem.divisionByZero) {
2338 context.Warn(common::UsageWarning::FoldingException,
2339 "UNSIGNED(%d) division by zero"_warn_en_US, T::kind);
2344 auto quotient{folded->first.Divide(
2345 folded->second, context.targetCharacteristics().roundingMode())};
2349 bool isCanonicalNaNOrInf{
false};
2350 if constexpr (T::category == TypeCategory::Real) {
2351 if (folded->second.IsZero() && context.moduleFileName().has_value()) {
2352 using IntType =
typename T::Scalar::Word;
2353 auto intNumerator{folded->first.template ToInteger<IntType>()};
2354 isCanonicalNaNOrInf = intNumerator.flags == RealFlags{} &&
2355 intNumerator.value >= IntType{-1} &&
2356 intNumerator.value <= IntType{1};
2359 if (!isCanonicalNaNOrInf) {
2360 context.RealFlagWarnings(quotient.flags,
"division");
2362 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2363 quotient.value = quotient.value.FlushSubnormalToZero();
2371template <
typename T>
2373 if (
auto array{ApplyElementwise(context, x)}) {
2376 if (
auto folded{OperandsAreConstants(x)}) {
2377 if constexpr (T::category == TypeCategory::Integer) {
2378 auto power{folded->first.Power(folded->second)};
2379 if (power.divisionByZero) {
2380 context.Warn(common::UsageWarning::FoldingException,
2381 "INTEGER(%d) zero to negative power"_warn_en_US, T::kind);
2382 }
else if (power.overflow) {
2383 context.Warn(common::UsageWarning::FoldingException,
2384 "INTEGER(%d) power overflowed"_warn_en_US, T::kind);
2385 }
else if (power.zeroToZero) {
2386 context.Warn(common::UsageWarning::FoldingException,
2387 "INTEGER(%d) 0**0 is not defined"_warn_en_US, T::kind);
2391 if (folded->first.IsZero()) {
2392 if (folded->second.IsZero()) {
2393 context.Warn(common::UsageWarning::FoldingException,
2394 "REAL/COMPLEX 0**0 is not defined"_warn_en_US);
2398 }
else if (
auto callable{GetHostRuntimeWrapper<T, T, T>(
"pow")}) {
2400 Constant<T>{(*callable)(context, folded->first, folded->second)}};
2402 context.Warn(common::UsageWarning::FoldingFailure,
2403 "Power for %s cannot be folded on host"_warn_en_US,
2411template <
typename T>
2413 if (
auto array{ApplyElementwise(context, x)}) {
2416 return common::visit(
2418 if (
auto folded{OperandsAreConstants(x.left(), y)}) {
2420 if constexpr (T::category == TypeCategory::Complex && T::kind == 4) {
2424 using DoubleComplex = Scalar<Type<TypeCategory::Complex, 8>>;
2425 using DoublePart = DoubleComplex::Part;
2426 using SinglePart =
typename Scalar<T>::Part;
2428 DoubleComplex wideBase{
2429 DoublePart::Convert(folded->first.REAL()).value,
2430 DoublePart::Convert(folded->first.AIMAG()).value};
2431 auto wide{evaluate::IntPower(wideBase, folded->second)};
2432 power.flags = wide.flags;
2433 power.value = Scalar<T>{SinglePart::Convert(wide.value.REAL())
2434 .AccumulateFlags(power.flags),
2435 SinglePart::Convert(wide.value.AIMAG())
2436 .AccumulateFlags(power.flags)};
2438 power = evaluate::IntPower(folded->first, folded->second);
2440 context.RealFlagWarnings(power.flags,
"power with INTEGER exponent");
2441 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2442 power.value = power.value.FlushSubnormalToZero();
2452template <
typename T>
2454 x.condition() = Fold(context, std::move(x.condition()));
2456 if (
auto cst{GetScalarConstantValue<LogicalResult>(x.condition())}) {
2457 return cst->IsTrue() ? Fold(context, std::move(x.thenValue()))
2458 : Fold(context, std::move(x.elseValue()));
2463template <
typename T>
2465 if (
auto array{ApplyElementwise(context, x,
2472 if (
auto folded{OperandsAreConstants(x)}) {
2473 if constexpr (T::category == TypeCategory::Integer) {
2474 if (folded->first.CompareSigned(folded->second) == x.ordering) {
2477 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2478 if (folded->first.CompareUnsigned(folded->second) == x.ordering) {
2481 }
else if constexpr (T::category == TypeCategory::Real) {
2482 if (folded->first.IsNotANumber() ||
2483 (folded->first.Compare(folded->second) == Relation::Less) ==
2484 (x.ordering == Ordering::Less)) {
2488 static_assert(T::category == TypeCategory::Character);
2491 auto maxLen{std::max(folded->first.length(), folded->second.length())};
2492 bool isFirst{x.ordering == Compare(folded->first, folded->second)};
2493 auto res{isFirst ? std::move(folded->first) : std::move(folded->second)};
2494 res = res.length() == maxLen
2496 : CharacterUtils<T::kind>::Resize(res, maxLen);
2508 std::optional<Expr<Result>> result;
2511 using From = std::decay_t<
decltype(x)>;
2512 if constexpr (std::is_same_v<From, BOZLiteralConstant>) {
2515 result = ConvertToType<Result>(std::move(x));
2516 const auto *constant{UnwrapExpr<Constant<Result>>(*result)};
2518 Scalar<Result> real{constant->GetScalarValue().value()};
2519 From converted{From::ConvertUnsigned(real.RawBits()).value};
2520 if (original != converted) {
2521 context.Warn(common::UsageWarning::FoldingValueChecks,
2522 "Nonzero bits truncated from BOZ literal constant in REAL intrinsic"_warn_en_US);
2524 }
else if constexpr (IsNumericCategoryExpr<From>()) {
2525 result = Fold(context, ConvertToType<Result>(std::move(x)));
2527 common::die(
"ToReal: bad argument expression");
2531 return result.value();
2540 if (
auto array{ApplyElementwise(context, x,
2544 x.isImaginaryPart, std::move(operand)}};
2548 auto &operand{x.left()};
2549 if (
auto value{GetScalarConstantValue<Operand>(operand)}) {
2550 if (x.isImaginaryPart) {
2559template <
typename T>
2561 return common::visit(
2563 if constexpr (IsSpecificIntrinsicType<T>) {
2564 return FoldOperation(context, std::move(x));
2565 }
else if constexpr (std::is_same_v<T, SomeDerived>) {
2566 return FoldOperation(context, std::move(x));
2567 }
else if constexpr (common::HasMember<
decltype(x),
2568 TypelessExpression>) {
2569 return std::move(expr);
2571 return Expr<T>{Fold(context, std::move(x))};
Definition fold-implementation.h:1487
Definition expression.h:478
Definition expression.h:506
Definition variable.h:205
Definition variable.h:243
Definition variable.h:357
Definition expression.h:394
Definition constant.h:147
Definition variable.h:381
Definition expression.h:65
Definition fold-implementation.h:54
Definition expression.h:444
Definition variable.h:101
Definition expression.h:113
Definition variable.h:442
Definition expression.h:784
Definition variable.h:304
Definition variable.h:160
Definition variable.h:136
Definition ParserActions.h:24
Definition expression.h:295
Definition expression.h:472
Definition expression.h:256
Definition expression.h:356
Definition expression.h:210
Definition variable.h:288
Definition expression.h:316
Definition expression.h:339
Definition expression.h:436
Definition expression.h:309
Definition expression.h:246
Definition expression.h:228
Definition expression.h:323
Definition expression.h:331
Definition variable.h:191
Definition expression.h:302