[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