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