FLANG
shape.h
1//===-- include/flang/Evaluate/shape.h --------------------------*- 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// GetShape() analyzes an expression and determines its shape, if possible,
10// representing the result as a vector of scalar integer expressions.
11
12#ifndef FORTRAN_EVALUATE_SHAPE_H_
13#define FORTRAN_EVALUATE_SHAPE_H_
14
15#include "expression.h"
16#include "traverse.h"
17#include "variable.h"
18#include "flang/Common/indirection.h"
19#include "flang/Evaluate/type.h"
20#include <optional>
21#include <variant>
22
23namespace Fortran::parser {
25}
26
27namespace Fortran::evaluate {
28
29class FoldingContext;
30
31using ExtentType = SubscriptInteger;
32using ExtentExpr = Expr<ExtentType>;
33using MaybeExtentExpr = std::optional<ExtentExpr>;
34using Shape = std::vector<MaybeExtentExpr>;
35
36bool IsImpliedShape(const Symbol &);
37bool IsExplicitShape(const Symbol &);
38
39// Conversions between various representations of shapes.
40std::optional<ExtentExpr> AsExtentArrayExpr(const Shape &);
41
42std::optional<Constant<ExtentType>> AsConstantShape(
43 FoldingContext &, const Shape &);
44Constant<ExtentType> AsConstantShape(const ConstantSubscripts &);
45
46// AsConstantExtents returns a constant shape. It may contain
47// invalid negative extents; use HasNegativeExtent() to check.
48ConstantSubscripts AsConstantExtents(const Constant<ExtentType> &);
49std::optional<ConstantSubscripts> AsConstantExtents(
50 FoldingContext &, const Shape &);
51inline std::optional<ConstantSubscripts> AsConstantExtents(
52 FoldingContext &foldingContext, const std::optional<Shape> &maybeShape) {
53 if (maybeShape) {
54 return AsConstantExtents(foldingContext, *maybeShape);
55 }
56 return std::nullopt;
57}
58
59Shape AsShape(const ConstantSubscripts &);
60std::optional<Shape> AsShape(const std::optional<ConstantSubscripts> &);
61
62inline int GetRank(const Shape &s) { return static_cast<int>(s.size()); }
63
64Shape Fold(FoldingContext &, Shape &&);
65std::optional<Shape> Fold(FoldingContext &, std::optional<Shape> &&);
66
67// Computes shapes in terms of expressions that are scope-invariant, by
68// default, which is nearly always what one wants outside of procedure
69// characterization.
70template <typename A>
71std::optional<Shape> GetShape(
72 FoldingContext &, const A &, bool invariantOnly = true);
73template <typename A>
74std::optional<Shape> GetShape(
75 FoldingContext *, const A &, bool invariantOnly = true);
76template <typename A>
77std::optional<Shape> GetShape(const A &, bool invariantOnly = true);
78
79// The dimension argument to these inquiries is zero-based,
80// unlike the DIM= arguments to many intrinsics.
81//
82// GetRawLowerBound() returns a lower bound expression, which may
83// not be suitable for all purposes; specifically, it might not be invariant
84// in its scope, and it will not have been forced to 1 on an empty dimension.
85// GetLBOUND()'s result is safer, but it is optional because it does fail
86// in those circumstances.
87// Similarly, GetUBOUND result will be forced to 0 on an empty dimension,
88// but will fail if the extent is not a compile time constant.
89ExtentExpr GetRawLowerBound(
90 const NamedEntity &, int dimension, bool invariantOnly = true);
91ExtentExpr GetRawLowerBound(FoldingContext &, const NamedEntity &,
92 int dimension, bool invariantOnly = true);
93MaybeExtentExpr GetLBOUND(
94 const NamedEntity &, int dimension, bool invariantOnly = true);
95MaybeExtentExpr GetLBOUND(FoldingContext &, const NamedEntity &, int dimension,
96 bool invariantOnly = true);
97MaybeExtentExpr GetRawUpperBound(
98 const NamedEntity &, int dimension, bool invariantOnly = true);
99MaybeExtentExpr GetRawUpperBound(FoldingContext &, const NamedEntity &,
100 int dimension, bool invariantOnly = true);
101MaybeExtentExpr GetUBOUND(
102 const NamedEntity &, int dimension, bool invariantOnly = true);
103MaybeExtentExpr GetUBOUND(FoldingContext &, const NamedEntity &, int dimension,
104 bool invariantOnly = true);
105MaybeExtentExpr ComputeUpperBound(ExtentExpr &&lower, MaybeExtentExpr &&extent);
106MaybeExtentExpr ComputeUpperBound(
107 FoldingContext &, ExtentExpr &&lower, MaybeExtentExpr &&extent);
108Shape GetRawLowerBounds(const NamedEntity &, bool invariantOnly = true);
109Shape GetRawLowerBounds(
110 FoldingContext &, const NamedEntity &, bool invariantOnly = true);
111Shape GetLBOUNDs(const NamedEntity &, bool invariantOnly = true);
112Shape GetLBOUNDs(
113 FoldingContext &, const NamedEntity &, bool invariantOnly = true);
114Shape GetUBOUNDs(const NamedEntity &, bool invariantOnly = true);
115Shape GetUBOUNDs(
116 FoldingContext &, const NamedEntity &, bool invariantOnly = true);
117MaybeExtentExpr GetExtent(
118 const NamedEntity &, int dimension, bool invariantOnly = true);
119MaybeExtentExpr GetExtent(FoldingContext &, const NamedEntity &, int dimension,
120 bool invariantOnly = true);
121MaybeExtentExpr GetExtent(const Subscript &, const NamedEntity &, int dimension,
122 bool invariantOnly = true);
123MaybeExtentExpr GetExtent(FoldingContext &, const Subscript &,
124 const NamedEntity &, int dimension, bool invariantOnly = true);
125
126// Similar analyses for coarrays
127MaybeExtentExpr GetLCOBOUND(
128 const Symbol &, int dimension, bool invariantOnly = true);
129MaybeExtentExpr GetUCOBOUND(
130 const Symbol &, int dimension, bool invariantOnly = true);
131Shape GetLCOBOUNDs(const Symbol &, bool invariantOnly = true);
132Shape GetUCOBOUNDs(const Symbol &, bool invariantOnly = true);
133
134// Compute an element count for a triplet or trip count for a DO.
135ExtentExpr CountTrips(
136 ExtentExpr &&lower, ExtentExpr &&upper, ExtentExpr &&stride);
137ExtentExpr CountTrips(
138 const ExtentExpr &lower, const ExtentExpr &upper, const ExtentExpr &stride);
139MaybeExtentExpr CountTrips(
140 MaybeExtentExpr &&lower, MaybeExtentExpr &&upper, MaybeExtentExpr &&stride);
141
142// Computes SIZE() == PRODUCT(shape)
143MaybeExtentExpr GetSize(Shape &&);
144ConstantSubscript GetSize(const ConstantSubscripts &);
145inline MaybeExtentExpr GetSize(const std::optional<Shape> &maybeShape) {
146 if (maybeShape) {
147 return GetSize(Shape(*maybeShape));
148 }
149 return std::nullopt;
150}
151
152// Utility predicate: does an expression reference any implied DO index?
153bool ContainsAnyImpliedDoIndex(const ExtentExpr &);
154
155// GetShape()
156
157class GetShapeHelper
158 : public AnyTraverse<GetShapeHelper, std::optional<Shape>> {
159public:
160 using Result = std::optional<Shape>;
161 using Base = AnyTraverse<GetShapeHelper, Result>;
162 using Base::operator();
163 GetShapeHelper(FoldingContext *context, bool invariantOnly)
164 : Base{*this}, context_{context}, invariantOnly_{invariantOnly} {}
165
166 Result operator()(const ImpliedDoIndex &) const { return ScalarShape(); }
167 Result operator()(const DescriptorInquiry &) const { return ScalarShape(); }
168 Result operator()(const TypeParamInquiry &) const { return ScalarShape(); }
169 Result operator()(const BOZLiteralConstant &) const { return ScalarShape(); }
170 Result operator()(const StaticDataObject::Pointer &) const {
171 return ScalarShape();
172 }
173 Result operator()(const StructureConstructor &) const {
174 return ScalarShape();
175 }
176
177 template <typename T> Result operator()(const Constant<T> &c) const {
178 return ConstantShape(c.SHAPE());
179 }
180
181 Result operator()(const Symbol &) const;
182 Result operator()(const Component &) const;
183 Result operator()(const ArrayRef &) const;
184 Result operator()(const CoarrayRef &) const;
185 Result operator()(const Substring &) const;
186 Result operator()(const ProcedureRef &) const;
187
188 template <typename T>
189 Result operator()(const ArrayConstructor<T> &aconst) const {
190 return Shape{GetArrayConstructorExtent(aconst)};
191 }
192 template <typename T>
193 Result operator()(const ConditionalExpr<T> &conditional) const {
194 // Per F2023 10.1.4(7), the shape is that of the selected branch.
195 // When all branches have identical static extents, return the common shape.
196 int rank{conditional.thenValue().Rank()};
197 Result thenShape{(*this)(conditional.thenValue())};
198 if (!thenShape) {
199 return Shape(rank, std::nullopt);
200 }
201 Result elseShape{(*this)(conditional.elseValue())};
202 if (thenShape != elseShape) {
203 return Shape(rank, std::nullopt);
204 }
205 return thenShape;
206 }
207 template <typename D, typename R, typename LO, typename RO>
208 Result operator()(const Operation<D, R, LO, RO> &operation) const {
209 if (int rr{operation.right().Rank()}; rr > 0) {
210 if (int lr{operation.left().Rank()}; lr == 0 || lr == rr) {
211 return (*this)(operation.right());
212 } else {
213 return std::nullopt;
214 }
215 } else {
216 return (*this)(operation.left());
217 }
218 }
219
220private:
221 static Result ScalarShape() { return Shape{}; }
222 static Shape ConstantShape(const Constant<ExtentType> &);
223 Result AsShapeResult(ExtentExpr &&) const;
224 Shape CreateShape(int rank, NamedEntity &) const;
225
226 template <typename T>
227 MaybeExtentExpr GetArrayConstructorValueExtent(
228 const ArrayConstructorValue<T> &value) const {
229 return common::visit(
231 [&](const Expr<T> &x) -> MaybeExtentExpr {
232 if (auto xShape{(*this)(x)}) {
233 // Array values in array constructors get linearized.
234 return GetSize(std::move(*xShape));
235 } else {
236 return std::nullopt;
237 }
238 },
239 [&](const ImpliedDo<T> &ido) -> MaybeExtentExpr {
240 // Don't be heroic and try to figure out triangular implied DO
241 // nests.
242 if (!ContainsAnyImpliedDoIndex(ido.lower()) &&
243 !ContainsAnyImpliedDoIndex(ido.upper()) &&
244 !ContainsAnyImpliedDoIndex(ido.stride())) {
245 if (auto nValues{GetArrayConstructorExtent(ido.values())}) {
246 if (!ContainsAnyImpliedDoIndex(*nValues)) {
247 return std::move(*nValues) *
248 CountTrips(ido.lower(), ido.upper(), ido.stride());
249 }
250 }
251 }
252 return std::nullopt;
253 },
254 },
255 value.u);
256 }
257
258 template <typename T>
259 MaybeExtentExpr GetArrayConstructorExtent(
260 const ArrayConstructorValues<T> &values) const {
261 ExtentExpr result{0};
262 for (const auto &value : values) {
263 if (MaybeExtentExpr n{GetArrayConstructorValueExtent(value)}) {
264 AccumulateExtent(result, std::move(*n));
265 } else {
266 return std::nullopt;
267 }
268 }
269 return result;
270 }
271
272 // Add an extent to another, with folding
273 void AccumulateExtent(ExtentExpr &, ExtentExpr &&) const;
274
275 FoldingContext *context_{nullptr};
276 mutable bool useResultSymbolShape_{true};
277 // When invariantOnly=false, the returned shape need not be invariant
278 // in its scope; in particular, it may contain references to dummy arguments.
279 bool invariantOnly_{true};
280};
281
282template <typename A>
283std::optional<Shape> GetShape(
284 FoldingContext *context, const A &x, bool invariantOnly) {
285 if (auto shape{GetShapeHelper{context, invariantOnly}(x)}) {
286 if (context) {
287 return Fold(*context, std::move(shape));
288 } else {
289 return shape;
290 }
291 } else {
292 return std::nullopt;
293 }
294}
295
296template <typename A>
297std::optional<Shape> GetShape(
298 FoldingContext &context, const A &x, bool invariantOnly) {
299 return GetShape(&context, x, invariantOnly);
300}
301
302template <typename A>
303std::optional<Shape> GetShape(const A &x, bool invariantOnly) {
304 return GetShape(/*context=*/nullptr, x, invariantOnly);
305}
306
307template <typename A>
308std::optional<Constant<ExtentType>> GetConstantShape(
309 FoldingContext &context, const A &x) {
310 if (auto shape{GetShape(context, x, /*invariantonly=*/true)}) {
311 return AsConstantShape(context, *shape);
312 } else {
313 return std::nullopt;
314 }
315}
316
317// Combines GetShape and AsConstantExtents; only returns valid shapes.
318template <typename A>
319std::optional<ConstantSubscripts> GetConstantExtents(
320 FoldingContext &context, const A &x) {
321 if (auto shape{GetShape(context, x, /*invariantOnly=*/true)}) {
322 if (auto extents{AsConstantExtents(context, *shape)}) {
323 if (!HasNegativeExtent(*extents)) {
324 return extents;
325 }
326 }
327 }
328 return std::nullopt;
329}
330
331// Get shape that does not depends on callee scope symbols if the expression
332// contains calls. Return std::nullopt if it is not possible to build such shape
333// (e.g. for calls to array-valued functions whose result shape depends on the
334// arguments).
335template <typename A>
336std::optional<Shape> GetContextFreeShape(FoldingContext &context, const A &x) {
337 return GetShapeHelper{&context, /*invariantOnly=*/true}(x);
338}
339
340// Compilation-time shape conformance checking, when corresponding extents
341// are or should be known. The result is an optional Boolean:
342// - nullopt: no error found or reported, but conformance cannot
343// be guaranteed during compilation; this result is possible only
344// when one or both arrays are allowed to have deferred shape
345// - true: no error found or reported, arrays conform
346// - false: errors found and reported
347// Use "CheckConformance(...).value_or()" to specify a default result
348// when you don't care whether messages have been emitted.
350 enum Flags {
351 None = 0,
352 LeftScalarExpandable = 1,
353 RightScalarExpandable = 2,
354 LeftIsDeferredShape = 4,
355 RightIsDeferredShape = 8,
356 EitherScalarExpandable = LeftScalarExpandable | RightScalarExpandable,
357 BothDeferredShape = LeftIsDeferredShape | RightIsDeferredShape,
358 RightIsExpandableDeferred = RightScalarExpandable | RightIsDeferredShape,
359 };
360};
361std::optional<bool> CheckConformance(parser::ContextualMessages &,
362 const Shape &left, const Shape &right,
363 CheckConformanceFlags::Flags flags = CheckConformanceFlags::None,
364 const char *leftIs = "left operand", const char *rightIs = "right operand");
365
366// Increments one-based subscripts in element order (first varies fastest)
367// and returns true when they remain in range; resets them all to one and
368// return false otherwise (including the case where one or more of the
369// extents are zero).
370bool IncrementSubscripts(
371 ConstantSubscripts &, const ConstantSubscripts &extents);
372
373} // namespace Fortran::evaluate
374#endif // FORTRAN_EVALUATE_SHAPE_H_
Definition expression.h:494
Definition variable.h:205
Definition variable.h:243
Definition variable.h:73
Definition expression.h:395
Definition constant.h:147
Definition variable.h:413
Definition common.h:215
Definition common.h:217
Definition shape.h:158
Definition expression.h:432
Definition variable.h:101
Definition expression.h:114
Definition call.h:233
Definition expression.h:769
Definition variable.h:304
Definition variable.h:136
Definition message.h:397
Definition symbol.h:825
Definition call.h:34
Definition check-expression.h:19
Definition idioms.h:61
Definition expression.h:460
Definition expression.h:424
Definition variable.h:191