[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