[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