[flang-commits] [flang] [flang][OpenMP] Disallow substrings as named parameter list items (PR #234609)

via flang-commits flang-commits at lists.llvm.org
Thu Oct 22 05:50:08 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-semantics

Author: Tom Eccles (tblah)

<details>
<summary>Changes</summary>

OpenMP 5.2+ explicitly disallows substrings as named parameter list items (and therefore as members of argument lists). This patch checks this restriction generically so that it applies everywhere.

AFFINITY and DEPEND both produced portability warnings instead of hard errors. I decided to keep those existing extensions.

Some other clauses (e.g. privatization, reduction) have the same restriction in earlier standard versions. This is checked separately for those clauses.

Assisted-by: Codex

---
Full diff: https://github.com/llvm/llvm-project/pull/234609.diff


9 Files Affected:

- (modified) flang/lib/Semantics/check-omp-structure.cpp (+58-15) 
- (modified) flang/lib/Semantics/check-omp-structure.h (+6-2) 
- (modified) flang/test/Semantics/OpenMP/depend-substring.f90 (+3-1) 
- (added) flang/test/Semantics/OpenMP/reduction-substring-task.f90 (+18) 
- (added) flang/test/Semantics/OpenMP/reduction-substring.f90 (+53) 
- (added) flang/test/Semantics/OpenMP/substring-list-items-pre52.f90 (+32) 
- (added) flang/test/Semantics/OpenMP/substring-list-items.f90 (+77) 
- (added) flang/test/Semantics/OpenMP/substring-locators-50.f90 (+10) 
- (modified) flang/test/Semantics/OpenMP/substring-strides.f90 (+2) 


``````````diff
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index e5b0433ef0220..81f0f146c3f2a 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -926,7 +926,6 @@ void OmpStructureChecker::CheckArgumentObjectKind(const parser::OmpClause &x) {
 
   for (auto &object : DEREF(GetOmpObjectList(x)).v) {
     AnalyzeObject(object);
-    // substring
     const Symbol *symbol{GetObjectSymbol(object, /*ultimate=*/true)};
     if (symbol == nullptr) {
       // This may happen with a blank common block. Skip these cases.
@@ -988,6 +987,17 @@ void OmpStructureChecker::CheckArgumentObjectKind(const parser::OmpClause &x) {
           "'%s' must be %s list item"_err_en_US, symbol->name(), neededType);
     }
   }
+
+  // OpenMP 5.2 3.2.1 and 6.0 5.2.1 exclude substrings from variable
+  // list items, and consequently from extended and locator list items.
+  // DEPEND and AFFINITY retain their existing substring extensions and
+  // diagnostics in CheckArraySection and the clause-specific checks.
+  if (version >= 52 && clauseId != llvm::omp::Clause::OMPC_depend &&
+      clauseId != llvm::omp::Clause::OMPC_affinity) {
+    if (const auto *objects{GetOmpObjectList(x)}) {
+      CheckSubstring(*objects, clauseId);
+    }
+  }
 }
 
 void OmpStructureChecker::CheckDirectiveSpelling(
@@ -2887,7 +2897,9 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareTargetDirective &x) {
                   std::is_same_v<TypeC, parser::OmpClause::Link> ||
                   std::is_same_v<TypeC, parser::OmpClause::To>) {
                 auto &objList{*GetOmpObjectList(c)};
-                CheckVarIsNotPartOfAnotherVar(dirName.source, objList);
+                CheckVarIsNotPartOfAnotherVar(dirName.source, objList, "",
+                    /*checkSubstrings=*/
+                    context_.langOptions().getOpenMPVersion() < 52);
                 CheckThreadprivateOrDeclareTargetVar(objList);
               }
               if constexpr (std::is_same_v<TypeC, parser::OmpClause::Enter>) {
@@ -4239,6 +4251,12 @@ void OmpStructureChecker::CheckReductionObjects(
   SymbolSourceMap symbols;
   GetSymbolsInObjectList(objects, symbols);
 
+  // Earlier versions prohibit parts of variables in reduction clauses too.
+  // From OpenMP 5.2, CheckArgumentObjectKind diagnoses substrings generically.
+  if (version < 52) {
+    CheckSubstring(objects, clauseId);
+  }
+
   // Array sections must be a contiguous storage, have non-zero length.
   for (const parser::OmpObject &object : objects.v) {
     CheckIfContiguous(object);
@@ -4542,6 +4560,19 @@ void OmpStructureChecker::CheckReductionModifier(
   }
 }
 
+void OmpStructureChecker::CheckSubstring(
+    const parser::OmpObjectList &objects, llvm::omp::Clause clauseId) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+  for (const parser::OmpObject &object : objects.v) {
+    if (IsSubstring(object, &context_)) {
+      auto source{GetObjectSource(object)};
+      context_.Say(source ? *source : GetContext().clauseSource,
+          "A substring cannot appear in a %s clause"_err_en_US,
+          parser::omp::GetUpperName(clauseId, version));
+    }
+  }
+}
+
 void OmpStructureChecker::CheckReductionArraySection(
     const parser::OmpObjectList &ompObjectList, llvm::omp::Clause clauseId) {
   for (const auto &ompObject : ompObjectList.v) {
@@ -4596,14 +4627,16 @@ void OmpStructureChecker::CheckSharedBindingInOuterContext(
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Shared &x) {
-  CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "SHARED");
+  CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "SHARED",
+      /*checkSubstrings=*/context_.langOptions().getOpenMPVersion() < 52);
   CheckCrayPointee(x.v, "SHARED");
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Private &x) {
   SymbolSourceMap symbols;
   GetSymbolsInObjectList(x.v, symbols);
-  CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "PRIVATE");
+  CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "PRIVATE",
+      /*checkSubstrings=*/context_.langOptions().getOpenMPVersion() < 52);
   CheckIntentInPointer(symbols, llvm::omp::Clause::OMPC_private);
   CheckCrayPointee(x.v, "PRIVATE");
 }
@@ -4632,15 +4665,20 @@ void OmpStructureChecker::CheckTypeParamInquiry(const parser::CharBlock &source,
 
 void OmpStructureChecker::CheckVarIsNotPartOfAnotherVar(
     const parser::CharBlock &source, const parser::OmpObjectList &objList,
-    llvm::StringRef clause) {
+    llvm::StringRef clause, bool checkSubstrings) {
   for (const auto &ompObject : objList.v) {
-    CheckVarIsNotPartOfAnotherVar(source, ompObject, clause);
+    CheckVarIsNotPartOfAnotherVar(source, ompObject, clause, checkSubstrings);
   }
 }
 
 void OmpStructureChecker::CheckVarIsNotPartOfAnotherVar(
     const parser::CharBlock &source, const parser::OmpObject &object,
-    llvm::StringRef clause) {
+    llvm::StringRef clause, bool checkSubstrings) {
+  if (!checkSubstrings && IsSubstring(object, &context_)) {
+    // Clause-specific substring checks are needed before OpenMP 5.2 only;
+    // CheckArgumentObjectKind diagnoses them generically in newer versions.
+    return;
+  }
   if (const Symbol *symbol{GetObjectSymbol(object)}) {
     if (IsTypeParamInquiry(*symbol)) {
       return;
@@ -4668,7 +4706,8 @@ void OmpStructureChecker::CheckVarIsNotPartOfAnotherVar(
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) {
-  CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "FIRSTPRIVATE");
+  CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "FIRSTPRIVATE",
+      /*checkSubstrings=*/context_.langOptions().getOpenMPVersion() < 52);
   CheckCrayPointee(x.v, "FIRSTPRIVATE");
 
   SymbolSourceMap currSymbols;
@@ -5352,8 +5391,9 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Copyprivate &x) {
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Lastprivate &x) {
   const auto &objectList{*GetOmpObjectList(x)};
-  CheckVarIsNotPartOfAnotherVar(
-      GetContext().clauseSource, objectList, "LASTPRIVATE");
+  CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, objectList,
+      "LASTPRIVATE",
+      /*checkSubstrings=*/context_.langOptions().getOpenMPVersion() < 52);
   CheckCrayPointee(objectList, "LASTPRIVATE");
 
   DirectivesClauseTriple dirClauseTriple;
@@ -6078,12 +6118,15 @@ void OmpStructureChecker::CheckArraySection(
       if (name.symbol->Rank() == 0) {
         if (std::optional<evaluate::DynamicType> type = expr->GetType()) {
           if (type->category() == evaluate::TypeCategory::Character) {
-            // Substrings are explicitly denied by the standard [6.0:163:9-11].
-            // This is supported as an extension. This restriction was added in
-            // OpenMP 5.2.
             isSubstring = true;
-            context_.Say(GetContext().clauseSource,
-                "The use of substrings in OpenMP argument lists has been disallowed since OpenMP 5.2."_port_en_US);
+            if (version >= 52 &&
+                (clause == llvm::omp::Clause::OMPC_depend ||
+                    clause == llvm::omp::Clause::OMPC_affinity)) {
+              // Preserve the existing substring extensions in DEPEND and
+              // AFFINITY, despite the OpenMP 5.2 list-item restriction.
+              context_.Say(GetContext().clauseSource,
+                  "The use of substrings in OpenMP argument lists has been disallowed since OpenMP 5.2."_port_en_US);
+            }
           } else if (expr->Rank() == 0) {
             llvm_unreachable(
                 "Array indexing on a variable that isn't an array");
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 59c731b4d4840..384b6228b5bc4 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -443,9 +443,11 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   void CheckTypeParamInquiry(const parser::CharBlock &source,
       const parser::OmpObject &object, llvm::omp::Clause clauseId);
   void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source,
-      const parser::OmpObject &object, llvm::StringRef clause = "");
+      const parser::OmpObject &object, llvm::StringRef clause = "",
+      bool checkSubstrings = true);
   void CheckVarIsNotPartOfAnotherVar(const parser::CharBlock &source,
-      const parser::OmpObjectList &objList, llvm::StringRef clause = "");
+      const parser::OmpObjectList &objList, llvm::StringRef clause = "",
+      bool checkSubstrings = true);
   void CheckThreadprivateOrDeclareTargetVar(const parser::Designator &);
   void CheckThreadprivateOrDeclareTargetVar(const parser::Name &);
   void CheckThreadprivateOrDeclareTargetVar(const parser::OmpObject &);
@@ -489,6 +491,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
       const parser::CharBlock &source, llvm::omp::Directive type);
   void CheckReductionObjects(
       const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
+  void CheckSubstring(
+      const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
   bool CheckReductionOperator(const parser::OmpReductionIdentifier &ident,
       parser::CharBlock source, llvm::omp::Clause clauseId);
   void CheckReductionObjectTypes(const parser::OmpObjectList &objects,
diff --git a/flang/test/Semantics/OpenMP/depend-substring.f90 b/flang/test/Semantics/OpenMP/depend-substring.f90
index f127d21e3d0c1..0d00f8f56fe0c 100644
--- a/flang/test/Semantics/OpenMP/depend-substring.f90
+++ b/flang/test/Semantics/OpenMP/depend-substring.f90
@@ -1,4 +1,6 @@
-! RUN: %python %S/../test_errors.py %s %flang -fopenmp
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=61
 ! Test for parsing confusion between array indexing and string subscripts
 
 ! This is okay: selects the whole substring
diff --git a/flang/test/Semantics/OpenMP/reduction-substring-task.f90 b/flang/test/Semantics/OpenMP/reduction-substring-task.f90
new file mode 100644
index 0000000000000..8b717ea6d1e7d
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/reduction-substring-task.f90
@@ -0,0 +1,18 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=50
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=51
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=52
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=60
+
+subroutine task_reductions(c)
+  character(4) :: c
+  !$omp declare reduction(+:character(2),character(4):omp_out=omp_in)
+  !ERROR: A substring cannot appear in a TASK_REDUCTION clause
+  !$omp taskgroup task_reduction(+:c(2:3))
+  !$omp end taskgroup
+
+  !$omp taskgroup task_reduction(+:c)
+  !ERROR: A substring cannot appear in a IN_REDUCTION clause
+  !$omp task in_reduction(+:c(2:3))
+  !$omp end task
+  !$omp end taskgroup
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/reduction-substring.f90 b/flang/test/Semantics/OpenMP/reduction-substring.f90
new file mode 100644
index 0000000000000..1662ca48ffede
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/reduction-substring.f90
@@ -0,0 +1,53 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=45
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=50
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=51
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=52
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=60
+
+subroutine scalar_substring(c)
+  character(4) :: c
+  !$omp declare reduction(r:character(2),character(4):omp_out=omp_in)
+  !ERROR: A substring cannot appear in a REDUCTION clause
+  !$omp parallel reduction(r:c(2:3))
+  !$omp end parallel
+end
+
+subroutine element_substring(c)
+  character(4) :: c(2)
+  !$omp declare reduction(r:character(2),character(4):omp_out=omp_in)
+  !ERROR: A substring cannot appear in a REDUCTION clause
+  !$omp parallel reduction(r:c(1)(2:3))
+  !$omp end parallel
+end
+
+subroutine component_substring()
+  type box
+    character(4) :: field
+  end type
+  type(box) :: a
+  !$omp declare reduction(r:type(box):omp_out=omp_in)
+  !ERROR: A substring cannot appear in a REDUCTION clause
+  !$omp parallel reduction(r:a%field(2:3))
+  !$omp end parallel
+end
+
+subroutine target_substring(c)
+  character(4) :: c
+  !$omp declare reduction(r:character(2),character(4):omp_out=omp_in)
+  !ERROR: A substring cannot appear in a REDUCTION clause
+  !$omp target parallel reduction(r:c(2:3))
+  !$omp end target parallel
+end
+
+subroutine whole_character(c)
+  character(4) :: c
+  !$omp declare reduction(r:character(4):omp_out=omp_in)
+  !$omp parallel reduction(r:c)
+  !$omp end parallel
+end
+
+subroutine complex_part(z)
+  complex :: z
+  !$omp parallel reduction(+:z%re)
+  !$omp end parallel
+end
diff --git a/flang/test/Semantics/OpenMP/substring-list-items-pre52.f90 b/flang/test/Semantics/OpenMP/substring-list-items-pre52.f90
new file mode 100644
index 0000000000000..cd094d507b113
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/substring-list-items-pre52.f90
@@ -0,0 +1,32 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=45
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=50
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=51
+
+! Before 5.2 there is no general substring exclusion. Data-sharing clauses
+! still prohibit parts of variables through their own restrictions.
+subroutine data_sharing(c)
+  character(8) :: c
+  integer :: i
+  !ERROR: A substring cannot appear in a SHARED clause
+  !$omp parallel shared(c(2:3))
+  !$omp end parallel
+  !ERROR: A substring cannot appear in a PRIVATE clause
+  !$omp parallel private(c(2:3))
+  !$omp end parallel
+  !ERROR: A substring cannot appear in a FIRSTPRIVATE clause
+  !$omp parallel firstprivate(c(2:3))
+  !$omp end parallel
+  !ERROR: A substring cannot appear in a LASTPRIVATE clause
+  !$omp parallel do lastprivate(c(2:3))
+  do i = 1, 2
+  end do
+  !$omp end parallel do
+end subroutine
+
+subroutine mapping(c, a)
+  character(8) :: c, a(2)
+  !$omp target map(tofrom: c(2:3), a(1)(2:3))
+  !$omp end target
+  !$omp target update to(c(2:3))
+  !$omp target update from(a(1)(2:3))
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/substring-list-items.f90 b/flang/test/Semantics/OpenMP/substring-list-items.f90
new file mode 100644
index 0000000000000..76e6a53265aac
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/substring-list-items.f90
@@ -0,0 +1,77 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=52
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=60
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=61
+
+! OpenMP 5.2 3.2.1 excludes substrings from variable, extended, and locator
+! list items, independently of the restrictions specific to each clause.
+module substring_lists
+  character(8) :: device_string
+  !ERROR: A substring cannot appear in a ENTER clause
+  !$omp declare target enter(device_string(2:3))
+  character(8) :: thread_string
+  !$omp threadprivate(thread_string)
+contains
+  subroutine data_sharing(c, a)
+    character(8) :: c, a(2)
+    type box
+      character(8) :: field
+    end type
+    type(box) :: b
+    integer :: i
+
+    !ERROR: A substring cannot appear in a SHARED clause
+    !$omp parallel shared(c(2:3))
+    !$omp end parallel
+    !ERROR: A substring cannot appear in a PRIVATE clause
+    !$omp parallel private(a(1)(2:3))
+    !$omp end parallel
+    !ERROR: A substring cannot appear in a FIRSTPRIVATE clause
+    !$omp parallel firstprivate(b%field(2:3))
+    !$omp end parallel
+    !ERROR: A substring cannot appear in a LASTPRIVATE clause
+    !$omp parallel do lastprivate(c(2:3))
+    do i = 1, 2
+    end do
+    !$omp end parallel do
+    !ERROR: A substring cannot appear in a COPYIN clause
+    !$omp parallel copyin(thread_string(2:3))
+    !$omp end parallel
+    !$omp parallel private(c)
+    !$omp single
+    !ERROR: A substring cannot appear in a COPYPRIVATE clause
+    !$omp end single copyprivate(c(2:3))
+    !$omp end parallel
+  end subroutine
+
+  subroutine locators(c, a)
+    character(8) :: c, a(2)
+    !ERROR: A substring cannot appear in a MAP clause
+    !$omp target map(tofrom: c(2:3))
+    !$omp end target
+    !ERROR: A substring cannot appear in a TO clause
+    !$omp target update to(a(1)(2:3))
+    !ERROR: A substring cannot appear in a FROM clause
+    !$omp target update from(c(2:3))
+    ! DEPEND retains its existing substring extension.
+    !PORTABILITY: The use of substrings in OpenMP argument lists has been disallowed since OpenMP 5.2.
+    !$omp task depend(in: c(2:3))
+    !$omp end task
+    !$omp task depend(in: a(1)(2:3))
+    !$omp end task
+    ! AFFINITY retains its warning cases and explicit-substring errors.
+    !PORTABILITY: The use of substrings in OpenMP argument lists has been disallowed since OpenMP 5.2.
+    !$omp task affinity(c(2:3))
+    !$omp end task
+    !ERROR: Substrings are not allowed on AFFINITY clause
+    !$omp task affinity(a(1)(2:3))
+    !$omp end task
+
+    ! Whole characters and character array sections remain valid list items.
+    !$omp parallel shared(c, a)
+    !$omp end parallel
+    !$omp target map(tofrom: c, a(1:2))
+    !$omp end target
+    !$omp task depend(in: c, a(1:2)) affinity(c, a(1:2))
+    !$omp end task
+  end subroutine
+end module
diff --git a/flang/test/Semantics/OpenMP/substring-locators-50.f90 b/flang/test/Semantics/OpenMP/substring-locators-50.f90
new file mode 100644
index 0000000000000..7caff524c6287
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/substring-locators-50.f90
@@ -0,0 +1,10 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=50
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=51
+
+! OpenMP 5.0 and 5.1 omit the 4.5 DEPEND restriction on parts of variables,
+! and do not yet exclude substrings from the general list-item definition.
+subroutine locators(c, a)
+  character(8) :: c, a(2)
+  !$omp task depend(in: c(2:3), a(1)(2:3)) affinity(c(2:3))
+  !$omp end task
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/substring-strides.f90 b/flang/test/Semantics/OpenMP/substring-strides.f90
index 1dcb7885d5881..24b26d68d8383 100644
--- a/flang/test/Semantics/OpenMP/substring-strides.f90
+++ b/flang/test/Semantics/OpenMP/substring-strides.f90
@@ -1,4 +1,6 @@
 ! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=61
 
 ! Scalar character components remain substrings even with array-valued parents.
 ! Substring strides are invalid whether or not either bound is present.

``````````

</details>


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


More information about the flang-commits mailing list