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

via flang-commits flang-commits at lists.llvm.org
Wed Jun 10 11:48:13 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: ivanrodriguez3753

<details>
<summary>Changes</summary>

This PR is part 2 of 3 for a stack PR implementing rank-1 integer arrays with constant extent being used as explicit shape bounds. There is also an associated llvm-test-suite PR.

Part 1: https://github.com/llvm/llvm-project/pull/188447
Part 2: (this PR)
Part 3: https://github.com/ivanrodriguez3753/llvm-project/pull/2
llvm-test-suite: https://github.com/llvm/llvm-test-suite/pull/411

---

Patch is 34.98 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/203030.diff


21 Files Affected:

- (modified) flang/include/flang/Evaluate/expression.h (+4-1) 
- (modified) flang/include/flang/Evaluate/shape.h (+1) 
- (modified) flang/include/flang/Evaluate/traverse.h (+3) 
- (modified) flang/include/flang/Evaluate/variable.h (+28) 
- (modified) flang/include/flang/Semantics/dump-expr.h (+1) 
- (modified) flang/lib/Evaluate/check-expression.cpp (+13) 
- (modified) flang/lib/Evaluate/fold-implementation.h (+2) 
- (modified) flang/lib/Evaluate/fold-integer.cpp (+14) 
- (modified) flang/lib/Evaluate/formatting.cpp (+5) 
- (modified) flang/lib/Evaluate/variable.cpp (+3) 
- (modified) flang/lib/Lower/ConvertExpr.cpp (+9) 
- (modified) flang/lib/Lower/ConvertExprToHLFIR.cpp (+5) 
- (modified) flang/lib/Lower/IterationSpace.cpp (+1) 
- (modified) flang/lib/Lower/Support/Utils.cpp (+9) 
- (modified) flang/lib/Parser/unparse.cpp (+2-2) 
- (modified) flang/lib/Semantics/dump-expr.cpp (+6) 
- (modified) flang/lib/Semantics/mod-file.cpp (+53-7) 
- (modified) flang/lib/Semantics/resolve-names-utils.cpp (+138-4) 
- (modified) flang/test/Semantics/declaration-explicit-array-bounds.f90 (+58-30) 
- (added) flang/test/Semantics/modfile-explicit-shape-bounds.f90 (+52) 
- (added) flang/test/Semantics/unparse-explicit-array-bounds.f90 (+42) 


``````````diff
diff --git a/flang/include/flang/Evaluate/expression.h b/flang/include/flang/Evaluate/expression.h
index 1afe8ae74c415..2df16d9a174a6 100644
--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -586,12 +586,15 @@ class Expr<Type<TypeCategory::Integer, KIND>>
   using DescriptorInquiries =
       std::conditional_t<KIND == DescriptorInquiry::Result::kind,
           std::tuple<DescriptorInquiry>, std::tuple<>>;
+  using RankOneBoundElements =
+      std::conditional_t<KIND == RankOneBoundElement::Result::kind,
+          std::tuple<RankOneBoundElement>, std::tuple<>>;
   using Others = std::tuple<Constant<Result>, ArrayConstructor<Result>,
       Designator<Result>, FunctionRef<Result>>;
 
 public:
   common::TupleToVariant<common::CombineTuples<Operations, Conversions, Indices,
-      TypeParamInquiries, DescriptorInquiries, Others>>
+      TypeParamInquiries, DescriptorInquiries, RankOneBoundElements, Others>>
       u;
 };
 
diff --git a/flang/include/flang/Evaluate/shape.h b/flang/include/flang/Evaluate/shape.h
index e5c2d6e8cb63d..e0148ef97a58d 100644
--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -165,6 +165,7 @@ class GetShapeHelper
 
   Result operator()(const ImpliedDoIndex &) const { return ScalarShape(); }
   Result operator()(const DescriptorInquiry &) const { return ScalarShape(); }
+  Result operator()(const RankOneBoundElement &) const { return ScalarShape(); }
   Result operator()(const TypeParamInquiry &) const { return ScalarShape(); }
   Result operator()(const BOZLiteralConstant &) const { return ScalarShape(); }
   Result operator()(const StaticDataObject::Pointer &) const {
diff --git a/flang/include/flang/Evaluate/traverse.h b/flang/include/flang/Evaluate/traverse.h
index 44cfaa2a7073d..2cf33201b1409 100644
--- a/flang/include/flang/Evaluate/traverse.h
+++ b/flang/include/flang/Evaluate/traverse.h
@@ -161,6 +161,9 @@ class Traverse {
   Result operator()(const DescriptorInquiry &x) const {
     return visitor_(x.base());
   }
+  Result operator()(const RankOneBoundElement &x) const {
+    return visitor_(x.base());
+  }
 
   // Calls
   Result operator()(const SpecificIntrinsic &) const {
diff --git a/flang/include/flang/Evaluate/variable.h b/flang/include/flang/Evaluate/variable.h
index 4f64ede3d407d..f510873ec2fe2 100644
--- a/flang/include/flang/Evaluate/variable.h
+++ b/flang/include/flang/Evaluate/variable.h
@@ -435,6 +435,34 @@ class DescriptorInquiry {
   int dimension_{0}; // zero-based
 };
 
+// Represents the extraction of a single scalar element from a rank-1
+// integer array expression used as an explicit-shape array bound (F2023).
+// The inner expression is rank-1; this node is scalar (Rank() == 0).
+// dimension_ is zero-based.
+class RankOneBoundElement {
+public:
+  using Result = SubscriptInteger;
+  CLASS_BOILERPLATE(RankOneBoundElement)
+  RankOneBoundElement(
+      common::CopyableIndirection<Expr<SubscriptInteger>> &&e, int dim)
+      : base_{std::move(e)}, dimension_{dim} {}
+  RankOneBoundElement(Expr<SubscriptInteger> &&e, int dim)
+      : base_{std::move(e)}, dimension_{dim} {}
+
+  const Expr<SubscriptInteger> &base() const { return base_.value(); }
+  Expr<SubscriptInteger> &base() { return base_.value(); }
+  int dimension() const { return dimension_; }
+
+  static constexpr int Rank() { return 0; } // always scalar
+  static constexpr int Corank() { return 0; }
+  bool operator==(const RankOneBoundElement &) const;
+  llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
+
+private:
+  common::CopyableIndirection<Expr<SubscriptInteger>> base_;
+  int dimension_{0}; // zero-based
+};
+
 #define INSTANTIATE_VARIABLE_TEMPLATES \
   FOR_EACH_SPECIFIC_TYPE(template class Designator, )
 } // namespace Fortran::evaluate
diff --git a/flang/include/flang/Semantics/dump-expr.h b/flang/include/flang/Semantics/dump-expr.h
index d79a294258ff1..868b64c64e60a 100644
--- a/flang/include/flang/Semantics/dump-expr.h
+++ b/flang/include/flang/Semantics/dump-expr.h
@@ -149,6 +149,7 @@ class DumpEvaluateExpr {
     Outdent();
   }
   void Show(const evaluate::DescriptorInquiry &x);
+  void Show(const evaluate::RankOneBoundElement &x);
   void Show(const evaluate::SpecificIntrinsic &);
   void Show(const evaluate::ProcedureDesignator &x);
   void Show(const evaluate::ActualArgument &x);
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index 62c93e5d20737..737502a504d61 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -91,6 +91,9 @@ class IsConstantExprHelper
             (IsIntentIn(sym) && !IsOptional(sym) &&
                 !sym.attrs().test(semantics::Attr::VALUE)));
   }
+  bool operator()(const RankOneBoundElement &x) const {
+    return (*this)(x.base());
+  }
 
   bool operator()(const ImpliedDoIndex &ido) const {
     return acImpliedDos_.find(ido.name) != acImpliedDos_.end() || !context_ ||
@@ -363,6 +366,9 @@ class IsInitialDataTargetHelper
         IsConstantExpr(x.upper(), context_) && (*this)(x.parent());
   }
   bool operator()(const DescriptorInquiry &) const { return false; }
+  bool operator()(const RankOneBoundElement &x) const {
+    return false;
+  } // unreachable
   template <typename T> bool operator()(const ArrayConstructor<T> &) const {
     return false;
   }
@@ -798,6 +804,10 @@ class CheckSpecificationExprHelper
     }
   }
 
+  Result operator()(const RankOneBoundElement &x) const {
+    return (*this)(x.base());
+  }
+
   Result operator()(const TypeParamInquiry &inq) const {
     if (scope_.IsDerivedType()) {
       if (!IsConstantExpr(inq, &context_) &&
@@ -1797,6 +1807,9 @@ class CollectUsedSymbolValuesHelper
   Result operator()(const DescriptorInquiry &) const {
     return {}; // doesn't count as a use
   }
+  Result operator()(const RankOneBoundElement &x) const {
+    return {}; // unreachable
+  }
 
   template <typename T> Result operator()(const ConditionalExpr<T> &condExpr) {
     auto restorer{common::ScopedSet(isDefinition_, false)};
diff --git a/flang/lib/Evaluate/fold-implementation.h b/flang/lib/Evaluate/fold-implementation.h
index 2df2b9e5a300b..5e7e85d33a92f 100644
--- a/flang/lib/Evaluate/fold-implementation.h
+++ b/flang/lib/Evaluate/fold-implementation.h
@@ -138,6 +138,8 @@ Expr<T> FoldOperation(FoldingContext &context, Designator<T> &&designator) {
 }
 Expr<TypeParamInquiry::Result> FoldOperation(
     FoldingContext &, TypeParamInquiry &&);
+Expr<RankOneBoundElement::Result> FoldOperation(
+    FoldingContext &, RankOneBoundElement &&);
 Expr<ImpliedDoIndex::Result> FoldOperation(
     FoldingContext &context, ImpliedDoIndex &&);
 template <typename T>
diff --git a/flang/lib/Evaluate/fold-integer.cpp b/flang/lib/Evaluate/fold-integer.cpp
index 9f2bb94a9213f..bfecf5917f178 100644
--- a/flang/lib/Evaluate/fold-integer.cpp
+++ b/flang/lib/Evaluate/fold-integer.cpp
@@ -1554,6 +1554,20 @@ Expr<TypeParamInquiry::Result> FoldOperation(
   return AsExpr(std::move(inquiry));
 }
 
+Expr<RankOneBoundElement::Result> FoldOperation(
+    FoldingContext &context, RankOneBoundElement &&x) {
+  using ResultType = RankOneBoundElement::Result;
+  auto folded{Fold(context, Expr<ResultType>{x.base()})};
+  if (auto *c{UnwrapConstantValue<ResultType>(folded)}) {
+    // Base is a constant array; extract the element at dimension_ (0-based).
+    ConstantSubscripts at{c->lbounds()};
+    at[0] = c->lbounds()[0] + x.dimension();
+    return Expr<ResultType>{Constant<ResultType>{c->At(at)}};
+  }
+  return Expr<ResultType>{
+      RankOneBoundElement{std::move(folded), x.dimension()}};
+}
+
 std::optional<std::int64_t> ToInt64(const Expr<SomeInteger> &expr) {
   return common::visit(
       [](const auto &kindExpr) { return ToInt64(kindExpr); }, expr.u);
diff --git a/flang/lib/Evaluate/formatting.cpp b/flang/lib/Evaluate/formatting.cpp
index 3604484254196..5cd8af4fa9019 100644
--- a/flang/lib/Evaluate/formatting.cpp
+++ b/flang/lib/Evaluate/formatting.cpp
@@ -853,6 +853,11 @@ llvm::raw_ostream &DescriptorInquiry::AsFortran(llvm::raw_ostream &o) const {
   return o << ",kind=" << DescriptorInquiry::Result::kind << ")";
 }
 
+llvm::raw_ostream &RankOneBoundElement::AsFortran(llvm::raw_ostream &o) const {
+  llvm_unreachable("RankOneBoundElement has no Fortran representation");
+  return o;
+}
+
 llvm::raw_ostream &Assignment::AsFortran(llvm::raw_ostream &o) const {
   common::visit(
       common::visitors{
diff --git a/flang/lib/Evaluate/variable.cpp b/flang/lib/Evaluate/variable.cpp
index b257dad42fc58..b5774ef9fe624 100644
--- a/flang/lib/Evaluate/variable.cpp
+++ b/flang/lib/Evaluate/variable.cpp
@@ -764,6 +764,9 @@ bool DescriptorInquiry::operator==(const DescriptorInquiry &that) const {
   return field_ == that.field_ && base_ == that.base_ &&
       dimension_ == that.dimension_;
 }
+bool RankOneBoundElement::operator==(const RankOneBoundElement &that) const {
+  return dimension_ == that.dimension_ && base_ == that.base_;
+}
 
 #ifdef _MSC_VER // disable bogus warning about missing definitions
 #pragma warning(disable : 4661)
diff --git a/flang/lib/Lower/ConvertExpr.cpp b/flang/lib/Lower/ConvertExpr.cpp
index 32cd710e9b5b4..fded2bb9678a1 100644
--- a/flang/lib/Lower/ConvertExpr.cpp
+++ b/flang/lib/Lower/ConvertExpr.cpp
@@ -1062,6 +1062,10 @@ class ScalarExprLowering {
     llvm_unreachable("unknown descriptor inquiry");
   }
 
+  ExtValue genval(const Fortran::evaluate::RankOneBoundElement &) {
+    llvm_unreachable("RankOneBoundElement in legacy lowering path");
+  }
+
   ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
     TODO(getLoc(), "type parameter inquiry");
   }
@@ -6568,6 +6572,11 @@ class ArrayExprLowering {
     TODO(getLoc(), "array expr descriptor inquiry");
     return [](IterSpace iters) -> ExtValue { return mlir::Value{}; };
   }
+  CC genarr(const Fortran::evaluate::RankOneBoundElement &x) {
+    fir::emitFatalError(getLoc(),
+                        "rank-1 bound element cannot appear in array context");
+    return [](IterSpace iters) -> ExtValue { return mlir::Value{}; };
+  }
   CC genarr(const Fortran::evaluate::StructureConstructor &x) {
     TODO(getLoc(), "structure constructor");
     return [](IterSpace iters) -> ExtValue { return mlir::Value{}; };
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index ad680269dea5c..56a1f59b87c34 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -1810,6 +1810,11 @@ class HlfirBuilder {
     llvm_unreachable("unknown descriptor inquiry");
   }
 
+  hlfir::EntityWithAttributes
+  gen(const Fortran::evaluate::RankOneBoundElement &x) {
+    TODO(getLoc(), "rank-1 bound element lowering");
+  }
+
   /// Build nested if-then-else chain by walking the right-skewed
   /// ConditionalExpr tree. The assignValue callback generates and assigns
   /// each value to avoid evaluating non-taken branches.
diff --git a/flang/lib/Lower/IterationSpace.cpp b/flang/lib/Lower/IterationSpace.cpp
index 52a15223bc1e6..4b085e6eed784 100644
--- a/flang/lib/Lower/IterationSpace.cpp
+++ b/flang/lib/Lower/IterationSpace.cpp
@@ -166,6 +166,7 @@ class ArrayBaseFinder {
     return find(x.u);
   }
   RT find(const Fortran::evaluate::DescriptorInquiry &) { return {}; }
+  RT find(const Fortran::evaluate::RankOneBoundElement &) { return {}; }
   RT find(const Fortran::evaluate::SpecificIntrinsic &) { return {}; }
   RT find(const Fortran::evaluate::ProcedureDesignator &x) { return {}; }
   RT find(const Fortran::evaluate::ProcedureRef &x) {
diff --git a/flang/lib/Lower/Support/Utils.cpp b/flang/lib/Lower/Support/Utils.cpp
index 6d38e1ff550a4..5f3be2f1783fd 100644
--- a/flang/lib/Lower/Support/Utils.cpp
+++ b/flang/lib/Lower/Support/Utils.cpp
@@ -243,6 +243,11 @@ class HashEvaluateExpr {
            static_cast<unsigned>(x.dimension());
   }
   static unsigned
+  getHashValue(const Fortran::evaluate::RankOneBoundElement &x) {
+    return getHashValue(x.base()) * 141u +
+           static_cast<unsigned>(x.dimension()) * 17u;
+  }
+  static unsigned
   getHashValue(const Fortran::evaluate::StructureConstructor &x) {
     // FIXME: hash the contents.
     return 149u;
@@ -548,6 +553,10 @@ class IsEqualEvaluateExpr {
     return isEqual(x.base(), y.base()) && x.field() == y.field() &&
            x.dimension() == y.dimension();
   }
+  static bool isEqual(const Fortran::evaluate::RankOneBoundElement &x,
+                      const Fortran::evaluate::RankOneBoundElement &y) {
+    return x.dimension() == y.dimension() && isEqual(x.base(), y.base());
+  }
   static bool isEqual(const Fortran::evaluate::StructureConstructor &x,
                       const Fortran::evaluate::StructureConstructor &y) {
     const auto &xValues = x.values();
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 42f042e470e81..6bf568bfe8b3a 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -592,8 +592,8 @@ class UnparseVisitor {
         common::visitors{
             [&](const std::list<ExplicitShapeSpec> &y) { Walk(y, ","); },
             [&](const ExplicitShapeBoundsSpec &y) {
-              llvm_unreachable(
-                  "Unparse for ExplicitShapeBoundsSpec should not be reached");
+              Walk(std::get<std::optional<IntExpr>>(y.t), ":");
+              Walk(std::get<IntExpr>(y.t));
             },
             [&](const std::list<AssumedShapeSpec> &y) { Walk(y, ","); },
             [&](const DeferredShapeSpecList &y) { Walk(y); },
diff --git a/flang/lib/Semantics/dump-expr.cpp b/flang/lib/Semantics/dump-expr.cpp
index 8d354cf65b61e..44c7d5a4058cf 100644
--- a/flang/lib/Semantics/dump-expr.cpp
+++ b/flang/lib/Semantics/dump-expr.cpp
@@ -195,6 +195,12 @@ void DumpEvaluateExpr::Show(const evaluate::DescriptorInquiry &x) {
   Outdent();
 }
 
+void DumpEvaluateExpr::Show(const evaluate::RankOneBoundElement &x) {
+  Indent(("rank-1 bound element [" + llvm::Twine(x.dimension()) + "]").str());
+  Show(x.base());
+  Outdent();
+}
+
 void DumpEvaluateExpr::Print(llvm::Twine twine) {
   outs_ << GetIndentString() << twine << '\n';
 }
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index 2865d16c68bb8..8cf64152bd783 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -61,6 +61,7 @@ static void PutBound(llvm::raw_ostream &, const Bound &);
 static void PutShapeSpec(llvm::raw_ostream &, const ShapeSpec &);
 static void PutShape(
     llvm::raw_ostream &, const ArraySpec &, char open, char close);
+static bool HasRankOneBound(const ArraySpec &);
 static void PutMapper(llvm::raw_ostream &, const Symbol &, SemanticsContext &);
 
 static llvm::raw_ostream &PutAttr(llvm::raw_ostream &, Attr);
@@ -993,18 +994,63 @@ void PutShapeSpec(llvm::raw_ostream &os, const ShapeSpec &x) {
     }
   }
 }
+
+// Check whether any bound in an ArraySpec holds a RankOneBoundElement,
+// indicating the shape came from a rank-1 integer array expression.
+bool HasRankOneBound(const ArraySpec &shape) {
+  const auto &first{shape.front()};
+  if (auto lb{first.lbound().GetExplicit()}) {
+    if (evaluate::UnwrapExpr<evaluate::RankOneBoundElement>(*lb)) {
+      return true;
+    }
+  }
+  if (auto ub{first.ubound().GetExplicit()}) {
+    if (evaluate::UnwrapExpr<evaluate::RankOneBoundElement>(*ub)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void PutShape(
     llvm::raw_ostream &os, const ArraySpec &shape, char open, char close) {
   if (!shape.empty()) {
     os << open;
-    bool first{true};
-    for (const auto &shapeSpec : shape) {
-      if (first) {
-        first = false;
-      } else {
-        os << ',';
+    if (HasRankOneBound(shape)) {
+      // Rank-1 bounds: all ShapeSpecs share the same rank-1 expression
+      // wrapped in RankOneBoundElement. Extract the base expression from the
+      // first element and emit it whole so the mod file round-trips through
+      // the parser as an ExplicitShapeBoundsSpec.
+      const auto &first{shape.front()};
+      if (!first.lbound().isColon()) {
+        auto lb{first.lbound().GetExplicit()};
+        if (auto *robe =
+                evaluate::UnwrapExpr<evaluate::RankOneBoundElement>(*lb)) {
+          robe->base().AsFortran(os);
+        } else {
+          PutBound(os, first.lbound());
+        }
+      }
+      os << ':';
+      if (!first.ubound().isColon()) {
+        auto ub{first.ubound().GetExplicit()};
+        if (auto *robe =
+                evaluate::UnwrapExpr<evaluate::RankOneBoundElement>(*ub)) {
+          robe->base().AsFortran(os);
+        } else {
+          PutBound(os, first.ubound());
+        }
+      }
+    } else {
+      bool first{true};
+      for (const auto &shapeSpec : shape) {
+        if (first) {
+          first = false;
+        } else {
+          os << ',';
+        }
+        PutShapeSpec(os, shapeSpec);
       }
-      PutShapeSpec(os, shapeSpec);
     }
     os << close;
   }
diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index 5adeb46f86a7e..21bd07b9c1c8e 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -213,6 +213,13 @@ class ArraySpecAnalyzer {
   void MakeDeferred(int);
   Bound GetBound(const std::optional<parser::SpecificationExpr> &);
   Bound GetBound(const parser::SpecificationExpr &);
+  struct ExplicitShapeBoundsResult {
+    Bound ubound;
+    std::optional<Bound> lbound;
+    std::int64_t numDims;
+  };
+  std::optional<ExplicitShapeBoundsResult> checkExplicitShapeBoundsSpec(
+      const parser::ExplicitShapeBoundsSpec &x);
 };
 
 ArraySpec AnalyzeArraySpec(
@@ -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)};
+    if (!extents) {
+      context_.Say(parser::FindSourceLocation(parseBound),
+          "Rank-1 integer array used as %s bounds in DECLARATION must "
+          "have constant size"_err_en_US,
+          isUpper ? "upper" : "lower");
+      hasError = true;
+      return std::nullopt;
+    }
+    return std::make_pair(
+        Bound{MaybeSubscriptIntExpr{std::move(asSI)}}, (*extents)[0]);
+  };
+
+  // Upper bound (required)
+  auto ubResult{analyzeBound(upperBound, /*isUpper=*/true)};
+
+  // Lower bound (optional)
+  std::optional<std::pair<Bound, std::int64_t>> lbResult;
+  if (lowerBoundOpt) {
+    lbResult = analyzeBound(*lowerBoundOpt, /*isUpper=*/false);
+  }
+
+  if (hasError) {
+    return std::nullopt;
+  }
+
+  std::int64_t ubExtent{ubResult->second};
+  std::int64_t lbExtent{lbResult ? lbResult->second : 0};
+
+  // Determine numDims from...
[truncated]

``````````

</details>


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


More information about the flang-commits mailing list