1 //===-- lib/Evaluate/shape.cpp --------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "flang/Evaluate/shape.h"
10 #include "flang/Common/idioms.h"
11 #include "flang/Common/template.h"
12 #include "flang/Evaluate/characteristics.h"
13 #include "flang/Evaluate/check-expression.h"
14 #include "flang/Evaluate/fold.h"
15 #include "flang/Evaluate/intrinsics.h"
16 #include "flang/Evaluate/tools.h"
17 #include "flang/Evaluate/type.h"
18 #include "flang/Parser/message.h"
19 #include "flang/Semantics/semantics.h"
20 #include "flang/Semantics/symbol.h"
23 using namespace std::placeholders
; // _1, _2, &c. for std::bind()
25 namespace Fortran::evaluate
{
27 FoldingContext
&GetFoldingContextFrom(const Symbol
&symbol
) {
28 return symbol
.owner().context().foldingContext();
31 bool IsImpliedShape(const Symbol
&original
) {
32 const Symbol
&symbol
{ResolveAssociations(original
)};
33 const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()};
34 return details
&& symbol
.attrs().test(semantics::Attr::PARAMETER
) &&
35 details
->shape().CanBeImpliedShape();
38 bool IsExplicitShape(const Symbol
&original
) {
39 const Symbol
&symbol
{ResolveAssociations(original
)};
40 if (const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
41 const auto &shape
{details
->shape()};
42 return shape
.Rank() == 0 ||
43 shape
.IsExplicitShape(); // true when scalar, too
46 .has
<semantics::AssocEntityDetails
>(); // exprs have explicit shape
50 Shape
GetShapeHelper::ConstantShape(const Constant
<ExtentType
> &arrayConstant
) {
51 CHECK(arrayConstant
.Rank() == 1);
53 std::size_t dimensions
{arrayConstant
.size()};
54 for (std::size_t j
{0}; j
< dimensions
; ++j
) {
55 Scalar
<ExtentType
> extent
{arrayConstant
.values().at(j
)};
56 result
.emplace_back(MaybeExtentExpr
{ExtentExpr
{std::move(extent
)}});
61 auto GetShapeHelper::AsShapeResult(ExtentExpr
&&arrayExpr
) const -> Result
{
63 arrayExpr
= Fold(*context_
, std::move(arrayExpr
));
65 if (const auto *constArray
{UnwrapConstantValue
<ExtentType
>(arrayExpr
)}) {
66 return ConstantShape(*constArray
);
68 if (auto *constructor
{UnwrapExpr
<ArrayConstructor
<ExtentType
>>(arrayExpr
)}) {
70 for (auto &value
: *constructor
) {
71 auto *expr
{std::get_if
<ExtentExpr
>(&value
.u
)};
72 if (expr
&& expr
->Rank() == 0) {
73 result
.emplace_back(std::move(*expr
));
84 Shape
GetShapeHelper::CreateShape(int rank
, NamedEntity
&base
) const {
86 for (int dimension
{0}; dimension
< rank
; ++dimension
) {
87 shape
.emplace_back(GetExtent(base
, dimension
, invariantOnly_
));
92 std::optional
<ExtentExpr
> AsExtentArrayExpr(const Shape
&shape
) {
93 ArrayConstructorValues
<ExtentType
> values
;
94 for (const auto &dim
: shape
) {
96 values
.Push(common::Clone(*dim
));
101 return ExtentExpr
{ArrayConstructor
<ExtentType
>{std::move(values
)}};
104 std::optional
<Constant
<ExtentType
>> AsConstantShape(
105 FoldingContext
&context
, const Shape
&shape
) {
106 if (auto shapeArray
{AsExtentArrayExpr(shape
)}) {
107 auto folded
{Fold(context
, std::move(*shapeArray
))};
108 if (auto *p
{UnwrapConstantValue
<ExtentType
>(folded
)}) {
109 return std::move(*p
);
115 Constant
<SubscriptInteger
> AsConstantShape(const ConstantSubscripts
&shape
) {
116 using IntType
= Scalar
<SubscriptInteger
>;
117 std::vector
<IntType
> result
;
118 for (auto dim
: shape
) {
119 result
.emplace_back(dim
);
121 return {std::move(result
), ConstantSubscripts
{GetRank(shape
)}};
124 ConstantSubscripts
AsConstantExtents(const Constant
<ExtentType
> &shape
) {
125 ConstantSubscripts result
;
126 for (const auto &extent
: shape
.values()) {
127 result
.push_back(extent
.ToInt64());
132 std::optional
<ConstantSubscripts
> AsConstantExtents(
133 FoldingContext
&context
, const Shape
&shape
) {
134 if (auto shapeConstant
{AsConstantShape(context
, shape
)}) {
135 return AsConstantExtents(*shapeConstant
);
141 Shape
AsShape(const ConstantSubscripts
&shape
) {
143 for (const auto &extent
: shape
) {
144 result
.emplace_back(ExtentExpr
{extent
});
149 std::optional
<Shape
> AsShape(const std::optional
<ConstantSubscripts
> &shape
) {
151 return AsShape(*shape
);
157 Shape
Fold(FoldingContext
&context
, Shape
&&shape
) {
158 for (auto &dim
: shape
) {
159 dim
= Fold(context
, std::move(dim
));
161 return std::move(shape
);
164 std::optional
<Shape
> Fold(
165 FoldingContext
&context
, std::optional
<Shape
> &&shape
) {
167 return Fold(context
, std::move(*shape
));
173 static ExtentExpr
ComputeTripCount(
174 ExtentExpr
&&lower
, ExtentExpr
&&upper
, ExtentExpr
&&stride
) {
175 ExtentExpr strideCopy
{common::Clone(stride
)};
177 (std::move(upper
) - std::move(lower
) + std::move(strideCopy
)) /
180 Extremum
<ExtentType
>{Ordering::Greater
, std::move(span
), ExtentExpr
{0}}};
183 ExtentExpr
CountTrips(
184 ExtentExpr
&&lower
, ExtentExpr
&&upper
, ExtentExpr
&&stride
) {
185 return ComputeTripCount(
186 std::move(lower
), std::move(upper
), std::move(stride
));
189 ExtentExpr
CountTrips(const ExtentExpr
&lower
, const ExtentExpr
&upper
,
190 const ExtentExpr
&stride
) {
191 return ComputeTripCount(
192 common::Clone(lower
), common::Clone(upper
), common::Clone(stride
));
195 MaybeExtentExpr
CountTrips(MaybeExtentExpr
&&lower
, MaybeExtentExpr
&&upper
,
196 MaybeExtentExpr
&&stride
) {
197 std::function
<ExtentExpr(ExtentExpr
&&, ExtentExpr
&&, ExtentExpr
&&)> bound
{
198 std::bind(ComputeTripCount
, _1
, _2
, _3
)};
199 return common::MapOptional(
200 std::move(bound
), std::move(lower
), std::move(upper
), std::move(stride
));
203 MaybeExtentExpr
GetSize(Shape
&&shape
) {
204 ExtentExpr extent
{1};
205 for (auto &&dim
: std::move(shape
)) {
207 extent
= std::move(extent
) * std::move(*dim
);
215 ConstantSubscript
GetSize(const ConstantSubscripts
&shape
) {
216 ConstantSubscript size
{1};
217 for (auto dim
: shape
) {
224 bool ContainsAnyImpliedDoIndex(const ExtentExpr
&expr
) {
225 struct MyVisitor
: public AnyTraverse
<MyVisitor
> {
226 using Base
= AnyTraverse
<MyVisitor
>;
227 MyVisitor() : Base
{*this} {}
228 using Base::operator();
229 bool operator()(const ImpliedDoIndex
&) { return true; }
231 return MyVisitor
{}(expr
);
234 // Determines lower bound on a dimension. This can be other than 1 only
235 // for a reference to a whole array object or component. (See LBOUND, 16.9.109).
236 // ASSOCIATE construct entities may require traversal of their referents.
237 template <typename RESULT
, bool LBOUND_SEMANTICS
>
238 class GetLowerBoundHelper
239 : public Traverse
<GetLowerBoundHelper
<RESULT
, LBOUND_SEMANTICS
>, RESULT
> {
241 using Result
= RESULT
;
242 using Base
= Traverse
<GetLowerBoundHelper
, RESULT
>;
243 using Base::operator();
244 explicit GetLowerBoundHelper(
245 int d
, FoldingContext
*context
, bool invariantOnly
)
246 : Base
{*this}, dimension_
{d
}, context_
{context
},
247 invariantOnly_
{invariantOnly
} {}
248 static Result
Default() { return Result
{1}; }
249 static Result
Combine(Result
&&, Result
&&) {
250 // Operator results and array references always have lower bounds == 1
254 Result
GetLowerBound(const Symbol
&symbol0
, NamedEntity
&&base
) const {
255 const Symbol
&symbol
{symbol0
.GetUltimate()};
256 if (const auto *object
{
257 symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
258 int rank
{object
->shape().Rank()};
259 if (dimension_
< rank
) {
260 const semantics::ShapeSpec
&shapeSpec
{object
->shape()[dimension_
]};
261 if (shapeSpec
.lbound().isExplicit()) {
262 if (const auto &lbound
{shapeSpec
.lbound().GetExplicit()};
263 lbound
&& lbound
->Rank() == 0) {
264 if constexpr (LBOUND_SEMANTICS
) {
266 auto lbValue
{ToInt64(*lbound
)};
267 if (dimension_
== rank
- 1 &&
268 semantics::IsAssumedSizeArray(symbol
)) {
269 // last dimension of assumed-size dummy array: don't worry
270 // about handling an empty dimension
271 ok
= !invariantOnly_
|| IsScopeInvariantExpr(*lbound
);
272 } else if (lbValue
.value_or(0) == 1) {
273 // Lower bound is 1, regardless of extent
275 } else if (const auto &ubound
{shapeSpec
.ubound().GetExplicit()};
276 ubound
&& ubound
->Rank() == 0) {
277 // If we can't prove that the dimension is nonempty,
278 // we must be conservative.
279 // TODO: simple symbolic math in expression rewriting to
280 // cope with cases like A(J:J)
282 auto extent
{ToInt64(Fold(*context_
,
283 ExtentExpr
{*ubound
} - ExtentExpr
{*lbound
} +
294 auto ubValue
{ToInt64(*ubound
)};
295 if (lbValue
&& ubValue
) {
296 if (*lbValue
> *ubValue
) {
305 return ok
? *lbound
: Result
{};
313 if (IsDescriptor(symbol
)) {
314 return ExtentExpr
{DescriptorInquiry
{std::move(base
),
315 DescriptorInquiry::Field::LowerBound
, dimension_
}};
318 } else if (const auto *assoc
{
319 symbol
.detailsIf
<semantics::AssocEntityDetails
>()}) {
320 if (assoc
->IsAssumedSize()) { // RANK(*)
322 } else if (assoc
->IsAssumedRank()) { // RANK DEFAULT
323 } else if (assoc
->rank()) { // RANK(n)
324 const Symbol
&resolved
{ResolveAssociations(symbol
)};
325 if (IsDescriptor(resolved
) && dimension_
< *assoc
->rank()) {
326 return ExtentExpr
{DescriptorInquiry
{std::move(base
),
327 DescriptorInquiry::Field::LowerBound
, dimension_
}};
330 Result exprLowerBound
{((*this)(assoc
->expr()))};
331 if (IsActuallyConstant(exprLowerBound
)) {
332 return std::move(exprLowerBound
);
334 // If the lower bound of the associated entity is not resolved to a
335 // constant expression at the time of the association, it is unsafe
336 // to re-evaluate it later in the associate construct. Statements
337 // in between may have modified its operands value.
338 return ExtentExpr
{DescriptorInquiry
{std::move(base
),
339 DescriptorInquiry::Field::LowerBound
, dimension_
}};
343 if constexpr (LBOUND_SEMANTICS
) {
350 Result
operator()(const Symbol
&symbol
) const {
351 return GetLowerBound(symbol
, NamedEntity
{symbol
});
354 Result
operator()(const Component
&component
) const {
355 if (component
.base().Rank() == 0) {
356 return GetLowerBound(
357 component
.GetLastSymbol(), NamedEntity
{common::Clone(component
)});
362 template <typename T
> Result
operator()(const Expr
<T
> &expr
) const {
363 if (const Symbol
* whole
{UnwrapWholeSymbolOrComponentDataRef(expr
)}) {
364 return (*this)(*whole
);
365 } else if constexpr (common::HasMember
<Constant
<T
>, decltype(expr
.u
)>) {
366 if (const auto *con
{std::get_if
<Constant
<T
>>(&expr
.u
)}) {
367 ConstantSubscripts lb
{con
->lbounds()};
368 if (dimension_
< GetRank(lb
)) {
369 return Result
{lb
[dimension_
]};
371 } else { // operation
375 return (*this)(expr
.u
);
377 if constexpr (LBOUND_SEMANTICS
) {
385 int dimension_
; // zero-based
386 FoldingContext
*context_
{nullptr};
387 bool invariantOnly_
{false};
390 ExtentExpr
GetRawLowerBound(
391 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
392 return GetLowerBoundHelper
<ExtentExpr
, false>{
393 dimension
, nullptr, invariantOnly
}(base
);
396 ExtentExpr
GetRawLowerBound(FoldingContext
&context
, const NamedEntity
&base
,
397 int dimension
, bool invariantOnly
) {
399 GetLowerBoundHelper
<ExtentExpr
, false>{
400 dimension
, &context
, invariantOnly
}(base
));
403 MaybeExtentExpr
GetLBOUND(
404 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
405 return GetLowerBoundHelper
<MaybeExtentExpr
, true>{
406 dimension
, nullptr, invariantOnly
}(base
);
409 MaybeExtentExpr
GetLBOUND(FoldingContext
&context
, const NamedEntity
&base
,
410 int dimension
, bool invariantOnly
) {
412 GetLowerBoundHelper
<MaybeExtentExpr
, true>{
413 dimension
, &context
, invariantOnly
}(base
));
416 Shape
GetRawLowerBounds(const NamedEntity
&base
, bool invariantOnly
) {
418 int rank
{base
.Rank()};
419 for (int dim
{0}; dim
< rank
; ++dim
) {
420 result
.emplace_back(GetRawLowerBound(base
, dim
, invariantOnly
));
425 Shape
GetRawLowerBounds(
426 FoldingContext
&context
, const NamedEntity
&base
, bool invariantOnly
) {
428 int rank
{base
.Rank()};
429 for (int dim
{0}; dim
< rank
; ++dim
) {
430 result
.emplace_back(GetRawLowerBound(context
, base
, dim
, invariantOnly
));
435 Shape
GetLBOUNDs(const NamedEntity
&base
, bool invariantOnly
) {
437 int rank
{base
.Rank()};
438 for (int dim
{0}; dim
< rank
; ++dim
) {
439 result
.emplace_back(GetLBOUND(base
, dim
, invariantOnly
));
445 FoldingContext
&context
, const NamedEntity
&base
, bool invariantOnly
) {
447 int rank
{base
.Rank()};
448 for (int dim
{0}; dim
< rank
; ++dim
) {
449 result
.emplace_back(GetLBOUND(context
, base
, dim
, invariantOnly
));
454 // If the upper and lower bounds are constant, return a constant expression for
455 // the extent. In particular, if the upper bound is less than the lower bound,
457 static MaybeExtentExpr
GetNonNegativeExtent(
458 const semantics::ShapeSpec
&shapeSpec
, bool invariantOnly
) {
459 const auto &ubound
{shapeSpec
.ubound().GetExplicit()};
460 const auto &lbound
{shapeSpec
.lbound().GetExplicit()};
461 std::optional
<ConstantSubscript
> uval
{ToInt64(ubound
)};
462 std::optional
<ConstantSubscript
> lval
{ToInt64(lbound
)};
465 return ExtentExpr
{0};
467 return ExtentExpr
{*uval
- *lval
+ 1};
469 } else if (lbound
&& ubound
&& lbound
->Rank() == 0 && ubound
->Rank() == 0 &&
471 (IsScopeInvariantExpr(*lbound
) && IsScopeInvariantExpr(*ubound
)))) {
472 // Apply effective IDIM (MAX calculation with 0) so thet the
473 // result is never negative
474 if (lval
.value_or(0) == 1) {
475 return ExtentExpr
{Extremum
<SubscriptInteger
>{
476 Ordering::Greater
, ExtentExpr
{0}, common::Clone(*ubound
)}};
479 Extremum
<SubscriptInteger
>{Ordering::Greater
, ExtentExpr
{0},
480 common::Clone(*ubound
) - common::Clone(*lbound
) + ExtentExpr
{1}}};
487 static MaybeExtentExpr
GetAssociatedExtent(
488 const Symbol
&symbol
, int dimension
) {
489 if (const auto *assoc
{symbol
.detailsIf
<semantics::AssocEntityDetails
>()};
490 assoc
&& !assoc
->rank()) { // not SELECT RANK case
491 if (auto shape
{GetShape(GetFoldingContextFrom(symbol
), assoc
->expr())};
492 shape
&& dimension
< static_cast<int>(shape
->size())) {
493 if (auto &extent
{shape
->at(dimension
)};
494 // Don't return a non-constant extent, as the variables that
495 // determine the shape of the selector's expression may change
496 // during execution of the construct.
497 extent
&& IsActuallyConstant(*extent
)) {
498 return std::move(extent
);
502 return ExtentExpr
{DescriptorInquiry
{
503 NamedEntity
{symbol
}, DescriptorInquiry::Field::Extent
, dimension
}};
506 MaybeExtentExpr
GetExtent(
507 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
508 CHECK(dimension
>= 0);
509 const Symbol
&last
{base
.GetLastSymbol()};
510 const Symbol
&symbol
{ResolveAssociations(last
)};
511 if (const auto *assoc
{last
.detailsIf
<semantics::AssocEntityDetails
>()}) {
512 if (assoc
->IsAssumedSize() || assoc
->IsAssumedRank()) { // RANK(*)/DEFAULT
514 } else if (assoc
->rank()) { // RANK(n)
515 if (semantics::IsDescriptor(symbol
) && dimension
< *assoc
->rank()) {
516 return ExtentExpr
{DescriptorInquiry
{
517 NamedEntity
{base
}, DescriptorInquiry::Field::Extent
, dimension
}};
522 return GetAssociatedExtent(last
, dimension
);
525 if (const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
526 if (IsImpliedShape(symbol
) && details
->init()) {
528 GetShape(GetFoldingContextFrom(symbol
), symbol
, invariantOnly
)}) {
529 if (dimension
< static_cast<int>(shape
->size())) {
530 return std::move(shape
->at(dimension
));
535 for (const auto &shapeSpec
: details
->shape()) {
536 if (j
++ == dimension
) {
537 if (auto extent
{GetNonNegativeExtent(shapeSpec
, invariantOnly
)}) {
539 } else if (semantics::IsAssumedSizeArray(symbol
) &&
540 j
== symbol
.Rank()) {
542 } else if (semantics::IsDescriptor(symbol
)) {
543 return ExtentExpr
{DescriptorInquiry
{NamedEntity
{base
},
544 DescriptorInquiry::Field::Extent
, dimension
}};
555 MaybeExtentExpr
GetExtent(FoldingContext
&context
, const NamedEntity
&base
,
556 int dimension
, bool invariantOnly
) {
557 return Fold(context
, GetExtent(base
, dimension
, invariantOnly
));
560 MaybeExtentExpr
GetExtent(const Subscript
&subscript
, const NamedEntity
&base
,
561 int dimension
, bool invariantOnly
) {
562 return common::visit(
564 [&](const Triplet
&triplet
) -> MaybeExtentExpr
{
565 MaybeExtentExpr upper
{triplet
.upper()};
567 upper
= GetUBOUND(base
, dimension
, invariantOnly
);
569 MaybeExtentExpr lower
{triplet
.lower()};
571 lower
= GetLBOUND(base
, dimension
, invariantOnly
);
573 return CountTrips(std::move(lower
), std::move(upper
),
574 MaybeExtentExpr
{triplet
.stride()});
576 [&](const IndirectSubscriptIntegerExpr
&subs
) -> MaybeExtentExpr
{
577 if (auto shape
{GetShape(
578 GetFoldingContextFrom(base
.GetLastSymbol()), subs
.value())};
579 shape
&& GetRank(*shape
) == 1) {
580 // vector-valued subscript
581 return std::move(shape
->at(0));
590 MaybeExtentExpr
GetExtent(FoldingContext
&context
, const Subscript
&subscript
,
591 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
592 return Fold(context
, GetExtent(subscript
, base
, dimension
, invariantOnly
));
595 MaybeExtentExpr
ComputeUpperBound(
596 ExtentExpr
&&lower
, MaybeExtentExpr
&&extent
) {
598 if (ToInt64(lower
).value_or(0) == 1) {
599 return std::move(*extent
);
601 return std::move(*extent
) + std::move(lower
) - ExtentExpr
{1};
608 MaybeExtentExpr
ComputeUpperBound(
609 FoldingContext
&context
, ExtentExpr
&&lower
, MaybeExtentExpr
&&extent
) {
610 return Fold(context
, ComputeUpperBound(std::move(lower
), std::move(extent
)));
613 MaybeExtentExpr
GetRawUpperBound(
614 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
615 const Symbol
&symbol
{ResolveAssociations(base
.GetLastSymbol())};
616 if (const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
617 int rank
{details
->shape().Rank()};
618 if (dimension
< rank
) {
619 const auto &bound
{details
->shape()[dimension
].ubound().GetExplicit()};
620 if (bound
&& bound
->Rank() == 0 &&
621 (!invariantOnly
|| IsScopeInvariantExpr(*bound
))) {
623 } else if (semantics::IsAssumedSizeArray(symbol
) &&
624 dimension
+ 1 == symbol
.Rank()) {
627 return ComputeUpperBound(
628 GetRawLowerBound(base
, dimension
), GetExtent(base
, dimension
));
631 } else if (const auto *assoc
{
632 symbol
.detailsIf
<semantics::AssocEntityDetails
>()}) {
633 if (assoc
->IsAssumedSize() || assoc
->IsAssumedRank()) {
635 } else if (assoc
->rank() && dimension
>= *assoc
->rank()) {
637 } else if (auto extent
{GetAssociatedExtent(symbol
, dimension
)}) {
638 return ComputeUpperBound(
639 GetRawLowerBound(base
, dimension
), std::move(extent
));
645 MaybeExtentExpr
GetRawUpperBound(FoldingContext
&context
,
646 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
647 return Fold(context
, GetRawUpperBound(base
, dimension
, invariantOnly
));
650 static MaybeExtentExpr
GetExplicitUBOUND(FoldingContext
*context
,
651 const semantics::ShapeSpec
&shapeSpec
, bool invariantOnly
) {
652 const auto &ubound
{shapeSpec
.ubound().GetExplicit()};
653 if (ubound
&& ubound
->Rank() == 0 &&
654 (!invariantOnly
|| IsScopeInvariantExpr(*ubound
))) {
655 if (auto extent
{GetNonNegativeExtent(shapeSpec
, invariantOnly
)}) {
656 if (auto cstExtent
{ToInt64(
657 context
? Fold(*context
, std::move(*extent
)) : *extent
)}) {
660 } else if (cstExtent
== 0) {
661 return ExtentExpr
{0};
669 static MaybeExtentExpr
GetUBOUND(FoldingContext
*context
,
670 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
671 const Symbol
&symbol
{ResolveAssociations(base
.GetLastSymbol())};
672 if (const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
673 int rank
{details
->shape().Rank()};
674 if (dimension
< rank
) {
675 const semantics::ShapeSpec
&shapeSpec
{details
->shape()[dimension
]};
676 if (auto ubound
{GetExplicitUBOUND(context
, shapeSpec
, invariantOnly
)}) {
678 } else if (semantics::IsAssumedSizeArray(symbol
) &&
679 dimension
+ 1 == symbol
.Rank()) {
680 return std::nullopt
; // UBOUND() folding replaces with -1
681 } else if (auto lb
{GetLBOUND(base
, dimension
, invariantOnly
)}) {
682 return ComputeUpperBound(
683 std::move(*lb
), GetExtent(base
, dimension
, invariantOnly
));
686 } else if (const auto *assoc
{
687 symbol
.detailsIf
<semantics::AssocEntityDetails
>()}) {
688 if (assoc
->IsAssumedSize() || assoc
->IsAssumedRank()) {
690 } else if (assoc
->rank()) { // RANK (n)
691 const Symbol
&resolved
{ResolveAssociations(symbol
)};
692 if (IsDescriptor(resolved
) && dimension
< *assoc
->rank()) {
693 ExtentExpr lb
{DescriptorInquiry
{NamedEntity
{base
},
694 DescriptorInquiry::Field::LowerBound
, dimension
}};
695 ExtentExpr extent
{DescriptorInquiry
{
696 std::move(base
), DescriptorInquiry::Field::Extent
, dimension
}};
697 return ComputeUpperBound(std::move(lb
), std::move(extent
));
699 } else if (auto extent
{GetAssociatedExtent(symbol
, dimension
)}) {
700 if (auto lb
{GetLBOUND(base
, dimension
, invariantOnly
)}) {
701 return ComputeUpperBound(std::move(*lb
), std::move(extent
));
708 MaybeExtentExpr
GetUBOUND(
709 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
710 return GetUBOUND(nullptr, base
, dimension
, invariantOnly
);
713 MaybeExtentExpr
GetUBOUND(FoldingContext
&context
, const NamedEntity
&base
,
714 int dimension
, bool invariantOnly
) {
715 return Fold(context
, GetUBOUND(&context
, base
, dimension
, invariantOnly
));
718 static Shape
GetUBOUNDs(
719 FoldingContext
*context
, const NamedEntity
&base
, bool invariantOnly
) {
721 int rank
{base
.Rank()};
722 for (int dim
{0}; dim
< rank
; ++dim
) {
723 result
.emplace_back(GetUBOUND(context
, base
, dim
, invariantOnly
));
729 FoldingContext
&context
, const NamedEntity
&base
, bool invariantOnly
) {
730 return Fold(context
, GetUBOUNDs(&context
, base
, invariantOnly
));
733 Shape
GetUBOUNDs(const NamedEntity
&base
, bool invariantOnly
) {
734 return GetUBOUNDs(nullptr, base
, invariantOnly
);
737 MaybeExtentExpr
GetLCOBOUND(
738 const Symbol
&symbol0
, int dimension
, bool invariantOnly
) {
739 const Symbol
&symbol
{ResolveAssociations(symbol0
)};
740 if (const auto *object
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
741 int corank
{object
->coshape().Rank()};
742 if (dimension
< corank
) {
743 const semantics::ShapeSpec
&shapeSpec
{object
->coshape()[dimension
]};
744 if (const auto &lcobound
{shapeSpec
.lbound().GetExplicit()}) {
745 if (lcobound
->Rank() == 0 &&
746 (!invariantOnly
|| IsScopeInvariantExpr(*lcobound
))) {
755 MaybeExtentExpr
GetUCOBOUND(
756 const Symbol
&symbol0
, int dimension
, bool invariantOnly
) {
757 const Symbol
&symbol
{ResolveAssociations(symbol0
)};
758 if (const auto *object
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
759 int corank
{object
->coshape().Rank()};
760 if (dimension
< corank
- 1) {
761 const semantics::ShapeSpec
&shapeSpec
{object
->coshape()[dimension
]};
762 if (const auto ucobound
{shapeSpec
.ubound().GetExplicit()}) {
763 if (ucobound
->Rank() == 0 &&
764 (!invariantOnly
|| IsScopeInvariantExpr(*ucobound
))) {
773 Shape
GetLCOBOUNDs(const Symbol
&symbol
, bool invariantOnly
) {
775 int corank
{symbol
.Corank()};
776 for (int dim
{0}; dim
< corank
; ++dim
) {
777 result
.emplace_back(GetLCOBOUND(symbol
, dim
, invariantOnly
));
782 Shape
GetUCOBOUNDs(const Symbol
&symbol
, bool invariantOnly
) {
784 int corank
{symbol
.Corank()};
785 for (int dim
{0}; dim
< corank
; ++dim
) {
786 result
.emplace_back(GetUCOBOUND(symbol
, dim
, invariantOnly
));
791 auto GetShapeHelper::operator()(const Symbol
&symbol
) const -> Result
{
792 return common::visit(
794 [&](const semantics::ObjectEntityDetails
&object
) {
795 if (IsImpliedShape(symbol
) && object
.init()) {
796 return (*this)(object
.init());
797 } else if (IsAssumedRank(symbol
)) {
800 int n
{object
.shape().Rank()};
801 NamedEntity base
{symbol
};
802 return Result
{CreateShape(n
, base
)};
805 [](const semantics::EntityDetails
&) {
806 return ScalarShape(); // no dimensions seen
808 [&](const semantics::ProcEntityDetails
&proc
) {
809 if (const Symbol
* interface
{proc
.procInterface()}) {
810 return (*this)(*interface
);
812 return ScalarShape();
815 [&](const semantics::AssocEntityDetails
&assoc
) {
816 NamedEntity base
{symbol
};
817 if (assoc
.rank()) { // SELECT RANK case
818 int n
{assoc
.rank().value()};
819 return Result
{CreateShape(n
, base
)};
821 auto exprShape
{((*this)(assoc
.expr()))};
823 int rank
{static_cast<int>(exprShape
->size())};
824 for (int dimension
{0}; dimension
< rank
; ++dimension
) {
825 auto &extent
{(*exprShape
)[dimension
]};
826 if (extent
&& !IsActuallyConstant(*extent
)) {
827 extent
= GetExtent(base
, dimension
);
834 [&](const semantics::SubprogramDetails
&subp
) -> Result
{
835 if (subp
.isFunction()) {
836 auto resultShape
{(*this)(subp
.result())};
837 if (resultShape
&& !useResultSymbolShape_
) {
838 // Ensure the shape is constant. Otherwise, it may be reerring
839 // to symbols that belong to the function's scope and are
840 // meaningless on the caller side without the related call
842 for (auto &extent
: *resultShape
) {
843 if (extent
&& !IsActuallyConstant(*extent
)) {
853 [&](const semantics::ProcBindingDetails
&binding
) {
854 return (*this)(binding
.symbol());
856 [](const semantics::TypeParamDetails
&) { return ScalarShape(); },
857 [](const auto &) { return Result
{}; },
859 symbol
.GetUltimate().details());
862 auto GetShapeHelper::operator()(const Component
&component
) const -> Result
{
863 const Symbol
&symbol
{component
.GetLastSymbol()};
864 int rank
{symbol
.Rank()};
866 return (*this)(component
.base());
867 } else if (symbol
.has
<semantics::ObjectEntityDetails
>()) {
868 NamedEntity base
{Component
{component
}};
869 return CreateShape(rank
, base
);
871 return (*this)(symbol
);
875 auto GetShapeHelper::operator()(const ArrayRef
&arrayRef
) const -> Result
{
878 const NamedEntity
&base
{arrayRef
.base()};
879 for (const Subscript
&ss
: arrayRef
.subscript()) {
881 shape
.emplace_back(GetExtent(ss
, base
, dimension
));
886 if (const Component
* component
{base
.UnwrapComponent()}) {
887 return (*this)(component
->base());
893 auto GetShapeHelper::operator()(const CoarrayRef
&coarrayRef
) const -> Result
{
894 NamedEntity base
{coarrayRef
.GetBase()};
895 if (coarrayRef
.subscript().empty()) {
896 return (*this)(base
);
900 for (const Subscript
&ss
: coarrayRef
.subscript()) {
902 shape
.emplace_back(GetExtent(ss
, base
, dimension
));
910 auto GetShapeHelper::operator()(const Substring
&substring
) const -> Result
{
911 return (*this)(substring
.parent());
914 auto GetShapeHelper::operator()(const ProcedureRef
&call
) const -> Result
{
915 if (call
.Rank() == 0) {
916 return ScalarShape();
917 } else if (call
.IsElemental()) {
918 // Use the shape of an actual array argument associated with a
919 // non-OPTIONAL dummy object argument.
921 if (auto chars
{characteristics::Procedure::FromActuals(
922 call
.proc(), call
.arguments(), *context_
)}) {
924 const ActualArgument
*nonOptionalArrayArg
{nullptr};
925 int anyArrayArgRank
{0};
926 for (const auto &arg
: call
.arguments()) {
927 if (arg
&& arg
->Rank() > 0 && j
< chars
->dummyArguments
.size()) {
928 if (!anyArrayArgRank
) {
929 anyArrayArgRank
= arg
->Rank();
930 } else if (arg
->Rank() != anyArrayArgRank
) {
931 return std::nullopt
; // error recovery
933 if (!nonOptionalArrayArg
&&
934 !chars
->dummyArguments
[j
].IsOptional()) {
935 nonOptionalArrayArg
= &*arg
;
940 if (anyArrayArgRank
) {
941 if (nonOptionalArrayArg
) {
942 return (*this)(*nonOptionalArrayArg
);
944 // All dummy array arguments of the procedure are OPTIONAL.
945 // We cannot take the shape from just any array argument,
946 // because all of them might be OPTIONAL dummy arguments
947 // of the caller. Return unknown shape ranked according
948 // to the last actual array argument.
949 return Shape(anyArrayArgRank
, MaybeExtentExpr
{});
954 return ScalarShape();
955 } else if (const Symbol
* symbol
{call
.proc().GetSymbol()}) {
956 auto restorer
{common::ScopedSet(useResultSymbolShape_
, false)};
957 return (*this)(*symbol
);
958 } else if (const auto *intrinsic
{call
.proc().GetSpecificIntrinsic()}) {
959 if (intrinsic
->name
== "shape" || intrinsic
->name
== "lbound" ||
960 intrinsic
->name
== "ubound") {
961 // For LBOUND/UBOUND, these are the array-valued cases (no DIM=)
962 if (!call
.arguments().empty() && call
.arguments().front()) {
963 if (IsAssumedRank(*call
.arguments().front())) {
964 return Shape
{MaybeExtentExpr
{}};
967 MaybeExtentExpr
{ExtentExpr
{call
.arguments().front()->Rank()}}};
970 } else if (intrinsic
->name
== "all" || intrinsic
->name
== "any" ||
971 intrinsic
->name
== "count" || intrinsic
->name
== "iall" ||
972 intrinsic
->name
== "iany" || intrinsic
->name
== "iparity" ||
973 intrinsic
->name
== "maxval" || intrinsic
->name
== "minval" ||
974 intrinsic
->name
== "norm2" || intrinsic
->name
== "parity" ||
975 intrinsic
->name
== "product" || intrinsic
->name
== "sum") {
976 // Reduction with DIM=
977 if (call
.arguments().size() >= 2) {
979 (*this)(UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(0)))};
980 const auto *dimArg
{UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(1))};
981 if (arrayShape
&& dimArg
) {
982 if (auto dim
{ToInt64(*dimArg
)}) {
984 static_cast<std::size_t>(*dim
) <= arrayShape
->size()) {
985 arrayShape
->erase(arrayShape
->begin() + (*dim
- 1));
986 return std::move(*arrayShape
);
991 } else if (intrinsic
->name
== "findloc" || intrinsic
->name
== "maxloc" ||
992 intrinsic
->name
== "minloc") {
993 std::size_t dimIndex
{intrinsic
->name
== "findloc" ? 2u : 1u};
994 if (call
.arguments().size() > dimIndex
) {
996 (*this)(UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(0)))}) {
997 auto rank
{static_cast<int>(arrayShape
->size())};
998 if (const auto *dimArg
{
999 UnwrapExpr
<Expr
<SomeType
>>(call
.arguments()[dimIndex
])}) {
1000 auto dim
{ToInt64(*dimArg
)};
1001 if (dim
&& *dim
>= 1 && *dim
<= rank
) {
1002 arrayShape
->erase(arrayShape
->begin() + (*dim
- 1));
1003 return std::move(*arrayShape
);
1006 // xxxLOC(no DIM=) result is vector(1:RANK(ARRAY=))
1007 return Shape
{ExtentExpr
{rank
}};
1011 } else if (intrinsic
->name
== "cshift" || intrinsic
->name
== "eoshift") {
1012 if (!call
.arguments().empty()) {
1013 return (*this)(call
.arguments()[0]);
1015 } else if (intrinsic
->name
== "lcobound" || intrinsic
->name
== "ucobound") {
1016 if (call
.arguments().size() == 3 && !call
.arguments().at(1).has_value()) {
1017 return Shape(1, ExtentExpr
{GetCorank(call
.arguments().at(0))});
1019 } else if (intrinsic
->name
== "matmul") {
1020 if (call
.arguments().size() == 2) {
1021 if (auto ashape
{(*this)(call
.arguments()[0])}) {
1022 if (auto bshape
{(*this)(call
.arguments()[1])}) {
1023 if (ashape
->size() == 1 && bshape
->size() == 2) {
1024 bshape
->erase(bshape
->begin());
1025 return std::move(*bshape
); // matmul(vector, matrix)
1026 } else if (ashape
->size() == 2 && bshape
->size() == 1) {
1028 return std::move(*ashape
); // matmul(matrix, vector)
1029 } else if (ashape
->size() == 2 && bshape
->size() == 2) {
1030 (*ashape
)[1] = std::move((*bshape
)[1]);
1031 return std::move(*ashape
); // matmul(matrix, matrix)
1036 } else if (intrinsic
->name
== "pack") {
1037 if (call
.arguments().size() >= 3 && call
.arguments().at(2)) {
1038 // SHAPE(PACK(,,VECTOR=v)) -> SHAPE(v)
1039 return (*this)(call
.arguments().at(2));
1040 } else if (call
.arguments().size() >= 2 && context_
) {
1041 if (auto maskShape
{(*this)(call
.arguments().at(1))}) {
1042 if (maskShape
->size() == 0) {
1043 // Scalar MASK= -> [MERGE(SIZE(ARRAY=), 0, mask)]
1044 if (auto arrayShape
{(*this)(call
.arguments().at(0))}) {
1045 if (auto arraySize
{GetSize(std::move(*arrayShape
))}) {
1046 ActualArguments toMerge
{
1047 ActualArgument
{AsGenericExpr(std::move(*arraySize
))},
1048 ActualArgument
{AsGenericExpr(ExtentExpr
{0})},
1049 common::Clone(call
.arguments().at(1))};
1050 auto specific
{context_
->intrinsics().Probe(
1051 CallCharacteristics
{"merge"}, toMerge
, *context_
)};
1053 return Shape
{ExtentExpr
{FunctionRef
<ExtentType
>{
1054 ProcedureDesignator
{std::move(specific
->specificIntrinsic
)},
1055 std::move(specific
->arguments
)}}};
1059 // Non-scalar MASK= -> [COUNT(mask, KIND=extent_kind)]
1060 ActualArgument kindArg
{
1061 AsGenericExpr(Constant
<ExtentType
>{ExtentType::kind
})};
1062 kindArg
.set_keyword(context_
->SaveTempName("kind"));
1063 ActualArguments toCount
{
1064 ActualArgument
{common::Clone(
1065 DEREF(call
.arguments().at(1).value().UnwrapExpr()))},
1066 std::move(kindArg
)};
1067 auto specific
{context_
->intrinsics().Probe(
1068 CallCharacteristics
{"count"}, toCount
, *context_
)};
1070 return Shape
{ExtentExpr
{FunctionRef
<ExtentType
>{
1071 ProcedureDesignator
{std::move(specific
->specificIntrinsic
)},
1072 std::move(specific
->arguments
)}}};
1076 } else if (intrinsic
->name
== "reshape") {
1077 if (call
.arguments().size() >= 2 && call
.arguments().at(1)) {
1078 // SHAPE(RESHAPE(array,shape)) -> shape
1079 if (const auto *shapeExpr
{
1080 call
.arguments().at(1).value().UnwrapExpr()}) {
1081 auto shapeArg
{std::get
<Expr
<SomeInteger
>>(shapeExpr
->u
)};
1082 if (auto result
{AsShapeResult(
1083 ConvertToType
<ExtentType
>(std::move(shapeArg
)))}) {
1088 } else if (intrinsic
->name
== "spread") {
1089 // SHAPE(SPREAD(ARRAY,DIM,NCOPIES)) = SHAPE(ARRAY) with NCOPIES inserted
1091 if (call
.arguments().size() == 3) {
1093 (*this)(UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(0)))};
1094 const auto *dimArg
{UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(1))};
1095 const auto *nCopies
{
1096 UnwrapExpr
<Expr
<SomeInteger
>>(call
.arguments().at(2))};
1097 if (arrayShape
&& dimArg
&& nCopies
) {
1098 if (auto dim
{ToInt64(*dimArg
)}) {
1100 static_cast<std::size_t>(*dim
) <= arrayShape
->size() + 1) {
1101 arrayShape
->emplace(arrayShape
->begin() + *dim
- 1,
1102 ConvertToType
<ExtentType
>(common::Clone(*nCopies
)));
1103 return std::move(*arrayShape
);
1108 } else if (intrinsic
->name
== "transfer") {
1109 if (call
.arguments().size() == 3 && call
.arguments().at(2)) {
1110 // SIZE= is present; shape is vector [SIZE=]
1111 if (const auto *size
{
1112 UnwrapExpr
<Expr
<SomeInteger
>>(call
.arguments().at(2))}) {
1114 MaybeExtentExpr
{ConvertToType
<ExtentType
>(common::Clone(*size
))}};
1116 } else if (context_
) {
1117 if (auto moldTypeAndShape
{characteristics::TypeAndShape::Characterize(
1118 call
.arguments().at(1), *context_
)}) {
1119 if (moldTypeAndShape
->Rank() == 0) {
1120 // SIZE= is absent and MOLD= is scalar: result is scalar
1121 return ScalarShape();
1123 // SIZE= is absent and MOLD= is array: result is vector whose
1124 // length is determined by sizes of types. See 16.9.193p4 case(ii).
1125 // Note that if sourceBytes is not known to be empty, we
1126 // can fold only when moldElementBytes is known to not be zero;
1127 // the most general case risks a division by zero otherwise.
1128 if (auto sourceTypeAndShape
{
1129 characteristics::TypeAndShape::Characterize(
1130 call
.arguments().at(0), *context_
)}) {
1131 if (auto sourceBytes
{
1132 sourceTypeAndShape
->MeasureSizeInBytes(*context_
)}) {
1133 *sourceBytes
= Fold(*context_
, std::move(*sourceBytes
));
1134 if (auto sourceBytesConst
{ToInt64(*sourceBytes
)}) {
1135 if (*sourceBytesConst
== 0) {
1136 return Shape
{ExtentExpr
{0}};
1139 if (auto moldElementBytes
{
1140 moldTypeAndShape
->MeasureElementSizeInBytes(
1141 *context_
, true)}) {
1143 Fold(*context_
, std::move(*moldElementBytes
));
1144 auto moldElementBytesConst
{ToInt64(*moldElementBytes
)};
1145 if (moldElementBytesConst
&& *moldElementBytesConst
!= 0) {
1146 ExtentExpr extent
{Fold(*context_
,
1147 (std::move(*sourceBytes
) +
1148 common::Clone(*moldElementBytes
) - ExtentExpr
{1}) /
1149 common::Clone(*moldElementBytes
))};
1150 return Shape
{MaybeExtentExpr
{std::move(extent
)}};
1158 } else if (intrinsic
->name
== "this_image") {
1159 if (call
.arguments().size() == 2) {
1160 // THIS_IMAGE(coarray, no DIM, [TEAM])
1161 return Shape(1, ExtentExpr
{GetCorank(call
.arguments().at(0))});
1163 } else if (intrinsic
->name
== "transpose") {
1164 if (call
.arguments().size() >= 1) {
1165 if (auto shape
{(*this)(call
.arguments().at(0))}) {
1166 if (shape
->size() == 2) {
1167 std::swap((*shape
)[0], (*shape
)[1]);
1172 } else if (intrinsic
->name
== "unpack") {
1173 if (call
.arguments().size() >= 2) {
1174 return (*this)(call
.arguments()[1]); // MASK=
1176 } else if (intrinsic
->characteristics
.value().attrs
.test(characteristics::
1177 Procedure::Attr::NullPointer
)) { // NULL(MOLD=)
1178 return (*this)(call
.arguments());
1180 // TODO: shapes of other non-elemental intrinsic results
1183 // The rank is always known even if the extents are not.
1184 return Shape(static_cast<std::size_t>(call
.Rank()), MaybeExtentExpr
{});
1187 void GetShapeHelper::AccumulateExtent(
1188 ExtentExpr
&result
, ExtentExpr
&&n
) const {
1189 result
= std::move(result
) + std::move(n
);
1191 // Fold during expression creation to avoid creating an expression so
1192 // large we can't evaluate it without overflowing the stack.
1193 result
= Fold(*context_
, std::move(result
));
1197 // Check conformance of the passed shapes.
1198 std::optional
<bool> CheckConformance(parser::ContextualMessages
&messages
,
1199 const Shape
&left
, const Shape
&right
, CheckConformanceFlags::Flags flags
,
1200 const char *leftIs
, const char *rightIs
) {
1201 int n
{GetRank(left
)};
1202 if (n
== 0 && (flags
& CheckConformanceFlags::LeftScalarExpandable
)) {
1205 int rn
{GetRank(right
)};
1206 if (rn
== 0 && (flags
& CheckConformanceFlags::RightScalarExpandable
)) {
1210 messages
.Say("Rank of %1$s is %2$d, but %3$s has rank %4$d"_err_en_US
,
1211 leftIs
, n
, rightIs
, rn
);
1214 for (int j
{0}; j
< n
; ++j
) {
1215 if (auto leftDim
{ToInt64(left
[j
])}) {
1216 if (auto rightDim
{ToInt64(right
[j
])}) {
1217 if (*leftDim
!= *rightDim
) {
1218 messages
.Say("Dimension %1$d of %2$s has extent %3$jd, "
1219 "but %4$s has extent %5$jd"_err_en_US
,
1220 j
+ 1, leftIs
, *leftDim
, rightIs
, *rightDim
);
1223 } else if (!(flags
& CheckConformanceFlags::RightIsDeferredShape
)) {
1224 return std::nullopt
;
1226 } else if (!(flags
& CheckConformanceFlags::LeftIsDeferredShape
)) {
1227 return std::nullopt
;
1233 bool IncrementSubscripts(
1234 ConstantSubscripts
&indices
, const ConstantSubscripts
&extents
) {
1235 std::size_t rank(indices
.size());
1236 CHECK(rank
<= extents
.size());
1237 for (std::size_t j
{0}; j
< rank
; ++j
) {
1238 if (extents
[j
] < 1) {
1242 for (std::size_t j
{0}; j
< rank
; ++j
) {
1243 if (indices
[j
]++ < extents
[j
]) {
1251 } // namespace Fortran::evaluate