[flang-commits] [flang] [flang] Add parser support for explicit-shape-bounds-spec (PR #188447)
via flang-commits
flang-commits at lists.llvm.org
Wed Mar 25 02:37:07 PDT 2026
https://github.com/ivanrodriguez3753 created https://github.com/llvm/llvm-project/pull/188447
Implement parse-tree rewrite in Sema. Emit TODO messages for semantic analysis, while still exiting gracefully and printing other error messages when applicable. `future_ERROR` messages in `LIT` test indicate expected error messages once semantic analysis is implemented in an upcoming PR.
>From cf019c7ce0319ea2cfd11dec45b58c550498f2c0 Mon Sep 17 00:00:00 2001
From: Ivan Rodriguez <ivan.rodriguez at hpe.com>
Date: Tue, 24 Mar 2026 21:39:31 -0500
Subject: [PATCH] Implement parser type and tree rewrite for
assumed-shape-bounds-spec
---
flang/include/flang/Parser/dump-parse-tree.h | 1 +
flang/include/flang/Parser/parse-tree.h | 19 ++++-
flang/lib/Parser/unparse.cpp | 1 +
flang/lib/Semantics/resolve-names-utils.cpp | 76 ++++++++++++++++-
.../declaration_explicit_array_bounds.f90 | 85 +++++++++++++++++++
5 files changed, 176 insertions(+), 6 deletions(-)
create mode 100644 flang/test/Semantics/declaration_explicit_array_bounds.f90
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 84c7b8d2a5349..9f4a826fe3f6a 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -346,6 +346,7 @@ class ParseTreeDumper {
NODE(parser, ExitStmt)
NODE(parser, ExplicitCoshapeSpec)
NODE(parser, ExplicitShapeSpec)
+ NODE(parser, ExplicitShapeBoundsSpec)
NODE(parser, Expr)
NODE(Expr, Parentheses)
NODE(Expr, UnaryPlus)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index a0106cac84620..23e965366a299 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1324,13 +1324,24 @@ WRAPPER_CLASS(ImpliedShapeSpec, std::list<AssumedImpliedSpec>);
EMPTY_CLASS(AssumedRankSpec);
// R815 array-spec ->
-// explicit-shape-spec-list | assumed-shape-spec-list |
-// deferred-shape-spec-list | assumed-size-spec | implied-shape-spec |
+// explicit-shape-spec-list | explicit-shape-bounds-spec |
+// assumed-shape-spec-list | deferred-shape-spec-list |
+// assumed-size-spec | implied-shape-spec |
// implied-shape-or-assumed-size-spec | assumed-rank-spec
+
+using ExplicitBoundsExpr = IntExpr;
+
+struct ExplicitShapeBoundsSpec {
+ TUPLE_CLASS_BOILERPLATE(ExplicitShapeBoundsSpec);
+ std::tuple<std::optional<ExplicitBoundsExpr>, ExplicitBoundsExpr>
+ t;
+};
+
struct ArraySpec {
UNION_CLASS_BOILERPLATE(ArraySpec);
- std::variant<std::list<ExplicitShapeSpec>, std::list<AssumedShapeSpec>,
- DeferredShapeSpecList, AssumedSizeSpec, ImpliedShapeSpec, AssumedRankSpec>
+ std::variant<std::list<ExplicitShapeSpec>, ExplicitShapeBoundsSpec,
+ std::list<AssumedShapeSpec>, DeferredShapeSpecList,
+ AssumedSizeSpec, ImpliedShapeSpec, AssumedRankSpec>
u;
};
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index c31eac0b3ff68..91b5bf8adf37c 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -571,6 +571,7 @@ class UnparseVisitor {
common::visit(
common::visitors{
[&](const std::list<ExplicitShapeSpec> &y) { Walk(y, ","); },
+ [&](const ExplicitShapeBoundsSpec &y) { /*TODO: unreachable until after semantic analysis is done */ },
[&](const std::list<AssumedShapeSpec> &y) { Walk(y, ","); },
[&](const DeferredShapeSpecList &y) { Walk(y); },
[&](const AssumedSizeSpec &y) { Walk(y); },
diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index ef34c89182f7f..ad454620d9973 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -203,6 +203,7 @@ class ArraySpecAnalyzer {
}
void Analyze(const parser::AssumedShapeSpec &);
void Analyze(const parser::ExplicitShapeSpec &);
+ void Analyze(const parser::ExplicitShapeBoundsSpec &);
void Analyze(const parser::AssumedImpliedSpec &);
void Analyze(const parser::DeferredShapeSpecList &);
void Analyze(const parser::AssumedRankSpec &);
@@ -237,15 +238,79 @@ ArraySpec ArraySpecAnalyzer::Analyze(const parser::ComponentArraySpec &x) {
CHECK(!arraySpec_.empty());
return arraySpec_;
}
+
+static bool shouldRewriteShapeSpecListToExplicitBounds(
+ SemanticsContext &context, const parser::ArraySpec &x) {
+ auto &explicitShapeSpecList{std::get<std::list<parser::ExplicitShapeSpec>>(
+ const_cast<parser::ArraySpec&>(x).u)};
+
+ if (explicitShapeSpecList.size() != 1) {
+ return false;
+ }
+
+ auto &explicitShapeSpec{explicitShapeSpecList.front()};
+ const auto &upperBound{std::get<1>(explicitShapeSpec.t)};
+ const auto &lowerBoundOpt{std::get<0>(explicitShapeSpec.t)};
+
+ bool foundArray{false};
+
+ if (MaybeExpr analyzedExpr = AnalyzeExpr(context, upperBound.v.thing.thing.value());
+ analyzedExpr && (analyzedExpr->Rank() > 0)) {
+ foundArray = true;
+ }
+
+ if (lowerBoundOpt) {
+ const auto &lowerBound{*lowerBoundOpt};
+ if (MaybeExpr analyzedExpr = AnalyzeExpr(context, lowerBound.v.thing.thing.value());
+ analyzedExpr && (analyzedExpr->Rank() > 0)) {
+ foundArray = true;
+ }
+ }
+
+ return foundArray;
+}
+
+static void rewriteShapeSpecListToExplicitBounds(const parser::ArraySpec &x) {
+ auto &explicitShapeSpecList{std::get<std::list<parser::ExplicitShapeSpec>>(
+ const_cast<parser::ArraySpec&>(x).u)};
+ auto &mutableArraySpec{const_cast<parser::ArraySpec&>(x)};
+ auto &mutableExplicitShapeSpec{explicitShapeSpecList.front()};
+
+ auto &mutableUpperBound{std::get<1>(mutableExplicitShapeSpec.t)};
+ parser::IntExpr upperIntExpr{std::move(mutableUpperBound.v.thing)};
+
+ auto &lowerBoundOpt{std::get<0>(mutableExplicitShapeSpec.t)};
+ std::optional<parser::IntExpr> lowerIntExpr;
+ if (lowerBoundOpt) {
+ auto &mutableLowerBound{std::get<0>(mutableExplicitShapeSpec.t)};
+ if(mutableLowerBound) {
+ lowerIntExpr = std::move(mutableLowerBound->v.thing);
+ }
+ }
+
+ parser::ExplicitShapeBoundsSpec boundsSpec{
+ std::make_tuple(std::move(lowerIntExpr), std::move(upperIntExpr))};
+ mutableArraySpec.u = std::move(boundsSpec);
+
+ return;
+}
+
ArraySpec ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
+ if(std::get_if<std::list<parser::ExplicitShapeSpec>>(&x.u) &&
+ shouldRewriteShapeSpecListToExplicitBounds(context_, x)) {
+ rewriteShapeSpecListToExplicitBounds(x);
+ }
common::visit(common::visitors{
[&](const parser::AssumedSizeSpec &y) {
Analyze(
std::get<std::list<parser::ExplicitShapeSpec>>(y.t));
Analyze(std::get<parser::AssumedImpliedSpec>(y.t));
},
- [&](const parser::ImpliedShapeSpec &y) { Analyze(y.v); },
- [&](const auto &y) { Analyze(y); },
+ [&](const parser::ImpliedShapeSpec &y) {
+ Analyze(y.v); },
+ [&](const auto &y) {
+ Analyze(y);
+ },
},
x.u);
CHECK(!arraySpec_.empty());
@@ -279,6 +344,13 @@ void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeSpec &x) {
MakeExplicit(std::get<std::optional<parser::SpecificationExpr>>(x.t),
std::get<parser::SpecificationExpr>(x.t));
}
+
+void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeBoundsSpec &x) {
+ context_.Say("TODO: Analyze overload for ExplicitShapeBoundsSpec"_err_en_US);
+ // prevent CHECK abort in Analyze(ArraySpec), otherwise it'll abort before printing error message
+ arraySpec_.push_back(ShapeSpec::MakeExplicit(Bound{1}));
+}
+
void ArraySpecAnalyzer::Analyze(const parser::AssumedImpliedSpec &x) {
MakeImplied(x.v);
}
diff --git a/flang/test/Semantics/declaration_explicit_array_bounds.f90 b/flang/test/Semantics/declaration_explicit_array_bounds.f90
new file mode 100644
index 0000000000000..385b20028d895
--- /dev/null
+++ b/flang/test/Semantics/declaration_explicit_array_bounds.f90
@@ -0,0 +1,85 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1 -Wautomatic-in-main-program -Wsaved-local-in-spec-expr
+! An explicit-shape-spec or explicit-shape-bounds-spec whose bounds are not constant expressions
+! shall appear only in a subprogram, derived type definition, BLOCK construct, or interface body.
+module data
+ integer :: rank1_array_module(3) = [5, 5, 5]
+ !future_ERROR: Automatic data object 'gg2' may not appear in a module
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: gg2(rank1_array_module)
+ integer, allocatable :: nonconstsize(:)
+ !future_ERROR: Rank-1 integer array used as lower bounds in DECLARATION must have constant size
+ !future_ERROR: Rank-1 integer array used as upper bounds in DECLARATION must have constant size
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: gg3(nonconstsize : nonconstsize)
+end module
+program declaration_array_bounds
+ implicit none
+
+ ! Valid cases (no errors expected)
+
+ ! Array upper bound only
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: c([3, 4, 5])
+
+ ! Array lower and upper bounds, same size
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: d((/2, 3/) : [10, 20])
+
+ ! Scalar lower, array upper
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: e(2 : [10, 20])
+
+ ! Array lower, scalar upper
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: f([2, 3] : 10)
+
+ ! Using non-literal PARAMETER variables
+ integer, parameter :: rank1_parameter_array(3) = [5,5,5]
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: g(rank1_parameter_array)
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: ggg(rank1_parameter_array * 2 : rank1_parameter_array - 1)
+
+
+ ! Negative cases (erros expected)
+ integer :: rank1_array(3) = [5,5,5]
+ ! Use existing error message for constness checking
+ !future_PORTABILITY: specification expression refers to local object 'rank1_array' (initialized and saved) [-Wsaved-local-in-spec-expr]
+ !future_PORTABILITY: Automatic data object 'gg' should not appear in the specification part of a main program [-Wautomatic-in-main-program]
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: gg(rank1_array)
+ integer :: scalar
+ !future_ERROR: Invalid specification expression: reference to local entity 'scalar'
+ !future_PORTABILITY: Automatic data object 'gggg' should not appear in the specification part of a main program [-Wautomatic-in-main-program]
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: gggg(rank1_parameter_array : scalar)
+
+ !ERROR: Must have INTEGER type, but is REAL(4)
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: h([1.2,2.2,3.2]:[1,2,3])
+ !future_ERROR: DECLARATION bounds integer rank-1 arrays must have the same size; lower bounds has 3 elements, upper bounds has 2 elements
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: i([1,2,3]:[3,3])
+
+ ! Test error for rank > 1, fulfilling constness
+ integer, parameter :: rank2_parameter_array(2,2) = reshape([[1,2],[3,4]], [2,2])
+ !future_ERROR: Integer array used as upper bounds in DECLARATION must be rank-1 but is rank-2
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: j(rank2_parameter_array)
+ ! Test combined bounds error, first bound as before but second bound as wrong rank
+ ! and nonconst
+ integer :: rank3_array(2,2,2)
+ !future_ERROR: Integer array used as lower bounds in DECLARATION must be rank-1 but is rank-2
+ !future_ERROR: Integer array used as upper bounds in DECLARATION must be rank-1 but is rank-3
+ !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+ integer :: k(rank2_parameter_array : rank3_array)
+
+ ! Test that any comma list is parsed as ExplicitShapeSpecList and not rewritten
+ ! to ExplicitShapeBonudsSpec, giving error messages expecting same number of
+ ! aruments as rank of test_array and scalar integers
+ !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)
+ integer :: test_array([1,2,3] : [2,3,4], 3, [1,2,3], 5.2)
+end program
\ No newline at end of file
More information about the flang-commits
mailing list