[flang-commits] [flang] [flang] Better messages and error recovery for a bad RESHAPE (PR #122604)
via flang-commits
flang-commits at lists.llvm.org
Sat Jan 11 10:28:37 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
Author: Peter Klausler (klausler)
<details>
<summary>Changes</summary>
Add tests for negative array extents where necessary, motivated by a compiler crash exposed by yet another fuzzer test, and improve overall error message quality for RESHAPE().
Fixes https://github.com/llvm/llvm-project/issues/122060.
---
Full diff: https://github.com/llvm/llvm-project/pull/122604.diff
8 Files Affected:
- (modified) flang/include/flang/Evaluate/shape.h (+10-3)
- (modified) flang/include/flang/Evaluate/tools.h (+1-1)
- (modified) flang/lib/Evaluate/check-expression.cpp (+2-1)
- (modified) flang/lib/Evaluate/fold-designator.cpp (+2-1)
- (modified) flang/lib/Evaluate/fold-implementation.h (+57-41)
- (modified) flang/lib/Semantics/tools.cpp (+2-1)
- (added) flang/test/Semantics/bug122060.f90 (+6)
- (modified) flang/test/Semantics/reshape.f90 (+6-6)
``````````diff
diff --git a/flang/include/flang/Evaluate/shape.h b/flang/include/flang/Evaluate/shape.h
index e679a001235490..dd9119e1461f25 100644
--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -43,6 +43,8 @@ std::optional<Constant<ExtentType>> AsConstantShape(
FoldingContext &, const Shape &);
Constant<ExtentType> AsConstantShape(const ConstantSubscripts &);
+// AsConstantExtents returns a constant shape. It may contain
+// invalid negative extents; use HasNegativeExtent() to check.
ConstantSubscripts AsConstantExtents(const Constant<ExtentType> &);
std::optional<ConstantSubscripts> AsConstantExtents(
FoldingContext &, const Shape &);
@@ -53,6 +55,7 @@ inline std::optional<ConstantSubscripts> AsConstantExtents(
}
return std::nullopt;
}
+
Shape AsShape(const ConstantSubscripts &);
std::optional<Shape> AsShape(const std::optional<ConstantSubscripts> &);
@@ -283,14 +286,18 @@ std::optional<Constant<ExtentType>> GetConstantShape(
}
}
+// Combines GetShape and AsConstantExtents; only returns valid shapes.
template <typename A>
std::optional<ConstantSubscripts> GetConstantExtents(
FoldingContext &context, const A &x) {
if (auto shape{GetShape(context, x, /*invariantOnly=*/true)}) {
- return AsConstantExtents(context, *shape);
- } else {
- return std::nullopt;
+ if (auto extents{AsConstantExtents(context, *shape)}) {
+ if (!HasNegativeExtent(*extents)) {
+ return extents;
+ }
+ }
}
+ return std::nullopt;
}
// Get shape that does not depends on callee scope symbols if the expression
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index ec5fc7ab014856..669efb41b03442 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -1154,7 +1154,7 @@ bool IsExpandableScalar(const Expr<T> &expr, FoldingContext &context,
const Shape &shape, bool admitPureCall = false) {
if (UnexpandabilityFindingVisitor{admitPureCall}(expr)) {
auto extents{AsConstantExtents(context, shape)};
- return extents && GetSize(*extents) == 1;
+ return extents && !HasNegativeExtent(*extents) && GetSize(*extents) == 1;
} else {
return true;
}
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 998378c44cdf41..726a0ab35ede4b 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -432,7 +432,8 @@ std::optional<Expr<SomeType>> NonPointerInitializationExpr(const Symbol &symbol,
"Implied-shape parameter '%s' has rank %d but its initializer has rank %d"_err_en_US,
symbol.name(), symRank, folded.Rank());
}
- } else if (auto extents{AsConstantExtents(context, symTS->shape())}) {
+ } else if (auto extents{AsConstantExtents(context, symTS->shape())};
+ extents && !HasNegativeExtent(*extents)) {
if (folded.Rank() == 0 && symRank == 0) {
// symbol and constant are both scalars
return {std::move(folded)};
diff --git a/flang/lib/Evaluate/fold-designator.cpp b/flang/lib/Evaluate/fold-designator.cpp
index 0d8c22fb297708..d7751ec3899173 100644
--- a/flang/lib/Evaluate/fold-designator.cpp
+++ b/flang/lib/Evaluate/fold-designator.cpp
@@ -220,7 +220,8 @@ static std::optional<ArrayRef> OffsetToArrayRef(FoldingContext &context,
Shape lbs{GetRawLowerBounds(context, entity)};
auto lower{AsConstantExtents(context, lbs)};
auto elementBytes{ToInt64(elementType.MeasureSizeInBytes(context, true))};
- if (!extents || !lower || !elementBytes || *elementBytes <= 0) {
+ if (!extents || HasNegativeExtent(*extents) || !lower || !elementBytes ||
+ *elementBytes <= 0) {
return std::nullopt;
}
int rank{GetRank(shape)};
diff --git a/flang/lib/Evaluate/fold-implementation.h b/flang/lib/Evaluate/fold-implementation.h
index c82995c38f79f7..310425cefb8d87 100644
--- a/flang/lib/Evaluate/fold-implementation.h
+++ b/flang/lib/Evaluate/fold-implementation.h
@@ -899,51 +899,66 @@ template <typename T> Expr<T> Folder<T>::RESHAPE(FunctionRef<T> &&funcRef) {
std::optional<std::vector<ConstantSubscript>> shape{
GetIntegerVector<ConstantSubscript>(args[1])};
std::optional<std::vector<int>> order{GetIntegerVector<int>(args[3])};
- if (!source || !shape || (args[2] && !pad) || (args[3] && !order)) {
- return Expr<T>{std::move(funcRef)}; // Non-constant arguments
- } else if (shape.value().size() > common::maxRank) {
- context_.messages().Say(
- "Size of 'shape=' argument must not be greater than %d"_err_en_US,
- common::maxRank);
- } else if (HasNegativeExtent(shape.value())) {
- context_.messages().Say(
- "'shape=' argument must not have a negative extent"_err_en_US);
- } else {
- std::optional<uint64_t> optResultElement{TotalElementCount(shape.value())};
- if (!optResultElement) {
+ std::optional<uint64_t> optResultElement;
+ std::optional<std::vector<int>> dimOrder;
+ bool ok{true};
+ if (shape) {
+ if (shape->size() > common::maxRank) {
+ context_.messages().Say(
+ "Size of 'shape=' argument (%zd) must not be greater than %d"_err_en_US,
+ shape->size(), common::maxRank);
+ ok = false;
+ } else if (HasNegativeExtent(*shape)) {
context_.messages().Say(
- "'shape=' argument has too many elements"_err_en_US);
+ "'shape=' argument (%s) must not have a negative extent"_err_en_US,
+ DEREF(args[1]->UnwrapExpr()).AsFortran());
+ ok = false;
} else {
- int rank{GetRank(shape.value())};
- uint64_t resultElements{*optResultElement};
- std::optional<std::vector<int>> dimOrder;
- if (order) {
- dimOrder = ValidateDimensionOrder(rank, *order);
- }
- std::vector<int> *dimOrderPtr{dimOrder ? &dimOrder.value() : nullptr};
- if (order && !dimOrder) {
+ optResultElement = TotalElementCount(*shape);
+ if (!optResultElement) {
context_.messages().Say(
- "Invalid 'order=' argument in RESHAPE"_err_en_US);
- } else if (resultElements > source->size() && (!pad || pad->empty())) {
+ "'shape=' argument (%s) specifies an array with too many elements"_err_en_US,
+ DEREF(args[1]->UnwrapExpr()).AsFortran());
+ ok = false;
+ }
+ }
+ if (order) {
+ dimOrder = ValidateDimensionOrder(GetRank(*shape), *order);
+ if (!dimOrder) {
context_.messages().Say(
- "Too few elements in 'source=' argument and 'pad=' "
- "argument is not present or has null size"_err_en_US);
- } else {
- Constant<T> result{!source->empty() || !pad
- ? source->Reshape(std::move(shape.value()))
- : pad->Reshape(std::move(shape.value()))};
- ConstantSubscripts subscripts{result.lbounds()};
- auto copied{result.CopyFrom(*source,
- std::min(static_cast<uint64_t>(source->size()), resultElements),
- subscripts, dimOrderPtr)};
- if (copied < resultElements) {
- CHECK(pad);
- copied += result.CopyFrom(
- *pad, resultElements - copied, subscripts, dimOrderPtr);
- }
- CHECK(copied == resultElements);
- return Expr<T>{std::move(result)};
+ "Invalid 'order=' argument (%s) in RESHAPE"_err_en_US,
+ DEREF(args[3]->UnwrapExpr()).AsFortran());
+ ok = false;
+ }
+ }
+ }
+ if (!ok) {
+ // convert into an invalid intrinsic procedure call below
+ } else if (!source || !shape || (args[2] && !pad) || (args[3] && !order)) {
+ return Expr<T>{std::move(funcRef)}; // Non-constant arguments
+ } else {
+ uint64_t resultElements{*optResultElement};
+ std::vector<int> *dimOrderPtr{dimOrder ? &dimOrder.value() : nullptr};
+ if (resultElements > source->size() && (!pad || pad->empty())) {
+ context_.messages().Say(
+ "Too few elements in 'source=' argument and 'pad=' "
+ "argument is not present or has null size"_err_en_US);
+ ok = false;
+ } else {
+ Constant<T> result{!source->empty() || !pad
+ ? source->Reshape(std::move(shape.value()))
+ : pad->Reshape(std::move(shape.value()))};
+ ConstantSubscripts subscripts{result.lbounds()};
+ auto copied{result.CopyFrom(*source,
+ std::min(static_cast<uint64_t>(source->size()), resultElements),
+ subscripts, dimOrderPtr)};
+ if (copied < resultElements) {
+ CHECK(pad);
+ copied += result.CopyFrom(
+ *pad, resultElements - copied, subscripts, dimOrderPtr);
}
+ CHECK(copied == resultElements);
+ return Expr<T>{std::move(result)};
}
}
// Invalid, prevent re-folding
@@ -1398,7 +1413,8 @@ AsFlatArrayConstructor(const Expr<SomeKind<CAT>> &expr) {
template <typename T>
std::optional<Expr<T>> FromArrayConstructor(
FoldingContext &context, ArrayConstructor<T> &&values, const Shape &shape) {
- if (auto constShape{AsConstantExtents(context, shape)}) {
+ if (auto constShape{AsConstantExtents(context, shape)};
+ constShape && !HasNegativeExtent(*constShape)) {
Expr<T> result{Fold(context, Expr<T>{std::move(values)})};
if (auto *constant{UnwrapConstantValue<T>(result)}) {
// Elements and shape are both constant.
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index 9e180605c1b3bd..9091c6b49c20e1 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -1584,7 +1584,8 @@ const std::optional<parser::Name> &MaybeGetNodeName(
std::optional<ArraySpec> ToArraySpec(
evaluate::FoldingContext &context, const evaluate::Shape &shape) {
- if (auto extents{evaluate::AsConstantExtents(context, shape)}) {
+ if (auto extents{evaluate::AsConstantExtents(context, shape)};
+ extents && !evaluate::HasNegativeExtent(*extents)) {
ArraySpec result;
for (const auto &extent : *extents) {
result.emplace_back(ShapeSpec::MakeExplicit(Bound{extent}));
diff --git a/flang/test/Semantics/bug122060.f90 b/flang/test/Semantics/bug122060.f90
new file mode 100644
index 00000000000000..ef0fbcf3acb1e9
--- /dev/null
+++ b/flang/test/Semantics/bug122060.f90
@@ -0,0 +1,6 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+integer, parameter :: a = -10
+! ERROR: Assignment to constant 'a' is not allowed
+! ERROR: 'shape=' argument ([INTEGER(4)::-10_4]) must not have a negative extent
+a = b() - reshape([c], [a])
+END
diff --git a/flang/test/Semantics/reshape.f90 b/flang/test/Semantics/reshape.f90
index b3b96985affc7a..d1a72070a3fb9d 100644
--- a/flang/test/Semantics/reshape.f90
+++ b/flang/test/Semantics/reshape.f90
@@ -20,13 +20,13 @@ program reshaper
integer :: array8(2,3) = RESHAPE([(n, n=1,4)], [2,3], [99_8])
!ERROR: Actual argument for 'pad=' has bad type or kind 'REAL(4)'
real :: array9(2,3) = RESHAPE([(n, n=1,4)], [2,3], [99.9])
- !ERROR: Invalid 'order=' argument in RESHAPE
+ !ERROR: Invalid 'order=' argument ([INTEGER(4)::2_4,3_4]) in RESHAPE
real :: array10(2,3) = RESHAPE([(n,n=1,4)],[2,3],[99],[2,3])
!ERROR: Actual argument for 'order=' has bad type 'REAL(4)'
real :: array11(2,3) = RESHAPE([(n, n=1,4)], [2,3], [99], [2.2,3.3])
- !ERROR: Invalid 'order=' argument in RESHAPE
+ !ERROR: Invalid 'order=' argument ([INTEGER(4)::1_4]) in RESHAPE
real :: array12(2,3) = RESHAPE([(n, n=1,4)], [2,3], [99], [1])
- !ERROR: Invalid 'order=' argument in RESHAPE
+ !ERROR: Invalid 'order=' argument ([INTEGER(4)::1_4,1_4]) in RESHAPE
real :: array13(2,3) = RESHAPE([(n, n = 1, 4)], [2, 3], [99], [1, 1])
! Examples that have caused problems
@@ -50,14 +50,14 @@ program reshaper
integer, parameter :: array22(2) = RESHAPE(array21, [2])
integer(8), parameter :: huge_shape(2) = [I64_MAX, I64_MAX]
- !ERROR: 'shape=' argument has too many elements
+ !ERROR: 'shape=' argument ([INTEGER(8)::9223372036854775807_8,9223372036854775807_8]) specifies an array with too many elements
integer :: array23(I64_MAX, I64_MAX) = RESHAPE([1, 2, 3], huge_shape)
- !ERROR: Size of 'shape=' argument must not be greater than 15
+ !ERROR: Size of 'shape=' argument (16) must not be greater than 15
CALL ext_sub(RESHAPE([(n, n=1,20)], &
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]))
!ERROR: Reference to the procedure 'ext_sub' has an implicit interface that is distinct from another reference: incompatible dummy argument #1: incompatible dummy data object shapes
- !ERROR: 'shape=' argument must not have a negative extent
+ !ERROR: 'shape=' argument ([INTEGER(4)::1_4,-5_4,3_4]) must not have a negative extent
CALL ext_sub(RESHAPE([(n, n=1,20)], [1, -5, 3]))
!ERROR: 'order=' argument has unacceptable rank 2
array20 = RESHAPE([(n, n = 1, 4)], [2, 3], order = bad_order)
``````````
</details>
https://github.com/llvm/llvm-project/pull/122604
More information about the flang-commits
mailing list