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/common.h"
20#include "flang/Evaluate/constant.h"
21#include "flang/Evaluate/expression.h"
22#include "flang/Evaluate/fold.h"
23#include "flang/Evaluate/intrinsics-library.h"
24#include "flang/Evaluate/intrinsics.h"
25#include "flang/Evaluate/shape.h"
26#include "flang/Evaluate/tools.h"
27#include "flang/Evaluate/traverse.h"
28#include "flang/Evaluate/type.h"
29#include "flang/Parser/message.h"
30#include "flang/Semantics/scope.h"
31#include "flang/Semantics/symbol.h"
32#include "flang/Semantics/tools.h"
50static constexpr bool useKahanSummation{
false};
53template <
typename T>
class Folder {
55 explicit Folder(
FoldingContext &c,
bool forOptionalArgument =
false)
56 : context_{c}, forOptionalArgument_{forOptionalArgument} {}
57 std::optional<Constant<T>> GetNamedConstant(
const Symbol &);
58 std::optional<Constant<T>> ApplySubscripts(
const Constant<T> &array,
63 std::optional<Constant<T>> GetConstantComponent(
65 std::optional<Constant<T>> Folding(
ArrayRef &);
66 std::optional<Constant<T>> Folding(
DataRef &);
68 Constant<T> *Folding(std::optional<ActualArgument> &);
83 bool forOptionalArgument_{
false};
86std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
90template <
typename TR,
typename... TA>
91std::optional<std::function<Scalar<TR>(
FoldingContext &, Scalar<TA>...)>>
92GetHostRuntimeWrapper(
const std::string &name) {
93 std::vector<DynamicType> argTypes{TA{}.GetType()...};
94 if (
auto hostWrapper{GetHostRuntimeWrapper(name, TR{}.GetType(), argTypes)}) {
97 std::vector<Expr<SomeType>> genericArgs{
99 return GetScalarConstantValue<TR>(
100 (*hostWrapper)(context, std::move(genericArgs)))
115common::IfNoLvalue<Expr<ResultType<A>>, A> FoldOperation(
117 static_assert(!std::is_same_v<A, Expr<ResultType<A>>>,
118 "call Fold() instead for Expr<>");
135 return Folder<T>{context}.Folding(std::move(designator));
148std::optional<Constant<T>> Folder<T>::GetNamedConstant(
const Symbol &symbol0) {
149 const Symbol &symbol{ResolveAssociations(symbol0)};
150 if (IsNamedConstant(symbol)) {
151 if (
const auto *
object{
152 symbol.detailsIf<semantics::ObjectEntityDetails>()}) {
153 if (
const auto *constant{UnwrapConstantValue<T>(object->init())}) {
162std::optional<Constant<T>> Folder<T>::Folding(
ArrayRef &aRef) {
163 std::vector<Constant<SubscriptInteger>> subscripts;
166 if (
auto constant{GetConstantSubscript(context_, ss, aRef.base(), dim++)}) {
167 subscripts.emplace_back(std::move(*constant));
172 if (
Component * component{aRef.base().UnwrapComponent()}) {
173 return GetConstantComponent(*component, &subscripts);
175 GetNamedConstant(aRef.base().GetLastSymbol())}) {
176 return ApplySubscripts(*array, subscripts);
183std::optional<Constant<T>> Folder<T>::Folding(
DataRef &ref) {
184 return common::visit(
186 [
this](SymbolRef &sym) {
return GetNamedConstant(*sym); },
188 comp = FoldOperation(context_, std::move(comp));
189 return GetConstantComponent(comp);
192 aRef = FoldOperation(context_, std::move(aRef));
193 return Folding(aRef);
195 [](
CoarrayRef &) {
return std::optional<Constant<T>>{}; },
202std::optional<Constant<T>> Folder<T>::ApplySubscripts(
const Constant<T> &array,
204 const auto &shape{array.shape()};
205 const auto &lbounds{array.lbounds()};
206 int rank{GetRank(shape)};
207 CHECK(rank ==
static_cast<int>(subscripts.size()));
208 std::size_t elements{1};
209 ConstantSubscripts resultShape;
210 ConstantSubscripts ssLB;
211 for (
const auto &ss : subscripts) {
212 if (ss.Rank() == 1) {
213 resultShape.push_back(
static_cast<ConstantSubscript
>(ss.size()));
214 elements *= ss.size();
215 ssLB.push_back(ss.lbounds().front());
216 }
else if (ss.Rank() > 1) {
220 ConstantSubscripts ssAt(rank, 0), at(rank, 0), tmp(1, 0);
221 std::vector<Scalar<T>> values;
222 while (elements-- > 0) {
223 bool increment{
true};
225 for (
int j{0}; j < rank; ++j) {
226 if (subscripts[j].Rank() == 0) {
227 at[j] = subscripts[j].GetScalarValue().value().ToInt64();
229 CHECK(k < GetRank(resultShape));
230 tmp[0] = ssLB.at(k) + ssAt.at(k);
231 at[j] = subscripts[j].At(tmp).ToInt64();
233 if (++ssAt[k] == resultShape[k]) {
241 if (at[j] < lbounds[j] || at[j] >= lbounds[j] + shape[j]) {
242 context_.messages().Say(
243 "Subscript value (%jd) is out of range on dimension %d in reference to a constant array value"_err_en_US,
248 values.emplace_back(array.At(at));
249 CHECK(!increment || elements == 0);
250 CHECK(k == GetRank(resultShape));
252 if constexpr (T::category == TypeCategory::Character) {
253 return Constant<T>{array.LEN(), std::move(values), std::move(resultShape)};
254 }
else if constexpr (std::is_same_v<T, SomeDerived>) {
255 return Constant<T>{array.result().derivedTypeSpec(), std::move(values),
256 std::move(resultShape)};
258 return Constant<T>{std::move(values), std::move(resultShape)};
263std::optional<Constant<T>> Folder<T>::ApplyComponent(
266 if (
auto scalar{structures.GetScalarValue()}) {
267 if (std::optional<
Expr<SomeType>> expr{scalar->Find(component)}) {
268 if (
const Constant<T> *value{UnwrapConstantValue<T>(*expr)}) {
270 return ApplySubscripts(*value, *subscripts);
278 std::unique_ptr<ArrayConstructor<T>> array;
279 if (structures.empty()) {
282 ConstantSubscripts at{structures.lbounds()};
286 if (
const Constant<T> *value{UnwrapConstantValue<T>(expr.value())}) {
290 auto *typedExpr{UnwrapExpr<Expr<T>>(expr.value())};
292 array = std::make_unique<ArrayConstructor<T>>(*typedExpr);
293 if constexpr (T::category == TypeCategory::Character) {
298 if (
auto element{ApplySubscripts(*value, *subscripts)}) {
299 CHECK(element->Rank() == 0);
300 array->Push(
Expr<T>{std::move(*element)});
305 CHECK(value->Rank() == 0);
312 }
while (structures.IncrementSubscripts(at));
316 if (
auto *constant{UnwrapConstantValue<T>(result)}) {
317 return constant->Reshape(common::Clone(structures.shape()));
324std::optional<Constant<T>> Folder<T>::GetConstantComponent(
Component &component,
328 [&](
const Symbol &symbol) {
338 return std::optional<Constant<SomeDerived>>{};
341 component.base().u)}) {
342 return ApplyComponent(
343 std::move(*structures), component.GetLastSymbol(), subscripts);
350 if constexpr (T::category == TypeCategory::Character) {
351 if (
auto *substring{common::Unwrap<Substring>(designator.u)}) {
353 substring->Fold(context_)}) {
354 if (
const auto *specific{std::get_if<Expr<T>>(&folded->u)}) {
355 return std::move(*specific);
362 }
else if constexpr (T::category == TypeCategory::Real) {
363 if (
auto *zPart{std::get_if<ComplexPart>(&designator.u)}) {
364 *zPart = FoldOperation(context_, std::move(*zPart));
367 return Fold(context_,
369 zPart->part() == ComplexPart::Part::IM,
376 return common::visit(
378 [&](SymbolRef &&symbol) {
379 if (
auto constant{GetNamedConstant(*symbol)}) {
380 return Expr<T>{std::move(*constant)};
382 return Expr<T>{std::move(designator)};
385 aRef = FoldOperation(context_, std::move(aRef));
386 if (
auto c{Folding(aRef)}) {
393 component = FoldOperation(context_, std::move(component));
394 if (
auto c{GetConstantComponent(component)}) {
405 std::move(designator.u));
411Constant<T> *Folder<T>::Folding(std::optional<ActualArgument> &arg) {
412 if (
auto *expr{UnwrapExpr<Expr<SomeType>>(arg)}) {
413 *expr = Fold(context_, std::move(*expr));
414 if constexpr (T::category != TypeCategory::Derived) {
415 if (!UnwrapExpr<
Expr<T>>(*expr)) {
417 var{forOptionalArgument_
418 ? UnwrapWholeSymbolOrComponentDataRef(*expr)
420 var && (IsOptional(*var) || IsAllocatableOrObjectPointer(var))) {
422 }
else if (
auto converted{
423 ConvertToType(T::GetType(), std::move(*expr))}) {
424 *expr = Fold(context_, std::move(*converted));
428 return UnwrapConstantValue<T>(*expr);
433template <
typename... A, std::size_t... I>
434std::optional<std::tuple<const Constant<A> *...>> GetConstantArgumentsHelper(
436 bool hasOptionalArgument, std::index_sequence<I...>) {
437 static_assert(
sizeof...(A) > 0);
438 std::tuple<const Constant<A> *...> args{
439 Folder<A>{context, hasOptionalArgument}.Folding(arguments.at(I))...};
440 if ((... && (std::get<I>(args)))) {
447template <
typename... A>
448std::optional<std::tuple<const Constant<A> *...>> GetConstantArguments(
449 FoldingContext &context, ActualArguments &args,
bool hasOptionalArgument) {
450 return GetConstantArgumentsHelper<A...>(
451 context, args, hasOptionalArgument, std::index_sequence_for<A...>{});
454template <
typename... A, std::size_t... I>
455std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArgumentsHelper(
456 FoldingContext &context, ActualArguments &args,
bool hasOptionalArgument,
457 std::index_sequence<I...>) {
459 GetConstantArguments<A...>(context, args, hasOptionalArgument)}) {
460 return std::tuple<Scalar<A>...>{
461 std::get<I>(*constArgs)->GetScalarValue().value()...};
467template <
typename... A>
468std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArguments(
469 FoldingContext &context, ActualArguments &args,
bool hasOptionalArgument) {
470 return GetScalarConstantArgumentsHelper<A...>(
471 context, args, hasOptionalArgument, std::index_sequence_for<A...>{});
478template <
typename TR,
typename... TArgs>
479using ScalarFunc = std::function<Scalar<TR>(
const Scalar<TArgs> &...)>;
480template <
typename TR,
typename... TArgs>
481using ScalarFuncWithContext =
482 std::function<Scalar<TR>(
FoldingContext &,
const Scalar<TArgs> &...)>;
484template <
template <
typename,
typename...>
typename WrapperType,
typename TR,
485 typename... TA, std::size_t... I>
488 bool hasOptionalArgument, std::index_sequence<I...>) {
489 if (std::optional<std::tuple<
const Constant<TA> *...>> args{
490 GetConstantArguments<TA...>(
491 context, funcRef.arguments(), hasOptionalArgument)}) {
493 ConstantSubscripts shape;
495 const ConstantSubscripts *shapes[]{&std::get<I>(*args)->shape()...};
496 const int ranks[]{std::get<I>(*args)->Rank()...};
497 for (
unsigned int i{0}; i <
sizeof...(TA); ++i) {
503 if (shape != *shapes[i]) {
508 context.messages().Say(
509 "Arguments in elemental intrinsic function are not conformable"_err_en_US);
510 return Expr<TR>{std::move(funcRef)};
515 CHECK(rank == GetRank(shape));
517 std::vector<Scalar<TR>> results;
518 std::optional<uint64_t> n{TotalElementCount(shape)};
520 context.messages().Say(
521 "Too many elements in elemental intrinsic function result"_err_en_US);
522 return Expr<TR>{std::move(funcRef)};
526 ConstantSubscripts resultIndex(rank, 1);
527 ConstantSubscripts argIndex[]{std::get<I>(*args)->lbounds()...};
529 if constexpr (std::is_same_v<WrapperType<TR, TA...>,
530 ScalarFuncWithContext<TR, TA...>>) {
531 results.emplace_back(
532 func(context, std::get<I>(*args)->At(argIndex[I])...));
533 }
else if constexpr (std::is_same_v<WrapperType<TR, TA...>,
534 ScalarFunc<TR, TA...>>) {
535 results.emplace_back(func(std::get<I>(*args)->At(argIndex[I])...));
537 (std::get<I>(*args)->IncrementSubscripts(argIndex[I]), ...);
538 }
while (bounds.IncrementSubscripts(resultIndex));
541 if constexpr (TR::category == TypeCategory::Character) {
542 auto len{
static_cast<ConstantSubscript
>(
543 results.empty() ? 0 : results[0].length())};
545 }
else if constexpr (TR::category == TypeCategory::Derived) {
546 if (!results.empty()) {
550 std::move(results), std::move(shape)}};
556 return Expr<TR>{std::move(funcRef)};
559template <
typename TR,
typename... TA>
562 bool hasOptionalArgument =
false) {
563 return FoldElementalIntrinsicHelper<ScalarFunc, TR, TA...>(context,
564 std::move(funcRef), func, hasOptionalArgument,
565 std::index_sequence_for<TA...>{});
567template <
typename TR,
typename... TA>
570 bool hasOptionalArgument =
false) {
571 return FoldElementalIntrinsicHelper<ScalarFuncWithContext, TR, TA...>(context,
572 std::move(funcRef), func, hasOptionalArgument,
573 std::index_sequence_for<TA...>{});
576std::optional<std::int64_t> GetInt64ArgOr(
577 const std::optional<ActualArgument> &, std::int64_t defaultValue);
579template <
typename A,
typename B>
580std::optional<std::vector<A>> GetIntegerVector(
const B &x) {
581 static_assert(std::is_integral_v<A>);
582 if (
const auto *someInteger{UnwrapExpr<Expr<SomeInteger>>(x)}) {
583 return common::visit(
584 [](
const auto &typedExpr) -> std::optional<std::vector<A>> {
585 using T = ResultType<
decltype(typedExpr)>;
586 if (
const auto *constant{UnwrapConstantValue<T>(typedExpr)}) {
587 if (constant->Rank() == 1) {
588 std::vector<A> result;
589 for (
const auto &value : constant->values()) {
590 result.push_back(
static_cast<A
>(value.ToInt64()));
608 invalid.name = IntrinsicProcTable::InvalidName;
610 ActualArguments{std::move(funcRef.arguments())}}};
614 auto args{funcRef.arguments()};
615 CHECK(args.size() == 3);
616 const auto *array{UnwrapConstantValue<T>(args[0])};
617 const auto *shiftExpr{UnwrapExpr<Expr<SomeInteger>>(args[1])};
618 auto dim{GetInt64ArgOr(args[2], 1)};
619 if (!array || !shiftExpr || !dim) {
620 return Expr<T>{std::move(funcRef)};
622 auto convertedShift{Fold(context_,
624 const auto *shift{UnwrapConstantValue<SubscriptInteger>(convertedShift)};
626 return Expr<T>{std::move(funcRef)};
629 if (*dim < 1 || *dim > array->Rank()) {
630 context_.messages().Say(
"Invalid 'dim=' argument (%jd) in CSHIFT"_err_en_US,
631 static_cast<std::intmax_t
>(*dim));
632 }
else if (shift->Rank() > 0 && shift->Rank() != array->Rank() - 1) {
635 int rank{array->Rank()};
636 int zbDim{
static_cast<int>(*dim) - 1};
638 if (shift->Rank() > 0) {
640 for (
int j{0}; j < rank; ++j) {
642 if (array->shape()[j] != shift->shape()[k]) {
643 context_.messages().Say(
644 "Invalid 'shift=' argument in CSHIFT: extent on dimension %d is %jd but must be %jd"_err_en_US,
645 k + 1,
static_cast<std::intmax_t
>(shift->shape()[k]),
646 static_cast<std::intmax_t
>(array->shape()[j]));
654 std::vector<Scalar<T>> resultElements;
655 ConstantSubscripts arrayLB{array->lbounds()};
656 ConstantSubscripts arrayAt{arrayLB};
657 ConstantSubscript &dimIndex{arrayAt[zbDim]};
658 ConstantSubscript dimLB{dimIndex};
659 ConstantSubscript dimExtent{array->shape()[zbDim]};
660 ConstantSubscripts shiftLB{shift->lbounds()};
661 for (
auto n{GetSize(array->shape())}; n > 0; --n) {
662 ConstantSubscript origDimIndex{dimIndex};
663 ConstantSubscripts shiftAt;
664 if (shift->Rank() > 0) {
666 for (
int j{0}; j < rank; ++j) {
668 shiftAt.emplace_back(shiftLB[k++] + arrayAt[j] - arrayLB[j]);
672 ConstantSubscript shiftCount{shift->At(shiftAt).ToInt64()};
673 dimIndex = dimLB + ((dimIndex - dimLB + shiftCount) % dimExtent);
674 if (dimIndex < dimLB) {
675 dimIndex += dimExtent;
676 }
else if (dimIndex >= dimLB + dimExtent) {
677 dimIndex -= dimExtent;
679 resultElements.push_back(array->At(arrayAt));
680 dimIndex = origDimIndex;
681 array->IncrementSubscripts(arrayAt);
683 return Expr<T>{PackageConstant<T>(
684 std::move(resultElements), *array, array->shape())};
688 return MakeInvalidIntrinsic(std::move(funcRef));
692 auto args{funcRef.arguments()};
693 CHECK(args.size() == 4);
694 const auto *array{UnwrapConstantValue<T>(args[0])};
695 const auto *shiftExpr{UnwrapExpr<Expr<SomeInteger>>(args[1])};
696 auto dim{GetInt64ArgOr(args[3], 1)};
697 if (!array || !shiftExpr || !dim) {
698 return Expr<T>{std::move(funcRef)};
701 auto convertedShift{Fold(context_,
703 const auto *shift{UnwrapConstantValue<SubscriptInteger>(convertedShift)};
705 return Expr<T>{std::move(funcRef)};
708 std::optional<Expr<SomeType>> convertedBoundary;
709 if (
const auto *boundaryExpr{UnwrapExpr<Expr<SomeType>>(args[2])}) {
710 convertedBoundary = Fold(context_,
712 boundary = UnwrapExpr<Constant<T>>(convertedBoundary);
714 return Expr<T>{std::move(funcRef)};
718 if (*dim < 1 || *dim > array->Rank()) {
719 context_.messages().Say(
720 "Invalid 'dim=' argument (%jd) in EOSHIFT"_err_en_US,
721 static_cast<std::intmax_t
>(*dim));
722 }
else if (shift->Rank() > 0 && shift->Rank() != array->Rank() - 1) {
724 }
else if (boundary && boundary->Rank() > 0 &&
725 boundary->Rank() != array->Rank() - 1) {
728 int rank{array->Rank()};
729 int zbDim{
static_cast<int>(*dim) - 1};
731 if (shift->Rank() > 0) {
733 for (
int j{0}; j < rank; ++j) {
735 if (array->shape()[j] != shift->shape()[k]) {
736 context_.messages().Say(
737 "Invalid 'shift=' argument in EOSHIFT: extent on dimension %d is %jd but must be %jd"_err_en_US,
738 k + 1,
static_cast<std::intmax_t
>(shift->shape()[k]),
739 static_cast<std::intmax_t
>(array->shape()[j]));
746 if (boundary && boundary->Rank() > 0) {
748 for (
int j{0}; j < rank; ++j) {
750 if (array->shape()[j] != boundary->shape()[k]) {
751 context_.messages().Say(
752 "Invalid 'boundary=' argument in EOSHIFT: extent on dimension %d is %jd but must be %jd"_err_en_US,
753 k + 1,
static_cast<std::intmax_t
>(boundary->shape()[k]),
754 static_cast<std::intmax_t
>(array->shape()[j]));
762 std::vector<Scalar<T>> resultElements;
763 ConstantSubscripts arrayLB{array->lbounds()};
764 ConstantSubscripts arrayAt{arrayLB};
765 ConstantSubscript &dimIndex{arrayAt[zbDim]};
766 ConstantSubscript dimLB{dimIndex};
767 ConstantSubscript dimExtent{array->shape()[zbDim]};
768 ConstantSubscripts shiftLB{shift->lbounds()};
769 ConstantSubscripts boundaryLB;
771 boundaryLB = boundary->lbounds();
773 for (
auto n{GetSize(array->shape())}; n > 0; --n) {
774 ConstantSubscript origDimIndex{dimIndex};
775 ConstantSubscripts shiftAt;
776 if (shift->Rank() > 0) {
778 for (
int j{0}; j < rank; ++j) {
780 shiftAt.emplace_back(shiftLB[k++] + arrayAt[j] - arrayLB[j]);
784 ConstantSubscript shiftCount{shift->At(shiftAt).ToInt64()};
785 dimIndex += shiftCount;
786 if (dimIndex >= dimLB && dimIndex < dimLB + dimExtent) {
787 resultElements.push_back(array->At(arrayAt));
788 }
else if (boundary) {
789 ConstantSubscripts boundaryAt;
790 if (boundary->Rank() > 0) {
791 for (
int j{0}; j < rank; ++j) {
794 boundaryAt.emplace_back(
795 boundaryLB[k++] + arrayAt[j] - arrayLB[j]);
799 resultElements.push_back(boundary->At(boundaryAt));
800 }
else if constexpr (T::category == TypeCategory::Integer ||
801 T::category == TypeCategory::Unsigned ||
802 T::category == TypeCategory::Real ||
803 T::category == TypeCategory::Complex ||
804 T::category == TypeCategory::Logical) {
805 resultElements.emplace_back();
806 }
else if constexpr (T::category == TypeCategory::Character) {
807 auto len{
static_cast<std::size_t
>(array->LEN())};
808 typename Scalar<T>::value_type space{
' '};
809 resultElements.emplace_back(len, space);
811 DIE(
"no derived type boundary");
813 dimIndex = origDimIndex;
814 array->IncrementSubscripts(arrayAt);
816 return Expr<T>{PackageConstant<T>(
817 std::move(resultElements), *array, array->shape())};
821 return MakeInvalidIntrinsic(std::move(funcRef));
825 return FoldElementalIntrinsic<T, T, T, LogicalResult>(context_,
827 ScalarFunc<T, T, T, LogicalResult>(
828 [](
const Scalar<T> &ifTrue,
const Scalar<T> &ifFalse,
829 const Scalar<LogicalResult> &predicate) -> Scalar<T> {
830 return predicate.IsTrue() ? ifTrue : ifFalse;
835 auto args{funcRef.arguments()};
836 CHECK(args.size() == 3);
837 const auto *array{UnwrapConstantValue<T>(args[0])};
838 const auto *vector{UnwrapConstantValue<T>(args[2])};
839 auto convertedMask{Fold(context_,
840 ConvertToType<LogicalResult>(
842 const auto *mask{UnwrapConstantValue<LogicalResult>(convertedMask)};
843 if (!array || !mask || (args[2] && !vector)) {
844 return Expr<T>{std::move(funcRef)};
847 ConstantSubscript arrayElements{GetSize(array->shape())};
848 ConstantSubscript truths{0};
849 ConstantSubscripts maskAt{mask->lbounds()};
850 if (mask->Rank() == 0) {
851 if (mask->At(maskAt).IsTrue()) {
852 truths = arrayElements;
854 }
else if (array->shape() != mask->shape()) {
856 return MakeInvalidIntrinsic(std::move(funcRef));
858 for (ConstantSubscript j{0}; j < arrayElements;
859 ++j, mask->IncrementSubscripts(maskAt)) {
860 if (mask->At(maskAt).IsTrue()) {
865 std::vector<Scalar<T>> resultElements;
866 ConstantSubscripts arrayAt{array->lbounds()};
867 ConstantSubscript resultSize{truths};
869 resultSize = vector->shape().at(0);
870 if (resultSize < truths) {
871 context_.messages().Say(
872 "Invalid 'vector=' argument in PACK: the 'mask=' argument has %jd true elements, but the vector has only %jd elements"_err_en_US,
873 static_cast<std::intmax_t
>(truths),
874 static_cast<std::intmax_t
>(resultSize));
875 return MakeInvalidIntrinsic(std::move(funcRef));
878 for (ConstantSubscript j{0}; j < truths;) {
879 if (mask->At(maskAt).IsTrue()) {
880 resultElements.push_back(array->At(arrayAt));
883 array->IncrementSubscripts(arrayAt);
884 mask->IncrementSubscripts(maskAt);
887 ConstantSubscripts vectorAt{vector->lbounds()};
888 vectorAt.at(0) += truths;
889 for (ConstantSubscript j{truths}; j < resultSize; ++j) {
890 resultElements.push_back(vector->At(vectorAt));
894 return Expr<T>{PackageConstant<T>(std::move(resultElements), *array,
895 ConstantSubscripts{
static_cast<ConstantSubscript
>(resultSize)})};
899 auto args{funcRef.arguments()};
900 CHECK(args.size() == 4);
901 const auto *source{UnwrapConstantValue<T>(args[0])};
902 const auto *pad{UnwrapConstantValue<T>(args[2])};
903 std::optional<std::vector<ConstantSubscript>> shape{
904 GetIntegerVector<ConstantSubscript>(args[1])};
905 std::optional<std::vector<int>> order{GetIntegerVector<int>(args[3])};
906 std::optional<uint64_t> optResultElement;
907 std::optional<std::vector<int>> dimOrder;
910 if (shape->size() > common::maxRank) {
911 context_.messages().Say(
912 "Size of 'shape=' argument (%zd) must not be greater than %d"_err_en_US,
913 shape->size(), common::maxRank);
915 }
else if (HasNegativeExtent(*shape)) {
916 context_.messages().Say(
917 "'shape=' argument (%s) must not have a negative extent"_err_en_US,
918 DEREF(args[1]->UnwrapExpr()).AsFortran());
921 optResultElement = TotalElementCount(*shape);
922 if (!optResultElement) {
923 context_.messages().Say(
924 "'shape=' argument (%s) specifies an array with too many elements"_err_en_US,
925 DEREF(args[1]->UnwrapExpr()).AsFortran());
930 dimOrder = ValidateDimensionOrder(GetRank(*shape), *order);
932 context_.messages().Say(
933 "Invalid 'order=' argument (%s) in RESHAPE"_err_en_US,
934 DEREF(args[3]->UnwrapExpr()).AsFortran());
941 }
else if (!source || !shape || (args[2] && !pad) || (args[3] && !order)) {
942 return Expr<T>{std::move(funcRef)};
944 uint64_t resultElements{*optResultElement};
945 std::vector<int> *dimOrderPtr{dimOrder ? &dimOrder.value() :
nullptr};
946 if (resultElements > source->size() && (!pad || pad->empty())) {
947 context_.messages().Say(
948 "Too few elements in 'source=' argument and 'pad=' "
949 "argument is not present or has null size"_err_en_US);
953 ? source->Reshape(std::move(shape.value()))
954 : pad->Reshape(std::move(shape.value()))};
955 ConstantSubscripts subscripts{result.lbounds()};
956 auto copied{result.CopyFrom(*source,
957 std::min(
static_cast<uint64_t
>(source->size()), resultElements),
958 subscripts, dimOrderPtr)};
959 if (copied < resultElements) {
961 copied += result.CopyFrom(
962 *pad, resultElements - copied, subscripts, dimOrderPtr);
964 CHECK(copied == resultElements);
965 return Expr<T>{std::move(result)};
969 return MakeInvalidIntrinsic(std::move(funcRef));
973 auto args{funcRef.arguments()};
974 CHECK(args.size() == 3);
975 const Constant<T> *source{UnwrapConstantValue<T>(args[0])};
976 auto dim{ToInt64(args[1])};
977 auto ncopies{ToInt64(args[2])};
978 if (!source || !dim) {
979 return Expr<T>{std::move(funcRef)};
981 int sourceRank{source->Rank()};
982 if (sourceRank >= common::maxRank) {
983 context_.messages().Say(
984 "SOURCE= argument to SPREAD has rank %d but must have rank less than %d"_err_en_US,
985 sourceRank, common::maxRank);
986 }
else if (*dim < 1 || *dim > sourceRank + 1) {
987 context_.messages().Say(
988 "DIM=%d argument to SPREAD must be between 1 and %d"_err_en_US, *dim,
990 }
else if (!ncopies) {
991 return Expr<T>{std::move(funcRef)};
1001 ConstantSubscripts shape{source->shape()};
1002 shape.insert(shape.begin() + *dim - 1, *ncopies);
1003 Constant<T> spread{source->Reshape(std::move(shape))};
1004 std::optional<uint64_t> n{TotalElementCount(spread.shape())};
1006 context_.messages().Say(
"Too many elements in SPREAD result"_err_en_US);
1008 std::vector<int> dimOrder;
1009 for (
int j{0}; j < sourceRank; ++j) {
1010 dimOrder.push_back(j < *dim - 1 ? j : j + 1);
1012 dimOrder.push_back(*dim - 1);
1013 ConstantSubscripts at{spread.lbounds()};
1014 spread.CopyFrom(*source, *n, at, &dimOrder);
1015 return Expr<T>{std::move(spread)};
1019 return MakeInvalidIntrinsic(std::move(funcRef));
1023 auto args{funcRef.arguments()};
1024 CHECK(args.size() == 1);
1025 const auto *matrix{UnwrapConstantValue<T>(args[0])};
1027 return Expr<T>{std::move(funcRef)};
1030 std::vector<Scalar<T>> resultElements;
1031 ConstantSubscripts at(2);
1032 for (ConstantSubscript j{0}; j < matrix->shape()[0]; ++j) {
1033 at[0] = matrix->lbounds()[0] + j;
1034 for (ConstantSubscript k{0}; k < matrix->shape()[1]; ++k) {
1035 at[1] = matrix->lbounds()[1] + k;
1036 resultElements.push_back(matrix->At(at));
1039 at = matrix->shape();
1040 std::swap(at[0], at[1]);
1041 return Expr<T>{PackageConstant<T>(std::move(resultElements), *matrix, at)};
1045 auto args{funcRef.arguments()};
1046 CHECK(args.size() == 3);
1047 const auto *vector{UnwrapConstantValue<T>(args[0])};
1048 auto convertedMask{Fold(context_,
1049 ConvertToType<LogicalResult>(
1051 const auto *mask{UnwrapConstantValue<LogicalResult>(convertedMask)};
1052 const auto *field{UnwrapConstantValue<T>(args[2])};
1053 if (!vector || !mask || !field) {
1054 return Expr<T>{std::move(funcRef)};
1057 if (field->Rank() > 0 && field->shape() != mask->shape()) {
1059 return MakeInvalidIntrinsic(std::move(funcRef));
1061 ConstantSubscript maskElements{GetSize(mask->shape())};
1062 ConstantSubscript truths{0};
1063 ConstantSubscripts maskAt{mask->lbounds()};
1064 for (ConstantSubscript j{0}; j < maskElements;
1065 ++j, mask->IncrementSubscripts(maskAt)) {
1066 if (mask->At(maskAt).IsTrue()) {
1070 if (truths > GetSize(vector->shape())) {
1071 context_.messages().Say(
1072 "Invalid 'vector=' argument in UNPACK: the 'mask=' argument has %jd true elements, but the vector has only %jd elements"_err_en_US,
1073 static_cast<std::intmax_t
>(truths),
1074 static_cast<std::intmax_t
>(GetSize(vector->shape())));
1075 return MakeInvalidIntrinsic(std::move(funcRef));
1077 std::vector<Scalar<T>> resultElements;
1078 ConstantSubscripts vectorAt{vector->lbounds()};
1079 ConstantSubscripts fieldAt{field->lbounds()};
1080 for (ConstantSubscript j{0}; j < maskElements; ++j) {
1081 if (mask->At(maskAt).IsTrue()) {
1082 resultElements.push_back(vector->At(vectorAt));
1083 vector->IncrementSubscripts(vectorAt);
1085 resultElements.push_back(field->At(fieldAt));
1087 mask->IncrementSubscripts(maskAt);
1088 field->IncrementSubscripts(fieldAt);
1091 PackageConstant<T>(std::move(resultElements), *vector, mask->shape())};
1094std::optional<Expr<SomeType>> FoldTransfer(
1098 if (
auto folded{FoldTransfer(context_, funcRef.arguments())}) {
1099 return DEREF(UnwrapExpr<
Expr<T>>(*folded));
1101 return Expr<T>{std::move(funcRef)};
1107template <
typename T>
1110 static_assert(T::category == TypeCategory::Integer ||
1111 T::category == TypeCategory::Unsigned ||
1112 T::category == TypeCategory::Real ||
1113 T::category == TypeCategory::Character);
1136 auto &args{funcRef.arguments()};
1137 std::size_t nargs{args.size()};
1138 bool allArgsConstant{
true};
1139 bool extremumAnyway{nargs == 2 && T::category != TypeCategory::Character};
1143 if (!folder.Folding(args[0])) {
1144 allArgsConstant =
false;
1146 if (!folder.Folding(args[1])) {
1147 allArgsConstant =
false;
1153 for (std::size_t i{2}; i < nargs; ++i) {
1155 if (!folder.Folding(args[i])) {
1156 allArgsConstant =
false;
1164 if (allArgsConstant || extremumAnyway) {
1167 if (
const auto *resultp{UnwrapExpr<Expr<T>>(args[0])}) {
1169 for (std::size_t i{1}; i < nargs; ++i) {
1170 if (
const auto *tExpr{UnwrapExpr<Expr<T>>(args[i])}) {
1171 result = FoldOperation(
1172 context,
Extremum<T>{order, std::move(result), *tExpr});
1175 return Expr<T>{std::move(funcRef)};
1183 return Expr<T>{std::move(funcRef)};
1193template <
typename T>
1194Expr<T> RewriteSpecificMINorMAX(
1196 ActualArguments &args{funcRef.arguments()};
1197 auto &intrinsic{DEREF(std::get_if<SpecificIntrinsic>(&funcRef.proc().u))};
1200 std::optional<DynamicType> resultType;
1202 for (
auto j{args.size()}; j-- > 0;) {
1209 (type.category() == resultType->category() &&
1210 type.kind() > resultType->kind()) ||
1211 resultType->category() == TypeCategory::Integer) {
1213 resultTypeArg = &*args[j];
1218 return Expr<T>{std::move(funcRef)};
1221 intrinsic.name.find(
"max") != std::string::npos ?
"max"s :
"min"s;
1222 intrinsic.characteristics.value().functionResult.value().SetType(*resultType);
1223 auto insertConversion{[&](
const auto &x) ->
Expr<T> {
1224 using TR = ResultType<
decltype(x)>;
1227 return Fold(context, ConvertToType<T>(AsCategoryExpr(std::move(maxRef))));
1229 if (
auto *sx{UnwrapExpr<Expr<SomeReal>>(*resultTypeArg)}) {
1230 return common::visit(insertConversion, sx->u);
1231 }
else if (
auto *sx{UnwrapExpr<Expr<SomeInteger>>(*resultTypeArg)}) {
1232 return common::visit(insertConversion, sx->u);
1234 return Expr<T>{std::move(funcRef)};
1256template <
typename T>
1258 ActualArguments &args{funcRef.arguments()};
1259 const auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
1260 if (!intrinsic || intrinsic->name !=
"kind") {
1263 for (std::optional<ActualArgument> &arg : args) {
1264 if (arg && arg->GetConditionalArg()) {
1265 FoldConditionalArg(context, arg);
1266 }
else if (
auto *expr{UnwrapExpr<Expr<SomeType>>(arg)}) {
1267 *expr = Fold(context, std::move(*expr));
1307 for (
const std::optional<ActualArgument> &arg : args) {
1308 if (arg && arg->isConditionalArg()) {
1309 return Expr<T>{std::move(funcRef)};
1312 const std::string name{intrinsic->name};
1313 if (name ==
"cshift") {
1314 return Folder<T>{context}.CSHIFT(std::move(funcRef));
1315 }
else if (name ==
"eoshift") {
1316 return Folder<T>{context}.EOSHIFT(std::move(funcRef));
1317 }
else if (name ==
"merge") {
1318 return Folder<T>{context}.MERGE(std::move(funcRef));
1319 }
else if (name ==
"pack") {
1320 return Folder<T>{context}.PACK(std::move(funcRef));
1321 }
else if (name ==
"reshape") {
1322 return Folder<T>{context}.RESHAPE(std::move(funcRef));
1323 }
else if (name ==
"spread") {
1324 return Folder<T>{context}.SPREAD(std::move(funcRef));
1325 }
else if (name ==
"transfer") {
1326 return Folder<T>{context}.TRANSFER(std::move(funcRef));
1327 }
else if (name ==
"transpose") {
1328 return Folder<T>{context}.TRANSPOSE(std::move(funcRef));
1329 }
else if (name ==
"unpack") {
1330 return Folder<T>{context}.UNPACK(std::move(funcRef));
1333 if constexpr (!std::is_same_v<T, SomeDerived>) {
1334 return FoldIntrinsicFunction(context, std::move(funcRef));
1337 return Expr<T>{std::move(funcRef)};
1341template <
typename T>
class ArrayConstructorFolder {
1343 explicit ArrayConstructorFolder(
FoldingContext &c) : context_{c} {}
1346 if constexpr (T::category == TypeCategory::Character) {
1347 if (
const auto *len{array.LEN()}) {
1348 charLength_ = ToInt64(Fold(context_, common::Clone(*len)));
1349 knownCharLength_ = charLength_.has_value();
1353 if (FoldArray(array)) {
1354 auto n{
static_cast<ConstantSubscript
>(elements_.size())};
1355 if constexpr (std::is_same_v<T, SomeDerived>) {
1357 std::move(elements_), ConstantSubscripts{n}}};
1358 }
else if constexpr (T::category == TypeCategory::Character) {
1361 *charLength_, std::move(elements_), ConstantSubscripts{n}}};
1365 std::move(elements_), ConstantSubscripts{n}, resultInfo_}};
1368 return Expr<T>{std::move(array)};
1372 bool FoldArray(
const Expr<T> &expr) {
1373 Expr<T> folded{Fold(context_, common::Clone(expr))};
1374 if (
const auto *c{UnwrapConstantValue<T>(folded)}) {
1377 ConstantSubscripts index{c->lbounds()};
1379 elements_.emplace_back(c->At(index));
1380 }
while (c->IncrementSubscripts(index));
1382 if constexpr (T::category == TypeCategory::Character) {
1383 if (!knownCharLength_) {
1384 charLength_ = std::max(c->LEN(), charLength_.value_or(-1));
1386 }
else if constexpr (T::category == TypeCategory::Real ||
1387 T::category == TypeCategory::Complex) {
1388 if (c->result().isFromInexactLiteralConversion()) {
1389 resultInfo_.set_isFromInexactLiteralConversion();
1397 bool FoldArray(
const common::CopyableIndirection<
Expr<T>> &expr) {
1398 return FoldArray(expr.value());
1407 std::optional<ConstantSubscript> start{ToInt64(
lower)}, end{ToInt64(upper)},
1408 step{ToInt64(stride)};
1409 if (start && end && step && *step != 0) {
1411 ConstantSubscript &j{context_.StartImpliedDo(iDo.name(), *start)};
1413 for (; j <= *end; j += *step) {
1414 result &= FoldArray(iDo.values());
1417 for (; j >= *end; j += *step) {
1418 result &= FoldArray(iDo.values());
1421 context_.EndImpliedDo(iDo.name());
1428 return common::visit([&](
const auto &y) {
return FoldArray(y); }, x.u);
1431 for (
const auto &x : xs) {
1432 if (!FoldArray(x)) {
1440 std::vector<Scalar<T>> elements_;
1441 std::optional<ConstantSubscript> charLength_;
1442 bool knownCharLength_{
false};
1443 typename Constant<T>::Result resultInfo_;
1446template <
typename T>
1459template <
typename T>
1460bool ArrayConstructorIsFlat(
const ArrayConstructorValues<T> &values) {
1461 for (
const ArrayConstructorValue<T> &x : values) {
1462 if (!std::holds_alternative<Expr<T>>(x.u)) {
1469template <
typename T>
1470std::optional<Expr<T>> AsFlatArrayConstructor(
const Expr<T> &expr) {
1471 if (
const auto *c{UnwrapConstantValue<T>(expr)}) {
1474 ConstantSubscripts at{c->lbounds()};
1477 }
while (c->IncrementSubscripts(at));
1479 return std::make_optional<Expr<T>>(std::move(result));
1480 }
else if (
const auto *a{UnwrapExpr<ArrayConstructor<T>>(expr)}) {
1481 if (ArrayConstructorIsFlat(*a)) {
1482 return std::make_optional<Expr<T>>(expr);
1484 }
else if (
const auto *p{UnwrapExpr<Parentheses<T>>(expr)}) {
1485 return AsFlatArrayConstructor(
Expr<T>{p->left()});
1487 return std::nullopt;
1490template <TypeCategory CAT>
1491std::enable_if_t<CAT != TypeCategory::Derived,
1492 std::optional<Expr<SomeKind<CAT>>>>
1494 return common::visit(
1496 if (
auto flattened{AsFlatArrayConstructor(kindExpr)}) {
1499 return std::nullopt;
1509template <
typename T>
1510std::optional<Expr<T>> FromArrayConstructor(
1512 if (
auto constShape{AsConstantExtents(context, shape)};
1513 constShape && !HasNegativeExtent(*constShape)) {
1515 if (
auto *constant{UnwrapConstantValue<T>(result)}) {
1517 return Expr<T>{constant->Reshape(std::move(*constShape))};
1519 if (constShape->size() == 1) {
1520 if (
auto elements{GetShape(context, result)}) {
1521 if (
auto constElements{AsConstantExtents(context, *elements)}) {
1522 if (constElements->size() == 1 &&
1523 constElements->at(0) == constShape->at(0)) {
1526 return std::move(result);
1532 return std::nullopt;
1543template <
typename RESULT,
typename OPERAND>
1549 if constexpr (common::HasMember<OPERAND, AllIntrinsicCategoryTypes>) {
1551 [&](
auto &&kindExpr) {
1552 using kindType = ResultType<
decltype(kindExpr)>;
1553 auto &aConst{std::get<ArrayConstructor<kindType>>(kindExpr.u)};
1554 for (
auto &acValue : aConst) {
1555 auto &scalar{std::get<Expr<kindType>>(acValue.u)};
1556 result.Push(Fold(context, f(
Expr<OPERAND>{std::move(scalar)})));
1559 std::move(values.u));
1561 auto &aConst{std::get<ArrayConstructor<OPERAND>>(values.u)};
1562 for (
auto &acValue : aConst) {
1563 auto &scalar{std::get<Expr<OPERAND>>(acValue.u)};
1564 result.Push(Fold(context, f(std::move(scalar))));
1567 if constexpr (RESULT::category == TypeCategory::Character) {
1569 result.set_LEN(std::move(*length));
1572 return FromArrayConstructor(context, std::move(result), shape);
1575template <
typename RESULT,
typename A>
1579 if constexpr (RESULT::category == TypeCategory::Character) {
1581 result.set_LEN(std::move(*length));
1587template <
typename LEFT,
typename RIGHT>
1591 auto rightIter{rightArrConst.begin()};
1592 for (
auto &leftValue : leftArrConst) {
1593 CHECK(rightIter != rightArrConst.end());
1594 auto &leftExpr{std::get<Expr<LEFT>>(leftValue.u)};
1595 auto &rightExpr{std::get<Expr<RIGHT>>(rightIter->u)};
1596 if (leftExpr.Rank() != rightExpr.Rank()) {
1599 std::optional<Shape> leftShape{GetShape(context, leftExpr)};
1600 std::optional<Shape> rightShape{GetShape(context, rightExpr)};
1601 if (!leftShape || !rightShape || *leftShape != *rightShape) {
1610template <
typename RESULT,
typename LEFT,
typename RIGHT>
1615 -> std::optional<Expr<RESULT>> {
1616 auto result{ArrayConstructorFromMold<RESULT>(leftValues, std::move(length))};
1617 auto &leftArrConst{std::get<ArrayConstructor<LEFT>>(leftValues.u)};
1618 if constexpr (common::HasMember<RIGHT, AllIntrinsicCategoryTypes>) {
1619 bool mapped{common::visit(
1620 [&](
auto &&kindExpr) ->
bool {
1621 using kindType = ResultType<
decltype(kindExpr)>;
1623 auto &rightArrConst{std::get<ArrayConstructor<kindType>>(kindExpr.u)};
1624 if (!ShapesMatch(context, leftArrConst, rightArrConst)) {
1627 auto rightIter{rightArrConst.begin()};
1628 for (
auto &leftValue : leftArrConst) {
1629 CHECK(rightIter != rightArrConst.end());
1630 auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
1631 auto &rightScalar{std::get<Expr<kindType>>(rightIter->u)};
1632 result.Push(Fold(context,
1633 f(std::move(leftScalar),
Expr<RIGHT>{std::move(rightScalar)})));
1638 std::move(rightValues.u))};
1640 return std::nullopt;
1643 auto &rightArrConst{std::get<ArrayConstructor<RIGHT>>(rightValues.u)};
1644 if (!ShapesMatch(context, leftArrConst, rightArrConst)) {
1645 return std::nullopt;
1647 auto rightIter{rightArrConst.begin()};
1648 for (
auto &leftValue : leftArrConst) {
1649 CHECK(rightIter != rightArrConst.end());
1650 auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
1651 auto &rightScalar{std::get<Expr<RIGHT>>(rightIter->u)};
1653 Fold(context, f(std::move(leftScalar), std::move(rightScalar))));
1657 return FromArrayConstructor(context, std::move(result), shape);
1661template <
typename RESULT,
typename LEFT,
typename RIGHT>
1666 -> std::optional<Expr<RESULT>> {
1667 auto result{ArrayConstructorFromMold<RESULT>(leftValues, std::move(length))};
1668 auto &leftArrConst{std::get<ArrayConstructor<LEFT>>(leftValues.u)};
1669 for (
auto &leftValue : leftArrConst) {
1670 auto &leftScalar{std::get<Expr<LEFT>>(leftValue.u)};
1672 Fold(context, f(std::move(leftScalar),
Expr<RIGHT>{rightScalar})));
1674 return FromArrayConstructor(context, std::move(result), shape);
1678template <
typename RESULT,
typename LEFT,
typename RIGHT>
1683 -> std::optional<Expr<RESULT>> {
1684 auto result{ArrayConstructorFromMold<RESULT>(leftScalar, std::move(length))};
1685 if constexpr (common::HasMember<RIGHT, AllIntrinsicCategoryTypes>) {
1687 [&](
auto &&kindExpr) {
1688 using kindType = ResultType<
decltype(kindExpr)>;
1689 auto &rightArrConst{std::get<ArrayConstructor<kindType>>(kindExpr.u)};
1690 for (
auto &rightValue : rightArrConst) {
1691 auto &rightScalar{std::get<Expr<kindType>>(rightValue.u)};
1692 result.Push(Fold(context,
1697 std::move(rightValues.u));
1699 auto &rightArrConst{std::get<ArrayConstructor<RIGHT>>(rightValues.u)};
1700 for (
auto &rightValue : rightArrConst) {
1701 auto &rightScalar{std::get<Expr<RIGHT>>(rightValue.u)};
1703 Fold(context, f(
Expr<LEFT>{leftScalar}, std::move(rightScalar))));
1706 return FromArrayConstructor(context, std::move(result), shape);
1709template <
typename DERIVED,
typename RESULT,
typename... OPD>
1710std::optional<Expr<SubscriptInteger>> ComputeResultLength(
1712 if constexpr (RESULT::category == TypeCategory::Character) {
1715 return std::nullopt;
1722template <
typename DERIVED,
typename RESULT,
typename OPERAND>
1726 -> std::optional<Expr<RESULT>> {
1727 auto &expr{operation.left()};
1728 expr = Fold(context, std::move(expr));
1729 if (expr.Rank() > 0) {
1730 if (std::optional<Shape> shape{GetShape(context, expr)}) {
1731 if (
auto values{AsFlatArrayConstructor(expr)}) {
1732 return MapOperation(context, std::move(f), *shape,
1733 ComputeResultLength(operation), std::move(*values));
1737 return std::nullopt;
1740template <
typename DERIVED,
typename RESULT,
typename OPERAND>
1741auto ApplyElementwise(
1743 -> std::optional<Expr<RESULT>> {
1744 return ApplyElementwise(context, operation,
1751template <
typename DERIVED,
typename RESULT,
typename LEFT,
typename RIGHT>
1755 -> std::optional<Expr<RESULT>> {
1756 auto resultLength{ComputeResultLength(operation)};
1757 auto &leftExpr{operation.left()};
1758 auto &rightExpr{operation.right()};
1759 if (leftExpr.Rank() != rightExpr.Rank() && leftExpr.Rank() != 0 &&
1760 rightExpr.Rank() != 0) {
1761 return std::nullopt;
1763 leftExpr = Fold(context, std::move(leftExpr));
1764 rightExpr = Fold(context, std::move(rightExpr));
1765 if (leftExpr.Rank() > 0) {
1766 if (std::optional<Shape> leftShape{GetShape(context, leftExpr)}) {
1767 if (
auto left{AsFlatArrayConstructor(leftExpr)}) {
1768 if (rightExpr.Rank() > 0) {
1769 if (std::optional<Shape> rightShape{GetShape(context, rightExpr)}) {
1770 if (
auto right{AsFlatArrayConstructor(rightExpr)}) {
1771 if (CheckConformance(context.messages(), *leftShape, *rightShape,
1772 CheckConformanceFlags::EitherScalarExpandable)
1773 .value_or(
false )) {
1774 return MapOperation(context, std::move(f), *leftShape,
1775 std::move(resultLength), std::move(*left),
1778 return std::nullopt;
1780 return MapOperation(context, std::move(f), *leftShape,
1781 std::move(resultLength), std::move(*left), std::move(*right));
1784 }
else if (IsExpandableScalar(rightExpr, context, *leftShape)) {
1785 return MapOperation(context, std::move(f), *leftShape,
1786 std::move(resultLength), std::move(*left), rightExpr);
1790 }
else if (rightExpr.Rank() > 0) {
1791 if (std::optional<Shape> rightShape{GetShape(context, rightExpr)}) {
1792 if (IsExpandableScalar(leftExpr, context, *rightShape)) {
1793 if (
auto right{AsFlatArrayConstructor(rightExpr)}) {
1794 return MapOperation(context, std::move(f), *rightShape,
1795 std::move(resultLength), leftExpr, std::move(*right));
1800 return std::nullopt;
1803template <
typename DERIVED,
typename RESULT,
typename LEFT,
typename RIGHT>
1804auto ApplyElementwise(
1806 -> std::optional<Expr<RESULT>> {
1807 return ApplyElementwise(context, operation,
1810 return Expr<RESULT>{DERIVED{std::move(left), std::move(right)}};
1816template <
typename TO,
typename FROM>
1817common::IfNoLvalue<std::optional<TO>, FROM> ConvertString(FROM &&s) {
1818 if constexpr (std::is_same_v<TO, FROM>) {
1819 return std::make_optional<TO>(std::move(s));
1824 for (
auto iter{s.cbegin()}; iter != s.cend(); ++iter) {
1825 if (
static_cast<std::uint64_t
>(*iter) > 127) {
1826 return std::nullopt;
1828 str.push_back(
static_cast<typename TO::value_type
>(*iter));
1830 return std::make_optional<TO>(std::move(str));
1834template <
typename TO, TypeCategory FROMCAT>
1837 if (
auto array{ApplyElementwise(context, convert)}) {
1843 } msvcWorkaround{context, convert};
1844 return common::visit(
1845 [&msvcWorkaround](
auto &kindExpr) ->
Expr<TO> {
1846 using Operand = ResultType<
decltype(kindExpr)>;
1849 TypeCategory
constexpr FromCat{FROMCAT};
1850 static_assert(FromCat == Operand::category);
1851 auto &convert{msvcWorkaround.convert};
1852 if (
auto value{GetScalarConstantValue<Operand>(kindExpr)}) {
1854 if constexpr (TO::category == TypeCategory::Integer) {
1855 if constexpr (FromCat == TypeCategory::Integer) {
1856 auto converted{Scalar<TO>::ConvertSigned(*value)};
1857 if (converted.overflow) {
1858 ctx.Warn(common::UsageWarning::FoldingException,
1859 "conversion of %s_%d to INTEGER(%d) overflowed; result is %s"_warn_en_US,
1860 value->SignedDecimal(), Operand::kind, TO::kind,
1861 converted.value.SignedDecimal());
1863 return ScalarConstantToExpr(std::move(converted.value));
1864 }
else if constexpr (FromCat == TypeCategory::Unsigned) {
1865 auto converted{Scalar<TO>::ConvertUnsigned(*value)};
1866 if ((converted.overflow || converted.value.IsNegative())) {
1867 ctx.Warn(common::UsageWarning::FoldingException,
1868 "conversion of %s_U%d to INTEGER(%d) overflowed; result is %s"_warn_en_US,
1869 value->UnsignedDecimal(), Operand::kind, TO::kind,
1870 converted.value.SignedDecimal());
1872 return ScalarConstantToExpr(std::move(converted.value));
1873 }
else if constexpr (FromCat == TypeCategory::Real) {
1874 auto converted{value->template ToInteger<Scalar<TO>>()};
1875 if (converted.flags.test(RealFlag::InvalidArgument)) {
1876 ctx.Warn(common::UsageWarning::FoldingException,
1877 "REAL(%d) to INTEGER(%d) conversion: invalid argument"_warn_en_US,
1878 Operand::kind, TO::kind);
1879 }
else if (converted.flags.test(RealFlag::Overflow)) {
1880 ctx.Warn(common::UsageWarning::FoldingException,
1881 "REAL(%d) to INTEGER(%d) conversion overflowed"_warn_en_US,
1882 Operand::kind, TO::kind);
1884 return ScalarConstantToExpr(std::move(converted.value));
1886 }
else if constexpr (TO::category == TypeCategory::Unsigned) {
1887 if constexpr (FromCat == TypeCategory::Integer ||
1888 FromCat == TypeCategory::Unsigned) {
1890 Constant<TO>{Scalar<TO>::ConvertUnsigned(*value).value}};
1891 }
else if constexpr (FromCat == TypeCategory::Real) {
1893 Constant<TO>{value->template ToInteger<Scalar<TO>>().value}};
1895 }
else if constexpr (TO::category == TypeCategory::Real) {
1896 if constexpr (FromCat == TypeCategory::Integer ||
1897 FromCat == TypeCategory::Unsigned) {
1898 auto converted{Scalar<TO>::FromInteger(
1899 *value, FromCat == TypeCategory::Unsigned)};
1900 if (!converted.flags.empty()) {
1902 std::snprintf(buffer,
sizeof buffer,
1903 "INTEGER(%d) to REAL(%d) conversion", Operand::kind,
1905 ctx.RealFlagWarnings(converted.flags, buffer);
1907 return ScalarConstantToExpr(std::move(converted.value));
1908 }
else if constexpr (FromCat == TypeCategory::Real) {
1909 auto converted{Scalar<TO>::Convert(*value)};
1911 if (!converted.flags.empty()) {
1912 std::snprintf(buffer,
sizeof buffer,
1913 "REAL(%d) to REAL(%d) conversion", Operand::kind, TO::kind);
1914 ctx.RealFlagWarnings(converted.flags, buffer);
1916 if (ctx.targetCharacteristics().areSubnormalsFlushedToZero()) {
1917 converted.value = converted.value.FlushSubnormalToZero();
1919 return ScalarConstantToExpr(std::move(converted.value));
1921 }
else if constexpr (TO::category == TypeCategory::Complex) {
1922 if constexpr (FromCat == TypeCategory::Complex) {
1923 return FoldOperation(ctx,
1930 }
else if constexpr (TO::category == TypeCategory::Character &&
1931 FromCat == TypeCategory::Character) {
1932 if (
auto converted{ConvertString<Scalar<TO>>(std::move(*value))}) {
1933 return ScalarConstantToExpr(std::move(*converted));
1935 }
else if constexpr (TO::category == TypeCategory::Logical &&
1936 FromCat == TypeCategory::Logical) {
1939 }
else if constexpr (TO::category == FromCat &&
1940 FromCat != TypeCategory::Character) {
1942 if constexpr (std::is_same_v<Operand, TO>) {
1943 return std::move(kindExpr);
1944 }
else if constexpr (TO::category == TypeCategory::Logical ||
1945 TO::category == TypeCategory::Integer) {
1946 if (
auto *innerConv{
1947 std::get_if<Convert<Operand, TO::category>>(&kindExpr.u)}) {
1949 if (
auto *x{std::get_if<Expr<TO>>(&innerConv->left().u)}) {
1950 if constexpr (TO::category == TypeCategory::Logical ||
1951 TO::kind <= Operand::kind) {
1952 return std::move(*x);
1954 }
else if constexpr (std::is_same_v<TO,
1955 DescriptorInquiry::Result>) {
1956 if (std::holds_alternative<DescriptorInquiry>(x->u) ||
1957 std::holds_alternative<TypeParamInquiry>(x->u)) {
1959 return std::move(*x);
1966 return Expr<TO>{std::move(convert)};
1971template <
typename T>
1973 auto &operand{x.left()};
1974 operand = Fold(context, std::move(operand));
1975 if (
auto value{GetScalarConstantValue<T>(operand)}) {
1980 return std::move(operand);
1986template <
typename T>
1988 if (
auto array{ApplyElementwise(context, x)}) {
1991 auto &operand{x.left()};
1992 if (
auto *nn{std::get_if<Negate<T>>(&x.left().u)}) {
1994 if (IsVariable(nn->left())) {
1995 return FoldOperation(context,
Parentheses<T>{std::move(nn->left())});
1997 return std::move(nn->left());
1999 }
else if (
auto value{GetScalarConstantValue<T>(operand)}) {
2000 if constexpr (T::category == TypeCategory::Integer) {
2001 auto negated{value->Negate()};
2002 if (negated.overflow) {
2003 context.Warn(common::UsageWarning::FoldingException,
2004 "INTEGER(%d) negation overflowed"_warn_en_US, T::kind);
2007 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2019template <
typename LEFT,
typename RIGHT>
2020std::optional<std::pair<Scalar<LEFT>, Scalar<RIGHT>>> OperandsAreConstants(
2022 if (
auto xvalue{GetScalarConstantValue<LEFT>(x)}) {
2023 if (
auto yvalue{GetScalarConstantValue<RIGHT>(y)}) {
2024 return {std::make_pair(*xvalue, *yvalue)};
2027 return std::nullopt;
2030template <
typename DERIVED,
typename RESULT,
typename LEFT,
typename RIGHT>
2031std::optional<std::pair<Scalar<LEFT>, Scalar<RIGHT>>> OperandsAreConstants(
2033 return OperandsAreConstants(operation.left(), operation.right());
2036template <
typename T>
2038 if (
auto array{ApplyElementwise(context, x)}) {
2041 if (
auto folded{OperandsAreConstants(x)}) {
2042 if constexpr (T::category == TypeCategory::Integer) {
2043 auto sum{folded->first.AddSigned(folded->second)};
2045 context.Warn(common::UsageWarning::FoldingException,
2046 "INTEGER(%d) addition overflowed"_warn_en_US, T::kind);
2049 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2051 Constant<T>{folded->first.AddUnsigned(folded->second).value}};
2053 auto sum{folded->first.Add(
2054 folded->second, context.targetCharacteristics().roundingMode())};
2055 context.RealFlagWarnings(sum.flags,
"addition");
2056 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2057 sum.value = sum.value.FlushSubnormalToZero();
2061 }
else if constexpr (T::category == TypeCategory::Integer ||
2062 T::category == TypeCategory::Unsigned) {
2063 if (
auto c{GetScalarConstantValue<T>(x.right())}) {
2064 if (c->IsZero() && x.left().Rank() == 0) {
2065 if (IsVariable(x.left())) {
2066 return FoldOperation(context,
Parentheses<T>{std::move(x.left())});
2068 return std::move(x.left());
2071 }
else if (
auto c{GetScalarConstantValue<T>(x.left())}) {
2072 if (c->IsZero() && x.right().Rank() == 0) {
2073 if (IsVariable(x.right())) {
2074 return FoldOperation(context,
Parentheses<T>{std::move(x.right())});
2076 return std::move(x.right());
2084template <
typename T>
2086 if (
auto array{ApplyElementwise(context, x)}) {
2089 if (
auto folded{OperandsAreConstants(x)}) {
2090 if constexpr (T::category == TypeCategory::Integer) {
2091 auto difference{folded->first.SubtractSigned(folded->second)};
2092 if (difference.overflow) {
2093 context.Warn(common::UsageWarning::FoldingException,
2094 "INTEGER(%d) subtraction overflowed"_warn_en_US, T::kind);
2097 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2099 Constant<T>{folded->first.SubtractSigned(folded->second).value}};
2101 auto difference{folded->first.Subtract(
2102 folded->second, context.targetCharacteristics().roundingMode())};
2103 context.RealFlagWarnings(difference.flags,
"subtraction");
2104 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2105 difference.value = difference.value.FlushSubnormalToZero();
2109 }
else if constexpr (T::category == TypeCategory::Integer ||
2110 T::category == TypeCategory::Unsigned) {
2111 if (
auto c{GetScalarConstantValue<T>(x.right())}) {
2112 if (c->IsZero() && x.left().Rank() == 0) {
2113 if (IsVariable(x.left())) {
2114 return FoldOperation(context,
Parentheses<T>{std::move(x.left())});
2116 return std::move(x.left());
2124template <
typename T>
2126 if (
auto array{ApplyElementwise(context, x)}) {
2129 if (
auto folded{OperandsAreConstants(x)}) {
2130 if constexpr (T::category == TypeCategory::Integer) {
2131 auto product{folded->first.MultiplySigned(folded->second)};
2132 if (product.SignedMultiplicationOverflowed()) {
2133 context.Warn(common::UsageWarning::FoldingException,
2134 "INTEGER(%d) multiplication overflowed"_warn_en_US, T::kind);
2137 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2139 Constant<T>{folded->first.MultiplyUnsigned(folded->second).lower}};
2141 auto product{folded->first.Multiply(
2142 folded->second, context.targetCharacteristics().roundingMode())};
2143 context.RealFlagWarnings(product.flags,
"multiplication");
2144 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2145 product.value = product.value.FlushSubnormalToZero();
2149 }
else if constexpr (T::category == TypeCategory::Integer) {
2150 if (
auto c{GetScalarConstantValue<T>(x.right())}) {
2151 x.right() = std::move(x.left());
2152 x.left() =
Expr<T>{std::move(*c)};
2154 if (
auto c{GetScalarConstantValue<T>(x.left())}) {
2155 if (c->IsZero() && x.right().Rank() == 0) {
2156 return std::move(x.left());
2157 }
else if (c->CompareSigned(Scalar<T>{1}) == Ordering::Equal) {
2158 if (IsVariable(x.right())) {
2159 return FoldOperation(context,
Parentheses<T>{std::move(x.right())});
2161 return std::move(x.right());
2163 }
else if (c->CompareSigned(Scalar<T>{-1}) == Ordering::Equal) {
2164 return FoldOperation(context,
Negate<T>{std::move(x.right())});
2171template <
typename T>
2173 if (
auto array{ApplyElementwise(context, x)}) {
2176 if (
auto folded{OperandsAreConstants(x)}) {
2177 if constexpr (T::category == TypeCategory::Integer) {
2178 auto quotAndRem{folded->first.DivideSigned(folded->second)};
2179 if (quotAndRem.divisionByZero) {
2180 context.Warn(common::UsageWarning::FoldingException,
2181 "INTEGER(%d) division by zero"_warn_en_US, T::kind);
2184 if (quotAndRem.overflow) {
2185 context.Warn(common::UsageWarning::FoldingException,
2186 "INTEGER(%d) division overflowed"_warn_en_US, T::kind);
2189 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2190 auto quotAndRem{folded->first.DivideUnsigned(folded->second)};
2191 if (quotAndRem.divisionByZero) {
2192 context.Warn(common::UsageWarning::FoldingException,
2193 "UNSIGNED(%d) division by zero"_warn_en_US, T::kind);
2198 auto quotient{folded->first.Divide(
2199 folded->second, context.targetCharacteristics().roundingMode())};
2203 bool isCanonicalNaNOrInf{
false};
2204 if constexpr (T::category == TypeCategory::Real) {
2205 if (folded->second.IsZero() && context.moduleFileName().has_value()) {
2206 using IntType =
typename T::Scalar::Word;
2207 auto intNumerator{folded->first.template ToInteger<IntType>()};
2208 isCanonicalNaNOrInf = intNumerator.flags == RealFlags{} &&
2209 intNumerator.value >= IntType{-1} &&
2210 intNumerator.value <= IntType{1};
2213 if (!isCanonicalNaNOrInf) {
2214 context.RealFlagWarnings(quotient.flags,
"division");
2216 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2217 quotient.value = quotient.value.FlushSubnormalToZero();
2225template <
typename T>
2227 if (
auto array{ApplyElementwise(context, x)}) {
2230 if (
auto folded{OperandsAreConstants(x)}) {
2231 if constexpr (T::category == TypeCategory::Integer) {
2232 auto power{folded->first.Power(folded->second)};
2233 if (power.divisionByZero) {
2234 context.Warn(common::UsageWarning::FoldingException,
2235 "INTEGER(%d) zero to negative power"_warn_en_US, T::kind);
2236 }
else if (power.overflow) {
2237 context.Warn(common::UsageWarning::FoldingException,
2238 "INTEGER(%d) power overflowed"_warn_en_US, T::kind);
2239 }
else if (power.zeroToZero) {
2240 context.Warn(common::UsageWarning::FoldingException,
2241 "INTEGER(%d) 0**0 is not defined"_warn_en_US, T::kind);
2245 if (folded->first.IsZero()) {
2246 if (folded->second.IsZero()) {
2247 context.Warn(common::UsageWarning::FoldingException,
2248 "REAL/COMPLEX 0**0 is not defined"_warn_en_US);
2252 }
else if (
auto callable{GetHostRuntimeWrapper<T, T, T>(
"pow")}) {
2254 Constant<T>{(*callable)(context, folded->first, folded->second)}};
2256 context.Warn(common::UsageWarning::FoldingFailure,
2257 "Power for %s cannot be folded on host"_warn_en_US,
2265template <
typename T>
2267 if (
auto array{ApplyElementwise(context, x)}) {
2270 return common::visit(
2272 if (
auto folded{OperandsAreConstants(x.left(), y)}) {
2273 auto power{evaluate::IntPower(folded->first, folded->second)};
2274 context.RealFlagWarnings(power.flags,
"power with INTEGER exponent");
2275 if (context.targetCharacteristics().areSubnormalsFlushedToZero()) {
2276 power.value = power.value.FlushSubnormalToZero();
2286template <
typename T>
2288 x.condition() = Fold(context, std::move(x.condition()));
2290 if (
auto cst{GetScalarConstantValue<LogicalResult>(x.condition())}) {
2291 return cst->IsTrue() ? Fold(context, std::move(x.thenValue()))
2292 : Fold(context, std::move(x.elseValue()));
2297template <
typename T>
2299 if (
auto array{ApplyElementwise(context, x,
2306 if (
auto folded{OperandsAreConstants(x)}) {
2307 if constexpr (T::category == TypeCategory::Integer) {
2308 if (folded->first.CompareSigned(folded->second) == x.ordering) {
2311 }
else if constexpr (T::category == TypeCategory::Unsigned) {
2312 if (folded->first.CompareUnsigned(folded->second) == x.ordering) {
2315 }
else if constexpr (T::category == TypeCategory::Real) {
2316 if (folded->first.IsNotANumber() ||
2317 (folded->first.Compare(folded->second) == Relation::Less) ==
2318 (x.ordering == Ordering::Less)) {
2322 static_assert(T::category == TypeCategory::Character);
2325 auto maxLen{std::max(folded->first.length(), folded->second.length())};
2326 bool isFirst{x.ordering == Compare(folded->first, folded->second)};
2327 auto res{isFirst ? std::move(folded->first) : std::move(folded->second)};
2328 res = res.length() == maxLen
2330 : CharacterUtils<T::kind>::Resize(res, maxLen);
2342 std::optional<Expr<Result>> result;
2345 using From = std::decay_t<
decltype(x)>;
2346 if constexpr (std::is_same_v<From, BOZLiteralConstant>) {
2349 result = ConvertToType<Result>(std::move(x));
2350 const auto *constant{UnwrapExpr<Constant<Result>>(*result)};
2352 Scalar<Result> real{constant->GetScalarValue().value()};
2353 From converted{From::ConvertUnsigned(real.RawBits()).value};
2354 if (original != converted) {
2355 context.Warn(common::UsageWarning::FoldingValueChecks,
2356 "Nonzero bits truncated from BOZ literal constant in REAL intrinsic"_warn_en_US);
2358 }
else if constexpr (IsNumericCategoryExpr<From>()) {
2359 result = Fold(context, ConvertToType<Result>(std::move(x)));
2361 common::die(
"ToReal: bad argument expression");
2365 return result.value();
2374 if (
auto array{ApplyElementwise(context, x,
2378 x.isImaginaryPart, std::move(operand)}};
2382 auto &operand{x.left()};
2383 if (
auto value{GetScalarConstantValue<Operand>(operand)}) {
2384 if (x.isImaginaryPart) {
2393template <
typename T>
2395 return common::visit(
2397 if constexpr (IsSpecificIntrinsicType<T>) {
2398 return FoldOperation(context, std::move(x));
2399 }
else if constexpr (std::is_same_v<T, SomeDerived>) {
2400 return FoldOperation(context, std::move(x));
2401 }
else if constexpr (common::HasMember<
decltype(x),
2402 TypelessExpression>) {
2403 return std::move(expr);
2405 return Expr<T>{Fold(context, std::move(x))};
Definition fold-implementation.h:1341
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:53
Definition expression.h:444
Definition variable.h:101
Definition expression.h:113
Definition expression.h:781
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