[flang-commits] [flang] [flang][OpenACC] Reland data-clause designator path handling (PR #220781)

Eugene Epshteyn via flang-commits flang-commits at lists.llvm.org
Thu Oct 8 19:58:37 PDT 2026


================
@@ -0,0 +1,496 @@
+//===-- lib/Evaluate/designator-path.cpp ---------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Evaluate/designator-path.h"
+#include "flang/Common/idioms.h"
+#include "flang/Evaluate/fold.h"
+#include "flang/Evaluate/tools.h"
+#include "flang/Semantics/symbol.h"
+#include <cstdint>
+
+namespace Fortran::evaluate {
+
+namespace {
+// The values selected by a subscript with constant bounds and stride, as the
+// ascending arithmetic progression first, first + stride, ..., last.
+struct ConstantSection {
+  std::int64_t first;
+  std::int64_t last;
+  std::int64_t stride;
+};
+
+struct ConstantSubscriptRange {
+  std::int64_t lower;
+  std::int64_t upper;
+};
+} // namespace
+
+static DesignatorRelation ComparePartSymbols(const Symbol *x, const Symbol *y) {
+  if (x == y) {
+    return DesignatorRelation::Equal;
+  }
+  if (!x) {
+    return DesignatorRelation::Contains;
+  }
+  if (!y) {
+    return DesignatorRelation::ContainedBy;
+  }
+  return DesignatorRelation::Disjoint;
+}
+
+static bool IsFullTriplet(const Triplet &triplet) {
+  // Surface syntax `x(:)` maps to a Triplet with no lower or upper bound and
+  // an implicit stride of one.
+  auto stride{ToInt64(triplet.GetStride())};
+  return !triplet.GetLower() && !triplet.GetUpper() && stride && *stride == 1;
+}
+
+static bool IsFullSubscriptList(const std::vector<Subscript> &subscripts) {
+  if (subscripts.empty()) {
+    return false;
+  }
+  for (const Subscript &subscript : subscripts) {
+    const auto *triplet{std::get_if<Triplet>(&subscript.u)};
+    if (!triplet || !IsFullTriplet(*triplet)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static bool IsFullSlicePart(const DesignatorPath::Part &part) {
+  return !part.symbol && IsFullSubscriptList(part.subscripts);
+}
+
+static bool AreAllFullSliceParts(
+    const std::vector<DesignatorPath::Part> &parts, std::size_t first) {
+  for (std::size_t i{first}; i < parts.size(); ++i) {
+    if (!IsFullSlicePart(parts[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static std::optional<ConstantSubscriptRange> GetConstantSubscriptRange(
+    const Subscript &subscript) {
+  // Surface syntax `x(i)` maps to a scalar Subscript that holds the integer
+  // expression `i`, not a Triplet.
+  if (const auto *expr{
+          std::get_if<IndirectSubscriptIntegerExpr>(&subscript.u)}) {
+    if (auto value{ToInt64(expr->value())}) {
+      return ConstantSubscriptRange{*value, *value};
+    }
+  } else if (const auto *triplet{std::get_if<Triplet>(&subscript.u)}) {
+    // Surface syntax `x(l:u)` maps to a Triplet with explicit lower and upper
+    // bounds and an implicit stride of one. Syntax like `x(:u)`, `x(l:)`, and
+    // `x(:)` maps to missing lower and/or upper bounds, so it is not a
+    // constant finite range here.
+    const auto *lowerExpr{triplet->GetLower()};
+    const auto *upperExpr{triplet->GetUpper()};
+    auto lower{lowerExpr ? ToInt64(*lowerExpr) : std::nullopt};
+    auto upper{upperExpr ? ToInt64(*upperExpr) : std::nullopt};
+    // Surface syntax `x(l:u:s)` maps to the same Triplet representation with a
+    // non-optional stride expression.
+    auto stride{ToInt64(triplet->GetStride())};
+    if (lower && upper && stride && *stride == 1) {
+      return ConstantSubscriptRange{*lower, *upper};
+    }
+  }
+  return std::nullopt;
+}
+
+// Like GetConstantSubscriptRange, but accepts any nonzero constant stride.
+// Returns nothing for a selection with a nonconstant or missing bound or
+// stride, and for an empty section.
+static std::optional<ConstantSection> GetConstantSection(
+    const Subscript &subscript) {
+  if (const auto *expr{
+          std::get_if<IndirectSubscriptIntegerExpr>(&subscript.u)}) {
+    if (auto value{ToInt64(expr->value())}) {
+      return ConstantSection{*value, *value, 1};
+    }
+    return std::nullopt;
+  }
+  const auto *triplet{std::get_if<Triplet>(&subscript.u)};
+  if (!triplet || !triplet->GetLower() || !triplet->GetUpper()) {
+    return std::nullopt;
+  }
+  auto lower{ToInt64(*triplet->GetLower())};
+  auto upper{ToInt64(*triplet->GetUpper())};
+  auto stride{ToInt64(triplet->GetStride())};
+  if (!lower || !upper || !stride || *stride == 0) {
+    return std::nullopt;
+  }
+  if (*stride > 0) {
+    if (*lower > *upper) {
+      return std::nullopt;
+    }
+    std::int64_t count{(*upper - *lower) / *stride};
+    return ConstantSection{*lower, *lower + count * *stride, *stride};
+  }
+  if (*lower < *upper) {
+    return std::nullopt;
+  }
+  std::int64_t count{(*lower - *upper) / -*stride};
+  return ConstantSection{*lower + count * *stride, *lower, -*stride};
+}
+
+// Every value of y is a value of x. The values of y are an arithmetic
+// progression, so they lie in x exactly when the ends do and, if y has more
+// than one value, the step of y is a multiple of the step of x.
+static bool SectionContains(
+    const ConstantSection &x, const ConstantSection &y) {
+  auto inX{[&](std::int64_t value) {
+    return value >= x.first && value <= x.last &&
+        (value - x.first) % x.stride == 0;
+  }};
+  return inX(y.first) && inX(y.last) &&
+      (y.first == y.last || y.stride % x.stride == 0);
+}
+
+static DesignatorRelation CompareSubscripts(
+    const Subscript &x, const Subscript &y) {
+  if (x == y) {
+    return DesignatorRelation::Equal;
+  }
+  const auto *xTriplet{std::get_if<Triplet>(&x.u)};
+  const auto *yTriplet{std::get_if<Triplet>(&y.u)};
+  if (xTriplet && IsFullTriplet(*xTriplet)) {
+    if (yTriplet && IsFullTriplet(*yTriplet)) {
+      return DesignatorRelation::Equal;
+    }
+    return DesignatorRelation::Contains;
+  }
+  if (yTriplet && IsFullTriplet(*yTriplet)) {
+    return DesignatorRelation::ContainedBy;
+  }
+  auto xRange{GetConstantSubscriptRange(x)};
+  auto yRange{GetConstantSubscriptRange(y)};
+  if (!xRange || !yRange) {
+    // Nonconstant selectors and constant triplets with non-unit strides cannot
+    // be compared precisely, so report them as Disjoint for now. Callers must
+    // not read that as proof that the selections do not overlap. Constant
+    // triplets could be made more precise by expanding them into index sets.
+    return DesignatorRelation::Disjoint;
+  }
+  if (xRange->upper < yRange->lower || yRange->upper < xRange->lower) {
+    return DesignatorRelation::Disjoint;
+  }
+  if (xRange->lower == yRange->lower && xRange->upper == yRange->upper) {
+    return DesignatorRelation::Equal;
+  }
+  if (xRange->lower <= yRange->lower && xRange->upper >= yRange->upper) {
+    return DesignatorRelation::Contains;
+  }
+  if (yRange->lower <= xRange->lower && yRange->upper >= xRange->upper) {
+    return DesignatorRelation::ContainedBy;
+  }
+  return DesignatorRelation::Overlaps;
+}
+
+static bool SubscriptMayContain(const Subscript &x, const Subscript &y) {
+  if (x == y) {
+    return true;
+  }
+  const auto *xTriplet{std::get_if<Triplet>(&x.u)};
+  const auto *yTriplet{std::get_if<Triplet>(&y.u)};
+  if (xTriplet && IsFullTriplet(*xTriplet)) {
+    return true;
+  }
+  if (yTriplet && IsFullTriplet(*yTriplet)) {
+    return false;
+  }
+  // Decide exactly whenever both selections are constant, including a scalar
+  // against a one-element triplet and any constant stride.
+  auto xSection{GetConstantSection(x)};
+  auto ySection{GetConstantSection(y)};
+  if (xSection && ySection) {
+    return SectionContains(*xSection, *ySection);
+  }
----------------
eugeneepshteyn wrote:

(This comment was generated with the help of AI)

Returning nothing for a known-empty section makes `SubscriptMayContain` optimistic about it, so a clause that covers no elements now satisfies DEFAULT(NONE). This was rejected before this revision and is accepted now:

```fortran
subroutine s(a)
  real :: a(10)
  !$acc parallel default(none) copy(a(5:1))
  a(3) = 1.0
  !$acc end parallel
end subroutine
```

`copy(a(5:1))` covers nothing, so the reference to `a(3)` should still require a listing. Distinguishing known-empty (covers nothing) from cannot-decide (stay optimistic) restores the diagnostic; accepting an empty *reference* remains fine.

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


More information about the flang-commits mailing list