xref: /llvm-project/flang/lib/Evaluate/fold.cpp (revision 3ddfb6807e905868a3a9df71fa5ea87309181270)
164ab3302SCarolineConcatto //===-- lib/Evaluate/fold.cpp ---------------------------------------------===//
264ab3302SCarolineConcatto //
364ab3302SCarolineConcatto // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
464ab3302SCarolineConcatto // See https://llvm.org/LICENSE.txt for license information.
564ab3302SCarolineConcatto // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
664ab3302SCarolineConcatto //
764ab3302SCarolineConcatto //===----------------------------------------------------------------------===//
864ab3302SCarolineConcatto 
964ab3302SCarolineConcatto #include "flang/Evaluate/fold.h"
1064ab3302SCarolineConcatto #include "fold-implementation.h"
11a50bb84eSpeter klausler #include "flang/Evaluate/characteristics.h"
12ae93d8eaSPeter Klausler #include "flang/Evaluate/initial-image.h"
133bbb2c2dSPeter Klausler #include "flang/Evaluate/tools.h"
1464ab3302SCarolineConcatto 
1564ab3302SCarolineConcatto namespace Fortran::evaluate {
1664ab3302SCarolineConcatto 
Fold(FoldingContext & context,characteristics::TypeAndShape && x)17a50bb84eSpeter klausler characteristics::TypeAndShape Fold(
18a50bb84eSpeter klausler     FoldingContext &context, characteristics::TypeAndShape &&x) {
19a50bb84eSpeter klausler   x.Rewrite(context);
20a50bb84eSpeter klausler   return std::move(x);
21a50bb84eSpeter klausler }
22a50bb84eSpeter klausler 
GetConstantSubscript(FoldingContext & context,Subscript & ss,const NamedEntity & base,int dim)2364ab3302SCarolineConcatto std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
2464ab3302SCarolineConcatto     FoldingContext &context, Subscript &ss, const NamedEntity &base, int dim) {
2564ab3302SCarolineConcatto   ss = FoldOperation(context, std::move(ss));
26cd03e96fSPeter Klausler   return common::visit(
2764ab3302SCarolineConcatto       common::visitors{
2864ab3302SCarolineConcatto           [](IndirectSubscriptIntegerExpr &expr)
2964ab3302SCarolineConcatto               -> std::optional<Constant<SubscriptInteger>> {
3064ab3302SCarolineConcatto             if (const auto *constant{
3164ab3302SCarolineConcatto                     UnwrapConstantValue<SubscriptInteger>(expr.value())}) {
3264ab3302SCarolineConcatto               return *constant;
3364ab3302SCarolineConcatto             } else {
3464ab3302SCarolineConcatto               return std::nullopt;
3564ab3302SCarolineConcatto             }
3664ab3302SCarolineConcatto           },
3764ab3302SCarolineConcatto           [&](Triplet &triplet) -> std::optional<Constant<SubscriptInteger>> {
3864ab3302SCarolineConcatto             auto lower{triplet.lower()}, upper{triplet.upper()};
3964ab3302SCarolineConcatto             std::optional<ConstantSubscript> stride{ToInt64(triplet.stride())};
4064ab3302SCarolineConcatto             if (!lower) {
413b61587cSPeter Klausler               lower = GetLBOUND(context, base, dim);
4264ab3302SCarolineConcatto             }
4364ab3302SCarolineConcatto             if (!upper) {
443b61587cSPeter Klausler               if (auto lb{GetLBOUND(context, base, dim)}) {
453b61587cSPeter Klausler                 upper = ComputeUpperBound(
463b61587cSPeter Klausler                     context, std::move(*lb), GetExtent(context, base, dim));
473b61587cSPeter Klausler               }
4864ab3302SCarolineConcatto             }
4964ab3302SCarolineConcatto             auto lbi{ToInt64(lower)}, ubi{ToInt64(upper)};
5064ab3302SCarolineConcatto             if (lbi && ubi && stride && *stride != 0) {
5164ab3302SCarolineConcatto               std::vector<SubscriptInteger::Scalar> values;
5264ab3302SCarolineConcatto               while ((*stride > 0 && *lbi <= *ubi) ||
5364ab3302SCarolineConcatto                   (*stride < 0 && *lbi >= *ubi)) {
5464ab3302SCarolineConcatto                 values.emplace_back(*lbi);
5564ab3302SCarolineConcatto                 *lbi += *stride;
5664ab3302SCarolineConcatto               }
5764ab3302SCarolineConcatto               return Constant<SubscriptInteger>{std::move(values),
5864ab3302SCarolineConcatto                   ConstantSubscripts{
5964ab3302SCarolineConcatto                       static_cast<ConstantSubscript>(values.size())}};
6064ab3302SCarolineConcatto             } else {
6164ab3302SCarolineConcatto               return std::nullopt;
6264ab3302SCarolineConcatto             }
6364ab3302SCarolineConcatto           },
6464ab3302SCarolineConcatto       },
6564ab3302SCarolineConcatto       ss.u);
6664ab3302SCarolineConcatto }
6764ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,StructureConstructor && structure)6864ab3302SCarolineConcatto Expr<SomeDerived> FoldOperation(
6964ab3302SCarolineConcatto     FoldingContext &context, StructureConstructor &&structure) {
704171f80dSpeter klausler   StructureConstructor ctor{structure.derivedTypeSpec()};
71c4360b45SPeter Klausler   bool isConstant{true};
72a054c882SPeter Klausler   auto restorer{context.WithPDTInstance(structure.derivedTypeSpec())};
7364ab3302SCarolineConcatto   for (auto &&[symbol, value] : std::move(structure)) {
744171f80dSpeter klausler     auto expr{Fold(context, std::move(value.value()))};
75c4360b45SPeter Klausler     if (IsPointer(symbol)) {
7647c998ccSPeter Klausler       if (IsNullPointer(expr)) {
7747c998ccSPeter Klausler         // Handle x%c when x designates a named constant of derived
7847c998ccSPeter Klausler         // type and %c is NULL() in that constant.
7947c998ccSPeter Klausler         expr = Expr<SomeType>{NullPointer{}};
8047c998ccSPeter Klausler       } else if (IsProcedure(symbol)) {
81c4360b45SPeter Klausler         isConstant &= IsInitialProcedureTarget(expr);
82c4360b45SPeter Klausler       } else {
83c4360b45SPeter Klausler         isConstant &= IsInitialDataTarget(expr);
84c4360b45SPeter Klausler       }
853d3c63daSKelvin Li     } else if (IsAllocatable(symbol)) {
863d3c63daSKelvin Li       // F2023: 10.1.12 (3)(a)
873d3c63daSKelvin Li       // If comp-spec is not null() for the allocatable component the
883d3c63daSKelvin Li       // structure constructor is not a constant expression.
893d3c63daSKelvin Li       isConstant &= IsNullPointer(expr);
90c4360b45SPeter Klausler     } else {
91cd9aff8aSPeter Klausler       isConstant &= IsActuallyConstant(expr) || IsNullPointer(expr);
92641ede93Speter klausler       if (auto valueShape{GetConstantExtents(context, expr)}) {
934171f80dSpeter klausler         if (auto componentShape{GetConstantExtents(context, symbol)}) {
944171f80dSpeter klausler           if (GetRank(*componentShape) > 0 && GetRank(*valueShape) == 0) {
95641ede93Speter klausler             expr = ScalarConstantExpander{std::move(*componentShape)}.Expand(
964171f80dSpeter klausler                 std::move(expr));
97c4360b45SPeter Klausler             isConstant &= expr.Rank() > 0;
984171f80dSpeter klausler           } else {
99c4360b45SPeter Klausler             isConstant &= *valueShape == *componentShape;
1004171f80dSpeter klausler           }
1013bbb2c2dSPeter Klausler           if (*valueShape == *componentShape) {
1023bbb2c2dSPeter Klausler             if (auto lbounds{AsConstantExtents(
1033bbb2c2dSPeter Klausler                     context, GetLBOUNDs(context, NamedEntity{symbol}))}) {
1043bbb2c2dSPeter Klausler               expr =
1053bbb2c2dSPeter Klausler                   ArrayConstantBoundChanger{std::move(*lbounds)}.ChangeLbounds(
1063bbb2c2dSPeter Klausler                       std::move(expr));
1073bbb2c2dSPeter Klausler             }
1083bbb2c2dSPeter Klausler           }
1094171f80dSpeter klausler         }
110641ede93Speter klausler       }
1114171f80dSpeter klausler     }
112c4360b45SPeter Klausler     ctor.Add(symbol, std::move(expr));
1134171f80dSpeter klausler   }
114c4360b45SPeter Klausler   if (isConstant) {
1154171f80dSpeter klausler     return Expr<SomeDerived>{Constant<SomeDerived>{std::move(ctor)}};
1164171f80dSpeter klausler   } else {
1174171f80dSpeter klausler     return Expr<SomeDerived>{std::move(ctor)};
1184171f80dSpeter klausler   }
11964ab3302SCarolineConcatto }
12064ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,Component && component)12164ab3302SCarolineConcatto Component FoldOperation(FoldingContext &context, Component &&component) {
12264ab3302SCarolineConcatto   return {FoldOperation(context, std::move(component.base())),
12364ab3302SCarolineConcatto       component.GetLastSymbol()};
12464ab3302SCarolineConcatto }
12564ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,NamedEntity && x)12664ab3302SCarolineConcatto NamedEntity FoldOperation(FoldingContext &context, NamedEntity &&x) {
12764ab3302SCarolineConcatto   if (Component * c{x.UnwrapComponent()}) {
12864ab3302SCarolineConcatto     return NamedEntity{FoldOperation(context, std::move(*c))};
12964ab3302SCarolineConcatto   } else {
13064ab3302SCarolineConcatto     return std::move(x);
13164ab3302SCarolineConcatto   }
13264ab3302SCarolineConcatto }
13364ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,Triplet && triplet)13464ab3302SCarolineConcatto Triplet FoldOperation(FoldingContext &context, Triplet &&triplet) {
13564ab3302SCarolineConcatto   MaybeExtentExpr lower{triplet.lower()};
13664ab3302SCarolineConcatto   MaybeExtentExpr upper{triplet.upper()};
13764ab3302SCarolineConcatto   return {Fold(context, std::move(lower)), Fold(context, std::move(upper)),
13864ab3302SCarolineConcatto       Fold(context, triplet.stride())};
13964ab3302SCarolineConcatto }
14064ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,Subscript && subscript)14164ab3302SCarolineConcatto Subscript FoldOperation(FoldingContext &context, Subscript &&subscript) {
142cd03e96fSPeter Klausler   return common::visit(
143cd03e96fSPeter Klausler       common::visitors{
14464ab3302SCarolineConcatto           [&](IndirectSubscriptIntegerExpr &&expr) {
14564ab3302SCarolineConcatto             expr.value() = Fold(context, std::move(expr.value()));
14664ab3302SCarolineConcatto             return Subscript(std::move(expr));
14764ab3302SCarolineConcatto           },
14864ab3302SCarolineConcatto           [&](Triplet &&triplet) {
149cd03e96fSPeter Klausler             return Subscript(FoldOperation(context, std::move(triplet)));
15064ab3302SCarolineConcatto           },
15164ab3302SCarolineConcatto       },
15264ab3302SCarolineConcatto       std::move(subscript.u));
15364ab3302SCarolineConcatto }
15464ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,ArrayRef && arrayRef)15564ab3302SCarolineConcatto ArrayRef FoldOperation(FoldingContext &context, ArrayRef &&arrayRef) {
15664ab3302SCarolineConcatto   NamedEntity base{FoldOperation(context, std::move(arrayRef.base()))};
15764ab3302SCarolineConcatto   for (Subscript &subscript : arrayRef.subscript()) {
15864ab3302SCarolineConcatto     subscript = FoldOperation(context, std::move(subscript));
15964ab3302SCarolineConcatto   }
16064ab3302SCarolineConcatto   return ArrayRef{std::move(base), std::move(arrayRef.subscript())};
16164ab3302SCarolineConcatto }
16264ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,CoarrayRef && coarrayRef)16364ab3302SCarolineConcatto CoarrayRef FoldOperation(FoldingContext &context, CoarrayRef &&coarrayRef) {
16464ab3302SCarolineConcatto   std::vector<Subscript> subscript;
16564ab3302SCarolineConcatto   for (Subscript x : coarrayRef.subscript()) {
16664ab3302SCarolineConcatto     subscript.emplace_back(FoldOperation(context, std::move(x)));
16764ab3302SCarolineConcatto   }
16864ab3302SCarolineConcatto   std::vector<Expr<SubscriptInteger>> cosubscript;
16964ab3302SCarolineConcatto   for (Expr<SubscriptInteger> x : coarrayRef.cosubscript()) {
17064ab3302SCarolineConcatto     cosubscript.emplace_back(Fold(context, std::move(x)));
17164ab3302SCarolineConcatto   }
17264ab3302SCarolineConcatto   CoarrayRef folded{std::move(coarrayRef.base()), std::move(subscript),
17364ab3302SCarolineConcatto       std::move(cosubscript)};
17464ab3302SCarolineConcatto   if (std::optional<Expr<SomeInteger>> stat{coarrayRef.stat()}) {
17564ab3302SCarolineConcatto     folded.set_stat(Fold(context, std::move(*stat)));
17664ab3302SCarolineConcatto   }
17764ab3302SCarolineConcatto   if (std::optional<Expr<SomeInteger>> team{coarrayRef.team()}) {
17864ab3302SCarolineConcatto     folded.set_team(
17964ab3302SCarolineConcatto         Fold(context, std::move(*team)), coarrayRef.teamIsTeamNumber());
18064ab3302SCarolineConcatto   }
18164ab3302SCarolineConcatto   return folded;
18264ab3302SCarolineConcatto }
18364ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,DataRef && dataRef)18464ab3302SCarolineConcatto DataRef FoldOperation(FoldingContext &context, DataRef &&dataRef) {
185cd03e96fSPeter Klausler   return common::visit(common::visitors{
18664ab3302SCarolineConcatto                            [&](SymbolRef symbol) { return DataRef{*symbol}; },
18764ab3302SCarolineConcatto                            [&](auto &&x) {
188cd03e96fSPeter Klausler                              return DataRef{
189cd03e96fSPeter Klausler                                  FoldOperation(context, std::move(x))};
19064ab3302SCarolineConcatto                            },
19164ab3302SCarolineConcatto                        },
19264ab3302SCarolineConcatto       std::move(dataRef.u));
19364ab3302SCarolineConcatto }
19464ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,Substring && substring)19564ab3302SCarolineConcatto Substring FoldOperation(FoldingContext &context, Substring &&substring) {
19664ab3302SCarolineConcatto   auto lower{Fold(context, substring.lower())};
19764ab3302SCarolineConcatto   auto upper{Fold(context, substring.upper())};
19864ab3302SCarolineConcatto   if (const DataRef * dataRef{substring.GetParentIf<DataRef>()}) {
19964ab3302SCarolineConcatto     return Substring{FoldOperation(context, DataRef{*dataRef}),
20064ab3302SCarolineConcatto         std::move(lower), std::move(upper)};
20164ab3302SCarolineConcatto   } else {
20264ab3302SCarolineConcatto     auto p{*substring.GetParentIf<StaticDataObject::Pointer>()};
20364ab3302SCarolineConcatto     return Substring{std::move(p), std::move(lower), std::move(upper)};
20464ab3302SCarolineConcatto   }
20564ab3302SCarolineConcatto }
20664ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,ComplexPart && complexPart)20764ab3302SCarolineConcatto ComplexPart FoldOperation(FoldingContext &context, ComplexPart &&complexPart) {
20864ab3302SCarolineConcatto   DataRef complex{complexPart.complex()};
20964ab3302SCarolineConcatto   return ComplexPart{
21064ab3302SCarolineConcatto       FoldOperation(context, std::move(complex)), complexPart.part()};
21164ab3302SCarolineConcatto }
21264ab3302SCarolineConcatto 
GetInt64ArgOr(const std::optional<ActualArgument> & arg,std::int64_t defaultValue)21364ab3302SCarolineConcatto std::optional<std::int64_t> GetInt64ArgOr(
21464ab3302SCarolineConcatto     const std::optional<ActualArgument> &arg, std::int64_t defaultValue) {
215af54b676SPeter Klausler   return arg ? ToInt64(*arg) : defaultValue;
21664ab3302SCarolineConcatto }
21764ab3302SCarolineConcatto 
FoldOperation(FoldingContext & context,ImpliedDoIndex && iDo)21864ab3302SCarolineConcatto Expr<ImpliedDoIndex::Result> FoldOperation(
21964ab3302SCarolineConcatto     FoldingContext &context, ImpliedDoIndex &&iDo) {
22064ab3302SCarolineConcatto   if (std::optional<ConstantSubscript> value{context.GetImpliedDo(iDo.name)}) {
22164ab3302SCarolineConcatto     return Expr<ImpliedDoIndex::Result>{*value};
22264ab3302SCarolineConcatto   } else {
22364ab3302SCarolineConcatto     return Expr<ImpliedDoIndex::Result>{std::move(iDo)};
22464ab3302SCarolineConcatto   }
22564ab3302SCarolineConcatto }
22664ab3302SCarolineConcatto 
227ae93d8eaSPeter Klausler // TRANSFER (F'2018 16.9.193)
FoldTransfer(FoldingContext & context,const ActualArguments & arguments)228ae93d8eaSPeter Klausler std::optional<Expr<SomeType>> FoldTransfer(
229ae93d8eaSPeter Klausler     FoldingContext &context, const ActualArguments &arguments) {
230ae93d8eaSPeter Klausler   CHECK(arguments.size() == 2 || arguments.size() == 3);
231ae93d8eaSPeter Klausler   const auto *source{UnwrapExpr<Expr<SomeType>>(arguments[0])};
232ae93d8eaSPeter Klausler   std::optional<std::size_t> sourceBytes;
233ae93d8eaSPeter Klausler   if (source) {
234ae93d8eaSPeter Klausler     if (auto sourceTypeAndShape{
235ae93d8eaSPeter Klausler             characteristics::TypeAndShape::Characterize(*source, context)}) {
236ae93d8eaSPeter Klausler       if (auto sourceBytesExpr{
237ae93d8eaSPeter Klausler               sourceTypeAndShape->MeasureSizeInBytes(context)}) {
238ae93d8eaSPeter Klausler         sourceBytes = ToInt64(*sourceBytesExpr);
239ae93d8eaSPeter Klausler       }
240ae93d8eaSPeter Klausler     }
241ae93d8eaSPeter Klausler   }
242ae93d8eaSPeter Klausler   std::optional<DynamicType> moldType;
243e6be8da1SPeter Klausler   std::optional<std::int64_t> moldLength;
244e6be8da1SPeter Klausler   if (arguments[1]) { // MOLD=
245ae93d8eaSPeter Klausler     moldType = arguments[1]->GetType();
246e6be8da1SPeter Klausler     if (moldType && moldType->category() == TypeCategory::Character) {
247e6be8da1SPeter Klausler       if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(arguments[1])}) {
248e6be8da1SPeter Klausler         moldLength = ToInt64(Fold(context, chExpr->LEN()));
249e6be8da1SPeter Klausler       }
250e6be8da1SPeter Klausler     }
251ae93d8eaSPeter Klausler   }
252ae93d8eaSPeter Klausler   std::optional<ConstantSubscripts> extents;
253ae93d8eaSPeter Klausler   if (arguments.size() == 2) { // no SIZE=
254ae93d8eaSPeter Klausler     if (moldType && sourceBytes) {
255ae93d8eaSPeter Klausler       if (arguments[1]->Rank() == 0) { // scalar MOLD=
256ae93d8eaSPeter Klausler         extents = ConstantSubscripts{}; // empty extents (scalar result)
257ae93d8eaSPeter Klausler       } else if (auto moldBytesExpr{
258ae93d8eaSPeter Klausler                      moldType->MeasureSizeInBytes(context, true)}) {
259ae93d8eaSPeter Klausler         if (auto moldBytes{ToInt64(Fold(context, std::move(*moldBytesExpr)))};
260ae93d8eaSPeter Klausler             *moldBytes > 0) {
261ae93d8eaSPeter Klausler           extents = ConstantSubscripts{
262ae93d8eaSPeter Klausler               static_cast<ConstantSubscript>((*sourceBytes) + *moldBytes - 1) /
263ae93d8eaSPeter Klausler               *moldBytes};
264ae93d8eaSPeter Klausler         }
265ae93d8eaSPeter Klausler       }
266ae93d8eaSPeter Klausler     }
267ae93d8eaSPeter Klausler   } else if (arguments[2]) { // SIZE= is present
268ae93d8eaSPeter Klausler     if (const auto *sizeExpr{arguments[2]->UnwrapExpr()}) {
269ae93d8eaSPeter Klausler       if (auto sizeValue{ToInt64(*sizeExpr)}) {
270ae93d8eaSPeter Klausler         extents = ConstantSubscripts{*sizeValue};
271ae93d8eaSPeter Klausler       }
272ae93d8eaSPeter Klausler     }
273ae93d8eaSPeter Klausler   }
274e6be8da1SPeter Klausler   if (sourceBytes && IsActuallyConstant(*source) && moldType && extents &&
275*3ddfb680SPeter Klausler       !moldType->IsPolymorphic() &&
276e6be8da1SPeter Klausler       (moldLength || moldType->category() != TypeCategory::Character)) {
277cf63261bSPeter Klausler     std::size_t elements{
278cf63261bSPeter Klausler         extents->empty() ? 1 : static_cast<std::size_t>((*extents)[0])};
279cf63261bSPeter Klausler     std::size_t totalBytes{*sourceBytes * elements};
280cf63261bSPeter Klausler     // Don't fold intentional overflow cases from sneaky tests
281cf63261bSPeter Klausler     if (totalBytes < std::size_t{1000000} &&
282cf63261bSPeter Klausler         (elements == 0 || totalBytes / elements == *sourceBytes)) {
283ae93d8eaSPeter Klausler       InitialImage image{*sourceBytes};
284beb437edSPeter Klausler       auto status{image.Add(0, *sourceBytes, *source, context)};
285beb437edSPeter Klausler       if (status == InitialImage::Ok) {
286cf63261bSPeter Klausler         return image.AsConstant(
287e6be8da1SPeter Klausler             context, *moldType, moldLength, *extents, true /*pad with 0*/);
288beb437edSPeter Klausler       } else {
289beb437edSPeter Klausler         // Can fail due to an allocatable or automatic component;
290beb437edSPeter Klausler         // a warning will also have been produced.
291beb437edSPeter Klausler         CHECK(status == InitialImage::NotAConstant);
292beb437edSPeter Klausler       }
293ae93d8eaSPeter Klausler     }
294ae93d8eaSPeter Klausler   }
295cf63261bSPeter Klausler   return std::nullopt;
296cf63261bSPeter Klausler }
297ae93d8eaSPeter Klausler 
29864ab3302SCarolineConcatto template class ExpressionBase<SomeDerived>;
29964ab3302SCarolineConcatto template class ExpressionBase<SomeType>;
30064ab3302SCarolineConcatto 
3011f879005STim Keith } // namespace Fortran::evaluate
302