1//===-- lib/Evaluate/fold.cpp ---------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "flang/Evaluate/fold.h"
10#include "fold-implementation.h"
11#include "flang/Evaluate/characteristics.h"
12#include "flang/Evaluate/initial-image.h"
13#include "flang/Evaluate/tools.h"
14
15namespace Fortran::evaluate {
16
17characteristics::TypeAndShape Fold(
18 FoldingContext &context, characteristics::TypeAndShape &&x) {
19 x.Rewrite(context);
20 return std::move(x);
21}
22
23std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
24 FoldingContext &context, Subscript &ss, const NamedEntity &base, int dim) {
25 ss = FoldOperation(context, std::move(ss));
26 return common::visit(
27 common::visitors{
28 [](IndirectSubscriptIntegerExpr &expr)
29 -> std::optional<Constant<SubscriptInteger>> {
30 if (const auto *constant{
31 UnwrapConstantValue<SubscriptInteger>(expr.value())}) {
32 return *constant;
33 } else {
34 return std::nullopt;
35 }
36 },
37 [&](Triplet &triplet) -> std::optional<Constant<SubscriptInteger>> {
38 auto lower{triplet.lower()}, upper{triplet.upper()};
39 std::optional<ConstantSubscript> stride{ToInt64(triplet.stride())};
40 if (!lower) {
41 lower = GetLBOUND(context, base, dim);
42 }
43 if (!upper) {
44 if (auto lb{GetLBOUND(context, base, dim)}) {
45 upper = ComputeUpperBound(
46 context, std::move(*lb), GetExtent(context, base, dim));
47 }
48 }
49 auto lbi{ToInt64(lower)}, ubi{ToInt64(upper)};
50 if (lbi && ubi && stride && *stride != 0) {
51 std::vector<SubscriptInteger::Scalar> values;
52 while ((*stride > 0 && *lbi <= *ubi) ||
53 (*stride < 0 && *lbi >= *ubi)) {
54 values.emplace_back(*lbi);
55 *lbi += *stride;
56 }
57 return Constant<SubscriptInteger>{std::move(values),
58 ConstantSubscripts{
59 static_cast<ConstantSubscript>(values.size())}};
60 } else {
61 return std::nullopt;
62 }
63 },
64 },
65 ss.u);
66}
67
68Expr<SomeDerived> FoldOperation(
69 FoldingContext &context, StructureConstructor &&structure) {
70 StructureConstructor ctor{structure.derivedTypeSpec()};
71 bool isConstant{true};
72 auto restorer{context.WithPDTInstance(structure.derivedTypeSpec())};
73 for (auto &&[symbol, value] : std::move(structure)) {
74 auto expr{Fold(context, std::move(value.value()))};
75 if (IsPointer(symbol)) {
76 if (IsNullPointer(expr)) {
77 // Handle x%c when x designates a named constant of derived
78 // type and %c is NULL() in that constant.
79 expr = Expr<SomeType>{NullPointer{}};
80 } else if (IsProcedure(symbol)) {
81 isConstant &= IsInitialProcedureTarget(expr);
82 } else {
83 isConstant &= IsInitialDataTarget(expr);
84 }
85 } else if (IsAllocatable(symbol)) {
86 // F2023: 10.1.12 (3)(a)
87 // If comp-spec is not null() for the allocatable component the
88 // structure constructor is not a constant expression.
89 isConstant &= IsNullPointer(expr);
90 } else {
91 isConstant &= IsActuallyConstant(expr) || IsNullPointer(expr);
92 if (auto valueShape{GetConstantExtents(context, expr)}) {
93 if (auto componentShape{GetConstantExtents(context, symbol)}) {
94 if (GetRank(*componentShape) > 0 && GetRank(*valueShape) == 0) {
95 expr = ScalarConstantExpander{std::move(*componentShape)}.Expand(
96 std::move(expr));
97 isConstant &= expr.Rank() > 0;
98 } else {
99 isConstant &= *valueShape == *componentShape;
100 }
101 if (*valueShape == *componentShape) {
102 if (auto lbounds{AsConstantExtents(
103 context, GetLBOUNDs(context, NamedEntity{symbol}))}) {
104 expr =
105 ArrayConstantBoundChanger{std::move(*lbounds)}.ChangeLbounds(
106 std::move(expr));
107 }
108 }
109 }
110 }
111 }
112 ctor.Add(symbol, std::move(expr));
113 }
114 if (isConstant) {
115 return Expr<SomeDerived>{Constant<SomeDerived>{std::move(ctor)}};
116 } else {
117 return Expr<SomeDerived>{std::move(ctor)};
118 }
119}
120
121Component FoldOperation(FoldingContext &context, Component &&component) {
122 return {FoldOperation(context, std::move(component.base())),
123 component.GetLastSymbol()};
124}
125
126NamedEntity FoldOperation(FoldingContext &context, NamedEntity &&x) {
127 if (Component * c{x.UnwrapComponent()}) {
128 return NamedEntity{FoldOperation(context, std::move(*c))};
129 } else {
130 return std::move(x);
131 }
132}
133
134Triplet FoldOperation(FoldingContext &context, Triplet &&triplet) {
135 MaybeExtentExpr lower{triplet.lower()};
136 MaybeExtentExpr upper{triplet.upper()};
137 return {Fold(context, std::move(lower)), Fold(context, std::move(upper)),
138 Fold(context, triplet.stride())};
139}
140
141Subscript FoldOperation(FoldingContext &context, Subscript &&subscript) {
142 return common::visit(
143 common::visitors{
144 [&](IndirectSubscriptIntegerExpr &&expr) {
145 expr.value() = Fold(context, std::move(expr.value()));
146 return Subscript(std::move(expr));
147 },
148 [&](Triplet &&triplet) {
149 return Subscript(FoldOperation(context, std::move(triplet)));
150 },
151 },
152 std::move(subscript.u));
153}
154
155ArrayRef FoldOperation(FoldingContext &context, ArrayRef &&arrayRef) {
156 NamedEntity base{FoldOperation(context, std::move(arrayRef.base()))};
157 for (Subscript &subscript : arrayRef.subscript()) {
158 subscript = FoldOperation(context, std::move(subscript));
159 }
160 return ArrayRef{std::move(base), std::move(arrayRef.subscript())};
161}
162
163CoarrayRef FoldOperation(FoldingContext &context, CoarrayRef &&coarrayRef) {
164 std::vector<Subscript> subscript;
165 for (Subscript x : coarrayRef.subscript()) {
166 subscript.emplace_back(FoldOperation(context, std::move(x)));
167 }
168 std::vector<Expr<SubscriptInteger>> cosubscript;
169 for (Expr<SubscriptInteger> x : coarrayRef.cosubscript()) {
170 cosubscript.emplace_back(Fold(context, std::move(x)));
171 }
172 CoarrayRef folded{std::move(coarrayRef.base()), std::move(subscript),
173 std::move(cosubscript)};
174 if (std::optional<Expr<SomeInteger>> stat{coarrayRef.stat()}) {
175 folded.set_stat(Fold(context, std::move(*stat)));
176 }
177 if (std::optional<Expr<SomeInteger>> team{coarrayRef.team()}) {
178 folded.set_team(
179 Fold(context, std::move(*team)), coarrayRef.teamIsTeamNumber());
180 }
181 return folded;
182}
183
184DataRef FoldOperation(FoldingContext &context, DataRef &&dataRef) {
185 return common::visit(common::visitors{
186 [&](SymbolRef symbol) { return DataRef{*symbol}; },
187 [&](auto &&x) {
188 return DataRef{
189 FoldOperation(context, std::move(x))};
190 },
191 },
192 std::move(dataRef.u));
193}
194
195Substring FoldOperation(FoldingContext &context, Substring &&substring) {
196 auto lower{Fold(context, substring.lower())};
197 auto upper{Fold(context, substring.upper())};
198 if (const DataRef * dataRef{substring.GetParentIf<DataRef>()}) {
199 return Substring{FoldOperation(context, DataRef{*dataRef}),
200 std::move(lower), std::move(upper)};
201 } else {
202 auto p{*substring.GetParentIf<StaticDataObject::Pointer>()};
203 return Substring{std::move(p), std::move(lower), std::move(upper)};
204 }
205}
206
207ComplexPart FoldOperation(FoldingContext &context, ComplexPart &&complexPart) {
208 DataRef complex{complexPart.complex()};
209 return ComplexPart{
210 FoldOperation(context, std::move(complex)), complexPart.part()};
211}
212
213std::optional<std::int64_t> GetInt64ArgOr(
214 const std::optional<ActualArgument> &arg, std::int64_t defaultValue) {
215 return arg ? ToInt64(*arg) : defaultValue;
216}
217
218Expr<ImpliedDoIndex::Result> FoldOperation(
219 FoldingContext &context, ImpliedDoIndex &&iDo) {
220 if (std::optional<ConstantSubscript> value{context.GetImpliedDo(iDo.name)}) {
221 return Expr<ImpliedDoIndex::Result>{*value};
222 } else {
223 return Expr<ImpliedDoIndex::Result>{std::move(iDo)};
224 }
225}
226
227// TRANSFER (F'2018 16.9.193)
228std::optional<Expr<SomeType>> FoldTransfer(
229 FoldingContext &context, const ActualArguments &arguments) {
230 CHECK(arguments.size() == 2 || arguments.size() == 3);
231 const auto *source{UnwrapExpr<Expr<SomeType>>(arguments[0])};
232 std::optional<std::size_t> sourceBytes;
233 if (source) {
234 if (auto sourceTypeAndShape{
235 characteristics::TypeAndShape::Characterize(*source, context)}) {
236 if (auto sourceBytesExpr{
237 sourceTypeAndShape->MeasureSizeInBytes(context)}) {
238 sourceBytes = ToInt64(*sourceBytesExpr);
239 }
240 }
241 }
242 std::optional<DynamicType> moldType;
243 std::optional<std::int64_t> moldLength;
244 if (arguments[1]) { // MOLD=
245 moldType = arguments[1]->GetType();
246 if (moldType && moldType->category() == TypeCategory::Character) {
247 if (const auto *chExpr{UnwrapExpr<Expr<SomeCharacter>>(arguments[1])}) {
248 moldLength = ToInt64(Fold(context, chExpr->LEN()));
249 }
250 }
251 }
252 std::optional<ConstantSubscripts> extents;
253 if (arguments.size() == 2) { // no SIZE=
254 if (moldType && sourceBytes) {
255 if (arguments[1]->Rank() == 0) { // scalar MOLD=
256 extents = ConstantSubscripts{}; // empty extents (scalar result)
257 } else if (auto moldBytesExpr{
258 moldType->MeasureSizeInBytes(context, true)}) {
259 if (auto moldBytes{ToInt64(Fold(context, std::move(*moldBytesExpr)))};
260 *moldBytes > 0) {
261 extents = ConstantSubscripts{
262 static_cast<ConstantSubscript>((*sourceBytes) + *moldBytes - 1) /
263 *moldBytes};
264 }
265 }
266 }
267 } else if (arguments[2]) { // SIZE= is present
268 if (const auto *sizeExpr{arguments[2]->UnwrapExpr()}) {
269 if (auto sizeValue{ToInt64(*sizeExpr)}) {
270 extents = ConstantSubscripts{*sizeValue};
271 }
272 }
273 }
274 if (sourceBytes && IsActuallyConstant(*source) && moldType && extents &&
275 (moldLength || moldType->category() != TypeCategory::Character)) {
276 std::size_t elements{
277 extents->empty() ? 1 : static_cast<std::size_t>((*extents)[0])};
278 std::size_t totalBytes{*sourceBytes * elements};
279 // Don't fold intentional overflow cases from sneaky tests
280 if (totalBytes < std::size_t{1000000} &&
281 (elements == 0 || totalBytes / elements == *sourceBytes)) {
282 InitialImage image{*sourceBytes};
283 auto status{image.Add(0, *sourceBytes, *source, context)};
284 if (status == InitialImage::Ok) {
285 return image.AsConstant(
286 context, *moldType, moldLength, *extents, true /*pad with 0*/);
287 } else {
288 // Can fail due to an allocatable or automatic component;
289 // a warning will also have been produced.
290 CHECK(status == InitialImage::NotAConstant);
291 }
292 }
293 }
294 return std::nullopt;
295}
296
297template class ExpressionBase<SomeDerived>;
298template class ExpressionBase<SomeType>;
299
300} // namespace Fortran::evaluate
301

source code of flang/lib/Evaluate/fold.cpp