[flang-commits] [flang] [flang][semantic] rewrite for explicit-shape-bounds-spec (PR #188447)

via flang-commits flang-commits at lists.llvm.org
Tue Jun 9 11:59:02 PDT 2026


https://github.com/ivanrodriguez3753 updated https://github.com/llvm/llvm-project/pull/188447

>From bf37fce6eb03fd3d850c9de7d40a9b6487267218 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 1/7] Implement parser type and tree rewrite for
 explicit-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

>From 5877ba3e42c6b2718099bc0bf456710dbd12da54 Mon Sep 17 00:00:00 2001
From: Ivan Rodriguez <ivan.rodriguez at hpe.com>
Date: Tue, 31 Mar 2026 14:01:33 -0500
Subject: [PATCH 2/7] Address PR comments

---
 flang/lib/Semantics/resolve-names-utils.cpp            | 10 ++++------
 .../Semantics/declaration_explicit_array_bounds.f90    |  4 +---
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index ad454620d9973..2d5842dce8bac 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -279,13 +279,11 @@ static void rewriteShapeSpecListToExplicitBounds(const parser::ArraySpec &x) {
   auto &mutableUpperBound{std::get<1>(mutableExplicitShapeSpec.t)};
   parser::IntExpr upperIntExpr{std::move(mutableUpperBound.v.thing)};
 
-  auto &lowerBoundOpt{std::get<0>(mutableExplicitShapeSpec.t)};
+
+  auto &mutableLowerBound{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);
-    }
+  if(mutableLowerBound) {
+    lowerIntExpr = std::move(mutableLowerBound->v.thing);
   }
   
   parser::ExplicitShapeBoundsSpec boundsSpec{
diff --git a/flang/test/Semantics/declaration_explicit_array_bounds.f90 b/flang/test/Semantics/declaration_explicit_array_bounds.f90
index 385b20028d895..1c473ef177839 100644
--- a/flang/test/Semantics/declaration_explicit_array_bounds.f90
+++ b/flang/test/Semantics/declaration_explicit_array_bounds.f90
@@ -1,6 +1,4 @@
 ! 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
@@ -82,4 +80,4 @@ program declaration_array_bounds
   !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
+end program

>From 7083f32a406e4e591dd0a58db460322c82d1314f Mon Sep 17 00:00:00 2001
From: Ivan Rodriguez <ivan.rodriguez at hpe.com>
Date: Wed, 1 Apr 2026 12:06:15 -0500
Subject: [PATCH 3/7] clang-format

---
 flang/include/flang/Parser/parse-tree.h     |  9 ++---
 flang/lib/Parser/unparse.cpp                |  4 +-
 flang/lib/Semantics/resolve-names-utils.cpp | 45 ++++++++++-----------
 3 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 23e965366a299..b7f8744b0e5dc 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1333,15 +1333,14 @@ using ExplicitBoundsExpr = IntExpr;
 
 struct ExplicitShapeBoundsSpec {
   TUPLE_CLASS_BOILERPLATE(ExplicitShapeBoundsSpec);
-  std::tuple<std::optional<ExplicitBoundsExpr>, ExplicitBoundsExpr>
-  t;
+  std::tuple<std::optional<ExplicitBoundsExpr>, ExplicitBoundsExpr> t;
 };
 
 struct ArraySpec {
   UNION_CLASS_BOILERPLATE(ArraySpec);
-  std::variant<std::list<ExplicitShapeSpec>, ExplicitShapeBoundsSpec, 
-      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 91b5bf8adf37c..1ddbc7361137d 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -571,7 +571,9 @@ 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 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 2d5842dce8bac..c7608f30216d6 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -242,8 +242,8 @@ ArraySpec ArraySpecAnalyzer::Analyze(const parser::ComponentArraySpec &x) {
 static bool shouldRewriteShapeSpecListToExplicitBounds(
     SemanticsContext &context, const parser::ArraySpec &x) {
   auto &explicitShapeSpecList{std::get<std::list<parser::ExplicitShapeSpec>>(
-      const_cast<parser::ArraySpec&>(x).u)};
-  
+      const_cast<parser::ArraySpec &>(x).u)};
+
   if (explicitShapeSpecList.size() != 1) {
     return false;
   }
@@ -251,51 +251,52 @@ static bool shouldRewriteShapeSpecListToExplicitBounds(
   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());
+
+  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());
+    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)};
+      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 &mutableLowerBound{std::get<0>(mutableExplicitShapeSpec.t)};
   std::optional<parser::IntExpr> lowerIntExpr;
-  if(mutableLowerBound) {
+  if (mutableLowerBound) {
     lowerIntExpr = std::move(mutableLowerBound->v.thing);
   }
-  
+
   parser::ExplicitShapeBoundsSpec boundsSpec{
-    std::make_tuple(std::move(lowerIntExpr), std::move(upperIntExpr))};
+      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)) {
+  if (std::get_if<std::list<parser::ExplicitShapeSpec>>(&x.u) &&
+      shouldRewriteShapeSpecListToExplicitBounds(context_, x)) {
     rewriteShapeSpecListToExplicitBounds(x);
   }
   common::visit(common::visitors{
@@ -304,11 +305,8 @@ ArraySpec ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
                           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());
@@ -345,7 +343,8 @@ void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeSpec &x) {
 
 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
+  // prevent CHECK abort in Analyze(ArraySpec), otherwise it'll abort before
+  // printing error message
   arraySpec_.push_back(ShapeSpec::MakeExplicit(Bound{1}));
 }
 

>From b98aafb16aeca8ac7cb139cc3a4cc9f88cb1d1f1 Mon Sep 17 00:00:00 2001
From: Ivan Rodriguez <ivan.rodriguez at hpe.com>
Date: Wed, 1 Apr 2026 14:18:46 -0500
Subject: [PATCH 4/7] Add module test cases, add dimension keyword test case

---
 .../declaration_explicit_array_bounds.f90     | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/flang/test/Semantics/declaration_explicit_array_bounds.f90 b/flang/test/Semantics/declaration_explicit_array_bounds.f90
index 1c473ef177839..092ceabac9707 100644
--- a/flang/test/Semantics/declaration_explicit_array_bounds.f90
+++ b/flang/test/Semantics/declaration_explicit_array_bounds.f90
@@ -1,4 +1,37 @@
 ! RUN: %python %S/test_errors.py %s %flang_fc1 -Wautomatic-in-main-program -Wsaved-local-in-spec-expr
+! ---- Module with rank-1 array-bounded declarations, USE'd elsewhere ----
+module bounds_provider
+  implicit none
+  integer, parameter :: dims(3) = [5, 5, 5]
+  integer, parameter :: lo(2) = [2, 3]
+  integer, parameter :: hi(2) = [10, 20]
+end module
+module consumer
+  use bounds_provider
+  implicit none
+  ! Declare arrays using USE-associated rank-1 parameter arrays
+  !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+  integer :: arr_upper(dims)
+  !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+  integer :: arr_both(lo : hi)
+end module
+subroutine sub_consumer()
+  use bounds_provider, only: dims, lo, hi
+  implicit none
+  ! USE'd parameter arrays as bounds in a subroutine
+  !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+  integer :: local_arr(dims)
+  !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+  integer :: local_arr2(lo : hi)
+end subroutine
+subroutine sub_use_consumer()
+  use consumer, only: arr_upper, arr_both
+  implicit none
+  ! USE the arrays that were themselves declared with rank-1 array bounds
+  arr_upper = 1
+  arr_both = 2
+end subroutine
+
 module data 
   integer :: rank1_array_module(3) = [5, 5, 5]
   !future_ERROR: Automatic data object 'gg2' may not appear in a module
@@ -18,6 +51,8 @@ program declaration_array_bounds
   ! Array upper bound only
   !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
   integer :: c([3, 4, 5])
+  !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec
+  integer, dimension([3, 4, 5]) :: cc
 
   ! Array lower and upper bounds, same size
   !ERROR: TODO: Analyze overload for ExplicitShapeBoundsSpec

>From 2888825f17675b6e832d90a9b8ebc1e7b1a912b3 Mon Sep 17 00:00:00 2001
From: Ivan Rodriguez <ivan.rodriguez at hpe.com>
Date: Mon, 20 Apr 2026 14:49:52 -0500
Subject: [PATCH 5/7] Address comments

Remove ExplicitBoundsExpr and just use IntExpr directly instead.
Change test name to hyphenate words instead of underscoring.
Remove unnecessary const_cast in shouldRewriteShapeSpecListToExplicitBounds.
Use UnwrapRef instead of manually unwrapping layers of .thing or .value().
---
 flang/include/flang/Parser/parse-tree.h                   | 4 +---
 flang/lib/Semantics/resolve-names-utils.cpp               | 8 ++++----
 ...y_bounds.f90 => declaration-explicit-array-bounds.f90} | 0
 3 files changed, 5 insertions(+), 7 deletions(-)
 rename flang/test/Semantics/{declaration_explicit_array_bounds.f90 => declaration-explicit-array-bounds.f90} (100%)

diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index b7f8744b0e5dc..44ff8a384199d 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1329,11 +1329,9 @@ EMPTY_CLASS(AssumedRankSpec);
 //        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;
+  std::tuple<std::optional<IntExpr>, IntExpr> t;
 };
 
 struct ArraySpec {
diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index c7608f30216d6..4d0fef3ad21e0 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -241,8 +241,8 @@ ArraySpec ArraySpecAnalyzer::Analyze(const parser::ComponentArraySpec &x) {
 
 static bool shouldRewriteShapeSpecListToExplicitBounds(
     SemanticsContext &context, const parser::ArraySpec &x) {
-  auto &explicitShapeSpecList{std::get<std::list<parser::ExplicitShapeSpec>>(
-      const_cast<parser::ArraySpec &>(x).u)};
+  auto &explicitShapeSpecList{
+      std::get<std::list<parser::ExplicitShapeSpec>>(x.u)};
 
   if (explicitShapeSpecList.size() != 1) {
     return false;
@@ -255,7 +255,7 @@ static bool shouldRewriteShapeSpecListToExplicitBounds(
   bool foundArray{false};
 
   if (MaybeExpr analyzedExpr =
-          AnalyzeExpr(context, upperBound.v.thing.thing.value());
+          AnalyzeExpr(context, parser::UnwrapRef<parser::Expr>(upperBound));
       analyzedExpr && (analyzedExpr->Rank() > 0)) {
     foundArray = true;
   }
@@ -263,7 +263,7 @@ static bool shouldRewriteShapeSpecListToExplicitBounds(
   if (lowerBoundOpt) {
     const auto &lowerBound{*lowerBoundOpt};
     if (MaybeExpr analyzedExpr =
-            AnalyzeExpr(context, lowerBound.v.thing.thing.value());
+            AnalyzeExpr(context, parser::UnwrapRef<parser::Expr>(lowerBound));
         analyzedExpr && (analyzedExpr->Rank() > 0)) {
       foundArray = true;
     }
diff --git a/flang/test/Semantics/declaration_explicit_array_bounds.f90 b/flang/test/Semantics/declaration-explicit-array-bounds.f90
similarity index 100%
rename from flang/test/Semantics/declaration_explicit_array_bounds.f90
rename to flang/test/Semantics/declaration-explicit-array-bounds.f90

>From 940ed78b118fce9586a79c34a4c09296daa4991c Mon Sep 17 00:00:00 2001
From: Ivan Rodriguez <ivan.rodriguez at hpe.com>
Date: Thu, 4 Jun 2026 14:08:39 -0500
Subject: [PATCH 6/7] Address all comments and run git-clang-format

---
 flang/lib/Parser/unparse.cpp                               | 3 ++-
 flang/lib/Semantics/resolve-names-utils.cpp                | 6 ++----
 flang/test/Semantics/declaration-explicit-array-bounds.f90 | 4 ++--
 3 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 2b3a2864bead6..075456fc6284f 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -578,7 +578,8 @@ class UnparseVisitor {
         common::visitors{
             [&](const std::list<ExplicitShapeSpec> &y) { Walk(y, ","); },
             [&](const ExplicitShapeBoundsSpec &y) {
-              /*TODO: unreachable until after semantic analysis is done */
+              llvm_unreachable(
+                  "Unparse for ExplicitShapeBoundsSpec should not be reached");
             },
             [&](const std::list<AssumedShapeSpec> &y) { Walk(y, ","); },
             [&](const DeferredShapeSpecList &y) { Walk(y); },
diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index 4d0fef3ad21e0..8164f6171d670 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -288,10 +288,8 @@ static void rewriteShapeSpecListToExplicitBounds(const parser::ArraySpec &x) {
   }
 
   parser::ExplicitShapeBoundsSpec boundsSpec{
-      std::make_tuple(std::move(lowerIntExpr), std::move(upperIntExpr))};
+      std::move(lowerIntExpr), std::move(upperIntExpr)};
   mutableArraySpec.u = std::move(boundsSpec);
-
-  return;
 }
 
 ArraySpec ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
@@ -342,7 +340,7 @@ void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeSpec &x) {
 }
 
 void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeBoundsSpec &x) {
-  context_.Say("TODO: Analyze overload for ExplicitShapeBoundsSpec"_err_en_US);
+  context_.Say("TODO: Analyze overload for ExplicitShapeBoundsSpec"_todo_en_US);
   // prevent CHECK abort in Analyze(ArraySpec), otherwise it'll abort before
   // printing error message
   arraySpec_.push_back(ShapeSpec::MakeExplicit(Bound{1}));
diff --git a/flang/test/Semantics/declaration-explicit-array-bounds.f90 b/flang/test/Semantics/declaration-explicit-array-bounds.f90
index 092ceabac9707..d2126c759397a 100644
--- a/flang/test/Semantics/declaration-explicit-array-bounds.f90
+++ b/flang/test/Semantics/declaration-explicit-array-bounds.f90
@@ -74,7 +74,7 @@ program declaration_array_bounds
   integer :: ggg(rank1_parameter_array * 2 : rank1_parameter_array - 1)
 
 
-  ! Negative cases (erros expected)
+  ! Negative cases (errors 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]
@@ -108,7 +108,7 @@ program declaration_array_bounds
   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 
+  ! to ExplicitShapeBoundsSpec, 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

>From 460f39c5dc663d44a34b458635d94e9ab8ad12fa Mon Sep 17 00:00:00 2001
From: Ivan Rodriguez <ivan.rodriguez at hpe.com>
Date: Tue, 9 Jun 2026 13:55:41 -0500
Subject: [PATCH 7/7] Add comment in code explaining early/manual rewrite,
 address typo

---
 flang/lib/Semantics/resolve-names-utils.cpp                | 4 ++++
 flang/test/Semantics/declaration-explicit-array-bounds.f90 | 3 +--
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index 8164f6171d670..0ce9d5ed90ad5 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -293,6 +293,10 @@ static void rewriteShapeSpecListToExplicitBounds(const parser::ArraySpec &x) {
 }
 
 ArraySpec ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
+  // This node is rewritten manually here, as opposed to using RewriteParseTree,
+  // because RewriteParseTree is called after ResolveNames in 
+  // PerformStatementSemantics, at which point we would have already
+  // aborted due to semantic errors before getting a chance to rewrite.
   if (std::get_if<std::list<parser::ExplicitShapeSpec>>(&x.u) &&
       shouldRewriteShapeSpecListToExplicitBounds(context_, x)) {
     rewriteShapeSpecListToExplicitBounds(x);
diff --git a/flang/test/Semantics/declaration-explicit-array-bounds.f90 b/flang/test/Semantics/declaration-explicit-array-bounds.f90
index d2126c759397a..83598f51768f2 100644
--- a/flang/test/Semantics/declaration-explicit-array-bounds.f90
+++ b/flang/test/Semantics/declaration-explicit-array-bounds.f90
@@ -108,8 +108,7 @@ program declaration_array_bounds
   integer :: k(rank2_parameter_array : rank3_array)
 
   ! Test that any comma list is parsed as ExplicitShapeSpecList and not rewritten 
-  ! to ExplicitShapeBoundsSpec, giving error messages expecting same number of 
-  ! aruments as rank of test_array and scalar integers
+  ! to ExplicitShapeBoundsSpec, giving error messages expecting scalar integer values.
   !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



More information about the flang-commits mailing list