[flang-commits] [flang] [flang][semantic] parser node types and rewrite for allocate-shape-bounds-spec (PR #188445)
via flang-commits
flang-commits at lists.llvm.org
Mon Jul 27 06:19:13 PDT 2026
https://github.com/ivanrodriguez3753 updated https://github.com/llvm/llvm-project/pull/188445
>From fc88926fb52394aa5d789ba9a340e4e6da970989 Mon Sep 17 00:00:00 2001
From: Ivan Rodriguez <ivan.rodriguez at hpe.com>
Date: Mon, 27 Jul 2026 08:18:41 -0500
Subject: [PATCH] [flang][semantic] parser node types and rewrite for
allocate-shape-bounds-spec
This commit lays the groundwork for semantic analysis of rank-1 integer array expressions being used as bounds in an allocate statement.
This should strongly resemble the changes for explicit-shape-bounds-spec and assumed-shape-bounds spec in f5a4294 and e200ea6, respectively.
---
flang/include/flang/Parser/dump-parse-tree.h | 2 +
flang/include/flang/Parser/parse-tree.h | 39 ++++++-
flang/include/flang/Semantics/expression.h | 5 +
flang/lib/Lower/Allocatable.cpp | 2 +-
flang/lib/Parser/unparse.cpp | 2 +-
flang/lib/Semantics/check-allocate.cpp | 18 ++-
flang/lib/Semantics/expression.cpp | 67 +++++++++++
.../test/Semantics/allocate_array_bounds.f90 | 105 ++++++++++++++++++
8 files changed, 233 insertions(+), 7 deletions(-)
create mode 100644 flang/test/Semantics/allocate_array_bounds.f90
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 29797b7d088a7..0a6498d682519 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -156,6 +156,8 @@ class ParseTreeDumper {
NODE(parser, AllocateCoarraySpec)
NODE(parser, AllocateObject)
NODE(parser, AllocateShapeSpec)
+ NODE(parser, AllocateShapeBoundsSpec)
+ NODE(parser, AllocateShapeSpecListOrBounds)
NODE(parser, AllocateStmt)
NODE(parser, Allocation)
NODE(parser, AltReturnSpec)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 283d7f4aa7c26..4d6429171e9f5 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1979,10 +1979,45 @@ struct AllocateCoarraySpec {
// R932 allocation ->
// allocate-object [( allocate-shape-spec-list )]
-// [lbracket allocate-coarray-spec rbracket]
+// [lbracket allocate-coarray-spec rbracket] |
+// ( [ lower-bounds-expr : ] upper-bounds-expr )
+// [ lbracket allocate-coarray-spec rbracket ]
+// The 2023 spec has a typo, as well as a deviation from the
+// similar explicit-shape-bounds-spec and assumed-shape-bounds-spec
+// rules for array-spec. The typo is that it's missing an allocate-object
+// for the second rule. The deviation is that array-spec has rules
+// differentiating the bound-list versus bounds-spec, while this
+// allocation rule has allocate-shape-spec-list as part of the first rule,
+// and what would be allocate-shape-bounds-spec written inline as
+// [ lower-bounds-expr : ] upper-bounds-expr
+// Altogether, we can use the following grammar:
+// R933 allocation ->
+// allocate-object [ ( allocate-shape-spec-list-or-bounds ) ]
+// [ lbracket allocate-coarray-spec rbracket ] |
+// allocate-shape-spec-list-or-bounds ->
+// allocate-shape-spec-list |
+// allocate-shape-bounds-spec
+// allocate-shape-bounds-spec ->
+// [ lower-bounds-expr : ] upper-bounds-expr
+
+using BoundsExpr = IntExpr;
+
+struct AllocateShapeBoundsSpec {
+ TUPLE_CLASS_BOILERPLATE(AllocateShapeBoundsSpec);
+ std::tuple<
+ std::optional<BoundsExpr>,
+ BoundsExpr>
+ t;
+};
+
+struct AllocateShapeSpecListOrBounds {
+ UNION_CLASS_BOILERPLATE(AllocateShapeSpecListOrBounds);
+ std::variant<std::list<AllocateShapeSpec>, AllocateShapeBoundsSpec> u;
+};
+
struct Allocation {
TUPLE_CLASS_BOILERPLATE(Allocation);
- std::tuple<AllocateObject, std::list<AllocateShapeSpec>,
+ std::tuple<AllocateObject, AllocateShapeSpecListOrBounds,
std::optional<AllocateCoarraySpec>>
t;
};
diff --git a/flang/include/flang/Semantics/expression.h b/flang/include/flang/Semantics/expression.h
index 3a19124985bc9..28e1dd5742c66 100644
--- a/flang/include/flang/Semantics/expression.h
+++ b/flang/include/flang/Semantics/expression.h
@@ -167,6 +167,7 @@ class ExpressionAnalyzer {
MaybeExpr Analyze(const parser::AllocateObject &);
MaybeExpr Analyze(const parser::PointerObject &);
MaybeExpr Analyze(const parser::ConditionalExpr &);
+ MaybeExpr Analyze(const parser::AllocateShapeSpecListOrBounds &x);
template <typename A> MaybeExpr Analyze(const common::Indirection<A> &x) {
return Analyze(x.value());
@@ -513,6 +514,10 @@ class ExprChecker {
AnalyzeAndNoteUses(x, /*isDefinition=*/true);
return false;
}
+ bool Pre(const parser::AllocateShapeSpecListOrBounds &x) {
+ exprAnalyzer_.Analyze(x);
+ return false;
+ }
bool Pre(const parser::DataStmtObject &);
void Post(const parser::DataStmtObject &);
bool Pre(const parser::DataImpliedDo &);
diff --git a/flang/lib/Lower/Allocatable.cpp b/flang/lib/Lower/Allocatable.cpp
index f51342b27a19d..ff57e8de86810 100644
--- a/flang/lib/Lower/Allocatable.cpp
+++ b/flang/lib/Lower/Allocatable.cpp
@@ -327,7 +327,7 @@ class AllocateStmtHelper {
return unwrapSymbol(getAllocObj());
}
const std::list<Fortran::parser::AllocateShapeSpec> &getShapeSpecs() const {
- return std::get<std::list<Fortran::parser::AllocateShapeSpec>>(alloc.t);
+ return std::get<std::list<Fortran::parser::AllocateShapeSpec>>(std::get<Fortran::parser::AllocateShapeSpecListOrBounds>(alloc.t).u);
}
};
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 23d04f2e3ea42..0cd26fbfa065a 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -877,7 +877,7 @@ class UnparseVisitor {
}
void Unparse(const Allocation &x) { // R932
Walk(std::get<AllocateObject>(x.t));
- Walk("(", std::get<std::list<AllocateShapeSpec>>(x.t), ",", ")");
+ Walk("(", std::get<std::list<AllocateShapeSpec>>(std::get<AllocateShapeSpecListOrBounds>(x.t).u), ",", ")");
Walk("[", std::get<std::optional<AllocateCoarraySpec>>(x.t), "]");
}
void Unparse(const AllocateShapeSpec &x) { // R934 & R938
diff --git a/flang/lib/Semantics/check-allocate.cpp b/flang/lib/Semantics/check-allocate.cpp
index 7f099d51221c0..380d4816b75b6 100644
--- a/flang/lib/Semantics/check-allocate.cpp
+++ b/flang/lib/Semantics/check-allocate.cpp
@@ -47,6 +47,7 @@ class AllocationCheckerHelper {
const parser::Allocation &alloc, AllocateCheckerInfo &info)
: allocateInfo_{info}, allocation_{alloc},
allocateObject_{std::get<parser::AllocateObject>(alloc.t)},
+ isArray(IsArray(alloc)),
allocateShapeSpecRank_{ShapeSpecRank(alloc)},
allocateCoarraySpecRank_{CoarraySpecRank(alloc)} {}
@@ -57,9 +58,15 @@ class AllocationCheckerHelper {
bool hasAllocateCoarraySpec() const { return allocateCoarraySpecRank_ != 0; }
bool RunCoarrayRelatedChecks(SemanticsContext &) const;
- static int ShapeSpecRank(const parser::Allocation &allocation) {
+ static bool IsArray(const parser::Allocation &allocation) {
+ const auto &listOrBounds{std::get<parser::AllocateShapeSpecListOrBounds>(allocation.t)};
+ return std::get_if<parser::AllocateShapeBoundsSpec>(&listOrBounds.u);
+ }
+
+ int ShapeSpecRank(const parser::Allocation &allocation) {
+ if(isArray) return 1; // just need hasAllocateShapeSpecList to return false
return static_cast<int>(
- std::get<std::list<parser::AllocateShapeSpec>>(allocation.t).size());
+ std::get<std::list<parser::AllocateShapeSpec>>(std::get<parser::AllocateShapeSpecListOrBounds>(allocation.t).u).size());
}
static int CoarraySpecRank(const parser::Allocation &allocation) {
@@ -91,6 +98,7 @@ class AllocationCheckerHelper {
AllocateCheckerInfo &allocateInfo_;
const parser::Allocation &allocation_;
const parser::AllocateObject &allocateObject_;
+ const bool isArray{false};
const int allocateShapeSpecRank_{0};
const int allocateCoarraySpecRank_{0};
const parser::Name &name_{parser::GetLastName(allocateObject_)};
@@ -600,6 +608,10 @@ bool AllocationCheckerHelper::RunChecks(SemanticsContext &context) {
}
} else {
// explicit shape-spec-list
+ if(isArray) {
+ context.Say("TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp"_err_en_US);
+ return false;
+ }
if (allocateShapeSpecRank_ != rank_) {
context
.Say(name_.source,
@@ -612,7 +624,7 @@ bool AllocationCheckerHelper::RunChecks(SemanticsContext &context) {
static_cast<std::size_t>(allocateShapeSpecRank_)) {
std::size_t j{0};
for (const auto &shapeSpec :
- std::get<std::list<parser::AllocateShapeSpec>>(allocation_.t)) {
+ std::get<std::list<parser::AllocateShapeSpec>>(std::get<parser::AllocateShapeSpecListOrBounds>(allocation_.t).u)) {
if (j >= allocateInfo_.sourceExprShape->size()) {
break;
}
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index fc57cc43e981c..9e22daa423eff 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -4714,6 +4714,73 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::PointerObject &x) {
return ExprOrVariable(x, parser::FindSourceLocation(x));
}
+MaybeExpr ExpressionAnalyzer::Analyze(const parser::AllocateShapeSpecListOrBounds &x) {
+ auto &shapeSpecList{
+ std::get<std::list<parser::AllocateShapeSpec>>(x.u)};
+ if(shapeSpecList.size() == 0) {
+ return std::nullopt;
+ }
+
+ if(shapeSpecList.size() == 1) {
+ // Get upper bound - BoundExpr is Scalar<Integer<Indirection<Expr>>>
+ const auto &upperBound{std::get<1>(shapeSpecList.front().t)};
+ const auto &lowerBoundOpt = std::get<0>(shapeSpecList.front().t);
+ const auto *lowerBound = lowerBoundOpt ? &*lowerBoundOpt : nullptr;
+
+ bool foundArray{false};
+ // We want to rewrite as an AllocateShapeBoundsSpec even if
+ // the element type is wrong (say a real instead of integer), so
+ // analyze as an unwrapped Expr for its rank, then analyze as
+ // an Integer<Indirection<Expr>>.
+ if(MaybeExpr analyzedExpr = Analyze(upperBound.thing.thing.value());
+ analyzedExpr && (analyzedExpr->Rank() > 0)) {
+ foundArray = true;
+ Analyze(upperBound.thing);
+ }
+ if(lowerBound) {
+ if(MaybeExpr analyzedExpr = Analyze(lowerBound->thing.thing.value());
+ analyzedExpr && analyzedExpr->Rank() > 0) {
+ foundArray = true;
+ Analyze(lowerBound->thing);
+ }
+ }
+
+ if(foundArray) {
+ // Get the IntExpr from the upper bound (BoundExpr.thing is the IntExpr)
+ auto &mutableUpperBound{const_cast<parser::BoundExpr&>(upperBound)};
+ parser::IntExpr upperIntExpr{std::move(mutableUpperBound.thing)};
+
+ // Handle optional lower bound
+ std::optional<parser::IntExpr> lowerIntExpr;
+ if(lowerBound) {
+ auto &mutableLowerBound{const_cast<parser::BoundExpr&>(*lowerBound)};
+ lowerIntExpr = std::move(mutableLowerBound.thing);
+ }
+
+ parser::AllocateShapeBoundsSpec boundsExpr{
+ std::make_tuple(std::move(lowerIntExpr), std::move(upperIntExpr))};
+ auto &mutableListOrBounds{const_cast<parser::AllocateShapeSpecListOrBounds&>(x)};
+ mutableListOrBounds.u = std::move(boundsExpr);
+
+ // Say("TODO: AllocateShapeBoundsSpec semantic chekcs in check-allocate.cpp"_err_en_US);
+ return std::nullopt;
+ }
+ }
+
+ // Analyze each AllocateShapeSpec, as a Scalar<Int<Expr>>
+ for(auto it = shapeSpecList.begin(); it != shapeSpecList.end(); ++it) {
+ const auto &upperBound{std::get<1>(it->t)};
+ Analyze(upperBound);
+ const auto &lowerBoundOpt{std::get<0>(it->t)};
+ if(lowerBoundOpt) {
+ Analyze(*lowerBoundOpt);
+ }
+ }
+
+ return std::nullopt;
+}
+
+
Expr<SubscriptInteger> ExpressionAnalyzer::AnalyzeKindSelector(
TypeCategory category,
const std::optional<parser::KindSelector> &selector) {
diff --git a/flang/test/Semantics/allocate_array_bounds.f90 b/flang/test/Semantics/allocate_array_bounds.f90
new file mode 100644
index 0000000000000..cfb29b7fa8661
--- /dev/null
+++ b/flang/test/Semantics/allocate_array_bounds.f90
@@ -0,0 +1,105 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+program int_array_alloc_03
+ implicit none
+ real, allocatable, dimension(:) :: rank1_test_array
+ real, allocatable, dimension(:,:,:) :: test_array
+
+ integer :: seven = 7
+ integer :: valid_lower(3) = [1,1,1]
+ integer :: lower(4), upper(4)
+ integer :: rank_2_array(3,3), rank_3_array(3,3,3)
+ ! Positive test cases, expecting no errors
+ ! Test direct use of scalar integer and array integer expressions
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(rank1_test_array([5]))
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(rank1_test_array([1]:[5]))
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(rank1_test_array(1:[5]))
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(rank1_test_array([1]:5))
+
+ ! Test indirect use of scalar integer and array integer expressions
+ ! array : array
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array([1,2,3] : [1,2,3] + 1))
+ ! array : array
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array(valid_lower - 1 : seven * (valid_lower + seven)))
+ ! array : scalar (broadcast)
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array(valid_lower : return_seven()))
+ ! scalar : array (broadcast)
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array(seven : [9,9,9]))
+
+ !Negative test cases, expecting errors
+ !ERROR: Must have INTEGER type, but is REAL(4)
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array([1.2,2.2,3.2]:[1,2,3]))
+
+ !future_ERROR: ALLOCATE bounds integer rank-1 arrays must have the same size; lower bounds has 3 elements, upper bounds has 2 elements
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array([1,2,3]:[3,3]))
+
+ !future_ERROR: ALLOCATE bounds integer rank-1 arrays have 4 elements but allocatable object 'test_array' has rank 3
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array(lower:upper))
+ !future_ERROR: ALLOCATE upper bounds integer rank-1 array has 4 elements but allocatable object 'test_array' has rank 3
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array(7 : [1,2,3,4]))
+ !future_ERROR: ALLOCATE lower bounds integer rank-1 array has 2 elements but allocatable object 'test_array' has rank 3
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array([1,2] : 7))
+
+ !future_ERROR: Integer array used as upper bounds in ALLOCATE must be rank-1 but is rank-3
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array([1,2,4] : rank_3_array))
+ !future_ERROR: Integer array used as lower bounds in ALLOCATE must be rank-1 but is rank-2
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array(rank_2_array : [1,2,4]))
+ !future_ERROR: Integer array used as lower bounds in ALLOCATE must be rank-1 but is rank-2
+ !future_ERROR: Integer array used as upper bounds in ALLOCATE must be rank-1 but is rank-3
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array(rank_2_array : rank_3_array))
+ !future_ERROR: Integer array used as lower bounds in ALLOCATE must be rank-1 but is rank-2
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array(rank_2_array : 7))
+ !future_ERROR: Integer array used as upper bounds in ALLOCATE must be rank-1 but is rank-3
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_array(7 : rank_3_array))
+
+ ! Test that any comma list is parsed as AllocateShapeSpecList and not rewritten
+ ! to AllocateShapeSpecArray, giving error messages expecting same number of
+ ! aruments as rank of test_array and scalar integers
+ !ERROR: The number of shape specifications, when they appear, must match the rank of allocatable object
+ !ERROR: Must be a scalar value, but is a rank-1 array
+ !ERROR: Must be a scalar value, but is a rank-1 array
+ !ERROR: Must be a scalar value, but is a rank-1 array
+ !ERROR: Must have INTEGER type, but is REAL(4)
+ allocate(test_array([1,2,3] : [2,3,4], 3, [1,2,3], 5.2))
+
+ contains
+ subroutine tmp02(unknown_size, test_ptr_01)
+ real, allocatable, dimension(:,:,:), INTENT(OUT) :: test_ptr_01
+ integer, INTENT(IN) :: unknown_size
+ integer :: lower(unknown_size), upper(unknown_size)
+
+ !future_ERROR: Rank-1 integer array used as upper bounds in ALLOCATE must have constant size
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_ptr_01(upper))
+ !future_ERROR: Rank-1 integer array used as lower bounds in ALLOCATE must have constant size
+ !future_ERROR: Rank-1 integer array used as upper bounds in ALLOCATE must have constant size
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_ptr_01(lower : upper))
+ !future_ERROR: Rank-1 integer array used as lower bounds in ALLOCATE must have constant size
+ !ERROR: TODO: AllocateShapeBoundsSpec semantic checks in check-allocate.cpp
+ allocate(test_ptr_01(lower : 10))
+ end subroutine
+
+ function return_seven()
+ integer :: return_seven
+ return_seven = 7
+ end function
+
+end program
More information about the flang-commits
mailing list