[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