1 //===-- lib/Evaluate/designate.cpp ------------------------------*- C++ -*-===//
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-designator.h"
10 #include "flang/Semantics/tools.h"
11 
12 namespace Fortran::evaluate {
13 
DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(OffsetSymbol)14 DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(OffsetSymbol)
15 
16 std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
17     const Symbol &symbol, ConstantSubscript which) {
18   if (IsAllocatableOrPointer(symbol)) {
19     // A pointer may appear as a DATA statement object if it is the
20     // rightmost symbol in a designator and has no subscripts.
21     // An allocatable may appear if its initializer is NULL().
22     if (which > 0) {
23       isEmpty_ = true;
24     } else {
25       return OffsetSymbol{symbol, symbol.size()};
26     }
27   } else if (symbol.has<semantics::ObjectEntityDetails>() &&
28       !IsNamedConstant(symbol)) {
29     if (auto type{DynamicType::From(symbol)}) {
30       if (auto extents{GetConstantExtents(context_, symbol)}) {
31         if (auto bytes{ToInt64(
32                 type->MeasureSizeInBytes(context_, GetRank(*extents) > 0))}) {
33           OffsetSymbol result{symbol, static_cast<std::size_t>(*bytes)};
34           if (which < GetSize(*extents)) {
35             result.Augment(*bytes * which);
36             return result;
37           } else {
38             isEmpty_ = true;
39           }
40         }
41       }
42     }
43   }
44   return std::nullopt;
45 }
46 
FoldDesignator(const ArrayRef & x,ConstantSubscript which)47 std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
48     const ArrayRef &x, ConstantSubscript which) {
49   const Symbol &array{x.base().GetLastSymbol()};
50   if (auto type{DynamicType::From(array)}) {
51     if (auto extents{GetConstantExtents(context_, array)}) {
52       if (auto bytes{ToInt64(type->MeasureSizeInBytes(context_, true))}) {
53         Shape lbs{GetLBOUNDs(context_, x.base())};
54         if (auto lowerBounds{AsConstantExtents(context_, lbs)}) {
55           std::optional<OffsetSymbol> result;
56           if (!x.base().IsSymbol() &&
57               x.base().GetComponent().base().Rank() > 0) {
58             // A(:)%B(1) - apply elementNumber_ to base
59             result = FoldDesignator(x.base(), which);
60             which = 0;
61           } else { // A(1)%B(:) - apply elementNumber_ to subscripts
62             result = FoldDesignator(x.base(), 0);
63           }
64           if (!result) {
65             return std::nullopt;
66           }
67           auto stride{*bytes};
68           int dim{0};
69           for (const Subscript &subscript : x.subscript()) {
70             ConstantSubscript lower{lowerBounds->at(dim)};
71             ConstantSubscript extent{extents->at(dim)};
72             ConstantSubscript upper{lower + extent - 1};
73             if (!common::visit(
74                     common::visitors{
75                         [&](const IndirectSubscriptIntegerExpr &expr) {
76                           auto folded{
77                               Fold(context_, common::Clone(expr.value()))};
78                           if (auto value{UnwrapConstantValue<SubscriptInteger>(
79                                   folded)}) {
80                             CHECK(value->Rank() <= 1);
81                             if (value->size() != 0) {
82                               // Apply subscript, possibly vector-valued
83                               auto quotient{which / value->size()};
84                               auto remainder{which - value->size() * quotient};
85                               ConstantSubscript at{
86                                   value->values().at(remainder).ToInt64()};
87                               if (at < lower || at > upper) {
88                                 isOutOfRange_ = true;
89                               }
90                               result->Augment((at - lower) * stride);
91                               which = quotient;
92                               return true;
93                             }
94                           }
95                           return false;
96                         },
97                         [&](const Triplet &triplet) {
98                           auto start{ToInt64(Fold(context_,
99                               triplet.lower().value_or(ExtentExpr{lower})))};
100                           auto end{ToInt64(Fold(context_,
101                               triplet.upper().value_or(ExtentExpr{upper})))};
102                           auto step{ToInt64(Fold(context_, triplet.stride()))};
103                           if (start && end && step && *step != 0) {
104                             ConstantSubscript range{
105                                 (*end - *start + *step) / *step};
106                             if (range > 0) {
107                               auto quotient{which / range};
108                               auto remainder{which - range * quotient};
109                               auto j{*start + remainder * *step};
110                               result->Augment((j - lower) * stride);
111                               which = quotient;
112                               return true;
113                             }
114                           }
115                           return false;
116                         },
117                     },
118                     subscript.u)) {
119               return std::nullopt;
120             }
121             ++dim;
122             stride *= extent;
123           }
124           if (which > 0) {
125             isEmpty_ = true;
126           } else {
127             return result;
128           }
129         }
130       }
131     }
132   }
133   return std::nullopt;
134 }
135 
FoldDesignator(const Component & component,ConstantSubscript which)136 std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
137     const Component &component, ConstantSubscript which) {
138   const Symbol &comp{component.GetLastSymbol()};
139   const DataRef &base{component.base()};
140   std::optional<OffsetSymbol> baseResult, compResult;
141   if (base.Rank() == 0) { // A%X(:) - apply "which" to component
142     baseResult = FoldDesignator(base, 0);
143     compResult = FoldDesignator(comp, which);
144   } else { // A(:)%X - apply "which" to base
145     baseResult = FoldDesignator(base, which);
146     compResult = FoldDesignator(comp, 0);
147   }
148   if (baseResult && compResult) {
149     OffsetSymbol result{baseResult->symbol(), compResult->size()};
150     result.Augment(baseResult->offset() + compResult->offset() + comp.offset());
151     return {std::move(result)};
152   } else {
153     return std::nullopt;
154   }
155 }
156 
FoldDesignator(const ComplexPart & z,ConstantSubscript which)157 std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
158     const ComplexPart &z, ConstantSubscript which) {
159   if (auto result{FoldDesignator(z.complex(), which)}) {
160     result->set_size(result->size() >> 1);
161     if (z.part() == ComplexPart::Part::IM) {
162       result->Augment(result->size());
163     }
164     return result;
165   } else {
166     return std::nullopt;
167   }
168 }
169 
FoldDesignator(const DataRef & dataRef,ConstantSubscript which)170 std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
171     const DataRef &dataRef, ConstantSubscript which) {
172   return common::visit(
173       [&](const auto &x) { return FoldDesignator(x, which); }, dataRef.u);
174 }
175 
FoldDesignator(const NamedEntity & entity,ConstantSubscript which)176 std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
177     const NamedEntity &entity, ConstantSubscript which) {
178   return entity.IsSymbol() ? FoldDesignator(entity.GetLastSymbol(), which)
179                            : FoldDesignator(entity.GetComponent(), which);
180 }
181 
FoldDesignator(const CoarrayRef &,ConstantSubscript)182 std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
183     const CoarrayRef &, ConstantSubscript) {
184   return std::nullopt;
185 }
186 
FoldDesignator(const ProcedureDesignator & proc,ConstantSubscript which)187 std::optional<OffsetSymbol> DesignatorFolder::FoldDesignator(
188     const ProcedureDesignator &proc, ConstantSubscript which) {
189   if (const Symbol * symbol{proc.GetSymbol()}) {
190     if (const Component * component{proc.GetComponent()}) {
191       return FoldDesignator(*component, which);
192     } else if (which > 0) {
193       isEmpty_ = true;
194     } else {
195       return FoldDesignator(*symbol, 0);
196     }
197   }
198   return std::nullopt;
199 }
200 
201 // Conversions of offset symbols (back) to Designators
202 
203 // Reconstructs subscripts.
204 // "offset" is decremented in place to hold remaining component offset.
OffsetToArrayRef(FoldingContext & context,NamedEntity && entity,const Shape & shape,const DynamicType & elementType,ConstantSubscript & offset)205 static std::optional<ArrayRef> OffsetToArrayRef(FoldingContext &context,
206     NamedEntity &&entity, const Shape &shape, const DynamicType &elementType,
207     ConstantSubscript &offset) {
208   auto extents{AsConstantExtents(context, shape)};
209   Shape lbs{GetRawLowerBounds(context, entity)};
210   auto lower{AsConstantExtents(context, lbs)};
211   auto elementBytes{ToInt64(elementType.MeasureSizeInBytes(context, true))};
212   if (!extents || !lower || !elementBytes || *elementBytes <= 0) {
213     return std::nullopt;
214   }
215   int rank{GetRank(shape)};
216   CHECK(extents->size() == static_cast<std::size_t>(rank) &&
217       lower->size() == extents->size());
218   auto element{offset / static_cast<std::size_t>(*elementBytes)};
219   std::vector<Subscript> subscripts;
220   auto at{element};
221   for (int dim{0}; dim + 1 < rank; ++dim) {
222     auto extent{(*extents)[dim]};
223     if (extent <= 0) {
224       return std::nullopt;
225     }
226     auto quotient{at / extent};
227     auto remainder{at - quotient * extent};
228     subscripts.emplace_back(ExtentExpr{(*lower)[dim] + remainder});
229     at = quotient;
230   }
231   // This final subscript might be out of range for use in error reporting.
232   subscripts.emplace_back(ExtentExpr{(*lower)[rank - 1] + at});
233   offset -= element * static_cast<std::size_t>(*elementBytes);
234   return ArrayRef{std::move(entity), std::move(subscripts)};
235 }
236 
237 // Maps an offset back to a component, when unambiguous.
OffsetToUniqueComponent(const semantics::DerivedTypeSpec & spec,ConstantSubscript offset)238 static const Symbol *OffsetToUniqueComponent(
239     const semantics::DerivedTypeSpec &spec, ConstantSubscript offset) {
240   const Symbol *result{nullptr};
241   if (const semantics::Scope * scope{spec.scope()}) {
242     for (const auto &pair : *scope) {
243       const Symbol &component{*pair.second};
244       if (offset >= static_cast<ConstantSubscript>(component.offset()) &&
245           offset < static_cast<ConstantSubscript>(
246                        component.offset() + component.size())) {
247         if (result) {
248           return nullptr; // MAP overlap or error recovery
249         }
250         result = &component;
251       }
252     }
253   }
254   return result;
255 }
256 
257 // Converts an offset into subscripts &/or component references.  Recursive.
258 // Any remaining offset is left in place in the "offset" reference argument.
OffsetToDataRef(FoldingContext & context,NamedEntity && entity,ConstantSubscript & offset,std::size_t size)259 static std::optional<DataRef> OffsetToDataRef(FoldingContext &context,
260     NamedEntity &&entity, ConstantSubscript &offset, std::size_t size) {
261   const Symbol &symbol{entity.GetLastSymbol()};
262   if (IsAllocatableOrPointer(symbol)) {
263     return entity.IsSymbol() ? DataRef{symbol}
264                              : DataRef{std::move(entity.GetComponent())};
265   }
266   std::optional<DataRef> result;
267   if (std::optional<DynamicType> type{DynamicType::From(symbol)}) {
268     if (!type->IsUnlimitedPolymorphic()) {
269       if (std::optional<Shape> shape{GetShape(context, symbol)}) {
270         if (GetRank(*shape) > 0) {
271           if (auto aref{OffsetToArrayRef(
272                   context, std::move(entity), *shape, *type, offset)}) {
273             result = DataRef{std::move(*aref)};
274           }
275         } else {
276           result = entity.IsSymbol()
277               ? DataRef{symbol}
278               : DataRef{std::move(entity.GetComponent())};
279         }
280         if (result && type->category() == TypeCategory::Derived &&
281             size < result->GetLastSymbol().size()) {
282           if (const Symbol *
283               component{OffsetToUniqueComponent(
284                   type->GetDerivedTypeSpec(), offset)}) {
285             offset -= component->offset();
286             return OffsetToDataRef(context,
287                 NamedEntity{Component{std::move(*result), *component}}, offset,
288                 size);
289           }
290           result.reset();
291         }
292       }
293     }
294   }
295   return result;
296 }
297 
298 // Reconstructs a Designator from a symbol, an offset, and a size.
OffsetToDesignator(FoldingContext & context,const Symbol & baseSymbol,ConstantSubscript offset,std::size_t size)299 std::optional<Expr<SomeType>> OffsetToDesignator(FoldingContext &context,
300     const Symbol &baseSymbol, ConstantSubscript offset, std::size_t size) {
301   if (offset < 0) {
302     return std::nullopt;
303   }
304   if (std::optional<DataRef> dataRef{
305           OffsetToDataRef(context, NamedEntity{baseSymbol}, offset, size)}) {
306     const Symbol &symbol{dataRef->GetLastSymbol()};
307     if (std::optional<Expr<SomeType>> result{
308             AsGenericExpr(std::move(*dataRef))}) {
309       if (IsAllocatableOrPointer(symbol)) {
310       } else if (auto type{DynamicType::From(symbol)}) {
311         if (auto elementBytes{
312                 ToInt64(type->MeasureSizeInBytes(context, true))}) {
313           if (auto *zExpr{std::get_if<Expr<SomeComplex>>(&result->u)}) {
314             if (size * 2 > static_cast<std::size_t>(*elementBytes)) {
315               return result;
316             } else if (offset == 0 || offset * 2 == *elementBytes) {
317               // Pick a COMPLEX component
318               auto part{
319                   offset == 0 ? ComplexPart::Part::RE : ComplexPart::Part::IM};
320               return common::visit(
321                   [&](const auto &z) -> std::optional<Expr<SomeType>> {
322                     using PartType = typename ResultType<decltype(z)>::Part;
323                     return AsGenericExpr(Designator<PartType>{ComplexPart{
324                         ExtractDataRef(std::move(*zExpr)).value(), part}});
325                   },
326                   zExpr->u);
327             }
328           } else if (auto *cExpr{
329                          std::get_if<Expr<SomeCharacter>>(&result->u)}) {
330             if (offset > 0 || size != static_cast<std::size_t>(*elementBytes)) {
331               // Select a substring
332               return common::visit(
333                   [&](const auto &x) -> std::optional<Expr<SomeType>> {
334                     using T = typename std::decay_t<decltype(x)>::Result;
335                     return AsGenericExpr(Designator<T>{
336                         Substring{ExtractDataRef(std::move(*cExpr)).value(),
337                             std::optional<Expr<SubscriptInteger>>{
338                                 1 + (offset / T::kind)},
339                             std::optional<Expr<SubscriptInteger>>{
340                                 1 + ((offset + size - 1) / T::kind)}}});
341                   },
342                   cExpr->u);
343             }
344           }
345         }
346       }
347       if (offset == 0) {
348         return result;
349       }
350     }
351   }
352   return std::nullopt;
353 }
354 
OffsetToDesignator(FoldingContext & context,const OffsetSymbol & offsetSymbol)355 std::optional<Expr<SomeType>> OffsetToDesignator(
356     FoldingContext &context, const OffsetSymbol &offsetSymbol) {
357   return OffsetToDesignator(context, offsetSymbol.symbol(),
358       offsetSymbol.offset(), offsetSymbol.size());
359 }
360 
From(FoldingContext & context,const Expr<SomeType> & expr)361 ConstantObjectPointer ConstantObjectPointer::From(
362     FoldingContext &context, const Expr<SomeType> &expr) {
363   auto extents{GetConstantExtents(context, expr)};
364   CHECK(extents);
365   std::size_t elements{TotalElementCount(*extents)};
366   CHECK(elements > 0);
367   int rank{GetRank(*extents)};
368   ConstantSubscripts at(rank, 1);
369   ConstantObjectPointer::Dimensions dimensions(rank);
370   for (int j{0}; j < rank; ++j) {
371     dimensions[j].extent = (*extents)[j];
372   }
373   DesignatorFolder designatorFolder{context};
374   const Symbol *symbol{nullptr};
375   ConstantSubscript baseOffset{0};
376   std::size_t elementSize{0};
377   for (std::size_t j{0}; j < elements; ++j) {
378     auto folded{designatorFolder.FoldDesignator(expr)};
379     CHECK(folded);
380     if (j == 0) {
381       symbol = &folded->symbol();
382       baseOffset = folded->offset();
383       elementSize = folded->size();
384     } else {
385       CHECK(symbol == &folded->symbol());
386       CHECK(elementSize == folded->size());
387     }
388     int twoDim{-1};
389     for (int k{0}; k < rank; ++k) {
390       if (at[k] == 2 && twoDim == -1) {
391         twoDim = k;
392       } else if (at[k] != 1) {
393         twoDim = -2;
394       }
395     }
396     if (twoDim >= 0) {
397       // Exactly one subscript is a 2 and the rest are 1.
398       dimensions[twoDim].byteStride = folded->offset() - baseOffset;
399     }
400     ConstantSubscript checkOffset{baseOffset};
401     for (int k{0}; k < rank; ++k) {
402       checkOffset += (at[k] - 1) * dimensions[twoDim].byteStride;
403     }
404     CHECK(checkOffset == folded->offset());
405     CHECK(IncrementSubscripts(at, *extents) == (j + 1 < elements));
406   }
407   CHECK(!designatorFolder.FoldDesignator(expr));
408   return ConstantObjectPointer{
409       DEREF(symbol), elementSize, std::move(dimensions)};
410 }
411 } // namespace Fortran::evaluate
412