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/symbol.h"
22 using namespace std::placeholders
; // _1, _2, &c. for std::bind()
24 namespace Fortran::evaluate
{
26 bool IsImpliedShape(const Symbol
&original
) {
27 const Symbol
&symbol
{ResolveAssociations(original
)};
28 const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()};
29 return details
&& symbol
.attrs().test(semantics::Attr::PARAMETER
) &&
30 details
->shape().CanBeImpliedShape();
33 bool IsExplicitShape(const Symbol
&original
) {
34 const Symbol
&symbol
{ResolveAssociations(original
)};
35 if (const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
36 const auto &shape
{details
->shape()};
37 return shape
.Rank() == 0 ||
38 shape
.IsExplicitShape(); // true when scalar, too
41 .has
<semantics::AssocEntityDetails
>(); // exprs have explicit shape
45 Shape
GetShapeHelper::ConstantShape(const Constant
<ExtentType
> &arrayConstant
) {
46 CHECK(arrayConstant
.Rank() == 1);
48 std::size_t dimensions
{arrayConstant
.size()};
49 for (std::size_t j
{0}; j
< dimensions
; ++j
) {
50 Scalar
<ExtentType
> extent
{arrayConstant
.values().at(j
)};
51 result
.emplace_back(MaybeExtentExpr
{ExtentExpr
{std::move(extent
)}});
56 auto GetShapeHelper::AsShapeResult(ExtentExpr
&&arrayExpr
) const -> Result
{
58 arrayExpr
= Fold(*context_
, std::move(arrayExpr
));
60 if (const auto *constArray
{UnwrapConstantValue
<ExtentType
>(arrayExpr
)}) {
61 return ConstantShape(*constArray
);
63 if (auto *constructor
{UnwrapExpr
<ArrayConstructor
<ExtentType
>>(arrayExpr
)}) {
65 for (auto &value
: *constructor
) {
66 auto *expr
{std::get_if
<ExtentExpr
>(&value
.u
)};
67 if (expr
&& expr
->Rank() == 0) {
68 result
.emplace_back(std::move(*expr
));
79 Shape
GetShapeHelper::CreateShape(int rank
, NamedEntity
&base
) const {
81 for (int dimension
{0}; dimension
< rank
; ++dimension
) {
82 shape
.emplace_back(GetExtent(base
, dimension
, invariantOnly_
));
87 std::optional
<ExtentExpr
> AsExtentArrayExpr(const Shape
&shape
) {
88 ArrayConstructorValues
<ExtentType
> values
;
89 for (const auto &dim
: shape
) {
91 values
.Push(common::Clone(*dim
));
96 return ExtentExpr
{ArrayConstructor
<ExtentType
>{std::move(values
)}};
99 std::optional
<Constant
<ExtentType
>> AsConstantShape(
100 FoldingContext
&context
, const Shape
&shape
) {
101 if (auto shapeArray
{AsExtentArrayExpr(shape
)}) {
102 auto folded
{Fold(context
, std::move(*shapeArray
))};
103 if (auto *p
{UnwrapConstantValue
<ExtentType
>(folded
)}) {
104 return std::move(*p
);
110 Constant
<SubscriptInteger
> AsConstantShape(const ConstantSubscripts
&shape
) {
111 using IntType
= Scalar
<SubscriptInteger
>;
112 std::vector
<IntType
> result
;
113 for (auto dim
: shape
) {
114 result
.emplace_back(dim
);
116 return {std::move(result
), ConstantSubscripts
{GetRank(shape
)}};
119 ConstantSubscripts
AsConstantExtents(const Constant
<ExtentType
> &shape
) {
120 ConstantSubscripts result
;
121 for (const auto &extent
: shape
.values()) {
122 result
.push_back(extent
.ToInt64());
127 std::optional
<ConstantSubscripts
> AsConstantExtents(
128 FoldingContext
&context
, const Shape
&shape
) {
129 if (auto shapeConstant
{AsConstantShape(context
, shape
)}) {
130 return AsConstantExtents(*shapeConstant
);
136 Shape
AsShape(const ConstantSubscripts
&shape
) {
138 for (const auto &extent
: shape
) {
139 result
.emplace_back(ExtentExpr
{extent
});
144 std::optional
<Shape
> AsShape(const std::optional
<ConstantSubscripts
> &shape
) {
146 return AsShape(*shape
);
152 Shape
Fold(FoldingContext
&context
, Shape
&&shape
) {
153 for (auto &dim
: shape
) {
154 dim
= Fold(context
, std::move(dim
));
156 return std::move(shape
);
159 std::optional
<Shape
> Fold(
160 FoldingContext
&context
, std::optional
<Shape
> &&shape
) {
162 return Fold(context
, std::move(*shape
));
168 static ExtentExpr
ComputeTripCount(
169 ExtentExpr
&&lower
, ExtentExpr
&&upper
, ExtentExpr
&&stride
) {
170 ExtentExpr strideCopy
{common::Clone(stride
)};
172 (std::move(upper
) - std::move(lower
) + std::move(strideCopy
)) /
175 Extremum
<ExtentType
>{Ordering::Greater
, std::move(span
), ExtentExpr
{0}}};
178 ExtentExpr
CountTrips(
179 ExtentExpr
&&lower
, ExtentExpr
&&upper
, ExtentExpr
&&stride
) {
180 return ComputeTripCount(
181 std::move(lower
), std::move(upper
), std::move(stride
));
184 ExtentExpr
CountTrips(const ExtentExpr
&lower
, const ExtentExpr
&upper
,
185 const ExtentExpr
&stride
) {
186 return ComputeTripCount(
187 common::Clone(lower
), common::Clone(upper
), common::Clone(stride
));
190 MaybeExtentExpr
CountTrips(MaybeExtentExpr
&&lower
, MaybeExtentExpr
&&upper
,
191 MaybeExtentExpr
&&stride
) {
192 std::function
<ExtentExpr(ExtentExpr
&&, ExtentExpr
&&, ExtentExpr
&&)> bound
{
193 std::bind(ComputeTripCount
, _1
, _2
, _3
)};
194 return common::MapOptional(
195 std::move(bound
), std::move(lower
), std::move(upper
), std::move(stride
));
198 MaybeExtentExpr
GetSize(Shape
&&shape
) {
199 ExtentExpr extent
{1};
200 for (auto &&dim
: std::move(shape
)) {
202 extent
= std::move(extent
) * std::move(*dim
);
210 ConstantSubscript
GetSize(const ConstantSubscripts
&shape
) {
211 ConstantSubscript size
{1};
212 for (auto dim
: shape
) {
219 bool ContainsAnyImpliedDoIndex(const ExtentExpr
&expr
) {
220 struct MyVisitor
: public AnyTraverse
<MyVisitor
> {
221 using Base
= AnyTraverse
<MyVisitor
>;
222 MyVisitor() : Base
{*this} {}
223 using Base::operator();
224 bool operator()(const ImpliedDoIndex
&) { return true; }
226 return MyVisitor
{}(expr
);
229 // Determines lower bound on a dimension. This can be other than 1 only
230 // for a reference to a whole array object or component. (See LBOUND, 16.9.109).
231 // ASSOCIATE construct entities may require traversal of their referents.
232 template <typename RESULT
, bool LBOUND_SEMANTICS
>
233 class GetLowerBoundHelper
234 : public Traverse
<GetLowerBoundHelper
<RESULT
, LBOUND_SEMANTICS
>, RESULT
> {
236 using Result
= RESULT
;
237 using Base
= Traverse
<GetLowerBoundHelper
, RESULT
>;
238 using Base::operator();
239 explicit GetLowerBoundHelper(
240 int d
, FoldingContext
*context
, bool invariantOnly
)
241 : Base
{*this}, dimension_
{d
}, context_
{context
},
242 invariantOnly_
{invariantOnly
} {}
243 static Result
Default() { return Result
{1}; }
244 static Result
Combine(Result
&&, Result
&&) {
245 // Operator results and array references always have lower bounds == 1
249 Result
GetLowerBound(const Symbol
&symbol0
, NamedEntity
&&base
) const {
250 const Symbol
&symbol
{symbol0
.GetUltimate()};
251 if (const auto *object
{
252 symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
253 int rank
{object
->shape().Rank()};
254 if (dimension_
< rank
) {
255 const semantics::ShapeSpec
&shapeSpec
{object
->shape()[dimension_
]};
256 if (shapeSpec
.lbound().isExplicit()) {
257 if (const auto &lbound
{shapeSpec
.lbound().GetExplicit()}) {
258 if constexpr (LBOUND_SEMANTICS
) {
260 auto lbValue
{ToInt64(*lbound
)};
261 if (dimension_
== rank
- 1 &&
262 semantics::IsAssumedSizeArray(symbol
)) {
263 // last dimension of assumed-size dummy array: don't worry
264 // about handling an empty dimension
265 ok
= !invariantOnly_
|| IsScopeInvariantExpr(*lbound
);
266 } else if (lbValue
.value_or(0) == 1) {
267 // Lower bound is 1, regardless of extent
269 } else if (const auto &ubound
{shapeSpec
.ubound().GetExplicit()}) {
270 // If we can't prove that the dimension is nonempty,
271 // we must be conservative.
272 // TODO: simple symbolic math in expression rewriting to
273 // cope with cases like A(J:J)
275 auto extent
{ToInt64(Fold(*context_
,
276 ExtentExpr
{*ubound
} - ExtentExpr
{*lbound
} +
287 auto ubValue
{ToInt64(*ubound
)};
288 if (lbValue
&& ubValue
) {
289 if (*lbValue
> *ubValue
) {
298 return ok
? *lbound
: Result
{};
306 if (IsDescriptor(symbol
)) {
307 return ExtentExpr
{DescriptorInquiry
{std::move(base
),
308 DescriptorInquiry::Field::LowerBound
, dimension_
}};
311 } else if (const auto *assoc
{
312 symbol
.detailsIf
<semantics::AssocEntityDetails
>()}) {
313 if (assoc
->IsAssumedSize()) { // RANK(*)
315 } else if (assoc
->IsAssumedRank()) { // RANK DEFAULT
316 } else if (assoc
->rank()) { // RANK(n)
317 const Symbol
&resolved
{ResolveAssociations(symbol
)};
318 if (IsDescriptor(resolved
) && dimension_
< *assoc
->rank()) {
319 return ExtentExpr
{DescriptorInquiry
{std::move(base
),
320 DescriptorInquiry::Field::LowerBound
, dimension_
}};
323 Result exprLowerBound
{((*this)(assoc
->expr()))};
324 if (IsActuallyConstant(exprLowerBound
)) {
325 return std::move(exprLowerBound
);
327 // If the lower bound of the associated entity is not resolved to a
328 // constant expression at the time of the association, it is unsafe
329 // to re-evaluate it later in the associate construct. Statements
330 // in between may have modified its operands value.
331 return ExtentExpr
{DescriptorInquiry
{std::move(base
),
332 DescriptorInquiry::Field::LowerBound
, dimension_
}};
336 if constexpr (LBOUND_SEMANTICS
) {
343 Result
operator()(const Symbol
&symbol
) const {
344 return GetLowerBound(symbol
, NamedEntity
{symbol
});
347 Result
operator()(const Component
&component
) const {
348 if (component
.base().Rank() == 0) {
349 return GetLowerBound(
350 component
.GetLastSymbol(), NamedEntity
{common::Clone(component
)});
355 template <typename T
> Result
operator()(const Expr
<T
> &expr
) const {
356 if (const Symbol
* whole
{UnwrapWholeSymbolOrComponentDataRef(expr
)}) {
357 return (*this)(*whole
);
358 } else if constexpr (common::HasMember
<Constant
<T
>, decltype(expr
.u
)>) {
359 if (const auto *con
{std::get_if
<Constant
<T
>>(&expr
.u
)}) {
360 ConstantSubscripts lb
{con
->lbounds()};
361 if (dimension_
< GetRank(lb
)) {
362 return Result
{lb
[dimension_
]};
364 } else { // operation
368 return (*this)(expr
.u
);
370 if constexpr (LBOUND_SEMANTICS
) {
378 int dimension_
; // zero-based
379 FoldingContext
*context_
{nullptr};
380 bool invariantOnly_
{false};
383 ExtentExpr
GetRawLowerBound(
384 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
385 return GetLowerBoundHelper
<ExtentExpr
, false>{
386 dimension
, nullptr, invariantOnly
}(base
);
389 ExtentExpr
GetRawLowerBound(FoldingContext
&context
, const NamedEntity
&base
,
390 int dimension
, bool invariantOnly
) {
392 GetLowerBoundHelper
<ExtentExpr
, false>{
393 dimension
, &context
, invariantOnly
}(base
));
396 MaybeExtentExpr
GetLBOUND(
397 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
398 return GetLowerBoundHelper
<MaybeExtentExpr
, true>{
399 dimension
, nullptr, invariantOnly
}(base
);
402 MaybeExtentExpr
GetLBOUND(FoldingContext
&context
, const NamedEntity
&base
,
403 int dimension
, bool invariantOnly
) {
405 GetLowerBoundHelper
<MaybeExtentExpr
, true>{
406 dimension
, &context
, invariantOnly
}(base
));
409 Shape
GetRawLowerBounds(const NamedEntity
&base
, bool invariantOnly
) {
411 int rank
{base
.Rank()};
412 for (int dim
{0}; dim
< rank
; ++dim
) {
413 result
.emplace_back(GetRawLowerBound(base
, dim
, invariantOnly
));
418 Shape
GetRawLowerBounds(
419 FoldingContext
&context
, const NamedEntity
&base
, bool invariantOnly
) {
421 int rank
{base
.Rank()};
422 for (int dim
{0}; dim
< rank
; ++dim
) {
423 result
.emplace_back(GetRawLowerBound(context
, base
, dim
, invariantOnly
));
428 Shape
GetLBOUNDs(const NamedEntity
&base
, bool invariantOnly
) {
430 int rank
{base
.Rank()};
431 for (int dim
{0}; dim
< rank
; ++dim
) {
432 result
.emplace_back(GetLBOUND(base
, dim
, invariantOnly
));
438 FoldingContext
&context
, const NamedEntity
&base
, bool invariantOnly
) {
440 int rank
{base
.Rank()};
441 for (int dim
{0}; dim
< rank
; ++dim
) {
442 result
.emplace_back(GetLBOUND(context
, base
, dim
, invariantOnly
));
447 // If the upper and lower bounds are constant, return a constant expression for
448 // the extent. In particular, if the upper bound is less than the lower bound,
450 static MaybeExtentExpr
GetNonNegativeExtent(
451 const semantics::ShapeSpec
&shapeSpec
, bool invariantOnly
) {
452 const auto &ubound
{shapeSpec
.ubound().GetExplicit()};
453 const auto &lbound
{shapeSpec
.lbound().GetExplicit()};
454 std::optional
<ConstantSubscript
> uval
{ToInt64(ubound
)};
455 std::optional
<ConstantSubscript
> lval
{ToInt64(lbound
)};
458 return ExtentExpr
{0};
460 return ExtentExpr
{*uval
- *lval
+ 1};
462 } else if (lbound
&& ubound
&&
464 (IsScopeInvariantExpr(*lbound
) && IsScopeInvariantExpr(*ubound
)))) {
465 // Apply effective IDIM (MAX calculation with 0) so thet the
466 // result is never negative
467 if (lval
.value_or(0) == 1) {
468 return ExtentExpr
{Extremum
<SubscriptInteger
>{
469 Ordering::Greater
, ExtentExpr
{0}, common::Clone(*ubound
)}};
472 Extremum
<SubscriptInteger
>{Ordering::Greater
, ExtentExpr
{0},
473 common::Clone(*ubound
) - common::Clone(*lbound
) + ExtentExpr
{1}}};
480 static MaybeExtentExpr
GetAssociatedExtent(
481 const Symbol
&symbol
, int dimension
) {
482 if (const auto *assoc
{symbol
.detailsIf
<semantics::AssocEntityDetails
>()};
483 assoc
&& !assoc
->rank()) { // not SELECT RANK case
484 if (auto shape
{GetShape(assoc
->expr())};
485 shape
&& dimension
< static_cast<int>(shape
->size())) {
486 if (auto &extent
{shape
->at(dimension
)};
487 // Don't return a non-constant extent, as the variables that
488 // determine the shape of the selector's expression may change
489 // during execution of the construct.
490 extent
&& IsActuallyConstant(*extent
)) {
491 return std::move(extent
);
495 return ExtentExpr
{DescriptorInquiry
{
496 NamedEntity
{symbol
}, DescriptorInquiry::Field::Extent
, dimension
}};
499 MaybeExtentExpr
GetExtent(
500 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
501 CHECK(dimension
>= 0);
502 const Symbol
&last
{base
.GetLastSymbol()};
503 const Symbol
&symbol
{ResolveAssociations(last
)};
504 if (const auto *assoc
{last
.detailsIf
<semantics::AssocEntityDetails
>()}) {
505 if (assoc
->IsAssumedSize() || assoc
->IsAssumedRank()) { // RANK(*)/DEFAULT
507 } else if (assoc
->rank()) { // RANK(n)
508 if (semantics::IsDescriptor(symbol
) && dimension
< *assoc
->rank()) {
509 return ExtentExpr
{DescriptorInquiry
{
510 NamedEntity
{base
}, DescriptorInquiry::Field::Extent
, dimension
}};
515 return GetAssociatedExtent(last
, dimension
);
518 if (const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
519 if (IsImpliedShape(symbol
) && details
->init()) {
520 if (auto shape
{GetShape(symbol
, invariantOnly
)}) {
521 if (dimension
< static_cast<int>(shape
->size())) {
522 return std::move(shape
->at(dimension
));
527 for (const auto &shapeSpec
: details
->shape()) {
528 if (j
++ == dimension
) {
529 if (auto extent
{GetNonNegativeExtent(shapeSpec
, invariantOnly
)}) {
531 } else if (semantics::IsAssumedSizeArray(symbol
) &&
532 j
== symbol
.Rank()) {
534 } else if (semantics::IsDescriptor(symbol
)) {
535 return ExtentExpr
{DescriptorInquiry
{NamedEntity
{base
},
536 DescriptorInquiry::Field::Extent
, dimension
}};
547 MaybeExtentExpr
GetExtent(FoldingContext
&context
, const NamedEntity
&base
,
548 int dimension
, bool invariantOnly
) {
549 return Fold(context
, GetExtent(base
, dimension
, invariantOnly
));
552 MaybeExtentExpr
GetExtent(const Subscript
&subscript
, const NamedEntity
&base
,
553 int dimension
, bool invariantOnly
) {
554 return common::visit(
556 [&](const Triplet
&triplet
) -> MaybeExtentExpr
{
557 MaybeExtentExpr upper
{triplet
.upper()};
559 upper
= GetUBOUND(base
, dimension
, invariantOnly
);
561 MaybeExtentExpr lower
{triplet
.lower()};
563 lower
= GetLBOUND(base
, dimension
, invariantOnly
);
565 return CountTrips(std::move(lower
), std::move(upper
),
566 MaybeExtentExpr
{triplet
.stride()});
568 [&](const IndirectSubscriptIntegerExpr
&subs
) -> MaybeExtentExpr
{
569 if (auto shape
{GetShape(subs
.value())}) {
570 if (GetRank(*shape
) > 0) {
571 CHECK(GetRank(*shape
) == 1); // vector-valued subscript
572 return std::move(shape
->at(0));
581 MaybeExtentExpr
GetExtent(FoldingContext
&context
, const Subscript
&subscript
,
582 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
583 return Fold(context
, GetExtent(subscript
, base
, dimension
, invariantOnly
));
586 MaybeExtentExpr
ComputeUpperBound(
587 ExtentExpr
&&lower
, MaybeExtentExpr
&&extent
) {
589 if (ToInt64(lower
).value_or(0) == 1) {
590 return std::move(*extent
);
592 return std::move(*extent
) + std::move(lower
) - ExtentExpr
{1};
599 MaybeExtentExpr
ComputeUpperBound(
600 FoldingContext
&context
, ExtentExpr
&&lower
, MaybeExtentExpr
&&extent
) {
601 return Fold(context
, ComputeUpperBound(std::move(lower
), std::move(extent
)));
604 MaybeExtentExpr
GetRawUpperBound(
605 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
606 const Symbol
&symbol
{ResolveAssociations(base
.GetLastSymbol())};
607 if (const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
608 int rank
{details
->shape().Rank()};
609 if (dimension
< rank
) {
610 const auto &bound
{details
->shape()[dimension
].ubound().GetExplicit()};
611 if (bound
&& (!invariantOnly
|| IsScopeInvariantExpr(*bound
))) {
613 } else if (semantics::IsAssumedSizeArray(symbol
) &&
614 dimension
+ 1 == symbol
.Rank()) {
617 return ComputeUpperBound(
618 GetRawLowerBound(base
, dimension
), GetExtent(base
, dimension
));
621 } else if (const auto *assoc
{
622 symbol
.detailsIf
<semantics::AssocEntityDetails
>()}) {
623 if (assoc
->IsAssumedSize() || assoc
->IsAssumedRank()) {
625 } else if (assoc
->rank() && dimension
>= *assoc
->rank()) {
627 } else if (auto extent
{GetAssociatedExtent(symbol
, dimension
)}) {
628 return ComputeUpperBound(
629 GetRawLowerBound(base
, dimension
), std::move(extent
));
635 MaybeExtentExpr
GetRawUpperBound(FoldingContext
&context
,
636 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
637 return Fold(context
, GetRawUpperBound(base
, dimension
, invariantOnly
));
640 static MaybeExtentExpr
GetExplicitUBOUND(FoldingContext
*context
,
641 const semantics::ShapeSpec
&shapeSpec
, bool invariantOnly
) {
642 const auto &ubound
{shapeSpec
.ubound().GetExplicit()};
643 if (ubound
&& (!invariantOnly
|| IsScopeInvariantExpr(*ubound
))) {
644 if (auto extent
{GetNonNegativeExtent(shapeSpec
, invariantOnly
)}) {
645 if (auto cstExtent
{ToInt64(
646 context
? Fold(*context
, std::move(*extent
)) : *extent
)}) {
649 } else if (cstExtent
== 0) {
650 return ExtentExpr
{0};
658 static MaybeExtentExpr
GetUBOUND(FoldingContext
*context
,
659 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
660 const Symbol
&symbol
{ResolveAssociations(base
.GetLastSymbol())};
661 if (const auto *details
{symbol
.detailsIf
<semantics::ObjectEntityDetails
>()}) {
662 int rank
{details
->shape().Rank()};
663 if (dimension
< rank
) {
664 const semantics::ShapeSpec
&shapeSpec
{details
->shape()[dimension
]};
665 if (auto ubound
{GetExplicitUBOUND(context
, shapeSpec
, invariantOnly
)}) {
667 } else if (semantics::IsAssumedSizeArray(symbol
) &&
668 dimension
+ 1 == symbol
.Rank()) {
669 return std::nullopt
; // UBOUND() folding replaces with -1
670 } else if (auto lb
{GetLBOUND(base
, dimension
, invariantOnly
)}) {
671 return ComputeUpperBound(
672 std::move(*lb
), GetExtent(base
, dimension
, invariantOnly
));
675 } else if (const auto *assoc
{
676 symbol
.detailsIf
<semantics::AssocEntityDetails
>()}) {
677 if (assoc
->IsAssumedSize() || assoc
->IsAssumedRank()) {
679 } else if (assoc
->rank()) { // RANK (n)
680 const Symbol
&resolved
{ResolveAssociations(symbol
)};
681 if (IsDescriptor(resolved
) && dimension
< *assoc
->rank()) {
682 ExtentExpr lb
{DescriptorInquiry
{NamedEntity
{base
},
683 DescriptorInquiry::Field::LowerBound
, dimension
}};
684 ExtentExpr extent
{DescriptorInquiry
{
685 std::move(base
), DescriptorInquiry::Field::Extent
, dimension
}};
686 return ComputeUpperBound(std::move(lb
), std::move(extent
));
688 } else if (auto extent
{GetAssociatedExtent(symbol
, dimension
)}) {
689 if (auto lb
{GetLBOUND(base
, dimension
, invariantOnly
)}) {
690 return ComputeUpperBound(std::move(*lb
), std::move(extent
));
697 MaybeExtentExpr
GetUBOUND(
698 const NamedEntity
&base
, int dimension
, bool invariantOnly
) {
699 return GetUBOUND(nullptr, base
, dimension
, invariantOnly
);
702 MaybeExtentExpr
GetUBOUND(FoldingContext
&context
, const NamedEntity
&base
,
703 int dimension
, bool invariantOnly
) {
704 return Fold(context
, GetUBOUND(&context
, base
, dimension
, invariantOnly
));
707 static Shape
GetUBOUNDs(
708 FoldingContext
*context
, const NamedEntity
&base
, bool invariantOnly
) {
710 int rank
{base
.Rank()};
711 for (int dim
{0}; dim
< rank
; ++dim
) {
712 result
.emplace_back(GetUBOUND(context
, base
, dim
, invariantOnly
));
718 FoldingContext
&context
, const NamedEntity
&base
, bool invariantOnly
) {
719 return Fold(context
, GetUBOUNDs(&context
, base
, invariantOnly
));
722 Shape
GetUBOUNDs(const NamedEntity
&base
, bool invariantOnly
) {
723 return GetUBOUNDs(nullptr, base
, invariantOnly
);
726 auto GetShapeHelper::operator()(const Symbol
&symbol
) const -> Result
{
727 return common::visit(
729 [&](const semantics::ObjectEntityDetails
&object
) {
730 if (IsImpliedShape(symbol
) && object
.init()) {
731 return (*this)(object
.init());
732 } else if (IsAssumedRank(symbol
)) {
735 int n
{object
.shape().Rank()};
736 NamedEntity base
{symbol
};
737 return Result
{CreateShape(n
, base
)};
740 [](const semantics::EntityDetails
&) {
741 return ScalarShape(); // no dimensions seen
743 [&](const semantics::ProcEntityDetails
&proc
) {
744 if (const Symbol
* interface
{proc
.procInterface()}) {
745 return (*this)(*interface
);
747 return ScalarShape();
750 [&](const semantics::AssocEntityDetails
&assoc
) {
751 NamedEntity base
{symbol
};
752 if (assoc
.rank()) { // SELECT RANK case
753 int n
{assoc
.rank().value()};
754 return Result
{CreateShape(n
, base
)};
756 auto exprShape
{((*this)(assoc
.expr()))};
758 int rank
{static_cast<int>(exprShape
->size())};
759 for (int dimension
{0}; dimension
< rank
; ++dimension
) {
760 auto &extent
{(*exprShape
)[dimension
]};
761 if (extent
&& !IsActuallyConstant(*extent
)) {
762 extent
= GetExtent(base
, dimension
);
769 [&](const semantics::SubprogramDetails
&subp
) -> Result
{
770 if (subp
.isFunction()) {
771 auto resultShape
{(*this)(subp
.result())};
772 if (resultShape
&& !useResultSymbolShape_
) {
773 // Ensure the shape is constant. Otherwise, it may be referring
774 // to symbols that belong to the function's scope and are
775 // meaningless on the caller side without the related call
777 for (auto &extent
: *resultShape
) {
778 if (extent
&& !IsActuallyConstant(*extent
)) {
788 [&](const semantics::ProcBindingDetails
&binding
) {
789 return (*this)(binding
.symbol());
791 [](const semantics::TypeParamDetails
&) { return ScalarShape(); },
792 [](const auto &) { return Result
{}; },
794 symbol
.GetUltimate().details());
797 auto GetShapeHelper::operator()(const Component
&component
) const -> Result
{
798 const Symbol
&symbol
{component
.GetLastSymbol()};
799 int rank
{symbol
.Rank()};
801 return (*this)(component
.base());
802 } else if (symbol
.has
<semantics::ObjectEntityDetails
>()) {
803 NamedEntity base
{Component
{component
}};
804 return CreateShape(rank
, base
);
806 return (*this)(symbol
);
810 auto GetShapeHelper::operator()(const ArrayRef
&arrayRef
) const -> Result
{
813 const NamedEntity
&base
{arrayRef
.base()};
814 for (const Subscript
&ss
: arrayRef
.subscript()) {
816 shape
.emplace_back(GetExtent(ss
, base
, dimension
));
821 if (const Component
* component
{base
.UnwrapComponent()}) {
822 return (*this)(component
->base());
828 auto GetShapeHelper::operator()(const CoarrayRef
&coarrayRef
) const -> Result
{
829 NamedEntity base
{coarrayRef
.GetBase()};
830 if (coarrayRef
.subscript().empty()) {
831 return (*this)(base
);
835 for (const Subscript
&ss
: coarrayRef
.subscript()) {
837 shape
.emplace_back(GetExtent(ss
, base
, dimension
));
845 auto GetShapeHelper::operator()(const Substring
&substring
) const -> Result
{
846 return (*this)(substring
.parent());
849 auto GetShapeHelper::operator()(const ProcedureRef
&call
) const -> Result
{
850 if (call
.Rank() == 0) {
851 return ScalarShape();
852 } else if (call
.IsElemental()) {
853 // Use the shape of an actual array argument associated with a
854 // non-OPTIONAL dummy object argument.
856 if (auto chars
{characteristics::Procedure::FromActuals(
857 call
.proc(), call
.arguments(), *context_
)}) {
859 std::size_t anyArrayArgRank
{0};
860 for (const auto &arg
: call
.arguments()) {
861 if (arg
&& arg
->Rank() > 0 && j
< chars
->dummyArguments
.size()) {
862 anyArrayArgRank
= arg
->Rank();
863 if (!chars
->dummyArguments
[j
].IsOptional()) {
864 return (*this)(*arg
);
869 if (anyArrayArgRank
) {
870 // All dummy array arguments of the procedure are OPTIONAL.
871 // We cannot take the shape from just any array argument,
872 // because all of them might be OPTIONAL dummy arguments
873 // of the caller. Return unknown shape ranked according
874 // to the last actual array argument.
875 return Shape(anyArrayArgRank
, MaybeExtentExpr
{});
879 return ScalarShape();
880 } else if (const Symbol
* symbol
{call
.proc().GetSymbol()}) {
881 auto restorer
{common::ScopedSet(useResultSymbolShape_
, false)};
882 return (*this)(*symbol
);
883 } else if (const auto *intrinsic
{call
.proc().GetSpecificIntrinsic()}) {
884 if (intrinsic
->name
== "shape" || intrinsic
->name
== "lbound" ||
885 intrinsic
->name
== "ubound") {
886 // For LBOUND/UBOUND, these are the array-valued cases (no DIM=)
887 if (!call
.arguments().empty() && call
.arguments().front()) {
888 if (IsAssumedRank(*call
.arguments().front())) {
889 return Shape
{MaybeExtentExpr
{}};
892 MaybeExtentExpr
{ExtentExpr
{call
.arguments().front()->Rank()}}};
895 } else if (intrinsic
->name
== "all" || intrinsic
->name
== "any" ||
896 intrinsic
->name
== "count" || intrinsic
->name
== "iall" ||
897 intrinsic
->name
== "iany" || intrinsic
->name
== "iparity" ||
898 intrinsic
->name
== "maxval" || intrinsic
->name
== "minval" ||
899 intrinsic
->name
== "norm2" || intrinsic
->name
== "parity" ||
900 intrinsic
->name
== "product" || intrinsic
->name
== "sum") {
901 // Reduction with DIM=
902 if (call
.arguments().size() >= 2) {
904 (*this)(UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(0)))};
905 const auto *dimArg
{UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(1))};
906 if (arrayShape
&& dimArg
) {
907 if (auto dim
{ToInt64(*dimArg
)}) {
909 static_cast<std::size_t>(*dim
) <= arrayShape
->size()) {
910 arrayShape
->erase(arrayShape
->begin() + (*dim
- 1));
911 return std::move(*arrayShape
);
916 } else if (intrinsic
->name
== "findloc" || intrinsic
->name
== "maxloc" ||
917 intrinsic
->name
== "minloc") {
918 std::size_t dimIndex
{intrinsic
->name
== "findloc" ? 2u : 1u};
919 if (call
.arguments().size() > dimIndex
) {
921 (*this)(UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(0)))}) {
922 auto rank
{static_cast<int>(arrayShape
->size())};
923 if (const auto *dimArg
{
924 UnwrapExpr
<Expr
<SomeType
>>(call
.arguments()[dimIndex
])}) {
925 auto dim
{ToInt64(*dimArg
)};
926 if (dim
&& *dim
>= 1 && *dim
<= rank
) {
927 arrayShape
->erase(arrayShape
->begin() + (*dim
- 1));
928 return std::move(*arrayShape
);
931 // xxxLOC(no DIM=) result is vector(1:RANK(ARRAY=))
932 return Shape
{ExtentExpr
{rank
}};
936 } else if (intrinsic
->name
== "cshift" || intrinsic
->name
== "eoshift") {
937 if (!call
.arguments().empty()) {
938 return (*this)(call
.arguments()[0]);
940 } else if (intrinsic
->name
== "matmul") {
941 if (call
.arguments().size() == 2) {
942 if (auto ashape
{(*this)(call
.arguments()[0])}) {
943 if (auto bshape
{(*this)(call
.arguments()[1])}) {
944 if (ashape
->size() == 1 && bshape
->size() == 2) {
945 bshape
->erase(bshape
->begin());
946 return std::move(*bshape
); // matmul(vector, matrix)
947 } else if (ashape
->size() == 2 && bshape
->size() == 1) {
949 return std::move(*ashape
); // matmul(matrix, vector)
950 } else if (ashape
->size() == 2 && bshape
->size() == 2) {
951 (*ashape
)[1] = std::move((*bshape
)[1]);
952 return std::move(*ashape
); // matmul(matrix, matrix)
957 } else if (intrinsic
->name
== "pack") {
958 if (call
.arguments().size() >= 3 && call
.arguments().at(2)) {
959 // SHAPE(PACK(,,VECTOR=v)) -> SHAPE(v)
960 return (*this)(call
.arguments().at(2));
961 } else if (call
.arguments().size() >= 2 && context_
) {
962 if (auto maskShape
{(*this)(call
.arguments().at(1))}) {
963 if (maskShape
->size() == 0) {
964 // Scalar MASK= -> [MERGE(SIZE(ARRAY=), 0, mask)]
965 if (auto arrayShape
{(*this)(call
.arguments().at(0))}) {
966 if (auto arraySize
{GetSize(std::move(*arrayShape
))}) {
967 ActualArguments toMerge
{
968 ActualArgument
{AsGenericExpr(std::move(*arraySize
))},
969 ActualArgument
{AsGenericExpr(ExtentExpr
{0})},
970 common::Clone(call
.arguments().at(1))};
971 auto specific
{context_
->intrinsics().Probe(
972 CallCharacteristics
{"merge"}, toMerge
, *context_
)};
974 return Shape
{ExtentExpr
{FunctionRef
<ExtentType
>{
975 ProcedureDesignator
{std::move(specific
->specificIntrinsic
)},
976 std::move(specific
->arguments
)}}};
980 // Non-scalar MASK= -> [COUNT(mask, KIND=extent_kind)]
981 ActualArgument kindArg
{
982 AsGenericExpr(Constant
<ExtentType
>{ExtentType::kind
})};
983 kindArg
.set_keyword(context_
->SaveTempName("kind"));
984 ActualArguments toCount
{
985 ActualArgument
{common::Clone(
986 DEREF(call
.arguments().at(1).value().UnwrapExpr()))},
988 auto specific
{context_
->intrinsics().Probe(
989 CallCharacteristics
{"count"}, toCount
, *context_
)};
991 return Shape
{ExtentExpr
{FunctionRef
<ExtentType
>{
992 ProcedureDesignator
{std::move(specific
->specificIntrinsic
)},
993 std::move(specific
->arguments
)}}};
997 } else if (intrinsic
->name
== "reshape") {
998 if (call
.arguments().size() >= 2 && call
.arguments().at(1)) {
999 // SHAPE(RESHAPE(array,shape)) -> shape
1000 if (const auto *shapeExpr
{
1001 call
.arguments().at(1).value().UnwrapExpr()}) {
1002 auto shapeArg
{std::get
<Expr
<SomeInteger
>>(shapeExpr
->u
)};
1003 if (auto result
{AsShapeResult(
1004 ConvertToType
<ExtentType
>(std::move(shapeArg
)))}) {
1009 } else if (intrinsic
->name
== "spread") {
1010 // SHAPE(SPREAD(ARRAY,DIM,NCOPIES)) = SHAPE(ARRAY) with NCOPIES inserted
1012 if (call
.arguments().size() == 3) {
1014 (*this)(UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(0)))};
1015 const auto *dimArg
{UnwrapExpr
<Expr
<SomeType
>>(call
.arguments().at(1))};
1016 const auto *nCopies
{
1017 UnwrapExpr
<Expr
<SomeInteger
>>(call
.arguments().at(2))};
1018 if (arrayShape
&& dimArg
&& nCopies
) {
1019 if (auto dim
{ToInt64(*dimArg
)}) {
1021 static_cast<std::size_t>(*dim
) <= arrayShape
->size() + 1) {
1022 arrayShape
->emplace(arrayShape
->begin() + *dim
- 1,
1023 ConvertToType
<ExtentType
>(common::Clone(*nCopies
)));
1024 return std::move(*arrayShape
);
1029 } else if (intrinsic
->name
== "transfer") {
1030 if (call
.arguments().size() == 3 && call
.arguments().at(2)) {
1031 // SIZE= is present; shape is vector [SIZE=]
1032 if (const auto *size
{
1033 UnwrapExpr
<Expr
<SomeInteger
>>(call
.arguments().at(2))}) {
1035 MaybeExtentExpr
{ConvertToType
<ExtentType
>(common::Clone(*size
))}};
1037 } else if (context_
) {
1038 if (auto moldTypeAndShape
{characteristics::TypeAndShape::Characterize(
1039 call
.arguments().at(1), *context_
)}) {
1040 if (moldTypeAndShape
->Rank() == 0) {
1041 // SIZE= is absent and MOLD= is scalar: result is scalar
1042 return ScalarShape();
1044 // SIZE= is absent and MOLD= is array: result is vector whose
1045 // length is determined by sizes of types. See 16.9.193p4 case(ii).
1046 // Note that if sourceBytes is not known to be empty, we
1047 // can fold only when moldElementBytes is known to not be zero;
1048 // the most general case risks a division by zero otherwise.
1049 if (auto sourceTypeAndShape
{
1050 characteristics::TypeAndShape::Characterize(
1051 call
.arguments().at(0), *context_
)}) {
1052 if (auto sourceBytes
{
1053 sourceTypeAndShape
->MeasureSizeInBytes(*context_
)}) {
1054 *sourceBytes
= Fold(*context_
, std::move(*sourceBytes
));
1055 if (auto sourceBytesConst
{ToInt64(*sourceBytes
)}) {
1056 if (*sourceBytesConst
== 0) {
1057 return Shape
{ExtentExpr
{0}};
1060 if (auto moldElementBytes
{
1061 moldTypeAndShape
->MeasureElementSizeInBytes(
1062 *context_
, true)}) {
1064 Fold(*context_
, std::move(*moldElementBytes
));
1065 auto moldElementBytesConst
{ToInt64(*moldElementBytes
)};
1066 if (moldElementBytesConst
&& *moldElementBytesConst
!= 0) {
1067 ExtentExpr extent
{Fold(*context_
,
1068 (std::move(*sourceBytes
) +
1069 common::Clone(*moldElementBytes
) - ExtentExpr
{1}) /
1070 common::Clone(*moldElementBytes
))};
1071 return Shape
{MaybeExtentExpr
{std::move(extent
)}};
1079 } else if (intrinsic
->name
== "transpose") {
1080 if (call
.arguments().size() >= 1) {
1081 if (auto shape
{(*this)(call
.arguments().at(0))}) {
1082 if (shape
->size() == 2) {
1083 std::swap((*shape
)[0], (*shape
)[1]);
1088 } else if (intrinsic
->name
== "unpack") {
1089 if (call
.arguments().size() >= 2) {
1090 return (*this)(call
.arguments()[1]); // MASK=
1092 } else if (intrinsic
->characteristics
.value().attrs
.test(characteristics::
1093 Procedure::Attr::NullPointer
)) { // NULL(MOLD=)
1094 return (*this)(call
.arguments());
1096 // TODO: shapes of other non-elemental intrinsic results
1099 // The rank is always known even if the extents are not.
1100 return Shape(static_cast<std::size_t>(call
.Rank()), MaybeExtentExpr
{});
1103 void GetShapeHelper::AccumulateExtent(
1104 ExtentExpr
&result
, ExtentExpr
&&n
) const {
1105 result
= std::move(result
) + std::move(n
);
1107 // Fold during expression creation to avoid creating an expression so
1108 // large we can't evaluate it without overflowing the stack.
1109 result
= Fold(*context_
, std::move(result
));
1113 // Check conformance of the passed shapes.
1114 std::optional
<bool> CheckConformance(parser::ContextualMessages
&messages
,
1115 const Shape
&left
, const Shape
&right
, CheckConformanceFlags::Flags flags
,
1116 const char *leftIs
, const char *rightIs
) {
1117 int n
{GetRank(left
)};
1118 if (n
== 0 && (flags
& CheckConformanceFlags::LeftScalarExpandable
)) {
1121 int rn
{GetRank(right
)};
1122 if (rn
== 0 && (flags
& CheckConformanceFlags::RightScalarExpandable
)) {
1126 messages
.Say("Rank of %1$s is %2$d, but %3$s has rank %4$d"_err_en_US
,
1127 leftIs
, n
, rightIs
, rn
);
1130 for (int j
{0}; j
< n
; ++j
) {
1131 if (auto leftDim
{ToInt64(left
[j
])}) {
1132 if (auto rightDim
{ToInt64(right
[j
])}) {
1133 if (*leftDim
!= *rightDim
) {
1134 messages
.Say("Dimension %1$d of %2$s has extent %3$jd, "
1135 "but %4$s has extent %5$jd"_err_en_US
,
1136 j
+ 1, leftIs
, *leftDim
, rightIs
, *rightDim
);
1139 } else if (!(flags
& CheckConformanceFlags::RightIsDeferredShape
)) {
1140 return std::nullopt
;
1142 } else if (!(flags
& CheckConformanceFlags::LeftIsDeferredShape
)) {
1143 return std::nullopt
;
1149 bool IncrementSubscripts(
1150 ConstantSubscripts
&indices
, const ConstantSubscripts
&extents
) {
1151 std::size_t rank(indices
.size());
1152 CHECK(rank
<= extents
.size());
1153 for (std::size_t j
{0}; j
< rank
; ++j
) {
1154 if (extents
[j
] < 1) {
1158 for (std::size_t j
{0}; j
< rank
; ++j
) {
1159 if (indices
[j
]++ < extents
[j
]) {
1167 } // namespace Fortran::evaluate