[flang-commits] [flang] [semantics][acc] start handling clauses that use sections and components (PR #211606)

Andre Kuhlenschmidt via flang-commits flang-commits at lists.llvm.org
Fri Aug 14 20:08:23 PDT 2026


https://github.com/akuhlens updated https://github.com/llvm/llvm-project/pull/211606

>From 8314bcc8d3fcdcb54ddc3a1885efa8247f9f8cea Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 8 Jul 2026 17:12:27 -0700
Subject: [PATCH 01/12] OpenACC: track exact data-sharing designators

---
 flang/lib/Semantics/resolve-directives.cpp    | 101 +++++++++++++-----
 .../OpenACC/acc-component-ref-dsa.f90         |   8 +-
 .../OpenACC/acc-dataclause-dedup.f90          |  41 +++++--
 .../OpenACC/acc-default-none-arrays.f90       |   9 +-
 4 files changed, 116 insertions(+), 43 deletions(-)

diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 15bb84d7e486f..42d3c748b6e40 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -32,6 +32,9 @@
 #include "llvm/Support/Debug.h"
 #include <list>
 #include <map>
+#include <optional>
+#include <string>
+#include <utility>
 
 namespace Fortran::semantics {
 
@@ -141,13 +144,38 @@ template <typename T> class DirectiveAttributeVisitor {
   Symbol &MakeAssocSymbol(const SourceName &name, const Symbol &prev) {
     return MakeAssocSymbol(name, prev, currScope());
   }
-  void AddDataSharingAttributeObject(SymbolRef object) {
-    dataSharingAttributeObjects_.insert(object);
+  struct DataSharingAttributeObjectKey {
+    SymbolRef symbol;
+    std::optional<std::string> designator;
+    bool operator<(const DataSharingAttributeObjectKey &that) const {
+      SymbolAddressCompare compare;
+      if (compare(symbol, that.symbol)) {
+        return true;
+      }
+      if (compare(that.symbol, symbol)) {
+        return false;
+      }
+      return designator < that.designator;
+    }
+  };
+  void AddDataSharingAttributeObject(
+      SymbolRef object, std::optional<std::string> designator = std::nullopt) {
+    dataSharingAttributeObjects_.try_emplace(
+        DataSharingAttributeObjectKey{object, std::move(designator)});
+  }
+  void AddDataSharingAttributeObject(SymbolRef object, Symbol::Flag flag,
+      std::optional<std::string> designator = std::nullopt) {
+    dataSharingAttributeObjects_.try_emplace(
+        DataSharingAttributeObjectKey{object, std::move(designator)}, flag);
   }
   void ClearDataSharingAttributeObjects() {
     dataSharingAttributeObjects_.clear();
   }
-  bool HasDataSharingAttributeObject(const Symbol &);
+  std::optional<Symbol::Flag> FindDataSharingAttributeObject(
+      const Symbol &, const std::optional<std::string> &designator);
+  bool HasDataSharingAttributeObject(
+      const Symbol &,
+      const std::optional<std::string> &designator = std::nullopt);
 
   /// Extract the iv and bounds of a DO loop:
   /// 1. The loop index/induction variable
@@ -174,7 +202,8 @@ template <typename T> class DirectiveAttributeVisitor {
   Symbol *DeclareAccessEntity(const parser::Name &, Symbol::Flag, Scope &);
   Symbol *DeclareAccessEntity(Symbol &, Symbol::Flag, Scope &);
 
-  UnorderedSymbolSet dataSharingAttributeObjects_; // on one directive
+  std::map<DataSharingAttributeObjectKey, std::optional<Symbol::Flag>>
+      dataSharingAttributeObjects_; // on one directive
   SemanticsContext &context_;
   std::vector<DirContext> dirContext_; // used as a stack
 };
@@ -387,7 +416,8 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
   void CheckMultipleAppearances(const parser::Name &, const Symbol &,
       Symbol::Flag, const parser::AccObject *occurrence = nullptr,
-      bool warnSameKindDuplicate = true);
+      bool warnSameKindDuplicate = true,
+      std::optional<std::string> designator = std::nullopt);
   void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
   void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
   void AllowOnlyVariable(const parser::AccObject &object);
@@ -1075,11 +1105,22 @@ void ResolveOmpParts(
   }
 }
 
+template <typename T>
+std::optional<Symbol::Flag>
+DirectiveAttributeVisitor<T>::FindDataSharingAttributeObject(
+    const Symbol &object, const std::optional<std::string> &designator) {
+  auto it{dataSharingAttributeObjects_.find({object, designator})};
+  if (it != dataSharingAttributeObjects_.end()) {
+    return it->second;
+  }
+  return std::nullopt;
+}
+
 template <typename T>
 bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
-    const Symbol &object) {
-  auto it{dataSharingAttributeObjects_.find(object)};
-  return it != dataSharingAttributeObjects_.end();
+    const Symbol &object, const std::optional<std::string> &designator) {
+  return dataSharingAttributeObjects_.find({object, designator}) !=
+      dataSharingAttributeObjects_.end();
 }
 
 template <typename T>
@@ -1955,9 +1996,11 @@ void AccAttributeVisitor::ResolveAccObject(
   common::visit(
       common::visitors{
           [&](const parser::Designator &designator) {
-            const bool preciseDesignator{
+            const bool isBareName{
                 parser::GetDesignatorNameIfDataRef(designator) != nullptr};
-            if (!preciseDesignator) {
+            std::optional<std::string> designatorKey;
+            if (!isBareName) {
+              designatorKey = designator.source.ToString();
               // Subscripted designator: evaluate subscripts and detect
               // the substring case that is disallowed in OpenACC clauses.
               if (AnalyzeExpr(context_, designator)) {
@@ -1969,9 +2012,17 @@ void AccAttributeVisitor::ResolveAccObject(
                 }
               }
             }
+            const bool isDataSharing{dataSharingAttributeFlags.test(accFlag)};
             if (ContainsStructureComponent(designator)) {
               // Do not register the base object for a component reference until
               // OpenACC DSA tracking can distinguish subcomponents.
+              if (isDataSharing) {
+                const parser::Name &baseName{parser::GetFirstName(designator)};
+                if (baseName.symbol) {
+                  CheckMultipleAppearances(baseName, *baseName.symbol, accFlag,
+                      &accObject, true, designatorKey);
+                }
+              }
               return;
             }
             // GetFirstName extracts the base symbol from both bare data
@@ -1979,15 +2030,13 @@ void AccAttributeVisitor::ResolveAccObject(
             // that DEFAULT(NONE) checking does not spuriously flag variables
             // that are explicitly listed in a data clause as array sections.
             // TODO: Multiple array sections of the same array with different
-            // data sharing attributes is not currently supported.
-            // TODO: Subcomponent designators should also be tracked precisely.
+            // data mapping attributes is not currently supported.
             const parser::Name &baseName{parser::GetFirstName(designator)};
             if (auto *symbol{ResolveAcc(baseName, accFlag, currScope())}) {
               AddToContextObjectWithDSA(*symbol, accFlag);
-              if (preciseDesignator &&
-                  dataSharingAttributeFlags.test(accFlag)) {
-                CheckMultipleAppearances(
-                    baseName, *symbol, accFlag, &accObject, preciseDesignator);
+              if (isDataSharing) {
+                CheckMultipleAppearances(baseName, *symbol, accFlag, &accObject,
+                    true, designatorKey);
               }
             }
           },
@@ -2045,9 +2094,10 @@ Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
 
 void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
     const Symbol &symbol, Symbol::Flag accFlag,
-    const parser::AccObject *occurrence, bool warnSameKindDuplicate) {
+    const parser::AccObject *occurrence, bool warnSameKindDuplicate,
+    std::optional<std::string> designator) {
   const auto *target{&symbol};
-  if (HasDataSharingAttributeObject(*target)) {
+  if (auto firstFlag{FindDataSharingAttributeObject(*target, designator)}) {
     // A same-kind duplicate (e.g. private(x, x) or private(x) private(x))
     // is benign: warn and tag this AccObject occurrence so rewrite-parse-tree
     // can drop it from the clause list. Cross-kind duplicates (e.g.
@@ -2056,23 +2106,22 @@ void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
     // Reduction is excluded from the benign case: two reduction clauses
     // with the same Symbol::Flag may still differ in operator, which is a
     // real conflict that dedup would silently hide.
-    auto firstFlag{GetContext().FindSymbolWithDSA(*target)};
-    if (warnSameKindDuplicate && occurrence && firstFlag &&
-        *firstFlag == accFlag && accFlag != Symbol::Flag::AccReduction) {
+    const std::string objectName{designator.value_or(name.ToString())};
+    if (warnSameKindDuplicate && occurrence && *firstFlag == accFlag &&
+        accFlag != Symbol::Flag::AccReduction) {
       context_.Warn(common::UsageWarning::OpenAccUsage, name.source,
           "'%s' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored"_warn_en_US,
-          name.ToString());
+          objectName);
       context_.MarkAccObjectDuplicate(occurrence);
-    } else if (firstFlag && *firstFlag == accFlag &&
-        accFlag != Symbol::Flag::AccReduction) {
+    } else if (*firstFlag == accFlag && accFlag != Symbol::Flag::AccReduction) {
       return;
     } else {
       context_.Say(name.source,
           "'%s' appears in more than one data-sharing clause on the same OpenACC directive"_err_en_US,
-          name.ToString());
+          objectName);
     }
   } else {
-    AddDataSharingAttributeObject(*target);
+    AddDataSharingAttributeObject(*target, accFlag, std::move(designator));
   }
 }
 
diff --git a/flang/test/Semantics/OpenACC/acc-component-ref-dsa.f90 b/flang/test/Semantics/OpenACC/acc-component-ref-dsa.f90
index 03ed2f1163314..e41c739cbcca3 100644
--- a/flang/test/Semantics/OpenACC/acc-component-ref-dsa.f90
+++ b/flang/test/Semantics/OpenACC/acc-component-ref-dsa.f90
@@ -1,8 +1,8 @@
 ! RUN: %python %S/../test_errors.py %s %flang -fopenacc -fno-openacc-default-none-scalars-strict
 
-! Derived-type component references in OpenACC clauses are accepted for now, but
-! the current DSA handling is deliberately imprecise:
-! - duplicate and conflicting component references are not diagnosed;
+! Derived-type component references in OpenACC clauses are accepted. Exact
+! duplicate and conflicting component references in data-sharing clauses are
+! diagnosed, but broader containment is deliberately limited for now:
 ! - component clauses do not satisfy DEFAULT(NONE) for the base object;
 ! - whole-object/component conflicts are not diagnosed.
 
@@ -67,6 +67,7 @@ subroutine test_same_object_same_dsa_components()
   use component_ref_types, only: point_t
   type(point_t) :: p
   integer :: i
+  !WARNING: 'p%x' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
   !$acc parallel loop private(p%x, p%y, p%x)
   do i = 1, 10
     p%x = real(i)
@@ -79,6 +80,7 @@ subroutine test_same_object_incompatible_same_component()
   use component_ref_types, only: point_t
   type(point_t) :: p
   integer :: i
+  !ERROR: 'p%x' appears in more than one data-sharing clause on the same OpenACC directive
   !$acc parallel loop private(p%x) firstprivate(p%x)
   do i = 1, 10
     p%x = real(i)
diff --git a/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90 b/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
index e29332578a0bf..b7208acab151d 100644
--- a/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
+++ b/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
@@ -71,9 +71,9 @@ program test_dataclause_dedup
   do i = 1, 10
   end do
 
-  ! Regression coverage for non-bare designators: the dedup machinery only
-  ! examines simple-Name DataRefs, so distinct array elements and array
-  ! sections must pass through untouched, with no warning and no erasure.
+  ! Regression coverage for non-bare designators: distinct array elements and
+  ! sections must pass through untouched, while exact duplicates are diagnosed
+  ! precisely rather than by the base array symbol.
   block
     integer :: arr(10)
     integer, target :: t1, t2
@@ -94,26 +94,49 @@ program test_dataclause_dedup
     do i = 1, 10
     end do
 
-    ! Same array element listed twice -- not deduped, since GetDesignatorName-
-    ! IfDataRef returns null for ArrayElement and CheckMultipleAppearances
-    ! is never invoked. Compiles without diagnostics.
+    ! Different array elements in different data-sharing clauses -- not
+    ! duplicates.
+    !$acc parallel loop private(arr(1)) firstprivate(arr(2))
+    do i = 1, 10
+    end do
+
+    ! Different array sections in different data-sharing clauses -- not
+    ! duplicates.
+    !$acc parallel loop private(arr(1:5)) firstprivate(arr(6:10))
+    do i = 1, 10
+    end do
+
+    ! Same array element listed twice in the same data-sharing clause.
+    !WARNING: 'arr(1)' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
     !$acc parallel loop private(arr(1), arr(1))
     do i = 1, 10
     end do
 
-    ! Same array section listed twice -- same reasoning, no diagnostic.
+    ! Same array element listed in conflicting data-sharing clauses.
+    !ERROR: 'arr(1)' appears in more than one data-sharing clause on the same OpenACC directive
+    !$acc parallel loop private(arr(1)) firstprivate(arr(1))
+    do i = 1, 10
+    end do
+
+    ! Same array section listed twice in the same data-sharing clause.
+    !WARNING: 'arr(1:5)' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
     !$acc parallel loop private(arr(1:5), arr(1:5))
     do i = 1, 10
     end do
 
+    ! Same array section listed in conflicting data-sharing clauses.
+    !ERROR: 'arr(1:5)' appears in more than one data-sharing clause on the same OpenACC directive
+    !$acc parallel loop private(arr(1:5)) firstprivate(arr(1:5))
+    do i = 1, 10
+    end do
+
     ! Distinct structure components -- not duplicates.
     !$acc parallel loop private(s%a, s%b)
     do i = 1, 10
     end do
 
     ! Mixing a bare-name designator and an array-element designator on the
-    ! same symbol must not trigger dedup -- the array element doesn't go
-    ! through the duplicate check at all.
+    ! same symbol is not an exact duplicate.
     !$acc parallel loop private(arr, arr(1))
     do i = 1, 10
     end do
diff --git a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90 b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
index ee1587f66034f..f96b426e19af0 100644
--- a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
+++ b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
@@ -3,8 +3,8 @@
 ! Verify that array sections explicitly listed in OpenACC data clauses are
 ! correctly registered as having a DSA, so DEFAULT(NONE) does not produce
 ! spurious errors.  This does not implement section-level overlap
-! detection or deduplication; duplicate/conflict diagnostics only apply to bare
-! names.  This also covers the substring-in-clause error.
+! detection; duplicate/conflict diagnostics only apply to exact data-sharing
+! designators.  This also covers the substring-in-clause error.
 
 ! 1. Data-mapping clauses with array sections: no DEFAULT(NONE) errors.
 subroutine test_data_mapping_sections(n)
@@ -65,9 +65,8 @@ subroutine test_unlisted_array(n)
 end subroutine
 
 ! 5. Duplicate bare-name under the same data-sharing clause: warn and dedup.
-!    (Array sections like private(a(1:5), a(6:10)) are not deduplicated or
-!    checked for overlap because base-name comparison cannot distinguish
-!    different sections of the same array.)
+!    Exact section duplicates are also diagnosed, but overlapping or distinct
+!    sections like private(a(1:5), a(6:10)) are not treated as duplicates.
 subroutine test_duplicate_private_bare(n)
   implicit none
   integer, intent(in) :: n

>From 4a43b056d3e06e69770c751c32d4597775eef956 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 23 Jul 2026 01:20:38 -0700
Subject: [PATCH 02/12] OpenACC: compare data-sharing designator paths

---
 .../include/flang/Evaluate/designator-path.h  | 123 +++++
 flang/lib/Evaluate/CMakeLists.txt             |   1 +
 flang/lib/Evaluate/designator-path.cpp        | 413 ++++++++++++++
 flang/lib/Semantics/resolve-directives.cpp    | 521 +++++++++++++++---
 .../test/Lower/OpenACC/acc-dedup-private.f90  |  91 +++
 .../OpenACC/acc-component-ref-dsa.f90         |  82 ++-
 .../OpenACC/acc-dataclause-dedup.f90          |  90 +++
 .../OpenACC/acc-default-none-arrays.f90       |  63 ++-
 flang/unittests/Evaluate/CMakeLists.txt       |   7 +
 flang/unittests/Evaluate/designator-path.cpp  | 374 +++++++++++++
 10 files changed, 1689 insertions(+), 76 deletions(-)
 create mode 100644 flang/include/flang/Evaluate/designator-path.h
 create mode 100644 flang/lib/Evaluate/designator-path.cpp
 create mode 100644 flang/unittests/Evaluate/designator-path.cpp

diff --git a/flang/include/flang/Evaluate/designator-path.h b/flang/include/flang/Evaluate/designator-path.h
new file mode 100644
index 0000000000000..7d48e16f46e2c
--- /dev/null
+++ b/flang/include/flang/Evaluate/designator-path.h
@@ -0,0 +1,123 @@
+//===-- include/flang/Evaluate/designator-path.h ---------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef FORTRAN_EVALUATE_DESIGNATOR_PATH_H_
+#define FORTRAN_EVALUATE_DESIGNATOR_PATH_H_
+
+#include "flang/Evaluate/expression.h"
+#include <cstdint>
+#include <optional>
+#include <utility>
+#include <vector>
+
+namespace Fortran::evaluate {
+
+enum class DesignatorRelation {
+  Equal,
+  Contains,
+  ContainedBy,
+  Overlaps,
+  Disjoint,
+};
+
+struct DesignatorPath {
+  // A DesignatorPath represents a constrained prefix of a valid Fortran
+  // designator:
+  //   - an optional NamedEntity base, and
+  //   - zero or more suffix parts.
+  //
+  // The optional base distinguishes the named entity from subsequent part
+  // references. Each suffix part first applies optional subscripts to the
+  // current entity and then optionally selects a component symbol. An empty
+  // subscript list means there is no explicit subscript selector on this part;
+  // a full slice `(:)` is represented as a single Triplet subscript with no
+  // lower or upper bound and stride one. This can later grow a final optional
+  // variant for terminal designator pieces that are not part refs, such as
+  // complex parts, character substrings, or coarray references, while still
+  // preserving a valid designator shape.
+  struct Part {
+    std::vector<Subscript> subscripts;
+    const Symbol *symbol{nullptr};
+    bool operator==(const Part &that) const {
+      return subscripts == that.subscripts && symbol == that.symbol;
+    }
+  };
+
+  static std::optional<DesignatorPath> Get(
+      const std::optional<Expr<SomeType>> &);
+  DesignatorRelation Compare(const DesignatorPath &) const;
+  bool MayContain(const DesignatorPath &) const;
+  void SetBase(NamedEntity);
+  void AddComponent(const Symbol &);
+  void AddSubscripts(std::vector<Subscript>);
+  const std::optional<NamedEntity> &Base() const { return base; }
+  const std::vector<Part> &Parts() const { return parts; }
+  bool empty() const { return !base && parts.empty(); }
+  bool HasBaseOnly() const { return base && parts.empty(); }
+  bool operator==(const DesignatorPath &that) const {
+    return base == that.base && parts == that.parts;
+  }
+
+  struct ConstantSubscriptRange {
+    std::int64_t lower;
+    std::int64_t upper;
+  };
+
+  static std::optional<ConstantSubscriptRange> GetConstantSubscriptRange(
+      const Subscript &);
+  static bool IsFullTriplet(const Triplet &);
+  static DesignatorRelation CompareSubscripts(
+      const Subscript &, const Subscript &);
+  static DesignatorRelation CompareSubscriptLists(
+      const std::vector<Subscript> &, const std::vector<Subscript> &);
+  static DesignatorRelation CompareParts(const Part &, const Part &);
+  static DesignatorRelation CombineRelations(
+      bool contains, bool containedBy, bool overlaps);
+  static bool SubscriptMayContain(const Subscript &, const Subscript &);
+  static bool SubscriptListMayContain(
+      const std::vector<Subscript> &, const std::vector<Subscript> &);
+  static bool PartMayContain(const Part &, const Part &);
+
+private:
+  void AddDataRef(const DataRef &);
+  void AddComponent(const Component &);
+  void AddNamedEntity(const NamedEntity &);
+  void AddArrayRef(const ArrayRef &);
+  void AddCoarrayRef(const CoarrayRef &);
+
+  std::optional<NamedEntity> base;
+  std::vector<Part> parts;
+};
+
+template <typename A> class DesignatorPathMap {
+public:
+  struct Entry {
+    DesignatorPath path;
+    A value;
+  };
+  using iterator = typename std::vector<Entry>::iterator;
+  using const_iterator = typename std::vector<Entry>::const_iterator;
+
+  iterator begin() { return entries_.begin(); }
+  iterator end() { return entries_.end(); }
+  const_iterator begin() const { return entries_.begin(); }
+  const_iterator end() const { return entries_.end(); }
+  bool empty() const { return entries_.empty(); }
+  void clear() { entries_.clear(); }
+  iterator erase(iterator iter) { return entries_.erase(iter); }
+  void push_back(DesignatorPath path, A value) {
+    entries_.push_back({std::move(path), std::move(value)});
+  }
+
+private:
+  std::vector<Entry> entries_;
+};
+
+} // namespace Fortran::evaluate
+
+#endif // FORTRAN_EVALUATE_DESIGNATOR_PATH_H_
diff --git a/flang/lib/Evaluate/CMakeLists.txt b/flang/lib/Evaluate/CMakeLists.txt
index 472ecb6d8d079..31b9269aaf869 100644
--- a/flang/lib/Evaluate/CMakeLists.txt
+++ b/flang/lib/Evaluate/CMakeLists.txt
@@ -35,6 +35,7 @@ add_flang_library(FortranEvaluate
   common.cpp
   complex.cpp
   constant.cpp
+  designator-path.cpp
   expression.cpp
   fold.cpp
   fold-character.cpp
diff --git a/flang/lib/Evaluate/designator-path.cpp b/flang/lib/Evaluate/designator-path.cpp
new file mode 100644
index 0000000000000..e8eac91c12daa
--- /dev/null
+++ b/flang/lib/Evaluate/designator-path.cpp
@@ -0,0 +1,413 @@
+//===-- 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/Evaluate/fold.h"
+#include "flang/Evaluate/tools.h"
+#include "llvm/Support/ErrorHandling.h"
+
+namespace Fortran::evaluate {
+
+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 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 || !DesignatorPath::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;
+}
+
+std::optional<DesignatorPath::ConstantSubscriptRange>
+DesignatorPath::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;
+}
+
+bool DesignatorPath::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;
+}
+
+DesignatorRelation DesignatorPath::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) {
+    // Constant triplets with strides other than one cannot be represented as a
+    // single range here, so they are treated as disjoint for now. This could be
+    // made more precise by expanding constant triplets into index sets and
+    // comparing those 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;
+}
+
+bool DesignatorPath::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;
+  }
+  if (!xTriplet && yTriplet) {
+    return false;
+  }
+  auto xRange{GetConstantSubscriptRange(x)};
+  auto yRange{GetConstantSubscriptRange(y)};
+  if (xRange && yRange) {
+    return xRange->lower <= yRange->lower && xRange->upper >= yRange->upper;
+  }
+  if (xTriplet) {
+    return true;
+  }
+  return !ToInt64(std::get<IndirectSubscriptIntegerExpr>(x.u).value()) ||
+      !ToInt64(std::get<IndirectSubscriptIntegerExpr>(y.u).value());
+}
+
+bool DesignatorPath::SubscriptListMayContain(
+    const std::vector<Subscript> &x, const std::vector<Subscript> &y) {
+  if (x.empty()) {
+    return true;
+  }
+  if (IsFullSubscriptList(x)) {
+    return y.empty() || x.size() == y.size();
+  }
+  if (y.empty()) {
+    return false;
+  }
+  if (x.size() != y.size()) {
+    return false;
+  }
+  for (std::size_t i{0}; i < x.size(); ++i) {
+    if (!SubscriptMayContain(x[i], y[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool DesignatorPath::PartMayContain(const Part &x, const Part &y) {
+  return SubscriptListMayContain(x.subscripts, y.subscripts) &&
+      (!x.symbol || x.symbol == y.symbol);
+}
+
+DesignatorRelation DesignatorPath::CombineRelations(
+    bool contains, bool containedBy, bool overlaps) {
+  if (overlaps || (contains && containedBy)) {
+    return DesignatorRelation::Overlaps;
+  }
+  if (contains) {
+    return DesignatorRelation::Contains;
+  }
+  if (containedBy) {
+    return DesignatorRelation::ContainedBy;
+  }
+  return DesignatorRelation::Equal;
+}
+
+DesignatorRelation DesignatorPath::CompareSubscriptLists(
+    const std::vector<Subscript> &x, const std::vector<Subscript> &y) {
+  if (x.empty() && y.empty()) {
+    return DesignatorRelation::Equal;
+  }
+  if (x.empty()) {
+    return IsFullSubscriptList(y) ? DesignatorRelation::Equal
+                                  : DesignatorRelation::Contains;
+  }
+  if (y.empty()) {
+    return IsFullSubscriptList(x) ? DesignatorRelation::Equal
+                                  : DesignatorRelation::ContainedBy;
+  }
+  const bool xFull{IsFullSubscriptList(x)};
+  const bool yFull{IsFullSubscriptList(y)};
+  if (xFull || yFull) {
+    if (x.size() != y.size()) {
+      return DesignatorRelation::Disjoint;
+    }
+    if (xFull && yFull) {
+      return DesignatorRelation::Equal;
+    }
+    return xFull ? DesignatorRelation::Contains
+                 : DesignatorRelation::ContainedBy;
+  }
+  if (x.size() != y.size()) {
+    return DesignatorRelation::Disjoint;
+  }
+  bool contains{false};
+  bool containedBy{false};
+  bool overlaps{false};
+  for (std::size_t i{0}; i < x.size(); ++i) {
+    switch (CompareSubscripts(x[i], y[i])) {
+    case DesignatorRelation::Equal:
+      break;
+    case DesignatorRelation::Contains:
+      contains = true;
+      break;
+    case DesignatorRelation::ContainedBy:
+      containedBy = true;
+      break;
+    case DesignatorRelation::Overlaps:
+      overlaps = true;
+      break;
+    case DesignatorRelation::Disjoint:
+      return DesignatorRelation::Disjoint;
+    }
+  }
+  return CombineRelations(contains, containedBy, overlaps);
+}
+
+DesignatorRelation DesignatorPath::CompareParts(const Part &x, const Part &y) {
+  DesignatorRelation subscriptRelation{
+      CompareSubscriptLists(x.subscripts, y.subscripts)};
+  if (subscriptRelation == DesignatorRelation::Disjoint) {
+    return DesignatorRelation::Disjoint;
+  }
+  DesignatorRelation symbolRelation{ComparePartSymbols(x.symbol, y.symbol)};
+  if (symbolRelation == DesignatorRelation::Disjoint) {
+    return DesignatorRelation::Disjoint;
+  }
+  bool contains{subscriptRelation == DesignatorRelation::Contains ||
+      symbolRelation == DesignatorRelation::Contains};
+  bool containedBy{subscriptRelation == DesignatorRelation::ContainedBy ||
+      symbolRelation == DesignatorRelation::ContainedBy};
+  bool overlaps{subscriptRelation == DesignatorRelation::Overlaps ||
+      symbolRelation == DesignatorRelation::Overlaps};
+  return CombineRelations(contains, containedBy, overlaps);
+}
+
+DesignatorRelation DesignatorPath::Compare(const DesignatorPath &that) const {
+  if (*this == that) {
+    return DesignatorRelation::Equal;
+  }
+  if (empty() || that.empty()) {
+    return DesignatorRelation::Disjoint;
+  }
+  if (base || that.base) {
+    if (!base || !that.base || !(*base == *that.base)) {
+      return DesignatorRelation::Disjoint;
+    }
+  }
+  if (parts.empty() || that.parts.empty()) {
+    if ((!parts.empty() && AreAllFullSliceParts(parts, 0)) ||
+        (!that.parts.empty() && AreAllFullSliceParts(that.parts, 0))) {
+      return DesignatorRelation::Equal;
+    }
+    return parts.empty() ? DesignatorRelation::Contains
+                         : DesignatorRelation::ContainedBy;
+  }
+  bool contains{false};
+  bool containedBy{false};
+  bool overlaps{false};
+  const std::size_t commonSize{
+      parts.size() < that.parts.size() ? parts.size() : that.parts.size()};
+  for (std::size_t i{0}; i < commonSize; ++i) {
+    switch (CompareParts(parts[i], that.parts[i])) {
+    case DesignatorRelation::Equal:
+      break;
+    case DesignatorRelation::Contains:
+      contains = true;
+      break;
+    case DesignatorRelation::ContainedBy:
+      containedBy = true;
+      break;
+    case DesignatorRelation::Overlaps:
+      overlaps = true;
+      break;
+    case DesignatorRelation::Disjoint:
+      return DesignatorRelation::Disjoint;
+    }
+  }
+  if (parts.size() < that.parts.size()) {
+    if (!AreAllFullSliceParts(that.parts, parts.size())) {
+      contains = true;
+    }
+  } else if (that.parts.size() < parts.size()) {
+    if (!AreAllFullSliceParts(parts, that.parts.size())) {
+      containedBy = true;
+    }
+  }
+  return CombineRelations(contains, containedBy, overlaps);
+}
+
+bool DesignatorPath::MayContain(const DesignatorPath &that) const {
+  if (*this == that || empty()) {
+    return true;
+  }
+  if (base || that.base) {
+    if (!base || !that.base || !(*base == *that.base)) {
+      return false;
+    }
+  }
+  if (that.parts.empty()) {
+    return AreAllFullSliceParts(parts, 0);
+  }
+  if (parts.size() > that.parts.size() &&
+      !AreAllFullSliceParts(parts, that.parts.size())) {
+    return false;
+  }
+  if (parts.empty()) {
+    return true;
+  }
+  for (std::size_t i{0}; i < parts.size(); ++i) {
+    if (i >= that.parts.size()) {
+      return AreAllFullSliceParts(parts, i);
+    }
+    if (!PartMayContain(parts[i], that.parts[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void DesignatorPath::SetBase(NamedEntity entity) { base = std::move(entity); }
+
+void DesignatorPath::AddComponent(const Symbol &symbol) {
+  if (!parts.empty() && !parts.back().symbol) {
+    parts.back().symbol = &symbol;
+  } else {
+    parts.push_back({{}, &symbol});
+  }
+}
+
+void DesignatorPath::AddSubscripts(std::vector<Subscript> subscripts) {
+  parts.push_back({std::move(subscripts), nullptr});
+}
+
+void DesignatorPath::AddComponent(const Component &component) {
+  AddDataRef(component.base());
+  AddComponent(*component.symbol());
+}
+
+void DesignatorPath::AddNamedEntity(const NamedEntity &entity) {
+  if (const auto *symbol{entity.UnwrapSymbolRef()}) {
+    SetBase(NamedEntity{symbol->get()});
+  } else if (const auto *component{entity.UnwrapComponent()}) {
+    AddComponent(*component);
+  }
+}
+
+void DesignatorPath::AddArrayRef(const ArrayRef &arrayRef) {
+  AddNamedEntity(arrayRef.base());
+  AddSubscripts(arrayRef.subscript());
+}
+
+void DesignatorPath::AddCoarrayRef(const CoarrayRef &coarrayRef) {
+  AddDataRef(coarrayRef.base());
+}
+
+void DesignatorPath::AddDataRef(const DataRef &dataRef) {
+  common::visit(
+      common::visitors{
+          [&](SymbolRef symbol) { SetBase(NamedEntity{symbol.get()}); },
+          [&](const Component &component) { AddComponent(component); },
+          [&](const ArrayRef &arrayRef) { AddArrayRef(arrayRef); },
+          [&](const CoarrayRef &coarrayRef) { AddCoarrayRef(coarrayRef); },
+      },
+      dataRef.u);
+}
+
+std::optional<DesignatorPath> DesignatorPath::Get(
+    const std::optional<Expr<SomeType>> &expr) {
+  if (std::optional<DataRef> dataRef{ExtractDataRef(expr)}) {
+    DesignatorPath path;
+    path.AddDataRef(*dataRef);
+    if (!path.empty()) {
+      return path;
+    }
+  }
+  return std::nullopt;
+}
+
+} // namespace Fortran::evaluate
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 42d3c748b6e40..426082c86d460 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -12,6 +12,7 @@
 #include "check-omp-structure.h"
 #include "resolve-names-utils.h"
 #include "flang/Common/idioms.h"
+#include "flang/Evaluate/designator-path.h"
 #include "flang/Evaluate/fold.h"
 #include "flang/Evaluate/tools.h"
 #include "flang/Evaluate/type.h"
@@ -38,6 +39,11 @@
 
 namespace Fortran::semantics {
 
+using evaluate::DesignatorPath;
+using evaluate::DesignatorPathMap;
+using evaluate::DesignatorRelation;
+using evaluate::NamedEntity;
+
 template <typename T>
 static Scope *GetScope(SemanticsContext &context, const T &x) {
   if (auto source{GetLastSource(x)}) {
@@ -173,8 +179,7 @@ template <typename T> class DirectiveAttributeVisitor {
   }
   std::optional<Symbol::Flag> FindDataSharingAttributeObject(
       const Symbol &, const std::optional<std::string> &designator);
-  bool HasDataSharingAttributeObject(
-      const Symbol &,
+  bool HasDataSharingAttributeObject(const Symbol &,
       const std::optional<std::string> &designator = std::nullopt);
 
   /// Extract the iv and bounds of a DO loop:
@@ -218,15 +223,15 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   template <typename A> void Post(const A &) {}
 
   bool Pre(const parser::OpenACCBlockConstruct &);
-  void Post(const parser::OpenACCBlockConstruct &) { PopContext(); }
+  void Post(const parser::OpenACCBlockConstruct &) { PopAccContext(); }
   bool Pre(const parser::OpenACCCombinedConstruct &);
-  void Post(const parser::OpenACCCombinedConstruct &) { PopContext(); }
+  void Post(const parser::OpenACCCombinedConstruct &) { PopAccContext(); }
   void Post(const parser::AccBeginCombinedDirective &) {
     GetContext().withinConstruct = true;
   }
 
   bool Pre(const parser::OpenACCDeclarativeConstruct &);
-  void Post(const parser::OpenACCDeclarativeConstruct &) { PopContext(); }
+  void Post(const parser::OpenACCDeclarativeConstruct &) { PopAccContext(); }
 
   void Post(const parser::AccDeclarativeDirective &) {
     GetContext().withinConstruct = true;
@@ -241,7 +246,7 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   }
 
   bool Pre(const parser::OpenACCLoopConstruct &);
-  void Post(const parser::OpenACCLoopConstruct &) { PopContext(); }
+  void Post(const parser::OpenACCLoopConstruct &) { PopAccContext(); }
   void Post(const parser::AccLoopDirective &) {
     GetContext().withinConstruct = true;
   }
@@ -252,25 +257,25 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
     if (!dirContext_.empty() && GetContext().withinConstruct) {
       if (auto *symbol{ResolveAcc(
               x.Name().thing, Symbol::Flag::AccPrivate, currScope())}) {
-        AddToContextObjectWithDSA(*symbol, Symbol::Flag::AccPrivate);
+        AddAccObjectWithDSA(*symbol, Symbol::Flag::AccPrivate);
       }
     }
     return true;
   }
 
   bool Pre(const parser::OpenACCStandaloneConstruct &);
-  void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); }
+  void Post(const parser::OpenACCStandaloneConstruct &) { PopAccContext(); }
   void Post(const parser::AccStandaloneDirective &) {
     GetContext().withinConstruct = true;
   }
 
   bool Pre(const parser::OpenACCWaitConstruct &);
-  void Post(const parser::OpenACCWaitConstruct &) { PopContext(); }
+  void Post(const parser::OpenACCWaitConstruct &) { PopAccContext(); }
   bool Pre(const parser::OpenACCAtomicConstruct &);
-  void Post(const parser::OpenACCAtomicConstruct &) { PopContext(); }
+  void Post(const parser::OpenACCAtomicConstruct &) { PopAccContext(); }
 
   bool Pre(const parser::OpenACCCacheConstruct &);
-  void Post(const parser::OpenACCCacheConstruct &) { PopContext(); }
+  void Post(const parser::OpenACCCacheConstruct &) { PopAccContext(); }
 
   void Post(const parser::AccDefaultClause &);
 
@@ -381,9 +386,32 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
     return false;
   }
 
+  bool Pre(const parser::Expr &);
+  void Post(const parser::Expr &);
+  bool Pre(const parser::Variable &);
+  void Post(const parser::Variable &);
+  void Post(const parser::ArrayElement &);
   void Post(const parser::Name &);
 
 private:
+  struct AccDataSharingEntry {
+    SymbolRef symbol;
+    Symbol::Flag flag;
+    const parser::AccObject *occurrence{nullptr};
+    std::string objectName;
+  };
+
+  void PushAccContext(const parser::CharBlock &, llvm::acc::Directive, Scope &);
+  void PushAccContext(const parser::CharBlock &, llvm::acc::Directive);
+  void PopAccContext();
+  void AddAccObjectWithDSA(
+      const Symbol &, Symbol::Flag, DesignatorPath designator = {});
+  bool AccObjectWithDSAVisible(
+      const Symbol &, const std::optional<DesignatorPath> &) const;
+  void AdjustAccSymbolReference(const parser::Name &);
+  void CheckAccDefaultNoneReference(
+      const parser::Name &, std::optional<DesignatorPath> = std::nullopt);
+
   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &);
   bool HasForceCollapseModifier(const parser::AccClauseList &);
 
@@ -417,7 +445,9 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   void CheckMultipleAppearances(const parser::Name &, const Symbol &,
       Symbol::Flag, const parser::AccObject *occurrence = nullptr,
       bool warnSameKindDuplicate = true,
-      std::optional<std::string> designator = std::nullopt);
+      std::optional<std::string> objectName = {},
+      DesignatorPath designator = {});
+  void ClearAccDataSharingEntries() { accDataSharingEntries_.clear(); }
   void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
   void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
   void AllowOnlyVariable(const parser::AccObject &object);
@@ -432,6 +462,8 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   void ClearUseDeviceObjects() { useDeviceObjects_.clear(); }
   UnorderedSymbolSet useDeviceObjects_;
 
+  DesignatorPathMap<AccDataSharingEntry> accDataSharingEntries_;
+  std::vector<DesignatorPathMap<AccDataSharingEntry>> accObjectWithDSA_;
   Scope *topScope_;
 };
 
@@ -1205,12 +1237,12 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) {
   case llvm::acc::Directive::ACCD_kernels:
   case llvm::acc::Directive::ACCD_parallel:
   case llvm::acc::Directive::ACCD_serial:
-    PushContext(blockDir.source, blockDir.v);
+    PushAccContext(blockDir.source, blockDir.v);
     break;
   default:
     break;
   }
-  ClearDataSharingAttributeObjects();
+  ClearAccDataSharingEntries();
   ClearUseDeviceObjects();
   return true;
 }
@@ -1220,9 +1252,9 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct &x) {
           std::get_if<parser::OpenACCStandaloneDeclarativeConstruct>(&x.u)}) {
     const auto &declDir{
         std::get<parser::AccDeclarativeDirective>(declConstruct->t)};
-    PushContext(declDir.source, llvm::acc::Directive::ACCD_declare);
+    PushAccContext(declDir.source, llvm::acc::Directive::ACCD_declare);
   }
-  ClearDataSharingAttributeObjects();
+  ClearAccDataSharingEntries();
   return true;
 }
 
@@ -1292,9 +1324,9 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) {
   const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
   const auto &clauseList{std::get<parser::AccClauseList>(beginDir.t)};
   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
-    PushContext(loopDir.source, loopDir.v);
+    PushAccContext(loopDir.source, loopDir.v);
   }
-  ClearDataSharingAttributeObjects();
+  ClearAccDataSharingEntries();
   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
   CheckAssociatedLoop(*outer, HasForceCollapseModifier(clauseList));
@@ -1310,12 +1342,12 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) {
   case llvm::acc::Directive::ACCD_set:
   case llvm::acc::Directive::ACCD_shutdown:
   case llvm::acc::Directive::ACCD_update:
-    PushContext(standaloneDir.source, standaloneDir.v);
+    PushAccContext(standaloneDir.source, standaloneDir.v);
     break;
   default:
     break;
   }
-  ClearDataSharingAttributeObjects();
+  ClearAccDataSharingEntries();
   return true;
 }
 
@@ -1437,10 +1469,10 @@ void AccAttributeVisitor::AddRoutineInfoToSymbol(
 bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct &x) {
   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
   if (topScope_) {
-    PushContext(
+    PushAccContext(
         verbatim.source, llvm::acc::Directive::ACCD_routine, *topScope_);
   } else {
-    PushContext(verbatim.source, llvm::acc::Directive::ACCD_routine);
+    PushAccContext(verbatim.source, llvm::acc::Directive::ACCD_routine);
   }
   const auto &names{std::get<std::list<parser::Name>>(x.t)};
   if (!names.empty()) {
@@ -1514,7 +1546,7 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) {
   case llvm::acc::Directive::ACCD_kernels_loop:
   case llvm::acc::Directive::ACCD_parallel_loop:
   case llvm::acc::Directive::ACCD_serial_loop:
-    PushContext(x.source, combinedDir.v);
+    PushAccContext(x.source, combinedDir.v);
     break;
   default:
     break;
@@ -1523,7 +1555,7 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) {
   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
   CheckAssociatedLoop(*outer, HasForceCollapseModifier(clauseList));
-  ClearDataSharingAttributeObjects();
+  ClearAccDataSharingEntries();
   return true;
 }
 
@@ -1619,8 +1651,8 @@ void AccAttributeVisitor::AllowOnlyVariable(const parser::AccObject &object) {
 
 bool AccAttributeVisitor::Pre(const parser::OpenACCWaitConstruct &x) {
   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
-  PushContext(verbatim.source, llvm::acc::Directive::ACCD_wait);
-  ClearDataSharingAttributeObjects();
+  PushAccContext(verbatim.source, llvm::acc::Directive::ACCD_wait);
+  ClearAccDataSharingEntries();
   return true;
 }
 
@@ -1637,15 +1669,15 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCAtomicConstruct &x) {
           },
       },
       x.u);
-  PushContext(verbatimSource, llvm::acc::Directive::ACCD_atomic);
-  ClearDataSharingAttributeObjects();
+  PushAccContext(verbatimSource, llvm::acc::Directive::ACCD_atomic);
+  ClearAccDataSharingEntries();
   return true;
 }
 
 bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) {
   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
-  PushContext(verbatim.source, llvm::acc::Directive::ACCD_cache);
-  ClearDataSharingAttributeObjects();
+  PushAccContext(verbatim.source, llvm::acc::Directive::ACCD_cache);
+  ClearAccDataSharingEntries();
 
   const auto &objectListWithModifier =
       std::get<parser::AccObjectListWithModifier>(x.t);
@@ -1861,6 +1893,56 @@ void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
   }
 }
 
+void AccAttributeVisitor::PushAccContext(
+    const parser::CharBlock &source, llvm::acc::Directive dir, Scope &scope) {
+  PushContext(source, dir, scope);
+  accObjectWithDSA_.emplace_back();
+}
+
+void AccAttributeVisitor::PushAccContext(
+    const parser::CharBlock &source, llvm::acc::Directive dir) {
+  PushAccContext(source, dir, context_.FindScope(source));
+}
+
+void AccAttributeVisitor::PopAccContext() {
+  CHECK(!accObjectWithDSA_.empty());
+  accObjectWithDSA_.pop_back();
+  PopContext();
+}
+
+void AccAttributeVisitor::AddAccObjectWithDSA(
+    const Symbol &symbol, Symbol::Flag flag, DesignatorPath designator) {
+  const Symbol &ultimate{symbol.GetUltimate()};
+  if (designator.empty()) {
+    designator.SetBase(NamedEntity{ultimate});
+  }
+  AddToContextObjectWithDSA(ultimate, flag);
+  CHECK(!accObjectWithDSA_.empty());
+  accObjectWithDSA_.back().push_back(std::move(designator),
+      {ultimate, flag, nullptr, ultimate.name().ToString()});
+}
+
+bool AccAttributeVisitor::AccObjectWithDSAVisible(const Symbol &symbol,
+    const std::optional<DesignatorPath> &reference) const {
+  for (std::size_t i{accObjectWithDSA_.size()}; i != 0; --i) {
+    for (const auto &entry : accObjectWithDSA_[i - 1]) {
+      if (&*entry.value.symbol != &symbol) {
+        continue;
+      }
+      if (entry.path.empty()) {
+        return true;
+      }
+      if (!reference && entry.path.HasBaseOnly()) {
+        return true;
+      }
+      if (reference && entry.path.MayContain(*reference)) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 // Returns true iff symbol qualifies for the pre-OpenACC-3.2 DEFAULT(NONE)
 // scalar extension: an intrinsic numeric or logical non-array, non-pointer,
 // non-allocatable variable.  Characters, derived types, allocatables, and
@@ -1880,11 +1962,40 @@ static bool IsAccScalar(const Symbol &symbol) {
   return det && !det->IsArray();
 }
 
-void AccAttributeVisitor::Post(const parser::Name &name) {
+static std::optional<DesignatorPath> GetDesignatorPath(
+    SemanticsContext &, const parser::Designator &);
+static std::optional<DesignatorPath> GetDesignatorPath(
+    SemanticsContext &, const parser::FunctionReference &);
+static bool AddDesignatorPath(
+    SemanticsContext &, const parser::DataRef &, DesignatorPath &);
+static std::optional<std::vector<evaluate::Subscript>> AnalyzeSectionSubscripts(
+    SemanticsContext &, const std::list<parser::SectionSubscript> &);
+
+void AccAttributeVisitor::AdjustAccSymbolReference(const parser::Name &name) {
+  if (!name.symbol || !WithinConstruct()) {
+    return;
+  }
+  const Symbol &symbol{name.symbol->GetUltimate()};
+  if (symbol.owner().IsDerivedType() || symbol.has<ProcEntityDetails>() ||
+      symbol.has<SubprogramDetails>() || symbol.has<AssocEntityDetails>() ||
+      symbol.has<MiscDetails>()) {
+    return;
+  }
+  if (Symbol *found{currScope().FindSymbol(name.source)};
+      found && &symbol != found) {
+    // Adjust the symbol within the region.
+    // TODO: why didn't name resolution set the right name originally?
+    name.symbol = found;
+  }
+}
+
+void AccAttributeVisitor::CheckAccDefaultNoneReference(
+    const parser::Name &name, std::optional<DesignatorPath> designator) {
   if (name.symbol && WithinConstruct()) {
     const Symbol &symbol{name.symbol->GetUltimate()};
     if (!symbol.owner().IsDerivedType() && !symbol.has<ProcEntityDetails>() &&
-        !symbol.has<SubprogramDetails>() && !IsObjectWithVisibleDSA(symbol) &&
+        !symbol.has<SubprogramDetails>() &&
+        !AccObjectWithDSAVisible(symbol, designator) &&
         !symbol.has<AssocEntityDetails>() && !symbol.has<MiscDetails>()) {
       if (Symbol * found{currScope().FindSymbol(name.source)}) {
         if (&symbol != found) {
@@ -1928,6 +2039,68 @@ void AccAttributeVisitor::Post(const parser::Name &name) {
   }
 }
 
+bool AccAttributeVisitor::Pre(const parser::Expr &) { return true; }
+
+void AccAttributeVisitor::Post(const parser::Expr &expr) {
+  if (const auto *designator{
+          std::get_if<common::Indirection<parser::Designator>>(&expr.u)}) {
+    std::optional<DesignatorPath> designatorPath{
+        GetDesignatorPath(context_, designator->value())};
+    CheckAccDefaultNoneReference(
+        parser::GetFirstName(designator->value()), designatorPath);
+  } else if (const auto *functionReference{
+                 std::get_if<common::Indirection<parser::FunctionReference>>(
+                     &expr.u)}) {
+    const parser::Name &name{parser::GetFirstName(functionReference->value())};
+    if (WithinConstruct() && GetContext().defaultDSA == Symbol::Flag::AccNone &&
+        name.symbol && name.symbol->has<ObjectEntityDetails>()) {
+      if (std::optional<DesignatorPath> designatorPath{
+              GetDesignatorPath(context_, functionReference->value())}) {
+        CheckAccDefaultNoneReference(name, designatorPath);
+      }
+    }
+  }
+}
+
+bool AccAttributeVisitor::Pre(const parser::Variable &) { return true; }
+
+void AccAttributeVisitor::Post(const parser::Variable &variable) {
+  if (const auto *designator{
+          std::get_if<common::Indirection<parser::Designator>>(&variable.u)}) {
+    std::optional<DesignatorPath> designatorPath{
+        GetDesignatorPath(context_, designator->value())};
+    CheckAccDefaultNoneReference(
+        parser::GetFirstName(designator->value()), designatorPath);
+  } else if (const auto *functionReference{
+                 std::get_if<common::Indirection<parser::FunctionReference>>(
+                     &variable.u)}) {
+    const parser::Name &name{parser::GetFirstName(functionReference->value())};
+    if (WithinConstruct() && GetContext().defaultDSA == Symbol::Flag::AccNone &&
+        name.symbol && name.symbol->has<ObjectEntityDetails>()) {
+      if (std::optional<DesignatorPath> designatorPath{
+              GetDesignatorPath(context_, functionReference->value())}) {
+        CheckAccDefaultNoneReference(name, designatorPath);
+      }
+    }
+  }
+}
+
+void AccAttributeVisitor::Post(const parser::ArrayElement &arrayElement) {
+  DesignatorPath path;
+  if (AddDesignatorPath(context_, arrayElement.Base(), path)) {
+    if (auto subscripts{
+            AnalyzeSectionSubscripts(context_, arrayElement.Subscripts())}) {
+      path.AddSubscripts(std::move(*subscripts));
+      CheckAccDefaultNoneReference(
+          parser::GetFirstName(arrayElement.Base()), path);
+    }
+  }
+}
+
+void AccAttributeVisitor::Post(const parser::Name &name) {
+  AdjustAccSymbolReference(name);
+}
+
 Symbol *AccAttributeVisitor::ResolveAccCommonBlockName(
     const parser::Name *name) {
   if (name) {
@@ -1991,6 +2164,170 @@ static bool ContainsStructureComponent(const parser::Designator &designator) {
       designator.u);
 }
 
+template <typename A>
+static std::optional<evaluate::Expr<evaluate::SubscriptInteger>>
+AnalyzeSubscriptExpr(SemanticsContext &context, const A &expr) {
+  if (auto value{EvaluateInt64(context, expr)}) {
+    return evaluate::Expr<evaluate::SubscriptInteger>{*value};
+  }
+  if (MaybeExpr maybe{evaluate::Fold(
+          context.foldingContext(), AnalyzeExpr(context, expr))}) {
+    if (auto *intExpr{
+            evaluate::UnwrapExpr<evaluate::Expr<evaluate::SomeInteger>>(
+                maybe)}) {
+      return evaluate::ConvertToType<evaluate::SubscriptInteger>(
+          std::move(*intExpr));
+    }
+  }
+  return std::nullopt;
+}
+
+static std::optional<evaluate::Subscript> AnalyzeSectionSubscript(
+    SemanticsContext &context, const parser::SectionSubscript &subscript) {
+  return common::visit(
+      common::visitors{
+          [&](const parser::SubscriptTriplet &triplet)
+              -> std::optional<evaluate::Subscript> {
+            const auto &lower{std::get<0>(triplet.t)};
+            const auto &upper{std::get<1>(triplet.t)};
+            const auto &stride{std::get<2>(triplet.t)};
+            auto lowerExpr{
+                lower ? AnalyzeSubscriptExpr(context, *lower) : std::nullopt};
+            auto upperExpr{
+                upper ? AnalyzeSubscriptExpr(context, *upper) : std::nullopt};
+            auto strideExpr{
+                stride ? AnalyzeSubscriptExpr(context, *stride) : std::nullopt};
+            if ((lower && !lowerExpr) || (upper && !upperExpr) ||
+                (stride && !strideExpr)) {
+              return std::nullopt;
+            }
+            auto result{evaluate::Triplet{std::move(lowerExpr),
+                std::move(upperExpr), std::move(strideExpr)}};
+            return evaluate::Subscript{std::move(result)};
+          },
+          [&](const parser::IntExpr &expr)
+              -> std::optional<evaluate::Subscript> {
+            if (auto subscript{AnalyzeSubscriptExpr(context, expr)}) {
+              return evaluate::Subscript{std::move(*subscript)};
+            }
+            return std::nullopt;
+          },
+      },
+      subscript.u);
+}
+
+static std::optional<std::vector<evaluate::Subscript>> AnalyzeSectionSubscripts(
+    SemanticsContext &context,
+    const std::list<parser::SectionSubscript> &list) {
+  std::vector<evaluate::Subscript> subscripts;
+  for (const parser::SectionSubscript &subscript : list) {
+    if (auto analyzed{AnalyzeSectionSubscript(context, subscript)}) {
+      subscripts.push_back(std::move(*analyzed));
+    } else {
+      return std::nullopt;
+    }
+  }
+  return subscripts;
+}
+
+static bool AddDesignatorPath(SemanticsContext &context,
+    const parser::DataRef &dataRef, DesignatorPath &path) {
+  return common::visit(
+      common::visitors{
+          [&](const parser::Name &name) {
+            if (!name.symbol) {
+              return false;
+            }
+            path.SetBase(NamedEntity{name.symbol->GetUltimate()});
+            return true;
+          },
+          [&](const common::Indirection<parser::StructureComponent>
+                  &component) {
+            if (!AddDesignatorPath(context, component.value().Base(), path)) {
+              return false;
+            }
+            if (const parser::Name &name{component.value().Component()};
+                name.symbol) {
+              path.AddComponent(name.symbol->GetUltimate());
+              return true;
+            }
+            return false;
+          },
+          [&](const common::Indirection<parser::ArrayElement> &arrayElement) {
+            if (!AddDesignatorPath(
+                    context, arrayElement.value().Base(), path)) {
+              return false;
+            }
+            if (auto subscripts{AnalyzeSectionSubscripts(
+                    context, arrayElement.value().Subscripts())}) {
+              path.AddSubscripts(std::move(*subscripts));
+              return true;
+            }
+            return false;
+          },
+          [&](const common::Indirection<parser::CoindexedNamedObject>
+                  &coindexed) {
+            return AddDesignatorPath(
+                context, std::get<parser::DataRef>(coindexed.value().t), path);
+          },
+      },
+      dataRef.u);
+}
+
+static std::optional<DesignatorPath> GetDesignatorPath(
+    SemanticsContext &context, const parser::Designator &designator) {
+  DesignatorPath path;
+  bool ok{common::visit(common::visitors{
+                            [&](const parser::DataRef &dataRef) {
+                              return AddDesignatorPath(context, dataRef, path);
+                            },
+                            [&](const parser::Substring &substring) {
+                              return AddDesignatorPath(context,
+                                  std::get<parser::DataRef>(substring.t), path);
+                            },
+                        },
+      designator.u)};
+  if (ok && !path.empty()) {
+    return path;
+  }
+  return std::nullopt;
+}
+
+static std::optional<DesignatorPath> GetDesignatorPath(
+    SemanticsContext &context, const parser::FunctionReference &funcRef) {
+  const auto &call{funcRef.v};
+  const auto &procedureDesignator{
+      std::get<parser::ProcedureDesignator>(call.t)};
+  const auto *name{std::get_if<parser::Name>(&procedureDesignator.u)};
+  if (!name || !name->symbol || !name->symbol->has<ObjectEntityDetails>()) {
+    return std::nullopt;
+  }
+  std::vector<evaluate::Subscript> subscripts;
+  for (const parser::ActualArgSpec &arg :
+      std::get<std::list<parser::ActualArgSpec>>(call.t)) {
+    if (std::get<std::optional<parser::Keyword>>(arg.t)) {
+      return std::nullopt;
+    }
+    const auto *expr{std::get_if<common::Indirection<parser::Expr>>(
+        &std::get<parser::ActualArg>(arg.t).u)};
+    if (!expr) {
+      return std::nullopt;
+    }
+    if (auto subscript{AnalyzeSubscriptExpr(context, expr->value())}) {
+      subscripts.emplace_back(std::move(*subscript));
+    } else {
+      return std::nullopt;
+    }
+  }
+  if (subscripts.empty()) {
+    return std::nullopt;
+  }
+  DesignatorPath path;
+  path.SetBase(NamedEntity{name->symbol->GetUltimate()});
+  path.AddSubscripts(std::move(subscripts));
+  return path;
+}
+
 void AccAttributeVisitor::ResolveAccObject(
     const parser::AccObject &accObject, Symbol::Flag accFlag) {
   common::visit(
@@ -1998,29 +2335,50 @@ void AccAttributeVisitor::ResolveAccObject(
           [&](const parser::Designator &designator) {
             const bool isBareName{
                 parser::GetDesignatorNameIfDataRef(designator) != nullptr};
-            std::optional<std::string> designatorKey;
+            DesignatorPath designatorPath;
+            std::optional<std::string> designatorName;
+            bool canCheckMultipleAppearances{isBareName};
             if (!isBareName) {
-              designatorKey = designator.source.ToString();
+              designatorName = designator.source.ToString();
+              if (std::optional<DesignatorPath> path{
+                      GetDesignatorPath(context_, designator)}) {
+                designatorPath = std::move(*path);
+                canCheckMultipleAppearances = true;
+              }
               // Subscripted designator: evaluate subscripts and detect
               // the substring case that is disallowed in OpenACC clauses.
-              if (AnalyzeExpr(context_, designator)) {
+              if (MaybeExpr expr{AnalyzeExpr(context_, designator)}) {
                 if (std::holds_alternative<parser::Substring>(designator.u)) {
                   context_.Say(designator.source,
                       "Substrings are not allowed on OpenACC "
                       "directives or clauses"_err_en_US);
                   return;
                 }
+                if (designatorPath.empty()) {
+                  if (std::optional<DesignatorPath> path{
+                          DesignatorPath::Get(expr)}) {
+                    designatorPath = std::move(*path);
+                    canCheckMultipleAppearances = true;
+                  } else {
+                    canCheckMultipleAppearances = false;
+                  }
+                }
               }
             }
             const bool isDataSharing{dataSharingAttributeFlags.test(accFlag)};
             if (ContainsStructureComponent(designator)) {
-              // Do not register the base object for a component reference until
-              // OpenACC DSA tracking can distinguish subcomponents.
-              if (isDataSharing) {
+              // Register component references only in the path-aware table; a
+              // component clause does not cover every reference to the base.
+              if (canCheckMultipleAppearances) {
                 const parser::Name &baseName{parser::GetFirstName(designator)};
                 if (baseName.symbol) {
-                  CheckMultipleAppearances(baseName, *baseName.symbol, accFlag,
-                      &accObject, true, designatorKey);
+                  AddAccObjectWithDSA(
+                      *baseName.symbol, accFlag, designatorPath);
+                  if (isDataSharing) {
+                    CheckMultipleAppearances(baseName, *baseName.symbol,
+                        accFlag, &accObject, true, designatorName,
+                        designatorPath);
+                  }
                 }
               }
               return;
@@ -2033,10 +2391,10 @@ void AccAttributeVisitor::ResolveAccObject(
             // data mapping attributes is not currently supported.
             const parser::Name &baseName{parser::GetFirstName(designator)};
             if (auto *symbol{ResolveAcc(baseName, accFlag, currScope())}) {
-              AddToContextObjectWithDSA(*symbol, accFlag);
-              if (isDataSharing) {
+              AddAccObjectWithDSA(*symbol, accFlag, designatorPath);
+              if (isDataSharing && canCheckMultipleAppearances) {
                 CheckMultipleAppearances(baseName, *symbol, accFlag, &accObject,
-                    true, designatorKey);
+                    true, designatorName, designatorPath);
               }
             }
           },
@@ -2047,7 +2405,7 @@ void AccAttributeVisitor::ResolveAccObject(
               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
                 if (auto *resolvedObject{
                         ResolveAcc(*object, accFlag, currScope())}) {
-                  AddToContextObjectWithDSA(*resolvedObject, accFlag);
+                  AddAccObjectWithDSA(*resolvedObject, accFlag);
                 }
               }
             } else {
@@ -2095,34 +2453,63 @@ Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
 void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
     const Symbol &symbol, Symbol::Flag accFlag,
     const parser::AccObject *occurrence, bool warnSameKindDuplicate,
-    std::optional<std::string> designator) {
+    std::optional<std::string> objectName, DesignatorPath designator) {
   const auto *target{&symbol};
-  if (auto firstFlag{FindDataSharingAttributeObject(*target, designator)}) {
-    // A same-kind duplicate (e.g. private(x, x) or private(x) private(x))
-    // is benign: warn and tag this AccObject occurrence so rewrite-parse-tree
-    // can drop it from the clause list. Cross-kind duplicates (e.g.
-    // private(x) firstprivate(x)) remain hard errors.
-    //
-    // Reduction is excluded from the benign case: two reduction clauses
-    // with the same Symbol::Flag may still differ in operator, which is a
-    // real conflict that dedup would silently hide.
-    const std::string objectName{designator.value_or(name.ToString())};
-    if (warnSameKindDuplicate && occurrence && *firstFlag == accFlag &&
-        accFlag != Symbol::Flag::AccReduction) {
-      context_.Warn(common::UsageWarning::OpenAccUsage, name.source,
-          "'%s' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored"_warn_en_US,
-          objectName);
-      context_.MarkAccObjectDuplicate(occurrence);
-    } else if (*firstFlag == accFlag && accFlag != Symbol::Flag::AccReduction) {
-      return;
-    } else {
+  if (designator.empty()) {
+    designator.SetBase(NamedEntity{*target});
+  }
+  const std::string displayName{objectName.value_or(name.ToString())};
+  for (auto iter{accDataSharingEntries_.begin()};
+      iter != accDataSharingEntries_.end();) {
+    AccDataSharingEntry &entry{iter->value};
+    if (&*entry.symbol != target) {
+      ++iter;
+      continue;
+    }
+    DesignatorRelation relation{iter->path.Compare(designator)};
+    if (relation == DesignatorRelation::Disjoint) {
+      ++iter;
+      continue;
+    }
+
+    // Reduction is excluded from same-kind duplicate elision: two reduction
+    // clauses with the same Symbol::Flag may still differ in operator.
+    if (entry.flag != accFlag || accFlag == Symbol::Flag::AccReduction) {
       context_.Say(name.source,
           "'%s' appears in more than one data-sharing clause on the same OpenACC directive"_err_en_US,
-          objectName);
+          displayName);
+      return;
+    }
+
+    switch (relation) {
+    case DesignatorRelation::Equal:
+      if (warnSameKindDuplicate && occurrence) {
+        context_.Warn(common::UsageWarning::OpenAccUsage, name.source,
+            "'%s' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored"_warn_en_US,
+            displayName);
+        context_.MarkAccObjectDuplicate(occurrence);
+      }
+      return;
+    case DesignatorRelation::Contains:
+      if (occurrence) {
+        context_.MarkAccObjectDuplicate(occurrence);
+      }
+      return;
+    case DesignatorRelation::ContainedBy:
+      if (entry.occurrence) {
+        context_.MarkAccObjectDuplicate(entry.occurrence);
+      }
+      iter = accDataSharingEntries_.erase(iter);
+      continue;
+    case DesignatorRelation::Overlaps:
+      ++iter;
+      continue;
+    case DesignatorRelation::Disjoint:
+      llvm_unreachable("disjoint relation handled above");
     }
-  } else {
-    AddDataSharingAttributeObject(*target, accFlag, std::move(designator));
   }
+  accDataSharingEntries_.push_back(
+      std::move(designator), {*target, accFlag, occurrence, displayName});
 }
 
 #ifndef NDEBUG
diff --git a/flang/test/Lower/OpenACC/acc-dedup-private.f90 b/flang/test/Lower/OpenACC/acc-dedup-private.f90
index 6399324070831..d7779e5f7186e 100644
--- a/flang/test/Lower/OpenACC/acc-dedup-private.f90
+++ b/flang/test/Lower/OpenACC/acc-dedup-private.f90
@@ -61,3 +61,94 @@ subroutine test_firstprivate_pair(i)
 ! CHECK-LABEL: func.func @_QPtest_firstprivate_pair
 ! CHECK: acc.firstprivate varPtr({{.*}}) recipe(@firstprivatization_ref_i32) -> !fir.ref<i32> {name = "x"}
 ! CHECK-NOT: acc.firstprivate varPtr({{.*}}) recipe(@firstprivatization_ref_i32) -> !fir.ref<i32> {name = "x"}
+
+! -----------------------------------------------------------------------
+! private(arr(1:5), arr(3)) -- contained array element in the same
+! data-sharing kind
+
+subroutine test_private_contained_array_parent_first(i)
+  real :: arr(10)
+  integer :: i
+  !$acc parallel loop private(arr(1:5), arr(3))
+  do i = 1, 10
+    arr(i) = real(i)
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_private_contained_array_parent_first
+! arr(1:5) is privatized, and the contained arr(3) occurrence is removed before lowering.
+! CHECK-NOT: acc.private {{.*}} {name = "arr(3)"}
+! CHECK: %[[ARR_PRIV:.*]] = acc.private {{.*}} {name = "arr(1:5)"}
+! CHECK-NOT: acc.private {{.*}} {name = "arr(3)"}
+! CHECK: acc.loop {{.*}}private(%[[ARR_PRIV]],
+
+! -----------------------------------------------------------------------
+! private(arr(3), arr(1:5)) -- contained array element appears first
+
+subroutine test_private_contained_array_child_first(i)
+  real :: arr(10)
+  integer :: i
+  !$acc parallel loop private(arr(3), arr(1:5))
+  do i = 1, 10
+    arr(i) = real(i)
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_private_contained_array_child_first
+! CHECK-NOT: acc.private {{.*}} {name = "arr(3)"}
+! CHECK: %[[ARR_PRIV:.*]] = acc.private {{.*}} {name = "arr(1:5)"}
+! CHECK-NOT: acc.private {{.*}} {name = "arr(3)"}
+! CHECK: acc.loop {{.*}}private(%[[ARR_PRIV]],
+
+! -----------------------------------------------------------------------
+! private(n%pt, n%pt%x) -- contained path in the same data-sharing kind
+
+subroutine test_private_contained_component_parent_first(i)
+  type point_t
+    real :: x
+    real :: y
+  end type
+  type nested_t
+    type(point_t) :: pt
+    integer :: tag
+  end type
+  type(nested_t) :: n
+  integer :: i
+  !$acc parallel loop private(n%pt, n%pt%x)
+  do i = 1, 10
+    n%pt%x = real(i)
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_private_contained_component_parent_first
+! n%pt is privatized, and the contained n%pt%x occurrence is removed before lowering.
+! CHECK-NOT: acc.private {{.*}} {name = "n%pt%x"}
+! CHECK: %[[N_PT_PRIV:.*]] = acc.private {{.*}} {name = "n%pt"}
+! CHECK-NOT: acc.private {{.*}} {name = "n%pt%x"}
+! CHECK: acc.loop {{.*}}private(%[[N_PT_PRIV]],
+
+! -----------------------------------------------------------------------
+! private(n%pt%x, n%pt) -- contained path appears before the containing path
+
+subroutine test_private_contained_component_child_first(i)
+  type point_t
+    real :: x
+    real :: y
+  end type
+  type nested_t
+    type(point_t) :: pt
+    integer :: tag
+  end type
+  type(nested_t) :: n
+  integer :: i
+  !$acc parallel loop private(n%pt%x, n%pt)
+  do i = 1, 10
+    n%pt%x = real(i)
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPtest_private_contained_component_child_first
+! CHECK-NOT: acc.private {{.*}} {name = "n%pt%x"}
+! CHECK: %[[N_PT_PRIV:.*]] = acc.private {{.*}} {name = "n%pt"}
+! CHECK-NOT: acc.private {{.*}} {name = "n%pt%x"}
+! CHECK: acc.loop {{.*}}private(%[[N_PT_PRIV]],
diff --git a/flang/test/Semantics/OpenACC/acc-component-ref-dsa.f90 b/flang/test/Semantics/OpenACC/acc-component-ref-dsa.f90
index e41c739cbcca3..5489fee5b2194 100644
--- a/flang/test/Semantics/OpenACC/acc-component-ref-dsa.f90
+++ b/flang/test/Semantics/OpenACC/acc-component-ref-dsa.f90
@@ -3,8 +3,8 @@
 ! Derived-type component references in OpenACC clauses are accepted. Exact
 ! duplicate and conflicting component references in data-sharing clauses are
 ! diagnosed, but broader containment is deliberately limited for now:
-! - component clauses do not satisfy DEFAULT(NONE) for the base object;
-! - whole-object/component conflicts are not diagnosed.
+! - component clauses satisfy DEFAULT(NONE) only for contained references;
+! - bare whole-object/component conflicts are not diagnosed.
 
 module component_ref_types
   implicit none
@@ -35,16 +35,41 @@ subroutine test_component_clauses_are_accepted()
   !$acc end parallel
 end subroutine
 
-subroutine test_default_none_component_does_not_cover_object()
+subroutine test_default_none_component_covers_same_component()
   use component_ref_types, only: point_t
   type(point_t) :: p
-  ! TODO: should be an error, needs precise tracking of component references.
   !$acc parallel default(none) copy(p%x)
-  !ERROR: The DEFAULT(NONE) clause requires that 'p' must be listed in a data-mapping clause
   p%x = 1.0
   !$acc end parallel
 end subroutine
 
+subroutine test_default_none_component_does_not_cover_sibling()
+  use component_ref_types, only: point_t
+  type(point_t) :: p
+  !$acc parallel default(none) copy(p%x)
+  !ERROR: The DEFAULT(NONE) clause requires that 'p' must be listed in a data-mapping clause
+  p%y = 1.0
+  !$acc end parallel
+end subroutine
+
+subroutine test_default_none_component_covers_contained_component()
+  use component_ref_types, only: nested_t
+  type(nested_t) :: n
+  !$acc parallel default(none) copy(n%pt)
+  n%pt%x = 1.0
+  !$acc end parallel
+end subroutine
+
+subroutine test_default_none_component_does_not_cover_parent()
+  use component_ref_types, only: nested_t, point_t
+  type(nested_t) :: n
+  type(point_t) :: p
+  !$acc parallel default(none) copy(n%pt%x, p)
+  !ERROR: The DEFAULT(NONE) clause requires that 'n' must be listed in a data-mapping clause
+  n%pt = p
+  !$acc end parallel
+end subroutine
+
 subroutine test_default_none_whole_object_covers_components()
   use component_ref_types, only: point_t
   type(point_t) :: p, q
@@ -100,10 +125,57 @@ subroutine test_same_object_incompatible_different_components()
   !$acc end parallel loop
 end subroutine
 
+subroutine test_contained_component_same_dsa()
+  use component_ref_types, only: nested_t
+  type(nested_t) :: n
+  integer :: i
+  !$acc parallel loop private(n%pt, n%pt%x)
+  do i = 1, 10
+    n%pt%x = real(i)
+  end do
+  !$acc end parallel loop
+end subroutine
+
+subroutine test_contained_component_same_dsa_child_first()
+  use component_ref_types, only: nested_t
+  type(nested_t) :: n
+  integer :: i
+  !$acc parallel loop private(n%pt%x, n%pt)
+  do i = 1, 10
+    n%pt%x = real(i)
+  end do
+  !$acc end parallel loop
+end subroutine
+
+subroutine test_contained_component_incompatible_parent_first()
+  use component_ref_types, only: nested_t
+  type(nested_t) :: n
+  integer :: i
+  !ERROR: 'n%pt%x' appears in more than one data-sharing clause on the same OpenACC directive
+  !$acc parallel loop private(n%pt) firstprivate(n%pt%x)
+  do i = 1, 10
+    n%pt%x = real(i)
+  end do
+  !$acc end parallel loop
+end subroutine
+
+subroutine test_contained_component_incompatible_child_first()
+  use component_ref_types, only: nested_t
+  type(nested_t) :: n
+  integer :: i
+  !ERROR: 'n%pt' appears in more than one data-sharing clause on the same OpenACC directive
+  !$acc parallel loop private(n%pt%x) firstprivate(n%pt)
+  do i = 1, 10
+    n%pt%x = real(i)
+  end do
+  !$acc end parallel loop
+end subroutine
+
 subroutine test_whole_object_incompatible_with_component()
   use component_ref_types, only: point_t
   type(point_t) :: p
   integer :: i
+  !ERROR: 'p%x' appears in more than one data-sharing clause on the same OpenACC directive
   !$acc parallel loop private(p) firstprivate(p%x)
   do i = 1, 10
     p%x = real(i)
diff --git a/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90 b/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
index b7208acab151d..913fbddb88adb 100644
--- a/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
+++ b/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
@@ -76,6 +76,8 @@ program test_dataclause_dedup
   ! precisely rather than by the base array symbol.
   block
     integer :: arr(10)
+    integer :: lo, mid, hi, idx
+    integer, parameter :: left = 1, split = 5, right = 10
     integer, target :: t1, t2
     integer, pointer :: p
     type :: pt
@@ -106,12 +108,29 @@ program test_dataclause_dedup
     do i = 1, 10
     end do
 
+    ! A literal element outside a literal section is disjoint across
+    ! data-sharing kinds.
+    !$acc parallel loop private(arr(1:5)) firstprivate(arr(6))
+    do i = 1, 10
+    end do
+
+    ! Named constant sections that fold to disjoint ranges are not conflicts.
+    !$acc parallel loop private(arr(left:split)) firstprivate(arr(split+1:right))
+    do i = 1, 10
+    end do
+
     ! Same array element listed twice in the same data-sharing clause.
     !WARNING: 'arr(1)' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
     !$acc parallel loop private(arr(1), arr(1))
     do i = 1, 10
     end do
 
+    ! Same array element with different source spelling.
+    !WARNING: 'arr(01)' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+    !$acc parallel loop private(arr(1), arr(01))
+    do i = 1, 10
+    end do
+
     ! Same array element listed in conflicting data-sharing clauses.
     !ERROR: 'arr(1)' appears in more than one data-sharing clause on the same OpenACC directive
     !$acc parallel loop private(arr(1)) firstprivate(arr(1))
@@ -130,6 +149,77 @@ program test_dataclause_dedup
     do i = 1, 10
     end do
 
+    ! Same array section with different source spelling.
+    !WARNING: 'arr(01:05)' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored [-Wopenacc-usage]
+    !$acc parallel loop private(arr(1:5), arr(01:05))
+    do i = 1, 10
+    end do
+
+    ! Equivalent array sections with different source spelling in conflicting
+    ! data-sharing clauses.
+    !ERROR: 'arr(01:05)' appears in more than one data-sharing clause on the same OpenACC directive
+    !$acc parallel loop private(arr(1:5)) firstprivate(arr(01:05))
+    do i = 1, 10
+    end do
+
+    ! Overlapping literal sections in the same data-sharing kind are accepted.
+    !$acc parallel loop private(arr(1:5), arr(5:10))
+    do i = 1, 10
+    end do
+
+    ! Overlapping literal sections in different data-sharing kinds conflict.
+    !ERROR: 'arr(5:10)' appears in more than one data-sharing clause on the same OpenACC directive
+    !$acc parallel loop private(arr(1:5)) firstprivate(arr(5:10))
+    do i = 1, 10
+    end do
+
+    ! An element contained in a section is accepted within the same
+    ! data-sharing kind.
+    !$acc parallel loop private(arr(3), arr(1:5))
+    do i = 1, 10
+    end do
+
+    ! An element contained in a section conflicts across data-sharing kinds.
+    !ERROR: 'arr(3)' appears in more than one data-sharing clause on the same OpenACC directive
+    !$acc parallel loop private(arr(1:5)) firstprivate(arr(3))
+    do i = 1, 10
+    end do
+
+    ! Variable index/section overlap is accepted within the same
+    ! data-sharing kind.
+    !$acc parallel loop private(arr(idx), arr(lo:hi))
+    do i = 1, 10
+    end do
+
+    ! Variable index/section overlap is ambiguous, so assume disjoint across
+    ! data-sharing kinds unless overlap can be proven.
+    !$acc parallel loop private(arr(lo:hi)) firstprivate(arr(idx))
+    do i = 1, 10
+    end do
+
+    ! Identical variable indices conflict across data-sharing kinds.
+    !ERROR: 'arr(idx)' appears in more than one data-sharing clause on the same OpenACC directive
+    !$acc parallel loop private(arr(idx)) firstprivate(arr(idx))
+    do i = 1, 10
+    end do
+
+    ! Variable section overlap is accepted within the same data-sharing kind.
+    !$acc parallel loop private(arr(lo:hi), arr(mid:hi))
+    do i = 1, 10
+    end do
+
+    ! Variable section overlap is ambiguous, so assume disjoint across
+    ! data-sharing kinds unless overlap can be proven.
+    !$acc parallel loop private(arr(lo:hi)) firstprivate(arr(mid:hi))
+    do i = 1, 10
+    end do
+
+    ! Identical variable sections conflict across data-sharing kinds.
+    !ERROR: 'arr(lo:hi)' appears in more than one data-sharing clause on the same OpenACC directive
+    !$acc parallel loop private(arr(lo:hi)) firstprivate(arr(lo:hi))
+    do i = 1, 10
+    end do
+
     ! Distinct structure components -- not duplicates.
     !$acc parallel loop private(s%a, s%b)
     do i = 1, 10
diff --git a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90 b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
index f96b426e19af0..93f99fbb247f3 100644
--- a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
+++ b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
@@ -1,10 +1,9 @@
 ! RUN: %python %S/../test_errors.py %s %flang -fopenacc -fno-openacc-default-none-scalars-strict -Wno-openacc-default-none-scalars-strict
 
 ! Verify that array sections explicitly listed in OpenACC data clauses are
-! correctly registered as having a DSA, so DEFAULT(NONE) does not produce
-! spurious errors.  This does not implement section-level overlap
-! detection; duplicate/conflict diagnostics only apply to exact data-sharing
-! designators.  This also covers the substring-in-clause error.
+! correctly registered as having a DSA, so DEFAULT(NONE) uses path containment
+! rather than treating a listed array section as covering every reference to the
+! base array.  This also covers the substring-in-clause error.
 
 ! 1. Data-mapping clauses with array sections: no DEFAULT(NONE) errors.
 subroutine test_data_mapping_sections(n)
@@ -26,6 +25,62 @@ subroutine test_data_mapping_sections(n)
   !$acc end kernels
 end subroutine
 
+subroutine test_default_none_literal_section_contains_element()
+  implicit none
+  real :: a(10)
+  !$acc parallel default(none) copy(a(1:5))
+  a(3) = 1.0
+  !$acc end parallel
+end subroutine
+
+subroutine test_default_none_literal_section_rejects_disjoint_element()
+  implicit none
+  real :: a(10)
+  !$acc parallel default(none) copy(a(1:5))
+  !ERROR: The DEFAULT(NONE) clause requires that 'a' must be listed in a data-mapping clause
+  a(6) = 1.0
+  !$acc end parallel
+end subroutine
+
+subroutine test_default_none_literal_section_rejects_partially_overlapping_section()
+  implicit none
+  real :: a(10)
+  !$acc parallel default(none) copy(a(1:5))
+  !ERROR: The DEFAULT(NONE) clause requires that 'a' must be listed in a data-mapping clause
+  a(5:10) = 1.0
+  !$acc end parallel
+end subroutine
+
+subroutine test_default_none_literal_section_rejects_full_section()
+  implicit none
+  real :: a(10)
+  !$acc parallel default(none) copy(a(1:5))
+  !ERROR: The DEFAULT(NONE) clause requires that 'a' must be listed in a data-mapping clause
+  a(:) = 1.0
+  !$acc end parallel
+end subroutine
+
+subroutine test_default_none_full_section_contains_element()
+  implicit none
+  real :: a(10)
+  !$acc parallel default(none) copy(a(:))
+  a = 0.0
+  a(10) = 1.0
+  !$acc end parallel
+end subroutine
+
+subroutine test_default_none_variable_section_lenient(n, lo, hi, i, j, mid)
+  implicit none
+  integer, intent(in) :: n, lo, hi, i, j, mid
+  real :: a(n), b(n), c(n)
+  !$acc parallel default(none) copy(a(lo:hi), b(i), c(1:5))
+  a(i) = 1.0
+  a(mid:hi) = 2.0
+  b(j) = 3.0
+  c(j) = 4.0
+  !$acc end parallel
+end subroutine
+
 ! 2. Private clause with array section: no DEFAULT(NONE) error.
 subroutine test_private_section(n)
   implicit none
diff --git a/flang/unittests/Evaluate/CMakeLists.txt b/flang/unittests/Evaluate/CMakeLists.txt
index ed012828a7258..d9ff4489f99bc 100644
--- a/flang/unittests/Evaluate/CMakeLists.txt
+++ b/flang/unittests/Evaluate/CMakeLists.txt
@@ -20,6 +20,13 @@ add_flang_nongtest_unittest(expression
   FortranParser
 )
 
+add_flang_nongtest_unittest(designator-path
+  FortranSupport
+  NonGTestTesting
+  FortranEvaluate
+  FortranSemantics
+)
+
 add_flang_nongtest_unittest(integer
   NonGTestTesting
   FortranEvaluate
diff --git a/flang/unittests/Evaluate/designator-path.cpp b/flang/unittests/Evaluate/designator-path.cpp
new file mode 100644
index 0000000000000..79d913f993d4d
--- /dev/null
+++ b/flang/unittests/Evaluate/designator-path.cpp
@@ -0,0 +1,374 @@
+//===-- flang/unittests/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/Evaluate/expression.h"
+#include "flang/Parser/provenance.h"
+#include "flang/Semantics/scope.h"
+#include "flang/Semantics/semantics.h"
+#include "flang/Semantics/symbol.h"
+#include "flang/Support/Fortran-features.h"
+#include "flang/Support/LangOptions.h"
+#include "flang/Support/default-kinds.h"
+#include "flang/Testing/testing.h"
+
+using namespace Fortran::evaluate;
+
+namespace {
+namespace common = Fortran::common;
+namespace parser = Fortran::parser;
+namespace semantics = Fortran::semantics;
+using IntExpr = Expr<SubscriptInteger>;
+
+IntExpr Int(int n) { return IntExpr{n}; }
+
+Subscript Scalar(int n) { return Subscript{Int(n)}; }
+
+Triplet TripletSubscript(
+    std::optional<int> lower, std::optional<int> upper, int stride = 1) {
+  return Triplet{lower ? std::optional<IntExpr>{Int(*lower)} : std::nullopt,
+      upper ? std::optional<IntExpr>{Int(*upper)} : std::nullopt, Int(stride)};
+}
+
+Subscript Section(
+    std::optional<int> lower, std::optional<int> upper, int stride = 1) {
+  return Subscript{TripletSubscript(lower, upper, stride)};
+}
+
+Subscript FullSection() {
+  return Subscript{Triplet{std::nullopt, std::nullopt, Int(1)}};
+}
+
+DesignatorPath PathWithSubscripts(std::vector<Subscript> subscripts) {
+  DesignatorPath path;
+  path.AddSubscripts(std::move(subscripts));
+  return path;
+}
+
+DesignatorPath PathWithComponent(const semantics::Symbol *symbol) {
+  DesignatorPath path;
+  path.AddComponent(*symbol);
+  return path;
+}
+
+void CheckRelation(const DesignatorPath &x, const DesignatorPath &y,
+    DesignatorRelation relation) {
+  TEST(x.Compare(y) == relation);
+}
+
+class SymbolFixture {
+public:
+  const semantics::Symbol &MakeSymbol(const char *name) {
+    return scope_.MakeSymbol(parser::CharBlock{name}, semantics::Attrs{},
+        semantics::UnknownDetails{});
+  }
+
+private:
+  parser::AllSources allSources_;
+  parser::AllCookedSources allCookedSources_{allSources_};
+  common::IntrinsicTypeDefaultKinds defaultKinds_;
+  common::LanguageFeatureControl languageFeatures_;
+  common::LangOptions langOptions_;
+  semantics::SemanticsContext context_{
+      defaultKinds_, languageFeatures_, langOptions_, allCookedSources_};
+  semantics::Scope &scope_{
+      context_.globalScope().MakeScope(semantics::Scope::Kind::MainProgram)};
+};
+
+void TestGetConstantSubscriptRange() {
+  auto scalarRange{DesignatorPath::GetConstantSubscriptRange(Scalar(4))};
+  TEST(scalarRange.has_value());
+  TEST(scalarRange->lower == 4);
+  TEST(scalarRange->upper == 4);
+
+  auto sectionRange{DesignatorPath::GetConstantSubscriptRange(Section(2, 7))};
+  TEST(sectionRange.has_value());
+  TEST(sectionRange->lower == 2);
+  TEST(sectionRange->upper == 7);
+
+  TEST(!DesignatorPath::GetConstantSubscriptRange(Section(2, 7, 2)));
+  TEST(!DesignatorPath::GetConstantSubscriptRange(FullSection()));
+}
+
+void TestFullTripletDetection() {
+  TEST(DesignatorPath::IsFullTriplet(TripletSubscript({}, {})));
+  TEST(!DesignatorPath::IsFullTriplet(TripletSubscript(1, {})));
+  TEST(!DesignatorPath::IsFullTriplet(TripletSubscript({}, 10)));
+  TEST(!DesignatorPath::IsFullTriplet(TripletSubscript({}, {}, 2)));
+}
+
+void TestCompareSubscripts() {
+  TEST(DesignatorPath::CompareSubscripts(Scalar(3), Scalar(3)) ==
+      DesignatorRelation::Equal);
+  TEST(DesignatorPath::CompareSubscripts(FullSection(), Scalar(3)) ==
+      DesignatorRelation::Contains);
+  TEST(DesignatorPath::CompareSubscripts(FullSection(), FullSection()) ==
+      DesignatorRelation::Equal);
+  TEST(DesignatorPath::CompareSubscripts(Scalar(3), FullSection()) ==
+      DesignatorRelation::ContainedBy);
+  TEST(DesignatorPath::CompareSubscripts(Section(1, 5), Section(6, 10)) ==
+      DesignatorRelation::Disjoint);
+  TEST(DesignatorPath::CompareSubscripts(Section(1, 5), Section(1, 5)) ==
+      DesignatorRelation::Equal);
+  TEST(DesignatorPath::CompareSubscripts(Section(1, 10), Section(3, 5)) ==
+      DesignatorRelation::Contains);
+  TEST(DesignatorPath::CompareSubscripts(Section(3, 5), Section(1, 10)) ==
+      DesignatorRelation::ContainedBy);
+  TEST(DesignatorPath::CompareSubscripts(Section(1, 5), Section(5, 10)) ==
+      DesignatorRelation::Overlaps);
+  TEST(DesignatorPath::CompareSubscripts(Section(1, 5, 2), Section(1, 5)) ==
+      DesignatorRelation::Disjoint);
+}
+
+void TestCompareSubscriptLists() {
+  TEST(DesignatorPath::CompareSubscriptLists({}, {FullSection()}) ==
+      DesignatorRelation::Equal);
+  TEST(DesignatorPath::CompareSubscriptLists({FullSection()}, {}) ==
+      DesignatorRelation::Equal);
+  TEST(DesignatorPath::CompareSubscriptLists({FullSection()},
+           {FullSection(), FullSection()}) == DesignatorRelation::Disjoint);
+  TEST(DesignatorPath::CompareSubscriptLists({Scalar(1)},
+           {Scalar(1), Scalar(2)}) == DesignatorRelation::Disjoint);
+  TEST(DesignatorPath::CompareSubscriptLists({Scalar(1), Scalar(2)},
+           {Scalar(1), Scalar(2)}) == DesignatorRelation::Equal);
+  TEST(DesignatorPath::CompareSubscriptLists({Section(1, 10), Scalar(2)},
+           {Section(3, 5), Scalar(2)}) == DesignatorRelation::Contains);
+  TEST(DesignatorPath::CompareSubscriptLists({Section(3, 5), Scalar(2)},
+           {Section(1, 10), Scalar(2)}) == DesignatorRelation::ContainedBy);
+  TEST(DesignatorPath::CompareSubscriptLists({Section(1, 10), Scalar(2)},
+           {Section(3, 5), FullSection()}) == DesignatorRelation::Overlaps);
+  TEST(DesignatorPath::CompareSubscriptLists({Section(1, 5), Scalar(2)},
+           {Section(6, 10), Scalar(2)}) == DesignatorRelation::Disjoint);
+}
+
+void TestCompareParts() {
+  SymbolFixture symbols;
+  const semantics::Symbol &symbol1{symbols.MakeSymbol("a")};
+  const semantics::Symbol &symbol2{symbols.MakeSymbol("b")};
+  DesignatorPath::Part component1{{}, &symbol1};
+  DesignatorPath::Part component1Again{{}, &symbol1};
+  DesignatorPath::Part component2{{}, &symbol2};
+  DesignatorPath::Part subscripts{{Section(1, 5)}, nullptr};
+  DesignatorPath::Part subscriptedComponent{{Scalar(3)}, &symbol1};
+
+  TEST(DesignatorPath::CompareParts(component1, component1Again) ==
+      DesignatorRelation::Equal);
+  TEST(DesignatorPath::CompareParts(component1, component2) ==
+      DesignatorRelation::Disjoint);
+  TEST(DesignatorPath::CompareParts(component1, subscripts) ==
+      DesignatorRelation::Overlaps);
+  TEST(DesignatorPath::CompareParts(subscripts, subscriptedComponent) ==
+      DesignatorRelation::Contains);
+}
+
+void TestCombineRelations() {
+  TEST(DesignatorPath::CombineRelations(false, false, false) ==
+      DesignatorRelation::Equal);
+  TEST(DesignatorPath::CombineRelations(true, false, false) ==
+      DesignatorRelation::Contains);
+  TEST(DesignatorPath::CombineRelations(false, true, false) ==
+      DesignatorRelation::ContainedBy);
+  TEST(DesignatorPath::CombineRelations(false, false, true) ==
+      DesignatorRelation::Overlaps);
+  TEST(DesignatorPath::CombineRelations(true, true, false) ==
+      DesignatorRelation::Overlaps);
+}
+
+void TestComparePaths() {
+  DesignatorPath empty;
+  CheckRelation(empty, empty, DesignatorRelation::Equal);
+  CheckRelation(
+      empty, PathWithSubscripts({Scalar(1)}), DesignatorRelation::Disjoint);
+
+  CheckRelation(PathWithSubscripts({Scalar(1)}),
+      PathWithSubscripts({Scalar(1)}), DesignatorRelation::Equal);
+  CheckRelation(PathWithSubscripts({Section(1, 10)}),
+      PathWithSubscripts({Scalar(5)}), DesignatorRelation::Contains);
+  CheckRelation(PathWithSubscripts({Scalar(5)}),
+      PathWithSubscripts({Section(1, 10)}), DesignatorRelation::ContainedBy);
+  CheckRelation(PathWithSubscripts({Section(1, 5)}),
+      PathWithSubscripts({Section(5, 10)}), DesignatorRelation::Overlaps);
+  CheckRelation(PathWithSubscripts({Section(1, 5)}),
+      PathWithSubscripts({Section(6, 10)}), DesignatorRelation::Disjoint);
+
+  SymbolFixture symbols;
+  const semantics::Symbol &symbol{symbols.MakeSymbol("c")};
+  DesignatorPath parent{PathWithComponent(&symbol)};
+  DesignatorPath child{PathWithComponent(&symbol)};
+  child.AddSubscripts({Scalar(1)});
+  CheckRelation(parent, child, DesignatorRelation::Contains);
+  CheckRelation(child, parent, DesignatorRelation::ContainedBy);
+}
+
+void TestMayContainSubscripts() {
+  TEST(DesignatorPath::SubscriptMayContain(Scalar(1), Scalar(1)));
+  TEST(DesignatorPath::SubscriptMayContain(FullSection(), Scalar(7)));
+  TEST(!DesignatorPath::SubscriptMayContain(Scalar(7), FullSection()));
+  TEST(!DesignatorPath::SubscriptMayContain(Section(1, 5), FullSection()));
+  TEST(DesignatorPath::SubscriptMayContain(Section(1, 10), Scalar(7)));
+  TEST(!DesignatorPath::SubscriptMayContain(Section(1, 5), Scalar(7)));
+  TEST(DesignatorPath::SubscriptMayContain(Section(1, 5, 2), Scalar(7)));
+  TEST(!DesignatorPath::SubscriptListMayContain(
+      {Scalar(1)}, {Scalar(1), Scalar(2)}));
+  TEST(DesignatorPath::SubscriptListMayContain({FullSection()}, {}));
+  TEST(!DesignatorPath::SubscriptListMayContain(
+      {FullSection()}, {Scalar(1), Scalar(2)}));
+  TEST(DesignatorPath::SubscriptListMayContain(
+      {FullSection(), Section(1, 10)}, {Scalar(2), Scalar(5)}));
+}
+
+void TestMayContainPartsAndPaths() {
+  SymbolFixture symbols;
+  const semantics::Symbol &symbol1{symbols.MakeSymbol("d")};
+  const semantics::Symbol &symbol2{symbols.MakeSymbol("e")};
+  DesignatorPath::Part component1{{}, &symbol1};
+  DesignatorPath::Part component2{{}, &symbol2};
+  DesignatorPath::Part subscripts{{Section(1, 10)}, nullptr};
+  DesignatorPath::Part scalarSubscript{{Scalar(5)}, nullptr};
+  DesignatorPath::Part scalarComponent{{Scalar(5)}, &symbol1};
+
+  TEST(DesignatorPath::PartMayContain(component1, component1));
+  TEST(!DesignatorPath::PartMayContain(component1, component2));
+  TEST(DesignatorPath::PartMayContain(subscripts, scalarComponent));
+  TEST(DesignatorPath::PartMayContain(subscripts, scalarSubscript));
+
+  DesignatorPath empty;
+  DesignatorPath parent{PathWithComponent(&symbol1)};
+  DesignatorPath child{PathWithComponent(&symbol1)};
+  child.AddSubscripts({Scalar(1)});
+  DesignatorPath sibling{PathWithComponent(&symbol2)};
+
+  TEST(empty.MayContain(parent));
+  TEST(parent.MayContain(parent));
+  TEST(parent.MayContain(child));
+  TEST(!parent.MayContain(empty));
+  TEST(!child.MayContain(parent));
+  TEST(!parent.MayContain(sibling));
+}
+
+void TestAddFunctionsAndMap() {
+  SymbolFixture symbols;
+  const semantics::Symbol &symbol{symbols.MakeSymbol("f")};
+  DesignatorPath path;
+  TEST(path.empty());
+  path.AddComponent(symbol);
+  path.AddSubscripts({Scalar(1), Scalar(2)});
+  TEST(path.Parts().size() == 2);
+  TEST(path.Parts()[0].subscripts.empty());
+  TEST(path.Parts()[0].symbol == &symbol);
+  TEST(path.Parts()[1].subscripts.size() == 2);
+  TEST(path.Parts()[1].symbol == nullptr);
+
+  DesignatorPathMap<int> map;
+  TEST(map.empty());
+  map.push_back(path, 42);
+  TEST(!map.empty());
+  TEST(map.begin()->value == 42);
+  map.erase(map.begin());
+  TEST(map.empty());
+  map.push_back(DesignatorPath{}, 7);
+  map.clear();
+  TEST(map.empty());
+}
+
+void TestSubscriptsPrecedeComponentWithinPart() {
+  SymbolFixture symbols;
+  const semantics::Symbol &base{symbols.MakeSymbol("g")};
+  const semantics::Symbol &y{symbols.MakeSymbol("h")};
+  const semantics::Symbol &z{symbols.MakeSymbol("i")};
+
+  DesignatorPath x;
+  x.SetBase(NamedEntity{base});
+  TEST(x.Base().has_value());
+  TEST(x.Parts().empty());
+
+  DesignatorPath differentBase;
+  differentBase.SetBase(NamedEntity{y});
+
+  DesignatorPath xFull;
+  xFull.SetBase(NamedEntity{base});
+  xFull.AddSubscripts({FullSection()});
+  TEST(xFull.Parts().size() == 1);
+  TEST(xFull.Parts()[0].subscripts.size() == 1);
+  TEST(xFull.Parts()[0].subscripts[0] == FullSection());
+  const auto *fullTriplet{
+      std::get_if<Triplet>(&xFull.Parts()[0].subscripts[0].u)};
+  TEST(fullTriplet != nullptr);
+  if (fullTriplet) {
+    TEST(!fullTriplet->GetLower());
+    TEST(!fullTriplet->GetUpper());
+    TEST(DesignatorPath::IsFullTriplet(*fullTriplet));
+  }
+  TEST(xFull.Parts()[0].symbol == nullptr);
+  TEST(!(xFull == x));
+  TEST(x.Compare(xFull) == DesignatorRelation::Equal);
+  TEST(xFull.Compare(x) == DesignatorRelation::Equal);
+  TEST(x.MayContain(xFull));
+  TEST(xFull.MayContain(x));
+  TEST(!xFull.MayContain(differentBase));
+
+  DesignatorPath xSection;
+  xSection.SetBase(NamedEntity{base});
+  xSection.AddSubscripts({Section(1, 10)});
+  TEST(xSection.Base().has_value());
+  TEST(xSection.Parts().size() == 1);
+  TEST(xSection.Parts()[0].subscripts.size() == 1);
+  TEST(xSection.Parts()[0].symbol == nullptr);
+
+  DesignatorPath xSectionY;
+  xSectionY.SetBase(NamedEntity{base});
+  xSectionY.AddSubscripts({Section(1, 10)});
+  xSectionY.AddComponent(y);
+  TEST(xSectionY.Parts().size() == 1);
+  TEST(xSectionY.Parts()[0].subscripts.size() == 1);
+  TEST(xSectionY.Parts()[0].symbol == &y);
+
+  DesignatorPath xSectionYFull{xSectionY};
+  xSectionYFull.AddSubscripts({FullSection()});
+  TEST(xSectionYFull.Parts().size() == 2);
+  TEST(xSectionYFull.Parts()[0].subscripts.size() == 1);
+  TEST(xSectionYFull.Parts()[0].symbol == &y);
+  TEST(xSectionYFull.Parts()[1].subscripts.size() == 1);
+  TEST(xSectionYFull.Parts()[1].subscripts[0] == FullSection());
+  TEST(xSectionYFull.Parts()[1].symbol == nullptr);
+  TEST(!(xSectionYFull == xSectionY));
+  TEST(xSectionYFull.Compare(xSectionY) == DesignatorRelation::Equal);
+  TEST(xSectionY.Compare(xSectionYFull) == DesignatorRelation::Equal);
+  TEST(xSectionYFull.MayContain(xSectionY));
+  TEST(xSectionY.MayContain(xSectionYFull));
+
+  DesignatorPath xSectionYFullZ;
+  xSectionYFullZ.SetBase(NamedEntity{base});
+  xSectionYFullZ.AddSubscripts({Section(1, 10)});
+  xSectionYFullZ.AddComponent(y);
+  xSectionYFullZ.AddSubscripts({FullSection()});
+  xSectionYFullZ.AddComponent(z);
+  TEST(xSectionYFullZ.Parts().size() == 2);
+  TEST(xSectionYFullZ.Parts()[0].subscripts.size() == 1);
+  TEST(xSectionYFullZ.Parts()[0].symbol == &y);
+  TEST(xSectionYFullZ.Parts()[1].subscripts.size() == 1);
+  TEST(xSectionYFullZ.Parts()[1].subscripts[0] == FullSection());
+  TEST(xSectionYFullZ.Parts()[1].symbol == &z);
+}
+
+} // namespace
+
+int main() {
+  TestGetConstantSubscriptRange();
+  TestFullTripletDetection();
+  TestCompareSubscripts();
+  TestCompareSubscriptLists();
+  TestCompareParts();
+  TestCombineRelations();
+  TestComparePaths();
+  TestMayContainSubscripts();
+  TestMayContainPartsAndPaths();
+  TestAddFunctionsAndMap();
+  TestSubscriptsPrecedeComponentWithinPart();
+  return testing::Complete();
+}

>From a152521351f932b9a8d3cd59d08bd7878de091c2 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Thu, 23 Jul 2026 03:20:10 -0700
Subject: [PATCH 03/12] [flang][OpenACC] Check DEFAULT(NONE) for whole-object
 references

References that appear in the parse tree as a bare name or structure
component -- the object of an ALLOCATE, DEALLOCATE, or NULLIFY, and the
pointer on the left of a pointer assignment -- were not checked under
DEFAULT(NONE), because the check only ran from the Expr, Variable, and
ArrayElement handlers. Track the depth of those reference contexts and
run the whole-object DEFAULT(NONE) check from Post(Name) when a name is
reached outside all of them, so precise path-aware checking still
applies to subscripted and component references.

Also simplify the data-sharing bookkeeping: drop the unused
FindDataSharingAttributeObject, restore dataSharingAttributeObjects_ to
an UnorderedSymbolSet, and reuse the IsObjectWithVisibleDSA name for the
path-aware visibility check.
---
 flang/lib/Semantics/resolve-directives.cpp    | 97 ++++++++-----------
 .../OpenACC/acc-default-none-object-refs.f90  | 73 ++++++++++++++
 2 files changed, 114 insertions(+), 56 deletions(-)
 create mode 100644 flang/test/Semantics/OpenACC/acc-default-none-object-refs.f90

diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 426082c86d460..ca233d76d4b54 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -126,14 +126,6 @@ template <typename T> class DirectiveAttributeVisitor {
   bool IsObjectWithDSA(const Symbol &symbol) {
     return GetContext().FindSymbolWithDSA(symbol).has_value();
   }
-  bool IsObjectWithVisibleDSA(const Symbol &symbol) {
-    for (std::size_t i{dirContext_.size()}; i != 0; i--) {
-      if (dirContext_[i - 1].FindSymbolWithDSA(symbol).has_value()) {
-        return true;
-      }
-    }
-    return false;
-  }
 
   bool WithinConstruct() {
     return !dirContext_.empty() && GetContext().withinConstruct;
@@ -150,37 +142,13 @@ template <typename T> class DirectiveAttributeVisitor {
   Symbol &MakeAssocSymbol(const SourceName &name, const Symbol &prev) {
     return MakeAssocSymbol(name, prev, currScope());
   }
-  struct DataSharingAttributeObjectKey {
-    SymbolRef symbol;
-    std::optional<std::string> designator;
-    bool operator<(const DataSharingAttributeObjectKey &that) const {
-      SymbolAddressCompare compare;
-      if (compare(symbol, that.symbol)) {
-        return true;
-      }
-      if (compare(that.symbol, symbol)) {
-        return false;
-      }
-      return designator < that.designator;
-    }
-  };
-  void AddDataSharingAttributeObject(
-      SymbolRef object, std::optional<std::string> designator = std::nullopt) {
-    dataSharingAttributeObjects_.try_emplace(
-        DataSharingAttributeObjectKey{object, std::move(designator)});
-  }
-  void AddDataSharingAttributeObject(SymbolRef object, Symbol::Flag flag,
-      std::optional<std::string> designator = std::nullopt) {
-    dataSharingAttributeObjects_.try_emplace(
-        DataSharingAttributeObjectKey{object, std::move(designator)}, flag);
+  void AddDataSharingAttributeObject(SymbolRef object) {
+    dataSharingAttributeObjects_.insert(object);
   }
   void ClearDataSharingAttributeObjects() {
     dataSharingAttributeObjects_.clear();
   }
-  std::optional<Symbol::Flag> FindDataSharingAttributeObject(
-      const Symbol &, const std::optional<std::string> &designator);
-  bool HasDataSharingAttributeObject(const Symbol &,
-      const std::optional<std::string> &designator = std::nullopt);
+  bool HasDataSharingAttributeObject(const Symbol &);
 
   /// Extract the iv and bounds of a DO loop:
   /// 1. The loop index/induction variable
@@ -207,8 +175,7 @@ template <typename T> class DirectiveAttributeVisitor {
   Symbol *DeclareAccessEntity(const parser::Name &, Symbol::Flag, Scope &);
   Symbol *DeclareAccessEntity(Symbol &, Symbol::Flag, Scope &);
 
-  std::map<DataSharingAttributeObjectKey, std::optional<Symbol::Flag>>
-      dataSharingAttributeObjects_; // on one directive
+  UnorderedSymbolSet dataSharingAttributeObjects_; // on one directive
   SemanticsContext &context_;
   std::vector<DirContext> dirContext_; // used as a stack
 };
@@ -390,6 +357,7 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   void Post(const parser::Expr &);
   bool Pre(const parser::Variable &);
   void Post(const parser::Variable &);
+  bool Pre(const parser::ArrayElement &);
   void Post(const parser::ArrayElement &);
   void Post(const parser::Name &);
 
@@ -406,7 +374,7 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   void PopAccContext();
   void AddAccObjectWithDSA(
       const Symbol &, Symbol::Flag, DesignatorPath designator = {});
-  bool AccObjectWithDSAVisible(
+  bool IsObjectWithVisibleDSA(
       const Symbol &, const std::optional<DesignatorPath> &) const;
   void AdjustAccSymbolReference(const parser::Name &);
   void CheckAccDefaultNoneReference(
@@ -464,6 +432,12 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
 
   DesignatorPathMap<AccDataSharingEntry> accDataSharingEntries_;
   std::vector<DesignatorPathMap<AccDataSharingEntry>> accObjectWithDSA_;
+  // Depth of the Expr, Variable, and ArrayElement nodes currently being
+  // visited.  A Name reached at depth zero is a whole-object reference that no
+  // precise Post handler covers (e.g. the object of an ALLOCATE, DEALLOCATE, or
+  // NULLIFY, or the pointer of a pointer assignment), so DEFAULT(NONE) is
+  // checked for it directly in Post(const parser::Name &).
+  int referenceContextDepth_{0};
   Scope *topScope_;
 };
 
@@ -1137,22 +1111,11 @@ void ResolveOmpParts(
   }
 }
 
-template <typename T>
-std::optional<Symbol::Flag>
-DirectiveAttributeVisitor<T>::FindDataSharingAttributeObject(
-    const Symbol &object, const std::optional<std::string> &designator) {
-  auto it{dataSharingAttributeObjects_.find({object, designator})};
-  if (it != dataSharingAttributeObjects_.end()) {
-    return it->second;
-  }
-  return std::nullopt;
-}
-
 template <typename T>
 bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
-    const Symbol &object, const std::optional<std::string> &designator) {
-  return dataSharingAttributeObjects_.find({object, designator}) !=
-      dataSharingAttributeObjects_.end();
+    const Symbol &object) {
+  auto it{dataSharingAttributeObjects_.find(object)};
+  return it != dataSharingAttributeObjects_.end();
 }
 
 template <typename T>
@@ -1922,7 +1885,7 @@ void AccAttributeVisitor::AddAccObjectWithDSA(
       {ultimate, flag, nullptr, ultimate.name().ToString()});
 }
 
-bool AccAttributeVisitor::AccObjectWithDSAVisible(const Symbol &symbol,
+bool AccAttributeVisitor::IsObjectWithVisibleDSA(const Symbol &symbol,
     const std::optional<DesignatorPath> &reference) const {
   for (std::size_t i{accObjectWithDSA_.size()}; i != 0; --i) {
     for (const auto &entry : accObjectWithDSA_[i - 1]) {
@@ -1995,7 +1958,7 @@ void AccAttributeVisitor::CheckAccDefaultNoneReference(
     const Symbol &symbol{name.symbol->GetUltimate()};
     if (!symbol.owner().IsDerivedType() && !symbol.has<ProcEntityDetails>() &&
         !symbol.has<SubprogramDetails>() &&
-        !AccObjectWithDSAVisible(symbol, designator) &&
+        !IsObjectWithVisibleDSA(symbol, designator) &&
         !symbol.has<AssocEntityDetails>() && !symbol.has<MiscDetails>()) {
       if (Symbol * found{currScope().FindSymbol(name.source)}) {
         if (&symbol != found) {
@@ -2039,9 +2002,13 @@ void AccAttributeVisitor::CheckAccDefaultNoneReference(
   }
 }
 
-bool AccAttributeVisitor::Pre(const parser::Expr &) { return true; }
+bool AccAttributeVisitor::Pre(const parser::Expr &) {
+  ++referenceContextDepth_;
+  return true;
+}
 
 void AccAttributeVisitor::Post(const parser::Expr &expr) {
+  --referenceContextDepth_;
   if (const auto *designator{
           std::get_if<common::Indirection<parser::Designator>>(&expr.u)}) {
     std::optional<DesignatorPath> designatorPath{
@@ -2062,9 +2029,13 @@ void AccAttributeVisitor::Post(const parser::Expr &expr) {
   }
 }
 
-bool AccAttributeVisitor::Pre(const parser::Variable &) { return true; }
+bool AccAttributeVisitor::Pre(const parser::Variable &) {
+  ++referenceContextDepth_;
+  return true;
+}
 
 void AccAttributeVisitor::Post(const parser::Variable &variable) {
+  --referenceContextDepth_;
   if (const auto *designator{
           std::get_if<common::Indirection<parser::Designator>>(&variable.u)}) {
     std::optional<DesignatorPath> designatorPath{
@@ -2085,7 +2056,13 @@ void AccAttributeVisitor::Post(const parser::Variable &variable) {
   }
 }
 
+bool AccAttributeVisitor::Pre(const parser::ArrayElement &) {
+  ++referenceContextDepth_;
+  return true;
+}
+
 void AccAttributeVisitor::Post(const parser::ArrayElement &arrayElement) {
+  --referenceContextDepth_;
   DesignatorPath path;
   if (AddDesignatorPath(context_, arrayElement.Base(), path)) {
     if (auto subscripts{
@@ -2099,6 +2076,14 @@ void AccAttributeVisitor::Post(const parser::ArrayElement &arrayElement) {
 
 void AccAttributeVisitor::Post(const parser::Name &name) {
   AdjustAccSymbolReference(name);
+  // A Name reached outside any Expr, Variable, or ArrayElement is a
+  // whole-object reference that no path-aware handler covers -- for instance
+  // the object of an ALLOCATE, DEALLOCATE, or NULLIFY, or the pointer of a
+  // pointer assignment. Check it for DEFAULT(NONE) here with no designator
+  // path.
+  if (referenceContextDepth_ == 0) {
+    CheckAccDefaultNoneReference(name);
+  }
 }
 
 Symbol *AccAttributeVisitor::ResolveAccCommonBlockName(
diff --git a/flang/test/Semantics/OpenACC/acc-default-none-object-refs.f90 b/flang/test/Semantics/OpenACC/acc-default-none-object-refs.f90
new file mode 100644
index 0000000000000..5347401dbf5f3
--- /dev/null
+++ b/flang/test/Semantics/OpenACC/acc-default-none-object-refs.f90
@@ -0,0 +1,73 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenacc
+
+! DEFAULT(NONE) must also diagnose references to variables that appear in the
+! parse tree as a bare object rather than as an Expr, Variable, or ArrayElement:
+! the objects of ALLOCATE/DEALLOCATE/NULLIFY and the pointer target of a pointer
+! assignment.  These reach the parse-tree walk as a Name or StructureComponent,
+! not wrapped in an Expr/Variable/ArrayElement, so they need dedicated handling.
+
+! 1. Unlisted objects of allocate/deallocate/nullify are diagnosed.
+subroutine test_object_refs_unlisted()
+  implicit none
+  real, allocatable :: q(:)
+  real, pointer :: p(:)
+  !$acc parallel default(none)
+  !ERROR: The DEFAULT(NONE) clause requires that 'q' must be listed in a data-mapping clause
+  allocate(q(10))
+  !ERROR: The DEFAULT(NONE) clause requires that 'q' must be listed in a data-mapping clause
+  deallocate(q)
+  !ERROR: The DEFAULT(NONE) clause requires that 'p' must be listed in a data-mapping clause
+  nullify(p)
+  !$acc end parallel
+end subroutine
+
+! 2. Unlisted pointer target and pointee of a pointer assignment are diagnosed.
+!    The left-hand side (the pointer) is the reference the parse tree exposes as
+!    a bare DataRef; the right-hand side is an Expr and was already checked.
+subroutine test_pointer_assign_unlisted()
+  implicit none
+  real, pointer :: p(:), tg(:)
+  !$acc parallel default(none)
+  !ERROR: The DEFAULT(NONE) clause requires that 'p' must be listed in a data-mapping clause
+  !ERROR: The DEFAULT(NONE) clause requires that 'tg' must be listed in a data-mapping clause
+  p => tg
+  !$acc end parallel
+end subroutine
+
+! 3. A structure-component object still reports its base variable.
+subroutine test_object_ref_component()
+  implicit none
+  type t
+    real, pointer :: p(:)
+  end type
+  type(t) :: a
+  !$acc parallel default(none)
+  !ERROR: The DEFAULT(NONE) clause requires that 'a' must be listed in a data-mapping clause
+  nullify(a%p)
+  !$acc end parallel
+end subroutine
+
+! 4. Listed objects do not error.
+subroutine test_object_refs_listed()
+  implicit none
+  real, allocatable :: q(:)
+  real, pointer :: p(:)
+  !$acc parallel default(none) create(q) copyin(p)
+  allocate(q(10))
+  deallocate(q)
+  nullify(p)
+  !$acc end parallel
+end subroutine
+
+! 5. Without DEFAULT(NONE) the object references are not flagged.
+subroutine test_object_refs_no_default_none()
+  implicit none
+  real, allocatable :: q(:)
+  real, pointer :: p(:), tg(:)
+  !$acc parallel
+  allocate(q(10))
+  deallocate(q)
+  nullify(p)
+  p => tg
+  !$acc end parallel
+end subroutine

>From 16af97943bee4ad0b162abc7cdff997592e4fe97 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 29 Jul 2026 17:26:28 -0700
Subject: [PATCH 04/12] flang: add designator path semantic helper

---
 flang/include/flang/Semantics/tools.h |  14 ++
 flang/lib/Semantics/tools.cpp         | 190 ++++++++++++++++++++++++++
 2 files changed, 204 insertions(+)

diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h
index 1b4e3d27f28f1..73aef76edb371 100644
--- a/flang/include/flang/Semantics/tools.h
+++ b/flang/include/flang/Semantics/tools.h
@@ -13,6 +13,7 @@
 // canonically for use in semantic checking.
 
 #include "flang/Common/visit.h"
+#include "flang/Evaluate/designator-path.h"
 #include "flang/Evaluate/expression.h"
 #include "flang/Evaluate/shape.h"
 #include "flang/Evaluate/type.h"
@@ -206,6 +207,19 @@ bool IsAssumedLengthCharacter(const Symbol &);
 bool IsExternal(const Symbol &);
 bool IsModuleProcedure(const Symbol &);
 bool HasCoarray(const parser::Expr &);
+
+// Builds an evaluate::DesignatorPath (the structural prefix of a designator
+// used by OpenACC data-sharing analysis) from the parse tree. It uses the
+// already-resolved base symbol and component structure, and analyzes and folds
+// subscript expressions. Returns std::nullopt when the designator cannot be
+// represented (e.g. an unresolved name or a non-integer/erroneous subscript).
+std::optional<evaluate::DesignatorPath> GetDesignatorPath(
+    SemanticsContext &, const parser::Designator &);
+std::optional<evaluate::DesignatorPath> GetDesignatorPath(
+    SemanticsContext &, const parser::FunctionReference &);
+std::optional<evaluate::DesignatorPath> GetDesignatorPath(
+    SemanticsContext &, const parser::ArrayElement &);
+
 bool IsAssumedType(const Symbol &);
 bool IsEnumerationType(const Symbol &);
 bool IsEnumerationType(const DerivedTypeSpec &);
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index 64e76aa02fe6d..fd2e3a14419bf 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -9,6 +9,8 @@
 #include "flang/Parser/tools.h"
 #include "flang/Common/indirection.h"
 #include "flang/Evaluate/characteristics.h"
+#include "flang/Evaluate/fold.h"
+#include "flang/Evaluate/tools.h"
 #include "flang/Parser/dump-parse-tree.h"
 #include "flang/Parser/message.h"
 #include "flang/Parser/parse-tree.h"
@@ -20,11 +22,199 @@
 #include "flang/Support/Fortran.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
+#include <list>
+#include <optional>
 #include <set>
+#include <utility>
 #include <variant>
+#include <vector>
 
 namespace Fortran::semantics {
 
+// Parse-tree designator to evaluate::DesignatorPath conversion.
+//
+// These helpers build a DesignatorPath from the parse tree (see the note on
+// GetDesignatorPath in the header). They use already-resolved base and
+// component symbols, and analyze only subscript expressions. Anything that
+// cannot be represented is reported as absent so the caller can skip it.
+template <typename A>
+static std::optional<evaluate::Expr<evaluate::SubscriptInteger>>
+AnalyzeSubscriptExpr(SemanticsContext &context, const A &expr) {
+  if (auto maybe{evaluate::Fold(
+          context.foldingContext(), AnalyzeExpr(context, expr))}) {
+    if (auto *intExpr{
+            evaluate::UnwrapExpr<evaluate::Expr<evaluate::SomeInteger>>(
+                maybe)}) {
+      if (auto value{evaluate::ToInt64(*intExpr)}) {
+        return evaluate::Expr<evaluate::SubscriptInteger>{*value};
+      }
+      return evaluate::ConvertToType<evaluate::SubscriptInteger>(
+          std::move(*intExpr));
+    }
+  }
+  return std::nullopt;
+}
+
+static std::optional<evaluate::Subscript> AnalyzeSectionSubscript(
+    SemanticsContext &context, const parser::SectionSubscript &subscript) {
+  return common::visit(
+      common::visitors{
+          [&](const parser::SubscriptTriplet &triplet)
+              -> std::optional<evaluate::Subscript> {
+            const auto &lower{std::get<0>(triplet.t)};
+            const auto &upper{std::get<1>(triplet.t)};
+            const auto &stride{std::get<2>(triplet.t)};
+            auto lowerExpr{
+                lower ? AnalyzeSubscriptExpr(context, *lower) : std::nullopt};
+            auto upperExpr{
+                upper ? AnalyzeSubscriptExpr(context, *upper) : std::nullopt};
+            auto strideExpr{
+                stride ? AnalyzeSubscriptExpr(context, *stride) : std::nullopt};
+            if ((lower && !lowerExpr) || (upper && !upperExpr) ||
+                (stride && !strideExpr)) {
+              return std::nullopt;
+            }
+            auto result{evaluate::Triplet{std::move(lowerExpr),
+                std::move(upperExpr), std::move(strideExpr)}};
+            return evaluate::Subscript{std::move(result)};
+          },
+          [&](const parser::IntExpr &expr)
+              -> std::optional<evaluate::Subscript> {
+            if (auto subscript{AnalyzeSubscriptExpr(context, expr)}) {
+              return evaluate::Subscript{std::move(*subscript)};
+            }
+            return std::nullopt;
+          },
+      },
+      subscript.u);
+}
+
+static std::optional<std::vector<evaluate::Subscript>> AnalyzeSectionSubscripts(
+    SemanticsContext &context,
+    const std::list<parser::SectionSubscript> &list) {
+  std::vector<evaluate::Subscript> subscripts;
+  for (const parser::SectionSubscript &subscript : list) {
+    if (auto analyzed{AnalyzeSectionSubscript(context, subscript)}) {
+      subscripts.push_back(std::move(*analyzed));
+    } else {
+      return std::nullopt;
+    }
+  }
+  return subscripts;
+}
+
+static bool AddDesignatorPath(SemanticsContext &context,
+    const parser::DataRef &dataRef, evaluate::DesignatorPath &path) {
+  return common::visit(
+      common::visitors{
+          [&](const parser::Name &name) {
+            if (!name.symbol) {
+              return false;
+            }
+            path.SetBase(evaluate::NamedEntity{name.symbol->GetUltimate()});
+            return true;
+          },
+          [&](const common::Indirection<parser::StructureComponent>
+                  &component) {
+            if (!AddDesignatorPath(context, component.value().Base(), path)) {
+              return false;
+            }
+            if (const parser::Name &name{component.value().Component()};
+                name.symbol) {
+              path.AddComponent(name.symbol->GetUltimate());
+              return true;
+            }
+            return false;
+          },
+          [&](const common::Indirection<parser::ArrayElement> &arrayElement) {
+            if (!AddDesignatorPath(
+                    context, arrayElement.value().Base(), path)) {
+              return false;
+            }
+            if (auto subscripts{AnalyzeSectionSubscripts(
+                    context, arrayElement.value().Subscripts())}) {
+              path.AddSubscripts(std::move(*subscripts));
+              return true;
+            }
+            return false;
+          },
+          [&](const common::Indirection<parser::CoindexedNamedObject>
+                  &coindexed) {
+            return AddDesignatorPath(
+                context, std::get<parser::DataRef>(coindexed.value().t), path);
+          },
+      },
+      dataRef.u);
+}
+
+std::optional<evaluate::DesignatorPath> GetDesignatorPath(
+    SemanticsContext &context, const parser::Designator &designator) {
+  evaluate::DesignatorPath path;
+  bool ok{common::visit(common::visitors{
+                            [&](const parser::DataRef &dataRef) {
+                              return AddDesignatorPath(context, dataRef, path);
+                            },
+                            [&](const parser::Substring &substring) {
+                              return AddDesignatorPath(context,
+                                  std::get<parser::DataRef>(substring.t), path);
+                            },
+                        },
+      designator.u)};
+  if (ok && !path.empty()) {
+    return path;
+  }
+  return std::nullopt;
+}
+
+std::optional<evaluate::DesignatorPath> GetDesignatorPath(
+    SemanticsContext &context, const parser::FunctionReference &funcRef) {
+  const auto &call{funcRef.v};
+  const auto &procedureDesignator{
+      std::get<parser::ProcedureDesignator>(call.t)};
+  const auto *name{std::get_if<parser::Name>(&procedureDesignator.u)};
+  if (!name || !name->symbol || !name->symbol->has<ObjectEntityDetails>()) {
+    return std::nullopt;
+  }
+  std::vector<evaluate::Subscript> subscripts;
+  for (const parser::ActualArgSpec &arg :
+      std::get<std::list<parser::ActualArgSpec>>(call.t)) {
+    if (std::get<std::optional<parser::Keyword>>(arg.t)) {
+      return std::nullopt;
+    }
+    const auto *expr{std::get_if<common::Indirection<parser::Expr>>(
+        &std::get<parser::ActualArg>(arg.t).u)};
+    if (!expr) {
+      return std::nullopt;
+    }
+    if (auto subscript{AnalyzeSubscriptExpr(context, expr->value())}) {
+      subscripts.emplace_back(std::move(*subscript));
+    } else {
+      return std::nullopt;
+    }
+  }
+  if (subscripts.empty()) {
+    return std::nullopt;
+  }
+  evaluate::DesignatorPath path;
+  path.SetBase(evaluate::NamedEntity{name->symbol->GetUltimate()});
+  path.AddSubscripts(std::move(subscripts));
+  return path;
+}
+
+std::optional<evaluate::DesignatorPath> GetDesignatorPath(
+    SemanticsContext &context, const parser::ArrayElement &arrayElement) {
+  evaluate::DesignatorPath path;
+  if (!AddDesignatorPath(context, arrayElement.Base(), path)) {
+    return std::nullopt;
+  }
+  if (auto subscripts{
+          AnalyzeSectionSubscripts(context, arrayElement.Subscripts())}) {
+    path.AddSubscripts(std::move(*subscripts));
+    return path;
+  }
+  return std::nullopt;
+}
+
 // Find this or containing scope that matches predicate
 static const Scope *FindScopeContaining(
     const Scope &start, std::function<bool(const Scope &)> predicate) {

>From dde9b13870e902de278b5a8d89b1f141eb8d1351 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 29 Jul 2026 17:26:35 -0700
Subject: [PATCH 05/12] flang: simplify OpenACC directive resolution

---
 flang/lib/Semantics/resolve-directives.cpp | 249 +++------------------
 1 file changed, 25 insertions(+), 224 deletions(-)

diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index ca233d76d4b54..9512002839460 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -222,8 +222,8 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   template <typename A>
   bool Pre(const parser::LoopBounds<parser::ScalarName, A> &x) {
     if (!dirContext_.empty() && GetContext().withinConstruct) {
-      if (auto *symbol{ResolveAcc(
-              x.Name().thing, Symbol::Flag::AccPrivate, currScope())}) {
+      if (auto *symbol{DeclareOrMarkOtherAccessEntity(
+              x.Name().thing, Symbol::Flag::AccPrivate)}) {
         AddAccObjectWithDSA(*symbol, Symbol::Flag::AccPrivate);
       }
     }
@@ -366,7 +366,6 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
     SymbolRef symbol;
     Symbol::Flag flag;
     const parser::AccObject *occurrence{nullptr};
-    std::string objectName;
   };
 
   void PushAccContext(const parser::CharBlock &, llvm::acc::Directive, Scope &);
@@ -379,6 +378,7 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   void AdjustAccSymbolReference(const parser::Name &);
   void CheckAccDefaultNoneReference(
       const parser::Name &, std::optional<DesignatorPath> = std::nullopt);
+  template <typename A> void CheckAccDefaultNoneReferenceIn(const A &);
 
   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &);
   bool HasForceCollapseModifier(const parser::AccClauseList &);
@@ -403,8 +403,6 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   void CheckAssociatedLoop(const parser::DoConstruct &, bool forceCollapsed);
   void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag);
   void ResolveAccObject(const parser::AccObject &, Symbol::Flag);
-  Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &);
-  Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &);
   Symbol *ResolveName(const parser::Name &);
   Symbol *ResolveFctName(const parser::Name &);
   Symbol *ResolveAccCommonBlockName(const parser::Name *);
@@ -1773,7 +1771,8 @@ void AccAttributeVisitor::CheckAssociatedLoop(
           if (level <= 0)
             return;
           if (ivName && lower && upper) {
-            if (auto *symbol{ResolveAcc(*ivName, flag, currScope())}) {
+            if (auto *symbol{
+                    DeclareOrMarkOtherAccessEntity(*ivName, flag)}) {
               if (auto lowerExpr{semantics::AnalyzeExpr(context_, *lower)}) {
                 semantics::UnorderedSymbolSet lowerSyms =
                     evaluate::CollectSymbols(*lowerExpr);
@@ -1881,8 +1880,8 @@ void AccAttributeVisitor::AddAccObjectWithDSA(
   }
   AddToContextObjectWithDSA(ultimate, flag);
   CHECK(!accObjectWithDSA_.empty());
-  accObjectWithDSA_.back().push_back(std::move(designator),
-      {ultimate, flag, nullptr, ultimate.name().ToString()});
+  accObjectWithDSA_.back().push_back(
+      std::move(designator), {ultimate, flag, nullptr});
 }
 
 bool AccAttributeVisitor::IsObjectWithVisibleDSA(const Symbol &symbol,
@@ -1925,15 +1924,6 @@ static bool IsAccScalar(const Symbol &symbol) {
   return det && !det->IsArray();
 }
 
-static std::optional<DesignatorPath> GetDesignatorPath(
-    SemanticsContext &, const parser::Designator &);
-static std::optional<DesignatorPath> GetDesignatorPath(
-    SemanticsContext &, const parser::FunctionReference &);
-static bool AddDesignatorPath(
-    SemanticsContext &, const parser::DataRef &, DesignatorPath &);
-static std::optional<std::vector<evaluate::Subscript>> AnalyzeSectionSubscripts(
-    SemanticsContext &, const std::list<parser::SectionSubscript> &);
-
 void AccAttributeVisitor::AdjustAccSymbolReference(const parser::Name &name) {
   if (!name.symbol || !WithinConstruct()) {
     return;
@@ -2007,17 +1997,17 @@ bool AccAttributeVisitor::Pre(const parser::Expr &) {
   return true;
 }
 
-void AccAttributeVisitor::Post(const parser::Expr &expr) {
-  --referenceContextDepth_;
+template <typename A>
+void AccAttributeVisitor::CheckAccDefaultNoneReferenceIn(const A &x) {
   if (const auto *designator{
-          std::get_if<common::Indirection<parser::Designator>>(&expr.u)}) {
+          std::get_if<common::Indirection<parser::Designator>>(&x.u)}) {
     std::optional<DesignatorPath> designatorPath{
         GetDesignatorPath(context_, designator->value())};
     CheckAccDefaultNoneReference(
         parser::GetFirstName(designator->value()), designatorPath);
   } else if (const auto *functionReference{
                  std::get_if<common::Indirection<parser::FunctionReference>>(
-                     &expr.u)}) {
+                     &x.u)}) {
     const parser::Name &name{parser::GetFirstName(functionReference->value())};
     if (WithinConstruct() && GetContext().defaultDSA == Symbol::Flag::AccNone &&
         name.symbol && name.symbol->has<ObjectEntityDetails>()) {
@@ -2029,6 +2019,11 @@ void AccAttributeVisitor::Post(const parser::Expr &expr) {
   }
 }
 
+void AccAttributeVisitor::Post(const parser::Expr &expr) {
+  --referenceContextDepth_;
+  CheckAccDefaultNoneReferenceIn(expr);
+}
+
 bool AccAttributeVisitor::Pre(const parser::Variable &) {
   ++referenceContextDepth_;
   return true;
@@ -2036,24 +2031,7 @@ bool AccAttributeVisitor::Pre(const parser::Variable &) {
 
 void AccAttributeVisitor::Post(const parser::Variable &variable) {
   --referenceContextDepth_;
-  if (const auto *designator{
-          std::get_if<common::Indirection<parser::Designator>>(&variable.u)}) {
-    std::optional<DesignatorPath> designatorPath{
-        GetDesignatorPath(context_, designator->value())};
-    CheckAccDefaultNoneReference(
-        parser::GetFirstName(designator->value()), designatorPath);
-  } else if (const auto *functionReference{
-                 std::get_if<common::Indirection<parser::FunctionReference>>(
-                     &variable.u)}) {
-    const parser::Name &name{parser::GetFirstName(functionReference->value())};
-    if (WithinConstruct() && GetContext().defaultDSA == Symbol::Flag::AccNone &&
-        name.symbol && name.symbol->has<ObjectEntityDetails>()) {
-      if (std::optional<DesignatorPath> designatorPath{
-              GetDesignatorPath(context_, functionReference->value())}) {
-        CheckAccDefaultNoneReference(name, designatorPath);
-      }
-    }
-  }
+  CheckAccDefaultNoneReferenceIn(variable);
 }
 
 bool AccAttributeVisitor::Pre(const parser::ArrayElement &) {
@@ -2063,14 +2041,10 @@ bool AccAttributeVisitor::Pre(const parser::ArrayElement &) {
 
 void AccAttributeVisitor::Post(const parser::ArrayElement &arrayElement) {
   --referenceContextDepth_;
-  DesignatorPath path;
-  if (AddDesignatorPath(context_, arrayElement.Base(), path)) {
-    if (auto subscripts{
-            AnalyzeSectionSubscripts(context_, arrayElement.Subscripts())}) {
-      path.AddSubscripts(std::move(*subscripts));
-      CheckAccDefaultNoneReference(
-          parser::GetFirstName(arrayElement.Base()), path);
-    }
+  if (std::optional<DesignatorPath> path{
+          GetDesignatorPath(context_, arrayElement)}) {
+    CheckAccDefaultNoneReference(
+        parser::GetFirstName(arrayElement.Base()), *path);
   }
 }
 
@@ -2149,170 +2123,6 @@ static bool ContainsStructureComponent(const parser::Designator &designator) {
       designator.u);
 }
 
-template <typename A>
-static std::optional<evaluate::Expr<evaluate::SubscriptInteger>>
-AnalyzeSubscriptExpr(SemanticsContext &context, const A &expr) {
-  if (auto value{EvaluateInt64(context, expr)}) {
-    return evaluate::Expr<evaluate::SubscriptInteger>{*value};
-  }
-  if (MaybeExpr maybe{evaluate::Fold(
-          context.foldingContext(), AnalyzeExpr(context, expr))}) {
-    if (auto *intExpr{
-            evaluate::UnwrapExpr<evaluate::Expr<evaluate::SomeInteger>>(
-                maybe)}) {
-      return evaluate::ConvertToType<evaluate::SubscriptInteger>(
-          std::move(*intExpr));
-    }
-  }
-  return std::nullopt;
-}
-
-static std::optional<evaluate::Subscript> AnalyzeSectionSubscript(
-    SemanticsContext &context, const parser::SectionSubscript &subscript) {
-  return common::visit(
-      common::visitors{
-          [&](const parser::SubscriptTriplet &triplet)
-              -> std::optional<evaluate::Subscript> {
-            const auto &lower{std::get<0>(triplet.t)};
-            const auto &upper{std::get<1>(triplet.t)};
-            const auto &stride{std::get<2>(triplet.t)};
-            auto lowerExpr{
-                lower ? AnalyzeSubscriptExpr(context, *lower) : std::nullopt};
-            auto upperExpr{
-                upper ? AnalyzeSubscriptExpr(context, *upper) : std::nullopt};
-            auto strideExpr{
-                stride ? AnalyzeSubscriptExpr(context, *stride) : std::nullopt};
-            if ((lower && !lowerExpr) || (upper && !upperExpr) ||
-                (stride && !strideExpr)) {
-              return std::nullopt;
-            }
-            auto result{evaluate::Triplet{std::move(lowerExpr),
-                std::move(upperExpr), std::move(strideExpr)}};
-            return evaluate::Subscript{std::move(result)};
-          },
-          [&](const parser::IntExpr &expr)
-              -> std::optional<evaluate::Subscript> {
-            if (auto subscript{AnalyzeSubscriptExpr(context, expr)}) {
-              return evaluate::Subscript{std::move(*subscript)};
-            }
-            return std::nullopt;
-          },
-      },
-      subscript.u);
-}
-
-static std::optional<std::vector<evaluate::Subscript>> AnalyzeSectionSubscripts(
-    SemanticsContext &context,
-    const std::list<parser::SectionSubscript> &list) {
-  std::vector<evaluate::Subscript> subscripts;
-  for (const parser::SectionSubscript &subscript : list) {
-    if (auto analyzed{AnalyzeSectionSubscript(context, subscript)}) {
-      subscripts.push_back(std::move(*analyzed));
-    } else {
-      return std::nullopt;
-    }
-  }
-  return subscripts;
-}
-
-static bool AddDesignatorPath(SemanticsContext &context,
-    const parser::DataRef &dataRef, DesignatorPath &path) {
-  return common::visit(
-      common::visitors{
-          [&](const parser::Name &name) {
-            if (!name.symbol) {
-              return false;
-            }
-            path.SetBase(NamedEntity{name.symbol->GetUltimate()});
-            return true;
-          },
-          [&](const common::Indirection<parser::StructureComponent>
-                  &component) {
-            if (!AddDesignatorPath(context, component.value().Base(), path)) {
-              return false;
-            }
-            if (const parser::Name &name{component.value().Component()};
-                name.symbol) {
-              path.AddComponent(name.symbol->GetUltimate());
-              return true;
-            }
-            return false;
-          },
-          [&](const common::Indirection<parser::ArrayElement> &arrayElement) {
-            if (!AddDesignatorPath(
-                    context, arrayElement.value().Base(), path)) {
-              return false;
-            }
-            if (auto subscripts{AnalyzeSectionSubscripts(
-                    context, arrayElement.value().Subscripts())}) {
-              path.AddSubscripts(std::move(*subscripts));
-              return true;
-            }
-            return false;
-          },
-          [&](const common::Indirection<parser::CoindexedNamedObject>
-                  &coindexed) {
-            return AddDesignatorPath(
-                context, std::get<parser::DataRef>(coindexed.value().t), path);
-          },
-      },
-      dataRef.u);
-}
-
-static std::optional<DesignatorPath> GetDesignatorPath(
-    SemanticsContext &context, const parser::Designator &designator) {
-  DesignatorPath path;
-  bool ok{common::visit(common::visitors{
-                            [&](const parser::DataRef &dataRef) {
-                              return AddDesignatorPath(context, dataRef, path);
-                            },
-                            [&](const parser::Substring &substring) {
-                              return AddDesignatorPath(context,
-                                  std::get<parser::DataRef>(substring.t), path);
-                            },
-                        },
-      designator.u)};
-  if (ok && !path.empty()) {
-    return path;
-  }
-  return std::nullopt;
-}
-
-static std::optional<DesignatorPath> GetDesignatorPath(
-    SemanticsContext &context, const parser::FunctionReference &funcRef) {
-  const auto &call{funcRef.v};
-  const auto &procedureDesignator{
-      std::get<parser::ProcedureDesignator>(call.t)};
-  const auto *name{std::get_if<parser::Name>(&procedureDesignator.u)};
-  if (!name || !name->symbol || !name->symbol->has<ObjectEntityDetails>()) {
-    return std::nullopt;
-  }
-  std::vector<evaluate::Subscript> subscripts;
-  for (const parser::ActualArgSpec &arg :
-      std::get<std::list<parser::ActualArgSpec>>(call.t)) {
-    if (std::get<std::optional<parser::Keyword>>(arg.t)) {
-      return std::nullopt;
-    }
-    const auto *expr{std::get_if<common::Indirection<parser::Expr>>(
-        &std::get<parser::ActualArg>(arg.t).u)};
-    if (!expr) {
-      return std::nullopt;
-    }
-    if (auto subscript{AnalyzeSubscriptExpr(context, expr->value())}) {
-      subscripts.emplace_back(std::move(*subscript));
-    } else {
-      return std::nullopt;
-    }
-  }
-  if (subscripts.empty()) {
-    return std::nullopt;
-  }
-  DesignatorPath path;
-  path.SetBase(NamedEntity{name->symbol->GetUltimate()});
-  path.AddSubscripts(std::move(subscripts));
-  return path;
-}
-
 void AccAttributeVisitor::ResolveAccObject(
     const parser::AccObject &accObject, Symbol::Flag accFlag) {
   common::visit(
@@ -2375,7 +2185,8 @@ void AccAttributeVisitor::ResolveAccObject(
             // TODO: Multiple array sections of the same array with different
             // data mapping attributes is not currently supported.
             const parser::Name &baseName{parser::GetFirstName(designator)};
-            if (auto *symbol{ResolveAcc(baseName, accFlag, currScope())}) {
+            if (auto *symbol{
+                    DeclareOrMarkOtherAccessEntity(baseName, accFlag)}) {
               AddAccObjectWithDSA(*symbol, accFlag, designatorPath);
               if (isDataSharing && canCheckMultipleAppearances) {
                 CheckMultipleAppearances(baseName, *symbol, accFlag, &accObject,
@@ -2389,7 +2200,7 @@ void AccAttributeVisitor::ResolveAccObject(
                   name, *symbol, Symbol::Flag::AccCommonBlock);
               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
                 if (auto *resolvedObject{
-                        ResolveAcc(*object, accFlag, currScope())}) {
+                        DeclareOrMarkOtherAccessEntity(*object, accFlag)}) {
                   AddAccObjectWithDSA(*resolvedObject, accFlag);
                 }
               }
@@ -2403,16 +2214,6 @@ void AccAttributeVisitor::ResolveAccObject(
       accObject.u);
 }
 
-Symbol *AccAttributeVisitor::ResolveAcc(
-    const parser::Name &name, Symbol::Flag accFlag, Scope &scope) {
-  return DeclareOrMarkOtherAccessEntity(name, accFlag);
-}
-
-Symbol *AccAttributeVisitor::ResolveAcc(
-    Symbol &symbol, Symbol::Flag accFlag, Scope &scope) {
-  return DeclareOrMarkOtherAccessEntity(symbol, accFlag);
-}
-
 Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
     const parser::Name &name, Symbol::Flag accFlag) {
   if (name.symbol) {
@@ -2494,7 +2295,7 @@ void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
     }
   }
   accDataSharingEntries_.push_back(
-      std::move(designator), {*target, accFlag, occurrence, displayName});
+      std::move(designator), {*target, accFlag, occurrence});
 }
 
 #ifndef NDEBUG

>From 4ed61a78d13eae929d1be825addc670f654eccc9 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 29 Jul 2026 17:44:49 -0700
Subject: [PATCH 06/12] flang: test scoped OpenACC data-sharing tracking

---
 .../OpenACC/acc-default-none-arrays.f90       | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90 b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
index 93f99fbb247f3..fb30fd72c349f 100644
--- a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
+++ b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
@@ -149,7 +149,20 @@ subroutine test_cross_kind_bare(n)
   !$acc end parallel loop
 end subroutine
 
-! 7. Substring in an OpenACC clause is disallowed.
+! 7. Data-sharing entries are scoped to one directive.  Reusing an object in a
+!    later, sequential region must not be diagnosed as a duplicate.
+subroutine test_sequential_regions_have_independent_data_sharing_entries()
+  implicit none
+  real :: a(10)
+  !$acc parallel copy(a)
+  a = 1.0
+  !$acc end parallel
+  !$acc parallel copy(a)
+  a = 2.0
+  !$acc end parallel
+end subroutine
+
+! 8. Substring in an OpenACC clause is disallowed.
 subroutine test_substring()
   implicit none
   character(len=10) :: str
@@ -158,7 +171,7 @@ subroutine test_substring()
   !$acc end parallel
 end subroutine
 
-! 8. Same array section in conflicting private and copy clauses.
+! 9. Same array section in conflicting private and copy clauses.
 ! TODO: cross-kind detection for array sections is not implemented; no error
 !       produced for 'a(1:n)' appearing in both copy and private.
 subroutine test_cross_kind_sections(n)
@@ -173,7 +186,7 @@ subroutine test_cross_kind_sections(n)
   !$acc end parallel loop
 end subroutine
 
-! 9. Different sections of the same array in conflicting copy and private clauses.
+! 10. Different sections of the same array in conflicting copy and private clauses.
 ! TODO: cross-kind detection for array sections is not implemented; no error
 !       produced for 'a' appearing in both copy and private.
 subroutine test_cross_kind_sections2(n)

>From 2c98e11b8532d397b78f637049f31b4450d5dabf Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 29 Jul 2026 17:57:29 -0700
Subject: [PATCH 07/12] flang: scope OpenACC data-sharing paths by directive

---
 flang/lib/Semantics/resolve-directives.cpp    | 128 +++++++++++++-----
 .../OpenACC/acc-default-none-arrays.f90       |   3 +-
 2 files changed, 92 insertions(+), 39 deletions(-)

diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 9512002839460..fc9a5f3da65fc 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -180,10 +180,54 @@ template <typename T> class DirectiveAttributeVisitor {
   std::vector<DirContext> dirContext_; // used as a stack
 };
 
-class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
+class AccAttributeVisitor {
+private:
+  struct AccDataSharingEntry {
+    SymbolRef symbol;
+    Symbol::Flag flag;
+    const parser::AccObject *occurrence{nullptr};
+  };
+
+  struct AccDirContext {
+    AccDirContext(const parser::CharBlock &source, llvm::acc::Directive d,
+        Scope &s)
+        : directiveSource{source}, directive{d}, scope{s} {}
+    parser::CharBlock directiveSource;
+    llvm::acc::Directive directive;
+    Scope &scope;
+    Symbol::Flag defaultDSA{Symbol::Flag::AccShared};
+    DesignatorPathMap<AccDataSharingEntry> objectsWithDSA;
+    bool withinConstruct{false};
+    std::int64_t associatedLoopLevel{0};
+  };
+
+  AccDirContext &GetContext() {
+    CHECK(!dirContext_.empty());
+    return dirContext_.back();
+  }
+  const AccDirContext &GetContext() const {
+    CHECK(!dirContext_.empty());
+    return dirContext_.back();
+  }
+  Scope &currScope() { return GetContext().scope; }
+  bool WithinConstruct() const {
+    return !dirContext_.empty() && GetContext().withinConstruct;
+  }
+  void SetContextDefaultDSA(Symbol::Flag flag) {
+    GetContext().defaultDSA = flag;
+  }
+  void SetContextAssociatedLoopLevel(std::int64_t level) {
+    GetContext().associatedLoopLevel = level;
+  }
+  std::tuple<const parser::Name *, const parser::ScalarExpr *,
+      const parser::ScalarExpr *, const parser::ScalarExpr *>
+  GetLoopBounds(const parser::DoConstruct &);
+  static const parser::DoConstruct *GetDoConstructIf(
+      const parser::ExecutionPartConstruct &);
+
 public:
   explicit AccAttributeVisitor(SemanticsContext &context, Scope *topScope)
-      : DirectiveAttributeVisitor(context), topScope_(topScope) {}
+      : context_{context}, topScope_(topScope) {}
 
   template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
   template <typename A> bool Pre(const A &) { return true; }
@@ -362,12 +406,6 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   void Post(const parser::Name &);
 
 private:
-  struct AccDataSharingEntry {
-    SymbolRef symbol;
-    Symbol::Flag flag;
-    const parser::AccObject *occurrence{nullptr};
-  };
-
   void PushAccContext(const parser::CharBlock &, llvm::acc::Directive, Scope &);
   void PushAccContext(const parser::CharBlock &, llvm::acc::Directive);
   void PopAccContext();
@@ -413,7 +451,6 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
       bool warnSameKindDuplicate = true,
       std::optional<std::string> objectName = {},
       DesignatorPath designator = {});
-  void ClearAccDataSharingEntries() { accDataSharingEntries_.clear(); }
   void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
   void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
   void AllowOnlyVariable(const parser::AccObject &object);
@@ -428,8 +465,8 @@ class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
   void ClearUseDeviceObjects() { useDeviceObjects_.clear(); }
   UnorderedSymbolSet useDeviceObjects_;
 
-  DesignatorPathMap<AccDataSharingEntry> accDataSharingEntries_;
-  std::vector<DesignatorPathMap<AccDataSharingEntry>> accObjectWithDSA_;
+  SemanticsContext &context_;
+  std::vector<AccDirContext> dirContext_; // used as a stack
   // Depth of the Expr, Variable, and ArrayElement nodes currently being
   // visited.  A Name reached at depth zero is a whole-object reference that no
   // precise Post handler covers (e.g. the object of an ALLOCATE, DEALLOCATE, or
@@ -1189,6 +1226,31 @@ Symbol *DirectiveAttributeVisitor<T>::DeclareAccessEntity(
   }
 }
 
+std::tuple<const parser::Name *, const parser::ScalarExpr *,
+    const parser::ScalarExpr *, const parser::ScalarExpr *>
+AccAttributeVisitor::GetLoopBounds(const parser::DoConstruct &x) {
+  using Bounds = parser::LoopControl::Bounds;
+  if (x.GetLoopControl()) {
+    if (const Bounds *b{std::get_if<Bounds>(&x.GetLoopControl()->u)}) {
+      const auto &step = b->Step();
+      return {&b->Name().thing, &b->Lower(), &b->Upper(),
+          step.has_value() ? &step.value() : nullptr};
+    }
+  } else {
+    context_
+        .Say(std::get<parser::Statement<parser::NonLabelDoStmt>>(x.t).source,
+            "Loop control is not present in the DO LOOP"_err_en_US)
+        .Attach(GetContext().directiveSource,
+            "associated with the enclosing LOOP construct"_en_US);
+  }
+  return {nullptr, nullptr, nullptr, nullptr};
+}
+
+const parser::DoConstruct *AccAttributeVisitor::GetDoConstructIf(
+    const parser::ExecutionPartConstruct &x) {
+  return parser::Unwrap<parser::DoConstruct>(x);
+}
+
 bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) {
   const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
   const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
@@ -1203,7 +1265,6 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) {
   default:
     break;
   }
-  ClearAccDataSharingEntries();
   ClearUseDeviceObjects();
   return true;
 }
@@ -1215,7 +1276,6 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCDeclarativeConstruct &x) {
         std::get<parser::AccDeclarativeDirective>(declConstruct->t)};
     PushAccContext(declDir.source, llvm::acc::Directive::ACCD_declare);
   }
-  ClearAccDataSharingEntries();
   return true;
 }
 
@@ -1287,7 +1347,6 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) {
   if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
     PushAccContext(loopDir.source, loopDir.v);
   }
-  ClearAccDataSharingEntries();
   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
   CheckAssociatedLoop(*outer, HasForceCollapseModifier(clauseList));
@@ -1308,7 +1367,6 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) {
   default:
     break;
   }
-  ClearAccDataSharingEntries();
   return true;
 }
 
@@ -1516,7 +1574,6 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) {
   SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
   const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
   CheckAssociatedLoop(*outer, HasForceCollapseModifier(clauseList));
-  ClearAccDataSharingEntries();
   return true;
 }
 
@@ -1613,7 +1670,6 @@ void AccAttributeVisitor::AllowOnlyVariable(const parser::AccObject &object) {
 bool AccAttributeVisitor::Pre(const parser::OpenACCWaitConstruct &x) {
   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
   PushAccContext(verbatim.source, llvm::acc::Directive::ACCD_wait);
-  ClearAccDataSharingEntries();
   return true;
 }
 
@@ -1631,15 +1687,12 @@ bool AccAttributeVisitor::Pre(const parser::OpenACCAtomicConstruct &x) {
       },
       x.u);
   PushAccContext(verbatimSource, llvm::acc::Directive::ACCD_atomic);
-  ClearAccDataSharingEntries();
   return true;
 }
 
 bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) {
   const auto &verbatim{std::get<parser::Verbatim>(x.t)};
   PushAccContext(verbatim.source, llvm::acc::Directive::ACCD_cache);
-  ClearAccDataSharingEntries();
-
   const auto &objectListWithModifier =
       std::get<parser::AccObjectListWithModifier>(x.t);
   const auto &objectList =
@@ -1857,8 +1910,10 @@ void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
 
 void AccAttributeVisitor::PushAccContext(
     const parser::CharBlock &source, llvm::acc::Directive dir, Scope &scope) {
-  PushContext(source, dir, scope);
-  accObjectWithDSA_.emplace_back();
+  dirContext_.emplace_back(source, dir, scope);
+  if (dirContext_.size() > 1) {
+    GetContext().defaultDSA = dirContext_[dirContext_.size() - 2].defaultDSA;
+  }
 }
 
 void AccAttributeVisitor::PushAccContext(
@@ -1867,9 +1922,8 @@ void AccAttributeVisitor::PushAccContext(
 }
 
 void AccAttributeVisitor::PopAccContext() {
-  CHECK(!accObjectWithDSA_.empty());
-  accObjectWithDSA_.pop_back();
-  PopContext();
+  CHECK(!dirContext_.empty());
+  dirContext_.pop_back();
 }
 
 void AccAttributeVisitor::AddAccObjectWithDSA(
@@ -1878,16 +1932,14 @@ void AccAttributeVisitor::AddAccObjectWithDSA(
   if (designator.empty()) {
     designator.SetBase(NamedEntity{ultimate});
   }
-  AddToContextObjectWithDSA(ultimate, flag);
-  CHECK(!accObjectWithDSA_.empty());
-  accObjectWithDSA_.back().push_back(
+  GetContext().objectsWithDSA.push_back(
       std::move(designator), {ultimate, flag, nullptr});
 }
 
 bool AccAttributeVisitor::IsObjectWithVisibleDSA(const Symbol &symbol,
     const std::optional<DesignatorPath> &reference) const {
-  for (std::size_t i{accObjectWithDSA_.size()}; i != 0; --i) {
-    for (const auto &entry : accObjectWithDSA_[i - 1]) {
+  for (std::size_t i{dirContext_.size()}; i != 0; --i) {
+    for (const auto &entry : dirContext_[i - 1].objectsWithDSA) {
       if (&*entry.value.symbol != &symbol) {
         continue;
       }
@@ -2167,12 +2219,13 @@ void AccAttributeVisitor::ResolveAccObject(
               if (canCheckMultipleAppearances) {
                 const parser::Name &baseName{parser::GetFirstName(designator)};
                 if (baseName.symbol) {
-                  AddAccObjectWithDSA(
-                      *baseName.symbol, accFlag, designatorPath);
                   if (isDataSharing) {
                     CheckMultipleAppearances(baseName, *baseName.symbol,
                         accFlag, &accObject, true, designatorName,
                         designatorPath);
+                  } else {
+                    AddAccObjectWithDSA(
+                        *baseName.symbol, accFlag, designatorPath);
                   }
                 }
               }
@@ -2187,10 +2240,11 @@ void AccAttributeVisitor::ResolveAccObject(
             const parser::Name &baseName{parser::GetFirstName(designator)};
             if (auto *symbol{
                     DeclareOrMarkOtherAccessEntity(baseName, accFlag)}) {
-              AddAccObjectWithDSA(*symbol, accFlag, designatorPath);
               if (isDataSharing && canCheckMultipleAppearances) {
                 CheckMultipleAppearances(baseName, *symbol, accFlag, &accObject,
                     true, designatorName, designatorPath);
+              } else {
+                AddAccObjectWithDSA(*symbol, accFlag, designatorPath);
               }
             }
           },
@@ -2245,8 +2299,8 @@ void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
     designator.SetBase(NamedEntity{*target});
   }
   const std::string displayName{objectName.value_or(name.ToString())};
-  for (auto iter{accDataSharingEntries_.begin()};
-      iter != accDataSharingEntries_.end();) {
+  auto &objectsWithDSA{GetContext().objectsWithDSA};
+  for (auto iter{objectsWithDSA.begin()}; iter != objectsWithDSA.end();) {
     AccDataSharingEntry &entry{iter->value};
     if (&*entry.symbol != target) {
       ++iter;
@@ -2285,7 +2339,7 @@ void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
       if (entry.occurrence) {
         context_.MarkAccObjectDuplicate(entry.occurrence);
       }
-      iter = accDataSharingEntries_.erase(iter);
+      iter = objectsWithDSA.erase(iter);
       continue;
     case DesignatorRelation::Overlaps:
       ++iter;
@@ -2294,7 +2348,7 @@ void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
       llvm_unreachable("disjoint relation handled above");
     }
   }
-  accDataSharingEntries_.push_back(
+  objectsWithDSA.push_back(
       std::move(designator), {*target, accFlag, occurrence});
 }
 
diff --git a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90 b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
index fb30fd72c349f..bcc3f549831ff 100644
--- a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
+++ b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
@@ -172,13 +172,12 @@ subroutine test_substring()
 end subroutine
 
 ! 9. Same array section in conflicting private and copy clauses.
-! TODO: cross-kind detection for array sections is not implemented; no error
-!       produced for 'a(1:n)' appearing in both copy and private.
 subroutine test_cross_kind_sections(n)
   implicit none
   integer, intent(in) :: n
   real :: a(n)
   integer :: i
+  !ERROR: 'a(1:n)' appears in more than one data-sharing clause on the same OpenACC directive
   !$acc parallel loop default(none) copy(a(1:n)) private(a(1:n))
   do i = 1, n
     a(i) = 0.0

>From e87294287d2ec83d2443b3740449a91ecc49d21a Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 29 Jul 2026 18:14:44 -0700
Subject: [PATCH 08/12] flang: normalize OpenACC data-sharing designator paths

---
 flang/lib/Semantics/resolve-directives.cpp | 147 +++++++++------------
 flang/lib/Semantics/tools.cpp              |  13 +-
 2 files changed, 71 insertions(+), 89 deletions(-)

diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index fc9a5f3da65fc..5305b01286747 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -183,7 +183,6 @@ template <typename T> class DirectiveAttributeVisitor {
 class AccAttributeVisitor {
 private:
   struct AccDataSharingEntry {
-    SymbolRef symbol;
     Symbol::Flag flag;
     const parser::AccObject *occurrence{nullptr};
   };
@@ -268,7 +267,8 @@ class AccAttributeVisitor {
     if (!dirContext_.empty() && GetContext().withinConstruct) {
       if (auto *symbol{DeclareOrMarkOtherAccessEntity(
               x.Name().thing, Symbol::Flag::AccPrivate)}) {
-        AddAccObjectWithDSA(*symbol, Symbol::Flag::AccPrivate);
+        AddAccObjectWithDSA(
+            MakeBaseDesignatorPath(*symbol), Symbol::Flag::AccPrivate);
       }
     }
     return true;
@@ -409,13 +409,11 @@ class AccAttributeVisitor {
   void PushAccContext(const parser::CharBlock &, llvm::acc::Directive, Scope &);
   void PushAccContext(const parser::CharBlock &, llvm::acc::Directive);
   void PopAccContext();
-  void AddAccObjectWithDSA(
-      const Symbol &, Symbol::Flag, DesignatorPath designator = {});
-  bool IsObjectWithVisibleDSA(
-      const Symbol &, const std::optional<DesignatorPath> &) const;
+  static DesignatorPath MakeBaseDesignatorPath(const Symbol &);
+  void AddAccObjectWithDSA(DesignatorPath, Symbol::Flag);
+  bool IsObjectWithVisibleDSA(const DesignatorPath &) const;
   void AdjustAccSymbolReference(const parser::Name &);
-  void CheckAccDefaultNoneReference(
-      const parser::Name &, std::optional<DesignatorPath> = std::nullopt);
+  void CheckAccDefaultNoneReference(const parser::Name &, DesignatorPath);
   template <typename A> void CheckAccDefaultNoneReferenceIn(const A &);
 
   std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &);
@@ -446,11 +444,10 @@ class AccAttributeVisitor {
   Symbol *ResolveAccCommonBlockName(const parser::Name *);
   Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
-  void CheckMultipleAppearances(const parser::Name &, const Symbol &,
-      Symbol::Flag, const parser::AccObject *occurrence = nullptr,
+  void CheckMultipleAppearances(const parser::Name &, Symbol::Flag,
+      DesignatorPath, const parser::AccObject *occurrence = nullptr,
       bool warnSameKindDuplicate = true,
-      std::optional<std::string> objectName = {},
-      DesignatorPath designator = {});
+      std::optional<std::string> objectName = {});
   void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
   void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
   void AllowOnlyVariable(const parser::AccObject &object);
@@ -1926,30 +1923,26 @@ void AccAttributeVisitor::PopAccContext() {
   dirContext_.pop_back();
 }
 
+DesignatorPath AccAttributeVisitor::MakeBaseDesignatorPath(
+    const Symbol &symbol) {
+  DesignatorPath path;
+  path.SetBase(NamedEntity{symbol.GetUltimate()});
+  return path;
+}
+
 void AccAttributeVisitor::AddAccObjectWithDSA(
-    const Symbol &symbol, Symbol::Flag flag, DesignatorPath designator) {
-  const Symbol &ultimate{symbol.GetUltimate()};
-  if (designator.empty()) {
-    designator.SetBase(NamedEntity{ultimate});
+    DesignatorPath designator, Symbol::Flag flag) {
+  if (!designator.empty()) {
+    GetContext().objectsWithDSA.push_back(
+        std::move(designator), {flag, nullptr});
   }
-  GetContext().objectsWithDSA.push_back(
-      std::move(designator), {ultimate, flag, nullptr});
 }
 
-bool AccAttributeVisitor::IsObjectWithVisibleDSA(const Symbol &symbol,
-    const std::optional<DesignatorPath> &reference) const {
+bool AccAttributeVisitor::IsObjectWithVisibleDSA(
+    const DesignatorPath &reference) const {
   for (std::size_t i{dirContext_.size()}; i != 0; --i) {
     for (const auto &entry : dirContext_[i - 1].objectsWithDSA) {
-      if (&*entry.value.symbol != &symbol) {
-        continue;
-      }
-      if (entry.path.empty()) {
-        return true;
-      }
-      if (!reference && entry.path.HasBaseOnly()) {
-        return true;
-      }
-      if (reference && entry.path.MayContain(*reference)) {
+      if (entry.path.MayContain(reference)) {
         return true;
       }
     }
@@ -1995,12 +1988,12 @@ void AccAttributeVisitor::AdjustAccSymbolReference(const parser::Name &name) {
 }
 
 void AccAttributeVisitor::CheckAccDefaultNoneReference(
-    const parser::Name &name, std::optional<DesignatorPath> designator) {
+    const parser::Name &name, DesignatorPath designator) {
   if (name.symbol && WithinConstruct()) {
     const Symbol &symbol{name.symbol->GetUltimate()};
     if (!symbol.owner().IsDerivedType() && !symbol.has<ProcEntityDetails>() &&
         !symbol.has<SubprogramDetails>() &&
-        !IsObjectWithVisibleDSA(symbol, designator) &&
+        !IsObjectWithVisibleDSA(designator) &&
         !symbol.has<AssocEntityDetails>() && !symbol.has<MiscDetails>()) {
       if (Symbol * found{currScope().FindSymbol(name.source)}) {
         if (&symbol != found) {
@@ -2055,8 +2048,10 @@ void AccAttributeVisitor::CheckAccDefaultNoneReferenceIn(const A &x) {
           std::get_if<common::Indirection<parser::Designator>>(&x.u)}) {
     std::optional<DesignatorPath> designatorPath{
         GetDesignatorPath(context_, designator->value())};
-    CheckAccDefaultNoneReference(
-        parser::GetFirstName(designator->value()), designatorPath);
+    if (designatorPath) {
+      CheckAccDefaultNoneReference(
+          parser::GetFirstName(designator->value()), std::move(*designatorPath));
+    }
   } else if (const auto *functionReference{
                  std::get_if<common::Indirection<parser::FunctionReference>>(
                      &x.u)}) {
@@ -2065,7 +2060,7 @@ void AccAttributeVisitor::CheckAccDefaultNoneReferenceIn(const A &x) {
         name.symbol && name.symbol->has<ObjectEntityDetails>()) {
       if (std::optional<DesignatorPath> designatorPath{
               GetDesignatorPath(context_, functionReference->value())}) {
-        CheckAccDefaultNoneReference(name, designatorPath);
+        CheckAccDefaultNoneReference(name, std::move(*designatorPath));
       }
     }
   }
@@ -2096,7 +2091,7 @@ void AccAttributeVisitor::Post(const parser::ArrayElement &arrayElement) {
   if (std::optional<DesignatorPath> path{
           GetDesignatorPath(context_, arrayElement)}) {
     CheckAccDefaultNoneReference(
-        parser::GetFirstName(arrayElement.Base()), *path);
+        parser::GetFirstName(arrayElement.Base()), std::move(*path));
   }
 }
 
@@ -2105,10 +2100,11 @@ void AccAttributeVisitor::Post(const parser::Name &name) {
   // A Name reached outside any Expr, Variable, or ArrayElement is a
   // whole-object reference that no path-aware handler covers -- for instance
   // the object of an ALLOCATE, DEALLOCATE, or NULLIFY, or the pointer of a
-  // pointer assignment. Check it for DEFAULT(NONE) here with no designator
-  // path.
+  // pointer assignment. Its resolved name is an exact base-only path.
   if (referenceContextDepth_ == 0) {
-    CheckAccDefaultNoneReference(name);
+    if (name.symbol) {
+      CheckAccDefaultNoneReference(name, MakeBaseDesignatorPath(*name.symbol));
+    }
   }
 }
 
@@ -2192,24 +2188,15 @@ void AccAttributeVisitor::ResolveAccObject(
                 designatorPath = std::move(*path);
                 canCheckMultipleAppearances = true;
               }
-              // Subscripted designator: evaluate subscripts and detect
-              // the substring case that is disallowed in OpenACC clauses.
-              if (MaybeExpr expr{AnalyzeExpr(context_, designator)}) {
-                if (std::holds_alternative<parser::Substring>(designator.u)) {
-                  context_.Say(designator.source,
-                      "Substrings are not allowed on OpenACC "
-                      "directives or clauses"_err_en_US);
-                  return;
-                }
-                if (designatorPath.empty()) {
-                  if (std::optional<DesignatorPath> path{
-                          DesignatorPath::Get(expr)}) {
-                    designatorPath = std::move(*path);
-                    canCheckMultipleAppearances = true;
-                  } else {
-                    canCheckMultipleAppearances = false;
-                  }
-                }
+              // Analyze first so a substring is recognized before emitting
+              // the OpenACC-specific restriction diagnostic.  A substring is
+              // intentionally never represented as a DesignatorPath.
+              if (AnalyzeExpr(context_, designator) &&
+                  std::holds_alternative<parser::Substring>(designator.u)) {
+                context_.Say(designator.source,
+                    "Substrings are not allowed on OpenACC directives or "
+                    "clauses"_err_en_US);
+                return;
               }
             }
             const bool isDataSharing{dataSharingAttributeFlags.test(accFlag)};
@@ -2218,14 +2205,13 @@ void AccAttributeVisitor::ResolveAccObject(
               // component clause does not cover every reference to the base.
               if (canCheckMultipleAppearances) {
                 const parser::Name &baseName{parser::GetFirstName(designator)};
-                if (baseName.symbol) {
+                if (baseName.symbol && !designatorPath.empty()) {
                   if (isDataSharing) {
-                    CheckMultipleAppearances(baseName, *baseName.symbol,
-                        accFlag, &accObject, true, designatorName,
-                        designatorPath);
+                    CheckMultipleAppearances(baseName, accFlag,
+                        std::move(designatorPath), &accObject, true,
+                        designatorName);
                   } else {
-                    AddAccObjectWithDSA(
-                        *baseName.symbol, accFlag, designatorPath);
+                    AddAccObjectWithDSA(std::move(designatorPath), accFlag);
                   }
                 }
               }
@@ -2240,22 +2226,26 @@ void AccAttributeVisitor::ResolveAccObject(
             const parser::Name &baseName{parser::GetFirstName(designator)};
             if (auto *symbol{
                     DeclareOrMarkOtherAccessEntity(baseName, accFlag)}) {
+              if (isBareName) {
+                designatorPath = MakeBaseDesignatorPath(*symbol);
+              }
               if (isDataSharing && canCheckMultipleAppearances) {
-                CheckMultipleAppearances(baseName, *symbol, accFlag, &accObject,
-                    true, designatorName, designatorPath);
-              } else {
-                AddAccObjectWithDSA(*symbol, accFlag, designatorPath);
+                CheckMultipleAppearances(baseName, accFlag,
+                    std::move(designatorPath), &accObject, true, designatorName);
+              } else if (!designatorPath.empty()) {
+                AddAccObjectWithDSA(std::move(designatorPath), accFlag);
               }
             }
           },
           [&](const parser::Name &name) { // common block
             if (auto *symbol{ResolveAccCommonBlockName(&name)}) {
-              CheckMultipleAppearances(
-                  name, *symbol, Symbol::Flag::AccCommonBlock);
+              CheckMultipleAppearances(name, Symbol::Flag::AccCommonBlock,
+                  MakeBaseDesignatorPath(*symbol));
               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
                 if (auto *resolvedObject{
                         DeclareOrMarkOtherAccessEntity(*object, accFlag)}) {
-                  AddAccObjectWithDSA(*resolvedObject, accFlag);
+                  AddAccObjectWithDSA(
+                      MakeBaseDesignatorPath(*resolvedObject), accFlag);
                 }
               }
             } else {
@@ -2291,29 +2281,24 @@ Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
 }
 
 void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
-    const Symbol &symbol, Symbol::Flag accFlag,
+    Symbol::Flag accFlag, DesignatorPath designator,
     const parser::AccObject *occurrence, bool warnSameKindDuplicate,
-    std::optional<std::string> objectName, DesignatorPath designator) {
-  const auto *target{&symbol};
+    std::optional<std::string> objectName) {
   if (designator.empty()) {
-    designator.SetBase(NamedEntity{*target});
+    return;
   }
   const std::string displayName{objectName.value_or(name.ToString())};
   auto &objectsWithDSA{GetContext().objectsWithDSA};
   for (auto iter{objectsWithDSA.begin()}; iter != objectsWithDSA.end();) {
     AccDataSharingEntry &entry{iter->value};
-    if (&*entry.symbol != target) {
-      ++iter;
-      continue;
-    }
     DesignatorRelation relation{iter->path.Compare(designator)};
     if (relation == DesignatorRelation::Disjoint) {
       ++iter;
       continue;
     }
 
-    // Reduction is excluded from same-kind duplicate elision: two reduction
-    // clauses with the same Symbol::Flag may still differ in operator.
+    // TODO: Record the reduction operator in AccDataSharingEntry so compatible
+    // reductions can use the ordinary same-kind duplicate handling.
     if (entry.flag != accFlag || accFlag == Symbol::Flag::AccReduction) {
       context_.Say(name.source,
           "'%s' appears in more than one data-sharing clause on the same OpenACC directive"_err_en_US,
@@ -2349,7 +2334,7 @@ void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
     }
   }
   objectsWithDSA.push_back(
-      std::move(designator), {*target, accFlag, occurrence});
+      std::move(designator), {accFlag, occurrence});
 }
 
 #ifndef NDEBUG
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index fd2e3a14419bf..3a2bfab292680 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -138,10 +138,10 @@ static bool AddDesignatorPath(SemanticsContext &context,
             }
             return false;
           },
-          [&](const common::Indirection<parser::CoindexedNamedObject>
-                  &coindexed) {
-            return AddDesignatorPath(
-                context, std::get<parser::DataRef>(coindexed.value().t), path);
+          [](const common::Indirection<parser::CoindexedNamedObject> &) {
+            // DesignatorPath does not represent cosubscripts yet.  Do not
+            // collapse a coindexed reference to its local base object.
+            return false;
           },
       },
       dataRef.u);
@@ -154,10 +154,7 @@ std::optional<evaluate::DesignatorPath> GetDesignatorPath(
                             [&](const parser::DataRef &dataRef) {
                               return AddDesignatorPath(context, dataRef, path);
                             },
-                            [&](const parser::Substring &substring) {
-                              return AddDesignatorPath(context,
-                                  std::get<parser::DataRef>(substring.t), path);
-                            },
+                            [](const parser::Substring &) { return false; },
                         },
       designator.u)};
   if (ok && !path.empty()) {

>From 63ff600c8af8a170f9225d367e2f900fee867d61 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 29 Jul 2026 18:19:13 -0700
Subject: [PATCH 09/12] flang: clarify OpenACC directive object resolution

---
 flang/lib/Semantics/resolve-directives.cpp | 37 +++++++++++++---------
 1 file changed, 22 insertions(+), 15 deletions(-)

diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 5305b01286747..3002999080e7b 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -413,6 +413,11 @@ class AccAttributeVisitor {
   void AddAccObjectWithDSA(DesignatorPath, Symbol::Flag);
   bool IsObjectWithVisibleDSA(const DesignatorPath &) const;
   void AdjustAccSymbolReference(const parser::Name &);
+  void enterExpressionLikeContext() { ++expressionLikeDepthCount_; }
+  void exitExpressionLikeContext() { --expressionLikeDepthCount_; }
+  bool inExpressionLikeContext() const {
+    return expressionLikeDepthCount_ != 0;
+  }
   void CheckAccDefaultNoneReference(const parser::Name &, DesignatorPath);
   template <typename A> void CheckAccDefaultNoneReferenceIn(const A &);
 
@@ -464,12 +469,11 @@ class AccAttributeVisitor {
 
   SemanticsContext &context_;
   std::vector<AccDirContext> dirContext_; // used as a stack
-  // Depth of the Expr, Variable, and ArrayElement nodes currently being
-  // visited.  A Name reached at depth zero is a whole-object reference that no
-  // precise Post handler covers (e.g. the object of an ALLOCATE, DEALLOCATE, or
-  // NULLIFY, or the pointer of a pointer assignment), so DEFAULT(NONE) is
-  // checked for it directly in Post(const parser::Name &).
-  int referenceContextDepth_{0};
+  // Expr, Variable, and ArrayElement visitor nodes have path-aware post
+  // handlers. A Name outside those expression-like contexts is a whole-object
+  // reference that needs DEFAULT(NONE) checking directly (e.g. ALLOCATE,
+  // DEALLOCATE, NULLIFY, or the pointer in a pointer assignment).
+  int expressionLikeDepthCount_{0};
   Scope *topScope_;
 };
 
@@ -2038,7 +2042,7 @@ void AccAttributeVisitor::CheckAccDefaultNoneReference(
 }
 
 bool AccAttributeVisitor::Pre(const parser::Expr &) {
-  ++referenceContextDepth_;
+  enterExpressionLikeContext();
   return true;
 }
 
@@ -2067,27 +2071,27 @@ void AccAttributeVisitor::CheckAccDefaultNoneReferenceIn(const A &x) {
 }
 
 void AccAttributeVisitor::Post(const parser::Expr &expr) {
-  --referenceContextDepth_;
+  exitExpressionLikeContext();
   CheckAccDefaultNoneReferenceIn(expr);
 }
 
 bool AccAttributeVisitor::Pre(const parser::Variable &) {
-  ++referenceContextDepth_;
+  enterExpressionLikeContext();
   return true;
 }
 
 void AccAttributeVisitor::Post(const parser::Variable &variable) {
-  --referenceContextDepth_;
+  exitExpressionLikeContext();
   CheckAccDefaultNoneReferenceIn(variable);
 }
 
 bool AccAttributeVisitor::Pre(const parser::ArrayElement &) {
-  ++referenceContextDepth_;
+  enterExpressionLikeContext();
   return true;
 }
 
 void AccAttributeVisitor::Post(const parser::ArrayElement &arrayElement) {
-  --referenceContextDepth_;
+  exitExpressionLikeContext();
   if (std::optional<DesignatorPath> path{
           GetDesignatorPath(context_, arrayElement)}) {
     CheckAccDefaultNoneReference(
@@ -2101,7 +2105,7 @@ void AccAttributeVisitor::Post(const parser::Name &name) {
   // whole-object reference that no path-aware handler covers -- for instance
   // the object of an ALLOCATE, DEALLOCATE, or NULLIFY, or the pointer of a
   // pointer assignment. Its resolved name is an exact base-only path.
-  if (referenceContextDepth_ == 0) {
+  if (!inExpressionLikeContext()) {
     if (name.symbol) {
       CheckAccDefaultNoneReference(name, MakeBaseDesignatorPath(*name.symbol));
     }
@@ -2176,6 +2180,8 @@ void AccAttributeVisitor::ResolveAccObject(
   common::visit(
       common::visitors{
           [&](const parser::Designator &designator) {
+            // First form an exact structural path.  If any part cannot be
+            // represented, later registration deliberately does nothing.
             const bool isBareName{
                 parser::GetDesignatorNameIfDataRef(designator) != nullptr};
             DesignatorPath designatorPath;
@@ -2199,10 +2205,11 @@ void AccAttributeVisitor::ResolveAccObject(
                 return;
               }
             }
+            // Then resolve the base entity so ACC_DECLARE flags are applied.
             const bool isDataSharing{dataSharingAttributeFlags.test(accFlag)};
             if (ContainsStructureComponent(designator)) {
-              // Register component references only in the path-aware table; a
-              // component clause does not cover every reference to the base.
+              // Finally register or compare only the complete component path;
+              // a component clause does not cover every reference to its base.
               if (canCheckMultipleAppearances) {
                 const parser::Name &baseName{parser::GetFirstName(designator)};
                 if (baseName.symbol && !designatorPath.empty()) {

>From 5e49741f8cf667c33c456e6c40743bd30d94bf73 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Wed, 12 Aug 2026 13:41:10 -0700
Subject: [PATCH 10/12] flang: preserve OpenACC construct entity bindings

---
 flang/lib/Semantics/resolve-directives.cpp | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 3002999080e7b..d9352517adc07 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -188,8 +188,8 @@ class AccAttributeVisitor {
   };
 
   struct AccDirContext {
-    AccDirContext(const parser::CharBlock &source, llvm::acc::Directive d,
-        Scope &s)
+    AccDirContext(
+        const parser::CharBlock &source, llvm::acc::Directive d, Scope &s)
         : directiveSource{source}, directive{d}, scope{s} {}
     parser::CharBlock directiveSource;
     llvm::acc::Directive directive;
@@ -1825,8 +1825,7 @@ void AccAttributeVisitor::CheckAssociatedLoop(
           if (level <= 0)
             return;
           if (ivName && lower && upper) {
-            if (auto *symbol{
-                    DeclareOrMarkOtherAccessEntity(*ivName, flag)}) {
+            if (auto *symbol{DeclareOrMarkOtherAccessEntity(*ivName, flag)}) {
               if (auto lowerExpr{semantics::AnalyzeExpr(context_, *lower)}) {
                 semantics::UnorderedSymbolSet lowerSyms =
                     evaluate::CollectSymbols(*lowerExpr);
@@ -1985,6 +1984,9 @@ void AccAttributeVisitor::AdjustAccSymbolReference(const parser::Name &name) {
   }
   if (Symbol *found{currScope().FindSymbol(name.source)};
       found && &symbol != found) {
+    if (DoesScopeContain(&currScope(), symbol)) {
+      return;
+    }
     // Adjust the symbol within the region.
     // TODO: why didn't name resolution set the right name originally?
     name.symbol = found;
@@ -2053,8 +2055,8 @@ void AccAttributeVisitor::CheckAccDefaultNoneReferenceIn(const A &x) {
     std::optional<DesignatorPath> designatorPath{
         GetDesignatorPath(context_, designator->value())};
     if (designatorPath) {
-      CheckAccDefaultNoneReference(
-          parser::GetFirstName(designator->value()), std::move(*designatorPath));
+      CheckAccDefaultNoneReference(parser::GetFirstName(designator->value()),
+          std::move(*designatorPath));
     }
   } else if (const auto *functionReference{
                  std::get_if<common::Indirection<parser::FunctionReference>>(
@@ -2238,7 +2240,8 @@ void AccAttributeVisitor::ResolveAccObject(
               }
               if (isDataSharing && canCheckMultipleAppearances) {
                 CheckMultipleAppearances(baseName, accFlag,
-                    std::move(designatorPath), &accObject, true, designatorName);
+                    std::move(designatorPath), &accObject, true,
+                    designatorName);
               } else if (!designatorPath.empty()) {
                 AddAccObjectWithDSA(std::move(designatorPath), accFlag);
               }
@@ -2340,8 +2343,7 @@ void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
       llvm_unreachable("disjoint relation handled above");
     }
   }
-  objectsWithDSA.push_back(
-      std::move(designator), {accFlag, occurrence});
+  objectsWithDSA.push_back(std::move(designator), {accFlag, occurrence});
 }
 
 #ifndef NDEBUG

>From f54b08546fe9502276d8084e5a5790486d70320a Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 14 Aug 2026 18:57:01 -0700
Subject: [PATCH 11/12] flang: tighten OpenACC data-sharing designator
 diagnostics

Reject resolved coindexed data-clause objects, retain ordinary diagnostics for unresolved designators, and provide an exact path printer for tests and future diagnostics. Reject non-identical same-kind duplicate paths until their semantics are defined.
---
 .../include/flang/Evaluate/designator-path.h  |  3 +
 flang/lib/Evaluate/designator-path.cpp        | 36 +++++++-
 flang/lib/Semantics/resolve-directives.cpp    | 92 ++++++++++++-------
 .../OpenACC/acc-dataclause-dedup.f90          | 25 +++--
 .../OpenACC/acc-default-none-arrays.f90       | 29 ++++++
 flang/unittests/Evaluate/designator-path.cpp  | 18 ++++
 6 files changed, 156 insertions(+), 47 deletions(-)

diff --git a/flang/include/flang/Evaluate/designator-path.h b/flang/include/flang/Evaluate/designator-path.h
index 7d48e16f46e2c..d81334b75bcde 100644
--- a/flang/include/flang/Evaluate/designator-path.h
+++ b/flang/include/flang/Evaluate/designator-path.h
@@ -12,6 +12,7 @@
 #include "flang/Evaluate/expression.h"
 #include <cstdint>
 #include <optional>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -52,6 +53,8 @@ struct DesignatorPath {
       const std::optional<Expr<SomeType>> &);
   DesignatorRelation Compare(const DesignatorPath &) const;
   bool MayContain(const DesignatorPath &) const;
+  std::string AsFortran() const;
+  llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
   void SetBase(NamedEntity);
   void AddComponent(const Symbol &);
   void AddSubscripts(std::vector<Subscript>);
diff --git a/flang/lib/Evaluate/designator-path.cpp b/flang/lib/Evaluate/designator-path.cpp
index e8eac91c12daa..8c498cf010b4f 100644
--- a/flang/lib/Evaluate/designator-path.cpp
+++ b/flang/lib/Evaluate/designator-path.cpp
@@ -10,6 +10,7 @@
 #include "flang/Evaluate/fold.h"
 #include "flang/Evaluate/tools.h"
 #include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
 
 namespace Fortran::evaluate {
 
@@ -107,10 +108,9 @@ DesignatorRelation DesignatorPath::CompareSubscripts(
   auto xRange{GetConstantSubscriptRange(x)};
   auto yRange{GetConstantSubscriptRange(y)};
   if (!xRange || !yRange) {
-    // Constant triplets with strides other than one cannot be represented as a
-    // single range here, so they are treated as disjoint for now. This could be
-    // made more precise by expanding constant triplets into index sets and
-    // comparing those sets.
+    // Nonconstant selectors and constant triplets with non-unit strides cannot
+    // be compared precisely, so treat them as disjoint for now. 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) {
@@ -351,6 +351,34 @@ bool DesignatorPath::MayContain(const DesignatorPath &that) const {
   return true;
 }
 
+std::string DesignatorPath::AsFortran() const {
+  std::string result;
+  llvm::raw_string_ostream stream{result};
+  AsFortran(stream);
+  return result;
+}
+
+llvm::raw_ostream &DesignatorPath::AsFortran(llvm::raw_ostream &o) const {
+  if (!base) {
+    return o;
+  }
+  base->AsFortran(o);
+  for (const Part &part : parts) {
+    if (!part.subscripts.empty()) {
+      char separator{'('};
+      for (const Subscript &subscript : part.subscripts) {
+        subscript.AsFortran(o << separator);
+        separator = ',';
+      }
+      o << ')';
+    }
+    if (part.symbol) {
+      o << '%' << part.symbol->name();
+    }
+  }
+  return o;
+}
+
 void DesignatorPath::SetBase(NamedEntity entity) { base = std::move(entity); }
 
 void DesignatorPath::AddComponent(const Symbol &symbol) {
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index d9352517adc07..3fd10552be229 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -444,6 +444,10 @@ class AccAttributeVisitor {
   void CheckAssociatedLoop(const parser::DoConstruct &, bool forceCollapsed);
   void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag);
   void ResolveAccObject(const parser::AccObject &, Symbol::Flag);
+  // Called by ResolveAccObject() for a non-bare designator. Returns true only
+  // when a resolved unsupported designator was diagnosed; the caller must then
+  // stop processing that object.
+  bool DiagnoseUnsupportedAccClauseDesignator(const parser::Designator &);
   Symbol *ResolveName(const parser::Name &);
   Symbol *ResolveFctName(const parser::Name &);
   Symbol *ResolveAccCommonBlockName(const parser::Name *);
@@ -451,8 +455,7 @@ class AccAttributeVisitor {
   Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
   void CheckMultipleAppearances(const parser::Name &, Symbol::Flag,
       DesignatorPath, const parser::AccObject *occurrence = nullptr,
-      bool warnSameKindDuplicate = true,
-      std::optional<std::string> objectName = {});
+      bool warnSameKindDuplicate = true);
   void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
   void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
   void AllowOnlyVariable(const parser::AccObject &object);
@@ -2177,33 +2180,49 @@ static bool ContainsStructureComponent(const parser::Designator &designator) {
       designator.u);
 }
 
+bool AccAttributeVisitor::DiagnoseUnsupportedAccClauseDesignator(
+    const parser::Designator &designator) {
+  // Do not diagnose a syntactically unsupported object until it has been
+  // semantically resolved; otherwise an unresolved name or component should
+  // receive its ordinary Fortran diagnostic instead.
+  if (!AnalyzeExpr(context_, designator)) {
+    return false;
+  }
+  if (std::holds_alternative<parser::Substring>(designator.u)) {
+    context_.Say(designator.source,
+        "Substrings are not allowed on OpenACC directives or clauses"_err_en_US);
+    return true;
+  }
+  if (const auto *dataRef{std::get_if<parser::DataRef>(&designator.u)};
+      dataRef &&
+      std::holds_alternative<common::Indirection<parser::CoindexedNamedObject>>(
+          dataRef->u)) {
+    context_.Say(designator.source,
+        "Coindexed objects are not allowed on OpenACC directives or clauses"_err_en_US);
+    return true;
+  }
+  return false;
+}
+
 void AccAttributeVisitor::ResolveAccObject(
     const parser::AccObject &accObject, Symbol::Flag accFlag) {
   common::visit(
       common::visitors{
           [&](const parser::Designator &designator) {
-            // First form an exact structural path.  If any part cannot be
-            // represented, later registration deliberately does nothing.
+            // First form an exact structural path. If it cannot be represented,
+            // the check below diagnoses resolved unsupported designators;
+            // unresolved or otherwise invalid designators are not registered.
             const bool isBareName{
                 parser::GetDesignatorNameIfDataRef(designator) != nullptr};
             DesignatorPath designatorPath;
-            std::optional<std::string> designatorName;
             bool canCheckMultipleAppearances{isBareName};
             if (!isBareName) {
-              designatorName = designator.source.ToString();
               if (std::optional<DesignatorPath> path{
                       GetDesignatorPath(context_, designator)}) {
                 designatorPath = std::move(*path);
                 canCheckMultipleAppearances = true;
               }
-              // Analyze first so a substring is recognized before emitting
-              // the OpenACC-specific restriction diagnostic.  A substring is
-              // intentionally never represented as a DesignatorPath.
-              if (AnalyzeExpr(context_, designator) &&
-                  std::holds_alternative<parser::Substring>(designator.u)) {
-                context_.Say(designator.source,
-                    "Substrings are not allowed on OpenACC directives or "
-                    "clauses"_err_en_US);
+              if (DiagnoseUnsupportedAccClauseDesignator(designator)) {
                 return;
               }
             }
@@ -2217,8 +2236,7 @@ void AccAttributeVisitor::ResolveAccObject(
                 if (baseName.symbol && !designatorPath.empty()) {
                   if (isDataSharing) {
                     CheckMultipleAppearances(baseName, accFlag,
-                        std::move(designatorPath), &accObject, true,
-                        designatorName);
+                        std::move(designatorPath), &accObject);
                   } else {
                     AddAccObjectWithDSA(std::move(designatorPath), accFlag);
                   }
@@ -2239,9 +2257,8 @@ void AccAttributeVisitor::ResolveAccObject(
                 designatorPath = MakeBaseDesignatorPath(*symbol);
               }
               if (isDataSharing && canCheckMultipleAppearances) {
-                CheckMultipleAppearances(baseName, accFlag,
-                    std::move(designatorPath), &accObject, true,
-                    designatorName);
+                CheckMultipleAppearances(
+                    baseName, accFlag, std::move(designatorPath), &accObject);
               } else if (!designatorPath.empty()) {
                 AddAccObjectWithDSA(std::move(designatorPath), accFlag);
               }
@@ -2292,12 +2309,13 @@ Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity(
 
 void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
     Symbol::Flag accFlag, DesignatorPath designator,
-    const parser::AccObject *occurrence, bool warnSameKindDuplicate,
-    std::optional<std::string> objectName) {
+    const parser::AccObject *occurrence, bool warnSameKindDuplicate) {
   if (designator.empty()) {
     return;
   }
-  const std::string displayName{objectName.value_or(name.ToString())};
+  const parser::CharBlock source{
+      occurrence ? parser::FindSourceLocation(*occurrence) : name.source};
+  const std::string displayName{source.ToString()};
   auto &objectsWithDSA{GetContext().objectsWithDSA};
   for (auto iter{objectsWithDSA.begin()}; iter != objectsWithDSA.end();) {
     AccDataSharingEntry &entry{iter->value};
@@ -2310,35 +2328,39 @@ void AccAttributeVisitor::CheckMultipleAppearances(const parser::Name &name,
     // TODO: Record the reduction operator in AccDataSharingEntry so compatible
     // reductions can use the ordinary same-kind duplicate handling.
     if (entry.flag != accFlag || accFlag == Symbol::Flag::AccReduction) {
-      context_.Say(name.source,
+      auto &message{context_.Say(source,
           "'%s' appears in more than one data-sharing clause on the same OpenACC directive"_err_en_US,
-          displayName);
+          displayName)};
+      if (entry.occurrence) {
+        message.Attach(parser::FindSourceLocation(*entry.occurrence),
+            "previous data-sharing object appears here"_en_US);
+      }
       return;
     }
 
     switch (relation) {
     case DesignatorRelation::Equal:
       if (warnSameKindDuplicate && occurrence) {
-        context_.Warn(common::UsageWarning::OpenAccUsage, name.source,
+        context_.Warn(common::UsageWarning::OpenAccUsage, source,
             "'%s' appears more than once in the same kind of data-sharing clause on an OpenACC directive; duplicate ignored"_warn_en_US,
             displayName);
         context_.MarkAccObjectDuplicate(occurrence);
       }
       return;
     case DesignatorRelation::Contains:
-      if (occurrence) {
-        context_.MarkAccObjectDuplicate(occurrence);
-      }
-      return;
     case DesignatorRelation::ContainedBy:
+    case DesignatorRelation::Overlaps: {
+      // TODO: Support non-identical same-kind objects once their containment
+      // and overlap semantics are defined.
+      auto &message{context_.Say(source,
+          "'%s' overlaps another object in the same kind of data-sharing clause on the same OpenACC directive"_err_en_US,
+          displayName)};
       if (entry.occurrence) {
-        context_.MarkAccObjectDuplicate(entry.occurrence);
+        message.Attach(parser::FindSourceLocation(*entry.occurrence),
+            "previous data-sharing object appears here"_en_US);
       }
-      iter = objectsWithDSA.erase(iter);
-      continue;
-    case DesignatorRelation::Overlaps:
-      ++iter;
-      continue;
+      return;
+    }
     case DesignatorRelation::Disjoint:
       llvm_unreachable("disjoint relation handled above");
     }
diff --git a/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90 b/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
index 913fbddb88adb..3fb4b42e52eb9 100644
--- a/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
+++ b/flang/test/Semantics/OpenACC/acc-dataclause-dedup.f90
@@ -162,7 +162,9 @@ program test_dataclause_dedup
     do i = 1, 10
     end do
 
-    ! Overlapping literal sections in the same data-sharing kind are accepted.
+    ! Non-identical sections that overlap in the same data-sharing kind are
+    ! rejected until precise overlap support is implemented.
+    !ERROR: 'arr(5:10)' overlaps another object in the same kind of data-sharing clause on the same OpenACC directive
     !$acc parallel loop private(arr(1:5), arr(5:10))
     do i = 1, 10
     end do
@@ -173,8 +175,14 @@ program test_dataclause_dedup
     do i = 1, 10
     end do
 
-    ! An element contained in a section is accepted within the same
-    ! data-sharing kind.
+    ! A section and its contained element are likewise rejected in either
+    ! order until precise containment support is implemented.
+    !ERROR: 'arr(3)' overlaps another object in the same kind of data-sharing clause on the same OpenACC directive
+    !$acc parallel loop private(arr(1:5), arr(3))
+    do i = 1, 10
+    end do
+
+    !ERROR: 'arr(1:5)' overlaps another object in the same kind of data-sharing clause on the same OpenACC directive
     !$acc parallel loop private(arr(3), arr(1:5))
     do i = 1, 10
     end do
@@ -185,8 +193,8 @@ program test_dataclause_dedup
     do i = 1, 10
     end do
 
-    ! Variable index/section overlap is accepted within the same
-    ! data-sharing kind.
+    ! Variable index/section containment cannot yet be proven, so it is treated
+    ! as disjoint.
     !$acc parallel loop private(arr(idx), arr(lo:hi))
     do i = 1, 10
     end do
@@ -203,7 +211,8 @@ program test_dataclause_dedup
     do i = 1, 10
     end do
 
-    ! Variable section overlap is accepted within the same data-sharing kind.
+    ! Variable section overlap cannot yet be proven, so it is treated as
+    ! disjoint.
     !$acc parallel loop private(arr(lo:hi), arr(mid:hi))
     do i = 1, 10
     end do
@@ -225,8 +234,8 @@ program test_dataclause_dedup
     do i = 1, 10
     end do
 
-    ! Mixing a bare-name designator and an array-element designator on the
-    ! same symbol is not an exact duplicate.
+    ! A whole array and an element are not distinct data-sharing objects.
+    !ERROR: 'arr(1)' overlaps another object in the same kind of data-sharing clause on the same OpenACC directive
     !$acc parallel loop private(arr, arr(1))
     do i = 1, 10
     end do
diff --git a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90 b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
index bcc3f549831ff..f611f5ba3d20c 100644
--- a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
+++ b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
@@ -171,6 +171,35 @@ subroutine test_substring()
   !$acc end parallel
 end subroutine
 
+! 8a. A coindexed object is not an OpenACC data-clause var.
+subroutine test_coindexed_object()
+  implicit none
+  integer, save :: coarray[*]
+  !ERROR: Coindexed objects are not allowed on OpenACC directives or clauses
+  !$acc parallel default(none) copyin(coarray[1])
+  !$acc end parallel
+end subroutine
+
+! 8b. Invalid objects must retain their ordinary Fortran semantic errors;
+!     they must not be mistaken for a resolved OpenACC object that happens
+!     not to have a DesignatorPath.
+subroutine test_unresolved_clause_objects()
+  implicit none
+  type :: t
+    integer :: present
+  end type
+  type(t) :: x
+  !ERROR: Component 'missing' not found in derived type 't'
+  !$acc parallel copyin(x%missing)
+  !$acc end parallel
+  !ERROR: No explicit type declared for 'missing_substring'
+  !$acc parallel copyin(missing_substring(1:5))
+  !$acc end parallel
+  !ERROR: No explicit type declared for 'missing_coarray'
+  !$acc parallel copyin(missing_coarray[1])
+  !$acc end parallel
+end subroutine
+
 ! 9. Same array section in conflicting private and copy clauses.
 subroutine test_cross_kind_sections(n)
   implicit none
diff --git a/flang/unittests/Evaluate/designator-path.cpp b/flang/unittests/Evaluate/designator-path.cpp
index 79d913f993d4d..60ab4fbe0a047 100644
--- a/flang/unittests/Evaluate/designator-path.cpp
+++ b/flang/unittests/Evaluate/designator-path.cpp
@@ -356,6 +356,23 @@ void TestSubscriptsPrecedeComponentWithinPart() {
   TEST(xSectionYFullZ.Parts()[1].symbol == &z);
 }
 
+void TestAsFortran() {
+  SymbolFixture symbols;
+  const semantics::Symbol &a{symbols.MakeSymbol("a")};
+  const semantics::Symbol &x{symbols.MakeSymbol("x")};
+  const semantics::Symbol &y{symbols.MakeSymbol("y")};
+  DesignatorPath path;
+  path.SetBase(NamedEntity{a});
+  TEST(path.AsFortran() == "a");
+  path.AddSubscripts({Scalar(1), Section(2, 4)});
+  TEST(path.AsFortran() == "a(1_8,2_8:4_8:1_8)");
+  path.AddComponent(x);
+  TEST(path.AsFortran() == "a(1_8,2_8:4_8:1_8)%x");
+  path.AddSubscripts({FullSection()});
+  path.AddComponent(y);
+  TEST(path.AsFortran() == "a(1_8,2_8:4_8:1_8)%x(::1_8)%y");
+}
+
 } // namespace
 
 int main() {
@@ -370,5 +387,6 @@ int main() {
   TestMayContainPartsAndPaths();
   TestAddFunctionsAndMap();
   TestSubscriptsPrecedeComponentWithinPart();
+  TestAsFortran();
   return testing::Complete();
 }

>From 7314880529c9060e78423d6f7530a0ed81c1a205 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 14 Aug 2026 19:51:42 -0700
Subject: [PATCH 12/12] flang: retain OpenACC mapping clause locations

---
 flang/lib/Semantics/resolve-directives.cpp    | 19 +++++++++++--------
 .../OpenACC/acc-default-none-arrays.f90       |  3 +++
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 3fd10552be229..fabb23332ffc7 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -410,7 +410,8 @@ class AccAttributeVisitor {
   void PushAccContext(const parser::CharBlock &, llvm::acc::Directive);
   void PopAccContext();
   static DesignatorPath MakeBaseDesignatorPath(const Symbol &);
-  void AddAccObjectWithDSA(DesignatorPath, Symbol::Flag);
+  void AddAccObjectWithDSA(DesignatorPath, Symbol::Flag,
+      const parser::AccObject *occurrence = nullptr);
   bool IsObjectWithVisibleDSA(const DesignatorPath &) const;
   void AdjustAccSymbolReference(const parser::Name &);
   void enterExpressionLikeContext() { ++expressionLikeDepthCount_; }
@@ -1936,11 +1937,11 @@ DesignatorPath AccAttributeVisitor::MakeBaseDesignatorPath(
   return path;
 }
 
-void AccAttributeVisitor::AddAccObjectWithDSA(
-    DesignatorPath designator, Symbol::Flag flag) {
+void AccAttributeVisitor::AddAccObjectWithDSA(DesignatorPath designator,
+    Symbol::Flag flag, const parser::AccObject *occurrence) {
   if (!designator.empty()) {
     GetContext().objectsWithDSA.push_back(
-        std::move(designator), {flag, nullptr});
+        std::move(designator), {flag, occurrence});
   }
 }
 
@@ -2238,7 +2239,8 @@ void AccAttributeVisitor::ResolveAccObject(
                     CheckMultipleAppearances(baseName, accFlag,
                         std::move(designatorPath), &accObject);
                   } else {
-                    AddAccObjectWithDSA(std::move(designatorPath), accFlag);
+                    AddAccObjectWithDSA(
+                        std::move(designatorPath), accFlag, &accObject);
                   }
                 }
               }
@@ -2260,7 +2262,8 @@ void AccAttributeVisitor::ResolveAccObject(
                 CheckMultipleAppearances(
                     baseName, accFlag, std::move(designatorPath), &accObject);
               } else if (!designatorPath.empty()) {
-                AddAccObjectWithDSA(std::move(designatorPath), accFlag);
+                AddAccObjectWithDSA(
+                    std::move(designatorPath), accFlag, &accObject);
               }
             }
           },
@@ -2271,8 +2274,8 @@ void AccAttributeVisitor::ResolveAccObject(
               for (auto &object : symbol->get<CommonBlockDetails>().objects()) {
                 if (auto *resolvedObject{
                         DeclareOrMarkOtherAccessEntity(*object, accFlag)}) {
-                  AddAccObjectWithDSA(
-                      MakeBaseDesignatorPath(*resolvedObject), accFlag);
+                  AddAccObjectWithDSA(MakeBaseDesignatorPath(*resolvedObject),
+                      accFlag, &accObject);
                 }
               }
             } else {
diff --git a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90 b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
index f611f5ba3d20c..e64c88c374aab 100644
--- a/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
+++ b/flang/test/Semantics/OpenACC/acc-default-none-arrays.f90
@@ -1,4 +1,5 @@
 ! RUN: %python %S/../test_errors.py %s %flang -fopenacc -fno-openacc-default-none-scalars-strict -Wno-openacc-default-none-scalars-strict
+! RUN: not %flang_fc1 -fopenacc -fno-openacc-default-none-scalars-strict -Wno-openacc-default-none-scalars-strict %s 2>&1 | FileCheck %s --check-prefix=CHECK-LOC
 
 ! Verify that array sections explicitly listed in OpenACC data clauses are
 ! correctly registered as having a DSA, so DEFAULT(NONE) uses path containment
@@ -208,6 +209,8 @@ subroutine test_cross_kind_sections(n)
   integer :: i
   !ERROR: 'a(1:n)' appears in more than one data-sharing clause on the same OpenACC directive
   !$acc parallel loop default(none) copy(a(1:n)) private(a(1:n))
+  ! CHECK-LOC: error: 'a(1:n)' appears in more than one data-sharing clause on the same OpenACC directive
+  ! CHECK-LOC: previous data-sharing object appears here
   do i = 1, n
     a(i) = 0.0
   end do



More information about the flang-commits mailing list