[flang-commits] [flang] [flang][semantic] Implement semantic checks and new data structure (PR #203030)

via flang-commits flang-commits at lists.llvm.org
Thu Jul 9 09:24:41 PDT 2026


================
@@ -343,11 +350,138 @@ void ArraySpecAnalyzer::Analyze(const parser::ExplicitShapeSpec &x) {
       std::get<parser::SpecificationExpr>(x.t));
 }
 
+std::optional<ArraySpecAnalyzer::ExplicitShapeBoundsResult>
+ArraySpecAnalyzer::checkExplicitShapeBoundsSpec(
+    const parser::ExplicitShapeBoundsSpec &x) {
+  const auto &lowerBoundOpt{std::get<0>(x.t)};
+  const auto &upperBound{std::get<1>(x.t)};
+
+  // Analyze, validate, fold, and wrap one bound expression in a Bound.
+  // Returns the Bound and, for rank-1, the constant extent; for scalar
+  // the extent is 0 (meaning "broadcast").
+  bool hasError{false};
+  auto analyzeBound =
+      [&](const auto &parseBound,
+          bool isUpper) -> std::optional<std::pair<Bound, std::int64_t>> {
+    MaybeExpr expr{AnalyzeExpr(context_, parseBound.thing)};
+    if (expr->Rank() > 1) {
+      context_.Say(parser::FindSourceLocation(parseBound),
+          "Integer array used as %s bounds in DECLARATION must be rank-1 "
+          "but is rank-%d"_err_en_US,
+          isUpper ? "upper" : "lower", expr->Rank());
+      hasError = true;
+      return std::nullopt;
+    }
+    auto folded{evaluate::Fold(context_.foldingContext(), std::move(*expr))};
+    const auto *someInt{evaluate::UnwrapExpr<SomeIntExpr>(folded)};
+    if (!someInt) {
+      hasError = true;
+      return std::nullopt;
+    }
+    auto asSI{evaluate::Fold(context_.foldingContext(),
+        evaluate::ConvertToType<evaluate::SubscriptInteger>(
+            common::Clone(*someInt)))};
+    if (folded.Rank() == 0) {
+      return std::make_pair(
+          Bound{MaybeSubscriptIntExpr{std::move(asSI)}}, std::int64_t{0});
+    }
+    // Rank-1: must have constant extent.
+    auto extents{
+        evaluate::GetConstantExtents(context_.foldingContext(), folded)};
----------------
ivanrodriguez3753 wrote:

These are interesting cases. The second is invalid because the spec states that if two arrays are provided, they must be the same size. 
In the first case, I believe the spec states that that would be a scalar:
```
The rank of an entity declared with an explicit-shape-spec-list is equal to the number of explicit-shape-specs;
the rank of an entity declared with an explicit-shape-bounds-spec is equal to the size of one of the explicit-
bounds-exprs. If the rank of such an entity is nonzero, the entity is an explicit-shape array; otherwise, it is
scalar
```
For two empty arrays, it would also be a scalar. The only case left is broadcast with an empty array. I don't think the spec addresses this, and I don't really know how we could make sense of it. Consider
```
integer :: arr1(7 : [integer::])
integer :: arr2([integer::] : 7)
```
There is nothing to broadcast to. We could ignore the empty array, but then it would be ambiguous whether the remaining scalar is the upper bound or lower bound. Furthermore, the spec says to use the size of the array, which is 0, which would mean it's a scalar. 

My thoughts are that we should flat out reject 0-size arrays when used in this context. The constant extent is already a requirement, so it can always be detected and rejected. But we could accept everything but the broadcast case? 

https://github.com/llvm/llvm-project/pull/203030


More information about the flang-commits mailing list