[flang-commits] [flang] 79f5bc4 - [flang] Implemented the RANK clause of an attr-spec, per the Fortran 2023 Standard (#176979)

via flang-commits flang-commits at lists.llvm.org
Tue Jan 27 08:16:22 PST 2026


Author: kwyatt-ext
Date: 2026-01-27T11:16:17-05:00
New Revision: 79f5bc4927ecae52138953879f4430a77bfe31e3

URL: https://github.com/llvm/llvm-project/commit/79f5bc4927ecae52138953879f4430a77bfe31e3
DIFF: https://github.com/llvm/llvm-project/commit/79f5bc4927ecae52138953879f4430a77bfe31e3.diff

LOG: [flang] Implemented the RANK clause of an attr-spec, per the Fortran 2023 Standard (#176979)

This implements the RANK clause per the Fortran 2023 Standard. This
includes both the parsing/semantics and un-parsing functionality. It is
a fairly straight-forward change.

An executable test was added that tests the examples from the Fortran
2023 What's New document.

check-flang, check-flang-rt, and llvm-lit tests have been executed
against this change.

---------

Co-authored-by: Kevin Wyatt <kwyatt at hpe.com>

Added: 
    flang/test/Lower/rank-clause02.f90
    flang/test/Parser/unparse-rank-clause.f90
    flang/test/Semantics/rank-clause01.f90

Modified: 
    flang/docs/FortranStandardsSupport.md
    flang/include/flang/Parser/dump-parse-tree.h
    flang/include/flang/Parser/parse-tree.h
    flang/lib/Parser/Fortran-parsers.cpp
    flang/lib/Parser/unparse.cpp
    flang/lib/Semantics/resolve-names.cpp

Removed: 
    


################################################################################
diff  --git a/flang/docs/FortranStandardsSupport.md b/flang/docs/FortranStandardsSupport.md
index 97363dbd048a3..28fe59ac565be 100644
--- a/flang/docs/FortranStandardsSupport.md
+++ b/flang/docs/FortranStandardsSupport.md
@@ -56,7 +56,7 @@ status of all important Fortran 2023 features. The table entries are based on th
 | Simple procedures                                          | N      | |
 | Using integer arrays to specify subscripts                 | N      | |
 | Using integer arrays to specify rank and bound of an array | N      | |
-| Using an integer constant to specify rank                  | N      | |
+| Using an integer constant to specify rank                  | Y      | |
 | Reduction specifier for do concurrent                      | P      | Syntax is accepted |
 | Enumerations                                               | N      | |
 

diff  --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index f6e4cce241ad3..84c7b8d2a5349 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -843,6 +843,7 @@ class ParseTreeDumper {
   NODE(parser, ProgramUnit)
   NODE(parser, Protected)
   NODE(parser, ProtectedStmt)
+  NODE(parser, RankClause)
   NODE(parser, ReadStmt)
   NODE(parser, RealLiteralConstant)
   NODE(RealLiteralConstant, Real)

diff  --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 6c1aace66275f..4aec99c80bdae 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1340,12 +1340,17 @@ struct IntentSpec {
   WRAPPER_CLASS_BOILERPLATE(IntentSpec, Intent);
 };
 
+// F2023_R829 rank-clause ->
+//        scalar-int-constant-expr
+WRAPPER_CLASS(RankClause, ScalarIntConstantExpr);
+
 // R802 attr-spec ->
 //        access-spec | ALLOCATABLE | ASYNCHRONOUS |
 //        CODIMENSION lbracket coarray-spec rbracket | CONTIGUOUS |
 //        DIMENSION ( array-spec ) | EXTERNAL | INTENT ( intent-spec ) |
 //        INTRINSIC | language-binding-spec | OPTIONAL | PARAMETER | POINTER |
-//        PROTECTED | SAVE | TARGET | VALUE | VOLATILE |
+//        PROTECTED | RANK ( scalar-int-constant-expr ) | SAVE | TARGET |
+//        VALUE | VOLATILE |
 // (CUDA) CONSTANT | DEVICE | MANAGED | PINNED | SHARED | TEXTURE
 EMPTY_CLASS(Asynchronous);
 EMPTY_CLASS(External);
@@ -1361,7 +1366,7 @@ struct AttrSpec {
   UNION_CLASS_BOILERPLATE(AttrSpec);
   std::variant<AccessSpec, Allocatable, Asynchronous, CoarraySpec, Contiguous,
       ArraySpec, External, IntentSpec, Intrinsic, LanguageBindingSpec, Optional,
-      Parameter, Pointer, Protected, Save, Target, Value, Volatile,
+      Parameter, Pointer, Protected, RankClause, Save, Target, Value, Volatile,
       common::CUDADataAttr>
       u;
 };

diff  --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index 988db5450abc9..229da72605bee 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -700,7 +700,8 @@ TYPE_PARSER(
 //        CODIMENSION lbracket coarray-spec rbracket | CONTIGUOUS |
 //        DIMENSION ( array-spec ) | EXTERNAL | INTENT ( intent-spec ) |
 //        INTRINSIC | language-binding-spec | OPTIONAL | PARAMETER | POINTER |
-//        PROTECTED | SAVE | TARGET | VALUE | VOLATILE |
+//        PROTECTED | RANK ( scalar-int-constant-expr ) | SAVE | TARGET |
+//        VALUE | VOLATILE |
 //        CUDA-data-attr
 TYPE_PARSER(construct<AttrSpec>(accessSpec) ||
     construct<AttrSpec>(allocatable) ||
@@ -714,6 +715,8 @@ TYPE_PARSER(construct<AttrSpec>(accessSpec) ||
     construct<AttrSpec>(languageBindingSpec) || construct<AttrSpec>(optional) ||
     construct<AttrSpec>(construct<Parameter>("PARAMETER"_tok)) ||
     construct<AttrSpec>(pointer) || construct<AttrSpec>(protectedAttr) ||
+    construct<AttrSpec>("RANK" >>
+        construct<RankClause>(parenthesized(scalarIntConstantExpr))) ||
     construct<AttrSpec>(save) ||
     construct<AttrSpec>(construct<Target>("TARGET"_tok)) ||
     construct<AttrSpec>(construct<Value>("VALUE"_tok)) ||

diff  --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index fb7a7ec8517f4..3d8ea9f703b2f 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -511,6 +511,7 @@ class UnparseVisitor {
     common::visit(common::visitors{
                       [&](const CoarraySpec &) { Word("CODIMENSION["); },
                       [&](const ArraySpec &) { Word("DIMENSION("); },
+                      [&](const RankClause &) { Word("RANK("); },
                       [](const auto &) {},
                   },
         x.u);
@@ -519,6 +520,7 @@ class UnparseVisitor {
     common::visit(common::visitors{
                       [&](const CoarraySpec &) { Put(']'); },
                       [&](const ArraySpec &) { Put(')'); },
+                      [&](const RankClause &) { Put(')'); },
                       [](const auto &) {},
                   },
         x.u);

diff  --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 057fa1db239c1..787744ff1b676 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -14,6 +14,7 @@
 #include "resolve-directives.h"
 #include "resolve-names-utils.h"
 #include "rewrite-parse-tree.h"
+#include "flang/Common/Fortran-consts.h"
 #include "flang/Common/indirection.h"
 #include "flang/Common/restorer.h"
 #include "flang/Common/visit.h"
@@ -445,6 +446,7 @@ class ImplicitRulesVisitor : public DeclTypeSpecVisitor {
 // 6. POINTER(p,x(10))
 class ArraySpecVisitor : public virtual BaseVisitor {
 public:
+  void Post(const parser::RankClause &);
   void Post(const parser::ArraySpec &);
   void Post(const parser::ComponentArraySpec &);
   void Post(const parser::CoarraySpec &);
@@ -2723,6 +2725,27 @@ bool ImplicitRulesVisitor::HandleImplicitNone(
 
 // ArraySpecVisitor implementation
 
+void ArraySpecVisitor::Post(const parser::RankClause &x) {
+  // RANK(n) is equivalent to DIMENSION with n deferred shape specs (:)
+  CHECK(arraySpec_.empty());
+
+  // Evaluate the rank value (must be a constant expression)
+  if (auto rank{EvaluateInt64(context(), x.v)}) {
+    if (*rank < 0 || *rank > common::maxRank) {
+      Say(currStmtSource().value(),
+          "RANK value (%lld) must be between 0 and %d"_err_en_US,
+          static_cast<long long>(*rank), common::maxRank);
+    } else {
+      // Create n deferred shape specs (:)
+      for (int i = 0; i < *rank; ++i) {
+        arraySpec_.push_back(ShapeSpec::MakeDeferred());
+      }
+    }
+  } else {
+    Say("RANK value must be a constant expression"_err_en_US);
+  }
+}
+
 void ArraySpecVisitor::Post(const parser::ArraySpec &x) {
   CHECK(arraySpec_.empty());
   arraySpec_ = AnalyzeArraySpec(context(), x);

diff  --git a/flang/test/Lower/rank-clause02.f90 b/flang/test/Lower/rank-clause02.f90
new file mode 100644
index 0000000000000..e373df65ccf5a
--- /dev/null
+++ b/flang/test/Lower/rank-clause02.f90
@@ -0,0 +1,66 @@
+! Test the new RANK clause.  This uses the examples from the F2023 Standard and
+! related explanation documents. This test verifies that RANK clause correctly
+! sets the rank of variables in HLFIR lowering output.
+!
+! RUN: %flang_fc1 -emit-hlfir -o - %s | FileCheck %s
+
+! CHECK-LABEL: func.func @_QQmain() attributes {fir.bindc_name = "RANK_CLAUSE02"}
+program rank_clause02
+    implicit none
+
+! CHECK-DAG: %{{.*}} = fir.address_of(@_QFEx0) : !fir.ref<!fir.array<10x10x10x!fir.logical<4>>>
+! CHECK-DAG: %{{.*}} = fir.address_of(@_QFEarray1) : !fir.ref<!fir.array<10x10xi32>>
+    logical :: X0(10,10,10)
+    integer :: array1(10,10)
+
+    interface
+      subroutine sub02(arg1)
+        integer, rank(2) :: arg1
+      end subroutine
+    end interface
+
+    call sub01(X0)
+
+    call sub02(array1)
+
+  contains
+
+! CHECK-LABEL: func.func private @_QFPsub01(
+! CHECK-SAME: %[[X3_ALLOC:.*]]: !fir.box<!fir.array<?x?x?x!fir.logical<4>>>{{.*}})
+    subroutine sub01(X3)
+
+! CHECK: %[[X0_ALLOC:.*]] = fir.alloca !fir.array<10x10x10xi32>
+! CHECK: %[[X0_DECL:.*]]:2 = hlfir.declare %[[X0_ALLOC]]
+      integer :: X0(10,10,10)
+
+! CHECK: %[[X1_ALLOC:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x?x?x!fir.logical<4>>>>
+! CHECK: %[[X1_DECL:.*]]:2 = hlfir.declare %[[X1_ALLOC]] {{{.*}}fortran_attrs = #fir.var_attrs<allocatable>{{.*}}} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?x?x!fir.logical<4>>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?x?x!fir.logical<4>>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?x?x!fir.logical<4>>>>>)
+      logical, rank(rank(X0)), allocatable :: X1 ! Rank 3, deferred shape
+
+! CHECK: %[[X2_ALLOC:.*]] = fir.alloca !fir.box<!fir.ptr<!fir.array<?x?xcomplex<f32>>>>
+! CHECK: %[[X2_DECL:.*]]:2 = hlfir.declare %[[X2_ALLOC]] {{{.*}}fortran_attrs = #fir.var_attrs<pointer>{{.*}}} : (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x?xcomplex<f32>>>>>) -> (!fir.ref<!fir.box<!fir.ptr<!fir.array<?x?xcomplex<f32>>>>>, !fir.ref<!fir.box<!fir.ptr<!fir.array<?x?xcomplex<f32>>>>>)
+      complex, rank(2), pointer :: X2 ! Rank 2, deferred-shape
+
+! CHECK: %[[X3_DECL:.*]]:2 = hlfir.declare %[[X3_ALLOC]] dummy_scope %{{.*}} arg 1 {{.*}}: (!fir.box<!fir.array<?x?x?x!fir.logical<4>>>, !fir.dscope) -> (!fir.box<!fir.array<?x?x?x!fir.logical<4>>>, !fir.box<!fir.array<?x?x?x!fir.logical<4>>>)
+      logical, rank(rank(X0)) :: X3 ! Rank 3, assumed-shape
+
+! CHECK: %[[X4_ALLOC:.*]] = fir.alloca !fir.box<!fir.heap<f32>>
+! CHECK: %[[X4_DECL:.*]]:2 = hlfir.declare %[[X4_ALLOC]] {{{.*}}fortran_attrs = #fir.var_attrs<allocatable>{{.*}}} : (!fir.ref<!fir.box<!fir.heap<f32>>>) -> (!fir.ref<!fir.box<!fir.heap<f32>>>, !fir.ref<!fir.box<!fir.heap<f32>>>)
+      real, rank(0) :: X4 ! Scalar
+      allocatable :: X4
+
+    end subroutine
+
+end program
+
+! CHECK-LABEL: func.func @_QPsub02(
+! CHECK-SAME: %[[A_ALLOC:.*]]: !fir.box<!fir.array<?x?xi32>>{{.*}})
+subroutine sub02(A)
+! CHECK: %[[A_DECL:.*]]:2 = hlfir.declare %[[A_ALLOC]] dummy_scope %{{.*}} arg 1 {{.*}}: (!fir.box<!fir.array<?x?xi32>>, !fir.dscope) -> (!fir.box<!fir.array<?x?xi32>>, !fir.box<!fir.array<?x?xi32>>)
+    integer, rank(2) :: A
+
+! CHECK: %[[B_ALLOC:.*]] = fir.alloca !fir.box<!fir.heap<!fir.array<?x?xi32>>>
+! CHECK: %[[B_DECL:.*]]:2 = hlfir.declare %[[B_ALLOC]] {{{.*}}fortran_attrs = #fir.var_attrs<allocatable>{{.*}}} : (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>) -> (!fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>, !fir.ref<!fir.box<!fir.heap<!fir.array<?x?xi32>>>>)
+    integer, allocatable, rank(2) :: B
+
+end subroutine

diff  --git a/flang/test/Parser/unparse-rank-clause.f90 b/flang/test/Parser/unparse-rank-clause.f90
new file mode 100644
index 0000000000000..f99e797ae5e7b
--- /dev/null
+++ b/flang/test/Parser/unparse-rank-clause.f90
@@ -0,0 +1,32 @@
+! RUN: %flang_fc1 -fdebug-unparse %s 2>&1 | FileCheck %s
+
+! Test unparsing of RANK clause in array declaration statements
+
+subroutine test_rank_clause(X3)
+  integer :: X0(10,10,10)
+  logical, rank(rank(X0)), allocatable :: X1 ! Rank 3, deferred shape
+  complex, rank(2), pointer :: X2            ! Rank 2, deferred-shape
+  logical, rank(rank(X0)) :: X3              ! Rank 3, assumed-shape
+  real, allocatable, rank(0) :: X4           ! Scalar
+
+  if (rank(X1) == 3 .and. rank(X2) == 2 .and. rank(X3) == 3 .and. &
+      rank(X4) == 0) then
+    print *, "PASS"
+  else
+    print *, "FAIL"
+  endif
+
+end subroutine
+
+! CHECK: SUBROUTINE test_rank_clause (x3)
+! CHECK:  INTEGER x0(10_4,10_4,10_4)
+! CHECK:  LOGICAL, RANK(3_4), ALLOCATABLE :: x1
+! CHECK:  COMPLEX, RANK(2_4), POINTER :: x2
+! CHECK:  LOGICAL, RANK(3_4) :: x3
+! CHECK:  REAL, ALLOCATABLE, RANK(0_4) :: x4
+! CHECK:  IF (.true._4) THEN
+! CHECK:   PRINT *, "PASS"
+! CHECK:  ELSE
+! CHECK:   PRINT *, "FAIL"
+! CHECK:  END IF
+! CHECK: END SUBROUTINE

diff  --git a/flang/test/Semantics/rank-clause01.f90 b/flang/test/Semantics/rank-clause01.f90
new file mode 100644
index 0000000000000..6543b6c2db967
--- /dev/null
+++ b/flang/test/Semantics/rank-clause01.f90
@@ -0,0 +1,70 @@
+! Test the new RANK clause.  This uses the examples from the F2023 Standard and
+! related explanation documents.
+!
+! RUN: %python %S/test_errors.py %s %flang_fc1
+
+program rank_clause01
+    implicit none
+
+    logical :: X0(10,10,10)
+    integer :: array1(10,10)
+
+    interface
+      subroutine sub02(arg1)
+        integer, rank(2) :: arg1
+      end subroutine
+    end interface
+
+    call sub01(X0)
+
+    call sub02(array1)
+
+    call sub_errors()
+
+  contains
+
+    subroutine sub01(X3)
+
+      integer :: X0(10,10,10)
+      logical, rank(rank(X0)), allocatable :: X1 ! Rank 3, deferred shape
+      complex, rank(2), pointer :: X2 ! Rank 2, deferred-shape
+      logical, rank(rank(X0)) :: X3 ! Rank 3, assumed-shape
+      real, rank(0) :: X4 ! Scalar
+      allocatable :: X4
+
+      if (rank(X1) == 3 .and. rank(X2) == 2 .and. rank(X3) == 3 .and. &
+          rank(X4) == 0) then
+        print *, "PASS"
+      else
+        print *, "FAIL"
+      endif
+
+    end subroutine
+
+    subroutine sub_errors()
+      integer :: not_constant
+      ! Rank below range
+      !ERROR: RANK value (-1) must be between 0 and 15
+      integer, rank(-1) :: err_rank01
+      ! Rank above range
+      !ERROR: RANK value (16) must be between 0 and 15
+      integer, rank(16) :: err_rank02
+      ! Non-Constant
+      !ERROR: RANK value must be a constant expression
+      !ERROR: Must be a constant value
+      integer, rank(not_constant) :: err_rank03
+    end subroutine
+
+end program
+
+subroutine sub02(A)
+    integer, rank(2) :: A
+    integer, allocatable, rank(2) :: B
+
+    if (rank(A) == rank(B)) then
+      print *, "PASS"
+    else
+      print *, "FAIL"
+    endif
+
+end subroutine


        


More information about the flang-commits mailing list