[flang-commits] [flang] [flang][semantic] parser node types and rewrite for assumed-shape-bounds-spec (PR #211690)
via flang-commits
flang-commits at lists.llvm.org
Thu Jul 23 15:51:46 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-parser
Author: ivanrodriguez3753
<details>
<summary>Changes</summary>
This commit lays the groundwork for semantic analysis of rank-1 integer array expressions being used as bounds in a declaration with assumed bounds. This should strongly resemble the changes for explicit-shape-bounds-spec in f5a4294.
---
Full diff: https://github.com/llvm/llvm-project/pull/211690.diff
5 Files Affected:
- (modified) flang/include/flang/Parser/dump-parse-tree.h (+1)
- (modified) flang/include/flang/Parser/parse-tree.h (+8-4)
- (modified) flang/lib/Parser/unparse.cpp (+4)
- (modified) flang/lib/Semantics/resolve-names-utils.cpp (+53)
- (added) flang/test/Semantics/declaration-assumed-array-bounds.f90 (+44)
``````````diff
diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 030b0cc6f5301..29797b7d088a7 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -172,6 +172,7 @@ class ParseTreeDumper {
NODE(parser, AssumedImpliedSpec)
NODE(parser, AssumedRankSpec)
NODE(parser, AssumedShapeSpec)
+ NODE(parser, AssumedShapeBoundsSpec)
NODE(parser, AssumedSizeSpec)
NODE(parser, Asynchronous)
NODE(parser, AsynchronousStmt)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index f1e483922eee4..ed605c4f1fc43 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -1359,10 +1359,14 @@ EMPTY_CLASS(AssumedRankSpec);
// R815 array-spec ->
// explicit-shape-spec-list | explicit-shape-bounds-spec |
-// assumed-shape-spec-list | deferred-shape-spec-list |
-// assumed-size-spec | implied-shape-spec |
+// assumed-shape-spec-list | assumed-shape-bounds-spec |
+// deferred-shape-spec-list | assumed-size-spec | implied-shape-spec |
// implied-shape-or-assumed-size-spec | assumed-rank-spec
+using ExplicitBoundsExpr = IntExpr;
+
+WRAPPER_CLASS(AssumedShapeBoundsSpec, ExplicitBoundsExpr);
+
struct ExplicitShapeBoundsSpec {
TUPLE_CLASS_BOILERPLATE(ExplicitShapeBoundsSpec);
std::tuple<std::optional<IntExpr>, IntExpr> t;
@@ -1371,8 +1375,8 @@ struct ExplicitShapeBoundsSpec {
struct ArraySpec {
UNION_CLASS_BOILERPLATE(ArraySpec);
std::variant<std::list<ExplicitShapeSpec>, ExplicitShapeBoundsSpec,
- std::list<AssumedShapeSpec>, DeferredShapeSpecList, AssumedSizeSpec,
- ImpliedShapeSpec, AssumedRankSpec>
+ std::list<AssumedShapeSpec>, AssumedShapeBoundsSpec,
+ DeferredShapeSpecList, AssumedSizeSpec, ImpliedShapeSpec, AssumedRankSpec>
u;
};
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index c7fc6da4b8268..23d04f2e3ea42 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -596,6 +596,10 @@ class UnparseVisitor {
"Unparse for ExplicitShapeBoundsSpec should not be reached");
},
[&](const std::list<AssumedShapeSpec> &y) { Walk(y, ","); },
+ [&](const AssumedShapeBoundsSpec &y) {
+ llvm_unreachable(
+ "Unparse for AssumedShapeBoundsSpec should not be reached");
+ },
[&](const DeferredShapeSpecList &y) { Walk(y); },
[&](const AssumedSizeSpec &y) { Walk(y); },
[&](const ImpliedShapeSpec &y) { Walk(y); },
diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index 817e73aeff6a1..5e982639750bb 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -202,6 +202,7 @@ class ArraySpecAnalyzer {
}
}
void Analyze(const parser::AssumedShapeSpec &);
+ void Analyze(const parser::AssumedShapeBoundsSpec &);
void Analyze(const parser::ExplicitShapeSpec &);
void Analyze(const parser::ExplicitShapeBoundsSpec &);
void Analyze(const parser::AssumedImpliedSpec &);
@@ -272,6 +273,32 @@ static bool shouldRewriteShapeSpecListToExplicitBounds(
return foundArray;
}
+static bool shouldRewriteAssumedShapeSpecListToAssumedBounds(
+ SemanticsContext &context, const parser::ArraySpec &x) {
+ auto &assumedShapeSpecList{std::get<std::list<parser::AssumedShapeSpec>>(
+ const_cast<parser::ArraySpec &>(x).u)};
+
+ if (assumedShapeSpecList.size() != 1) {
+ return false;
+ }
+
+ auto &assumedShapeSpec{assumedShapeSpecList.front()};
+ const auto &lowerBoundOpt{assumedShapeSpec.v};
+
+ bool foundArray{false};
+
+ 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)};
@@ -292,6 +319,21 @@ static void rewriteShapeSpecListToExplicitBounds(const parser::ArraySpec &x) {
mutableArraySpec.u = std::move(boundsSpec);
}
+static void rewriteAssumedShapeSpecListToAssumedBounds(
+ const parser::ArraySpec &x) {
+ auto &assumedShapeSpecList{std::get<std::list<parser::AssumedShapeSpec>>(
+ const_cast<parser::ArraySpec &>(x).u)};
+ auto &mutableArraySpec{const_cast<parser::ArraySpec &>(x)};
+ auto &mutableAssumedShapeSpec{assumedShapeSpecList.front()};
+
+ auto &lowerBoundOpt{mutableAssumedShapeSpec.v};
+ CHECK(lowerBoundOpt.has_value());
+
+ parser::IntExpr lowerIntExpr{std::move(lowerBoundOpt->v.thing)};
+ parser::AssumedShapeBoundsSpec boundsSpec{std::move(lowerIntExpr)};
+ mutableArraySpec.u = std::move(boundsSpec);
+}
+
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
@@ -301,6 +343,10 @@ ArraySpec ArraySpecAnalyzer::Analyze(const parser::ArraySpec &x) {
shouldRewriteShapeSpecListToExplicitBounds(context_, x)) {
rewriteShapeSpecListToExplicitBounds(x);
}
+ if (std::get_if<std::list<parser::AssumedShapeSpec>>(&x.u) &&
+ shouldRewriteAssumedShapeSpecListToAssumedBounds(context_, x)) {
+ rewriteAssumedShapeSpecListToAssumedBounds(x);
+ }
common::visit(common::visitors{
[&](const parser::AssumedSizeSpec &y) {
Analyze(
@@ -335,6 +381,13 @@ ArraySpec ArraySpecAnalyzer::Analyze(const parser::CoarraySpec &x) {
return arraySpec_;
}
+void ArraySpecAnalyzer::Analyze(const parser::AssumedShapeBoundsSpec &x) {
+ context_.Say("Analyze overload for AssumedShapeBoundsSpec"_todo_en_US);
+ // prevent CHECK abort in Analyze(ArraySpec), otherwise it'll abort before
+ // printing error message
+ arraySpec_.push_back(ShapeSpec::MakeAssumedShape(Bound{1}));
+}
+
void ArraySpecAnalyzer::Analyze(const parser::AssumedShapeSpec &x) {
arraySpec_.push_back(ShapeSpec::MakeAssumedShape(GetBound(x.v)));
}
diff --git a/flang/test/Semantics/declaration-assumed-array-bounds.f90 b/flang/test/Semantics/declaration-assumed-array-bounds.f90
new file mode 100644
index 0000000000000..d9176e0f024d3
--- /dev/null
+++ b/flang/test/Semantics/declaration-assumed-array-bounds.f90
@@ -0,0 +1,44 @@
+! RUN: %python %S/test_errors.py %s %flang_fc1
+program main
+ implicit none
+contains
+ subroutine good(x, y, z, dim_a, scalar1, scalar2)
+ integer :: dim_a(1)
+ !valid cases
+ !simple rank-1 integer array reference
+ ! ERROR: not yet implemented: Analyze overload for AssumedShapeBoundsSpec
+ integer :: x(dim_a:)
+ !rank-1 integer array + scalar = rank-1 integer array
+ ! ERROR: not yet implemented: Analyze overload for AssumedShapeBoundsSpec
+ integer :: y(dim_a + 2:)
+ !rank-1 integer array via array constructor literal, with some non-const values
+ ! ERROR: not yet implemented: Analyze overload for AssumedShapeBoundsSpec
+ integer :: z([1,2,dim_a(1) + x(dim_a(1))]:)
+ !rank-1 zero sized array is valid and should declare a scalar
+ ! ERROR: not yet implemented: Analyze overload for AssumedShapeBoundsSpec
+ integer :: scalar1([integer::]:)
+ integer :: empty_arr(0) = [integer::]
+ ! ERROR: not yet implemented: Analyze overload for AssumedShapeBoundsSpec
+ integer :: scalar2(empty_arr:)
+ end subroutine
+
+ subroutine bad(x, y, z, dim1_assumed, dim2_assumed)
+ integer :: dim3(3, 3, 3)
+ integer :: dim1_assumed(:)
+ integer :: dim2_assumed(:,:)
+ ! invalid cases:
+ ! future_ERROR: Rank-1 integer array used as lower bounds in DECLARATION must have constant size
+ ! ERROR: not yet implemented: Analyze overload for AssumedShapeBoundsSpec
+ integer :: x(dim1_assumed:)
+ ! future_ERROR: Integer array used as lower bounds in DECLARATION must be rank-1 but is rank-3
+ ! ERROR: not yet implemented: Analyze overload for AssumedShapeBoundsSpec
+ integer :: y(dim3:)
+ ! Combining both errors in one declaration, plus integer-check from
+ ! type wrapper
+ ! future_ERROR: Integer array used as lower bounds in DECLARATION must be rank-1 but is rank-2
+ ! future_ERROR: Rank-1 integer array used as lower bounds in DECLARATION must have constant size
+ ! ERROR: not yet implemented: Analyze overload for AssumedShapeBoundsSpec
+ ! ERROR: Must have INTEGER type, but is REAL(4)
+ integer :: z(dim2_assumed + 3.7:)
+ end subroutine
+end program
``````````
</details>
https://github.com/llvm/llvm-project/pull/211690
More information about the flang-commits
mailing list