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