[flang] Don't duplicate impure function call for UBOUND() (#153648)

Because the per-dimension information in a descriptor holds an extent
and a lower bound, but not an upper bound, the calculation of the upper
bound sometimes requires that the extent and lower bound be extracted
from a descriptor and added together, minus 1. This shouldn't be
attempted when the NamedEntity of the descriptor is something that
shouldn't be duplicated and used twice; specifically, it shouldn't apply
to NamedEntities containing references to impure functions as parts of
subscript expressions.

Fixes https://github.com/llvm/llvm-project/issues/153031.
This commit is contained in:
Peter Klausler 2025-08-18 14:43:13 -07:00 committed by GitHub
parent 48232594a0
commit 2cf982c0f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 41 additions and 14 deletions

View File

@ -1144,15 +1144,14 @@ std::optional<std::string> FindImpureCall(
std::optional<std::string> FindImpureCall(
FoldingContext &, const ProcedureRef &);
// Predicate: is a scalar expression suitable for naive scalar expansion
// in the flattening of an array expression?
// TODO: capture such scalar expansions in temporaries, flatten everything
class UnexpandabilityFindingVisitor
: public AnyTraverse<UnexpandabilityFindingVisitor> {
// Predicate: does an expression contain anything that would prevent it from
// being duplicated so that two instances of it then appear in the same
// expression?
class UnsafeToCopyVisitor : public AnyTraverse<UnsafeToCopyVisitor> {
public:
using Base = AnyTraverse<UnexpandabilityFindingVisitor>;
using Base = AnyTraverse<UnsafeToCopyVisitor>;
using Base::operator();
explicit UnexpandabilityFindingVisitor(bool admitPureCall)
explicit UnsafeToCopyVisitor(bool admitPureCall)
: Base{*this}, admitPureCall_{admitPureCall} {}
template <typename T> bool operator()(const FunctionRef<T> &procRef) {
return !admitPureCall_ || !procRef.proc().IsPure();
@ -1163,14 +1162,22 @@ private:
bool admitPureCall_{false};
};
template <typename A>
bool IsSafelyCopyable(const A &x, bool admitPureCall = false) {
return !UnsafeToCopyVisitor{admitPureCall}(x);
}
// Predicate: is a scalar expression suitable for naive scalar expansion
// in the flattening of an array expression?
// TODO: capture such scalar expansions in temporaries, flatten everything
template <typename T>
bool IsExpandableScalar(const Expr<T> &expr, FoldingContext &context,
const Shape &shape, bool admitPureCall = false) {
if (UnexpandabilityFindingVisitor{admitPureCall}(expr)) {
if (IsSafelyCopyable(expr, admitPureCall)) {
return true;
} else {
auto extents{AsConstantExtents(context, shape)};
return extents && !HasNegativeExtent(*extents) && GetSize(*extents) == 1;
} else {
return true;
}
}

View File

@ -623,7 +623,7 @@ MaybeExtentExpr GetRawUpperBound(
} else if (semantics::IsAssumedSizeArray(symbol) &&
dimension + 1 == symbol.Rank()) {
return std::nullopt;
} else {
} else if (IsSafelyCopyable(base, /*admitPureCall=*/true)) {
return ComputeUpperBound(
GetRawLowerBound(base, dimension), GetExtent(base, dimension));
}
@ -678,9 +678,11 @@ static MaybeExtentExpr GetUBOUND(FoldingContext *context,
} else if (semantics::IsAssumedSizeArray(symbol) &&
dimension + 1 == symbol.Rank()) {
return std::nullopt; // UBOUND() folding replaces with -1
} else if (auto lb{GetLBOUND(base, dimension, invariantOnly)}) {
return ComputeUpperBound(
std::move(*lb), GetExtent(base, dimension, invariantOnly));
} else if (IsSafelyCopyable(base, /*admitPureCall=*/true)) {
if (auto lb{GetLBOUND(base, dimension, invariantOnly)}) {
return ComputeUpperBound(
std::move(*lb), GetExtent(base, dimension, invariantOnly));
}
}
}
} else if (const auto *assoc{

View File

@ -0,0 +1,18 @@
! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
! Ensure that UBOUND() calculation from LBOUND()+SIZE() isn't applied to
! variables containing references to impure functions.
type t
real, allocatable :: a(:)
end type
interface
pure integer function pure(n)
integer, intent(in) :: n
end
end interface
type(t) :: x(10)
allocate(x(1)%a(2))
!CHECK: PRINT *, ubound(x(int(impure(1_4),kind=8))%a,dim=1_4)
print *, ubound(x(impure(1))%a, dim=1)
!CHECK: PRINT *, int(size(x(int(pure(1_4),kind=8))%a,dim=1,kind=8)+lbound(x(int(pure(1_4),kind=8))%a,dim=1,kind=8)-1_8,kind=4)
print *, ubound(x(pure(1))%a, dim=1)
end