diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp --- a/flang/lib/Evaluate/check-expression.cpp +++ b/flang/lib/Evaluate/check-expression.cpp @@ -756,10 +756,16 @@ explicit IsContiguousHelper(FoldingContext &c) : Base{*this}, context_{c} {} using Base::operator(); + template Result operator()(const Constant &) const { + return true; + } + Result operator()(const StaticDataObject &) const { return true; } Result operator()(const semantics::Symbol &symbol) const { const auto &ultimate{symbol.GetUltimate()}; if (ultimate.attrs().test(semantics::Attr::CONTIGUOUS)) { return true; + } else if (!IsVariable(symbol)) { + return true; } else if (ultimate.Rank() == 0) { // Extension: accept scalars as a degenerate case of // simple contiguity to allow their use in contexts like @@ -853,24 +859,44 @@ // Detect any provably empty dimension in this array section, which would // render the whole section empty and therefore vacuously contiguous. std::optional result; - for (auto j{subscript.size()}; j-- > 0;) { + bool mayBeEmpty{false}; + auto dims{subscript.size()}; + std::vector knownPartialSlice(dims, false); + for (auto j{dims}; j-- > 0;) { + std::optional dimLbound; + std::optional dimUbound; + std::optional dimExtent; + if (baseLbounds && j < baseLbounds->size()) { + if (const auto &lb{baseLbounds->at(j)}) { + dimLbound = ToInt64(Fold(context_, Expr{*lb})); + } + } + if (baseUbounds && j < baseUbounds->size()) { + if (const auto &ub{baseUbounds->at(j)}) { + dimUbound = ToInt64(Fold(context_, Expr{*ub})); + } + } + if (dimLbound && dimUbound) { + if (*dimLbound <= *dimUbound) { + dimExtent = *dimUbound - *dimLbound + 1; + } else { + // This is an empty dimension. + result = true; + dimExtent = 0; + } + } + if (const auto *triplet{std::get_if(&subscript[j].u)}) { ++rank; if (auto stride{ToInt64(triplet->stride())}) { const Expr *lowerBound{triplet->GetLower()}; - if (!lowerBound && baseLbounds && j < baseLbounds->size()) { - lowerBound = common::GetPtrFromOptional(baseLbounds->at(j)); - } const Expr *upperBound{triplet->GetUpper()}; - if (!upperBound && baseUbounds && j < baseUbounds->size()) { - upperBound = common::GetPtrFromOptional(baseUbounds->at(j)); - } std::optional lowerVal{lowerBound ? ToInt64(Fold(context_, Expr{*lowerBound})) - : std::nullopt}; + : dimLbound}; std::optional upperVal{upperBound ? ToInt64(Fold(context_, Expr{*upperBound})) - : std::nullopt}; + : dimUbound}; if (lowerVal && upperVal) { if (*lowerVal < *upperVal) { if (*stride < 0) { @@ -886,14 +912,26 @@ *lowerVal + *stride >= *upperVal) { result = false; // discontiguous if not empty } + } else { + mayBeEmpty = true; } + } else { + mayBeEmpty = true; } + } else { + mayBeEmpty = true; } } else if (subscript[j].Rank() > 0) { ++rank; if (!result) { result = false; // vector subscript } + mayBeEmpty = true; + } else { + // Scalar subscript. + if (dimExtent && *dimExtent > 1) { + knownPartialSlice[j] = true; + } } } if (rank == 0) { @@ -920,7 +958,13 @@ } ++rank; } else if (anyTriplet) { - return std::nullopt; + // If the section cannot be empty, and this dimension's + // scalar subscript is known not to cover the whole + // dimension, then the array section is provably + // discontiguous. + return (mayBeEmpty || !knownPartialSlice[j]) + ? std::nullopt + : std::make_optional(false); } } return true; // simply contiguous @@ -931,11 +975,7 @@ template std::optional IsContiguous(const A &x, FoldingContext &context) { - if (IsVariable(x)) { - return IsContiguousHelper{context}(x); - } else { - return true; // not a variable - } + return IsContiguousHelper{context}(x); } template std::optional IsContiguous( diff --git a/flang/test/Evaluate/folding09.f90 b/flang/test/Evaluate/folding09.f90 --- a/flang/test/Evaluate/folding09.f90 +++ b/flang/test/Evaluate/folding09.f90 @@ -3,6 +3,14 @@ module m real, target :: hosted(2) + integer, parameter :: cst(2,2) = reshape([1, 2, 3, 4], shape(cst)) + integer, parameter :: empty_cst(2,0) = reshape([1], shape(empty_cst)) + integer :: n + logical, parameter :: test_param1 = is_contiguous(cst(:,1)) + logical, parameter :: test_param2 = is_contiguous(cst(1,:)) + logical, parameter :: test_param3 = is_contiguous(cst(:,n)) + logical, parameter :: test_param4 = .not. is_contiguous(cst(n,:)) + logical, parameter :: test_param5 = is_contiguous(empty_cst(n,-1:n:2)) contains function f() real, pointer, contiguous :: f(:) diff --git a/flang/test/Lower/HLFIR/designators-parameter-array-slice.f90 b/flang/test/Lower/HLFIR/designators-parameter-array-slice.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/HLFIR/designators-parameter-array-slice.f90 @@ -0,0 +1,11 @@ +! Test non-contiguous slice of parameter array. +! RUN: bbc -emit-hlfir --polymorphic-type -o - %s | FileCheck %s +subroutine test2(i) + integer, parameter :: a(*,*) = reshape( [ 1,2,3,4 ], [ 2,2 ]) + integer :: x(2) + x = a(i,:) +end subroutine test2 +! Check that the result type of the designate operation +! is a box (as opposed to !fir.ref>) that is able +! to represent non-contiguous array section: +! CHECK: hlfir.designate {{.*}} shape {{.*}} : (!fir.ref>, i64, index, index, index, !fir.shape<1>) -> !fir.box>