[flang-commits] [flang] [flang] Implemented the RANK clause of an attr-spec, per the Fortran 2023 Standard (PR #176979)
via flang-commits
flang-commits at lists.llvm.org
Fri Jan 23 11:48:05 PST 2026
https://github.com/kwyatt-ext updated https://github.com/llvm/llvm-project/pull/176979
>From d95c7aeb2f8ed66563498e41ae41643164930949 Mon Sep 17 00:00:00 2001
From: Kevin Wyatt <kwyatt at hpe.com>
Date: Fri, 16 Jan 2026 15:52:09 -0600
Subject: [PATCH 1/5] Implemented the RANK clause of an attr-spec, per the
Fortran 2023 Standard
---
flang/include/flang/Parser/dump-parse-tree.h | 1 +
flang/include/flang/Parser/parse-tree.h | 9 ++-
flang/lib/Parser/Fortran-parsers.cpp | 5 +-
flang/lib/Parser/unparse.cpp | 2 +
flang/lib/Semantics/resolve-names.cpp | 32 ++++++++++
flang/test/Semantics/rank-clause01.f90 | 64 ++++++++++++++++++++
6 files changed, 110 insertions(+), 3 deletions(-)
create mode 100644 flang/test/Semantics/rank-clause01.f90
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 37c0f699361eb..1a53591e4f89f 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1353,12 +1353,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);
@@ -1374,7 +1379,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..2a89d43b8d4f6 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 9b31454537df5..c88cb323e820f 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -508,6 +508,7 @@ class UnparseVisitor {
common::visit(common::visitors{
[&](const CoarraySpec &) { Word("CODIMENSION["); },
[&](const ArraySpec &) { Word("DIMENSION("); },
+ [&](const RankClause &) { Word("RANK("); },
[](const auto &) {},
},
x.u);
@@ -516,6 +517,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 310f7ab97e9a0..6301ff21a7c5d 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 &); // Should this be a void?
void Post(const parser::ArraySpec &);
void Post(const parser::ComponentArraySpec &);
void Post(const parser::CoarraySpec &);
@@ -2723,6 +2725,36 @@ 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{evaluate::ToInt64(EvaluateInt64(context(), x.v))}) {
+ if (auto rank{EvaluateInt64(context(), x.v)}) {
+ if (*rank < 0 || *rank > common::maxRank) {
+
+ // Say(x.v.thing.thing.value().source,
+ // Say(x.v.source,
+ // "RANK value (%lld) must be between 0 and %d"_err_en_US,
+ // static_cast<long long>(*rank), common::maxRank);
+ // currStmtSource().value()
+ Say("RANK value (%lld) must be between 0 and %d"_err_en_US);
+ // static_cast<long long>(*rank), common::maxRank);
+ } else { // KKW: I'm not sure about this pushback... it might be ok since it is Pre, but I'm suspect...
+ // 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);
+ // Say(x.source,
+ // "RANK value must be a constant expression"_err_en_US);
+ // Say(x.v.thing.thing.value().source,
+ // "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/Semantics/rank-clause01.f90 b/flang/test/Semantics/rank-clause01.f90
new file mode 100644
index 0000000000000..40ba470cb4168
--- /dev/null
+++ b/flang/test/Semantics/rank-clause01.f90
@@ -0,0 +1,64 @@
+! Test the new RANK clause. This uses the examples from the F2023 Standard and
+! related explanation documents.
+!
+! RUN: %flang %s -o %t
+! RUN: env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:%libdir" %t | FileCheck %s
+! CHECK-NOT: FAIL
+
+program rank_clause01
+ implicit none
+
+ logical :: X0(10,10,10)
+ integer :: array1(10,10)
+
+ interface
+
+ subroutine sub02(arg1)
+ integer, rank(2) :: arg1
+ end subroutine
+
+ subroutine new(arg1)
+ integer, rank(2) :: arg1
+ end subroutine
+
+ end interface
+
+ call sub01(X0)
+
+ call sub02(array1)
+
+ call new(array1)
+
+ 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
+
+end program
+
+subroutine sub02(A)
+ integer, rank(2) :: A, B
+ entry new(B)
+
+ if (rank(A) == rank(B)) then
+ print *, "PASS"
+ else
+ print *, "FAIL"
+ endif
+
+end subroutine
>From 782248234650941f7d152174c95e78c0bc060dc9 Mon Sep 17 00:00:00 2001
From: Kevin Wyatt <kwyatt at hpe.com>
Date: Tue, 20 Jan 2026 11:21:02 -0600
Subject: [PATCH 2/5] [flang] Added the RANK clause from the F2023 Standard
---
flang/lib/Semantics/resolve-names.cpp | 21 ++++++---------------
1 file changed, 6 insertions(+), 15 deletions(-)
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 6301ff21a7c5d..5bf690c10c064 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -446,7 +446,7 @@ class ImplicitRulesVisitor : public DeclTypeSpecVisitor {
// 6. POINTER(p,x(10))
class ArraySpecVisitor : public virtual BaseVisitor {
public:
- void Post(const parser::RankClause &); // Should this be a void?
+ void Post(const parser::RankClause &);
void Post(const parser::ArraySpec &);
void Post(const parser::ComponentArraySpec &);
void Post(const parser::CoarraySpec &);
@@ -2728,19 +2728,14 @@ bool ImplicitRulesVisitor::HandleImplicitNone(
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{evaluate::ToInt64(EvaluateInt64(context(), x.v))}) {
if (auto rank{EvaluateInt64(context(), x.v)}) {
if (*rank < 0 || *rank > common::maxRank) {
-
- // Say(x.v.thing.thing.value().source,
- // Say(x.v.source,
- // "RANK value (%lld) must be between 0 and %d"_err_en_US,
- // static_cast<long long>(*rank), common::maxRank);
- // currStmtSource().value()
- Say("RANK value (%lld) must be between 0 and %d"_err_en_US);
- // static_cast<long long>(*rank), common::maxRank);
- } else { // KKW: I'm not sure about this pushback... it might be ok since it is Pre, but I'm suspect...
+ 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());
@@ -2748,10 +2743,6 @@ void ArraySpecVisitor::Post(const parser::RankClause &x) {
}
} else {
Say("RANK value must be a constant expression"_err_en_US);
- // Say(x.source,
- // "RANK value must be a constant expression"_err_en_US);
- // Say(x.v.thing.thing.value().source,
- // "RANK value must be a constant expression"_err_en_US);
}
}
>From 650138b65f8f66b3bcc7a43d25647c367af37737 Mon Sep 17 00:00:00 2001
From: Kevin Wyatt <kwyatt at hpe.com>
Date: Wed, 21 Jan 2026 13:20:56 -0600
Subject: [PATCH 3/5] I made original test compile only and added check for new
error messages. I also added a test in ./Lower that verifies the shape of
the declared variables.
---
flang/test/Lower/rank-clause02.f90 | 66 ++++++++++++++++++++++++++
flang/test/Semantics/rank-clause01.f90 | 30 +++++++-----
2 files changed, 84 insertions(+), 12 deletions(-)
create mode 100644 flang/test/Lower/rank-clause02.f90
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/Semantics/rank-clause01.f90 b/flang/test/Semantics/rank-clause01.f90
index 40ba470cb4168..6543b6c2db967 100644
--- a/flang/test/Semantics/rank-clause01.f90
+++ b/flang/test/Semantics/rank-clause01.f90
@@ -1,9 +1,7 @@
! Test the new RANK clause. This uses the examples from the F2023 Standard and
! related explanation documents.
!
-! RUN: %flang %s -o %t
-! RUN: env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:%libdir" %t | FileCheck %s
-! CHECK-NOT: FAIL
+! RUN: %python %S/test_errors.py %s %flang_fc1
program rank_clause01
implicit none
@@ -12,22 +10,16 @@ program rank_clause01
integer :: array1(10,10)
interface
-
subroutine sub02(arg1)
integer, rank(2) :: arg1
end subroutine
-
- subroutine new(arg1)
- integer, rank(2) :: arg1
- end subroutine
-
end interface
call sub01(X0)
call sub02(array1)
- call new(array1)
+ call sub_errors()
contains
@@ -49,11 +41,25 @@ subroutine sub01(X3)
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, B
- entry new(B)
+ integer, rank(2) :: A
+ integer, allocatable, rank(2) :: B
if (rank(A) == rank(B)) then
print *, "PASS"
>From 3d47e55913de0ea2c0a527f17d9374136dcd11cf Mon Sep 17 00:00:00 2001
From: Kevin Wyatt <kwyatt at hpe.com>
Date: Fri, 23 Jan 2026 10:07:58 -0600
Subject: [PATCH 4/5] Correcting format.
---
flang/lib/Parser/Fortran-parsers.cpp | 4 ++--
flang/lib/Semantics/resolve-names.cpp | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp
index 2a89d43b8d4f6..229da72605bee 100644
--- a/flang/lib/Parser/Fortran-parsers.cpp
+++ b/flang/lib/Parser/Fortran-parsers.cpp
@@ -715,8 +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>("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/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 5bf690c10c064..c209f3501cc87 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -2743,7 +2743,7 @@ void ArraySpecVisitor::Post(const parser::RankClause &x) {
}
} else {
Say("RANK value must be a constant expression"_err_en_US);
- }
+ }
}
void ArraySpecVisitor::Post(const parser::ArraySpec &x) {
>From 417b488185fd511447572a0e8aa1634e49aa8f78 Mon Sep 17 00:00:00 2001
From: Kevin Wyatt <kwyatt at hpe.com>
Date: Fri, 23 Jan 2026 13:47:50 -0600
Subject: [PATCH 5/5] An unparse test has been added. The
FortranStandardsSupport doc now reflects the added F2023 feature.
---
flang/docs/FortranStandardsSupport.md | 2 +-
flang/test/Parser/unparse-rank-clause.f90 | 32 +++++++++++++++++++++++
2 files changed, 33 insertions(+), 1 deletion(-)
create mode 100644 flang/test/Parser/unparse-rank-clause.f90
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/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
More information about the flang-commits
mailing list