[flang-commits] [flang] [llvm] [flang][OpenMP] Check conflicts between predetermined/explicit DSA (PR #194961)

Krzysztof Parzyszek via flang-commits flang-commits at lists.llvm.org
Thu Apr 30 04:13:46 PDT 2026


https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/194961

>From 865fd2a6509dca71178cce61d431a2d5230dd09c Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Tue, 28 Apr 2026 07:29:16 -0500
Subject: [PATCH 1/2] [flang][OpenMP] Check conflicts between
 predetermined/explicit DSA

Improve checks for loop iteration variables with predetermined DSA
appearing in DSA clauses. Show both the location of the variable
in the offending clause, and in the loop.

Make the checks a bit more accurate as well: only allow LINEAR clause
on SIMD construct with a single affected loop.
---
 flang/include/flang/Semantics/openmp-utils.h  |   4 +-
 flang/lib/Semantics/check-omp-loop.cpp        | 102 ++++++++++++++----
 flang/lib/Semantics/check-omp-structure.cpp   |  15 ---
 flang/lib/Semantics/check-omp-structure.h     |   5 +-
 flang/lib/Semantics/openmp-utils.cpp          |  20 ++--
 flang/lib/Semantics/resolve-directives.cpp    |  12 ---
 flang/test/Parser/OpenMP/linear-clause.f90    |  25 ++---
 flang/test/Semantics/OpenMP/do01.f90          |   3 +-
 flang/test/Semantics/OpenMP/do04.f90          |  14 +--
 flang/test/Semantics/OpenMP/do10.f90          |   3 -
 .../test/Semantics/OpenMP/linear-clause03.f90 |   2 +
 flang/test/Semantics/OpenMP/linear-iter.f90   |   4 +
 llvm/include/llvm/Frontend/OpenMP/OMP.h       |  27 +++++
 13 files changed, 151 insertions(+), 85 deletions(-)

diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 3124005a4bbff..aab63df2e6e14 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -99,6 +99,8 @@ bool IsVarOrFunctionRef(const MaybeExpr &expr);
 
 bool IsWholeAssumedSizeArray(const parser::OmpObject &object);
 
+const Symbol *GetHostSymbol(const Symbol &sym);
+
 bool IsMapEnteringType(parser::OmpMapType::Value type);
 bool IsMapExitingType(parser::OmpMapType::Value type);
 
@@ -150,7 +152,7 @@ struct LoopControl {
   LoopControl(const parser::LoopControl::Bounds &x);
   LoopControl(const parser::ConcurrentControl &x);
 
-  const Symbol *iv{nullptr};
+  parser::Name iv;
   WithSource<MaybeExpr> lbound, ubound, step;
 
 private:
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index b246d027e4807..42626efe8b4fd 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -414,7 +414,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
     // nesting check
     HasInvalidWorksharingNesting(beginName, llvm::omp::nestedWorkshareErrSet);
   }
-  SetLoopInfo(x);
 
   for (auto &construct : std::get<parser::Block>(x.t)) {
     if (const auto *doConstruct{parser::omp::GetDoConstruct(construct)}) {
@@ -422,7 +421,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
       CheckNoBranching(doBlock, beginName.v, beginName.source);
     }
   }
-  CheckIterationVariableType(x);
+  CheckIterationVariables(x);
   CheckNestedConstruct(x);
   CheckAssociatedLoopConstraints(x);
   HasInvalidDistributeNesting(x);
@@ -443,30 +442,89 @@ const parser::Name OmpStructureChecker::GetLoopIndex(
   return std::get<Bounds>(x->GetLoopControl()->u).Name().thing;
 }
 
-void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) {
-  if (const auto *loop{x.GetNestedLoop()}) {
-    if (loop->IsDoNormal()) {
-      const parser::Name &itrVal{GetLoopIndex(loop)};
-      SetLoopIv(itrVal.symbol);
+void OmpStructureChecker::CheckIterationVariables(
+    const parser::OpenMPLoopConstruct &x) {
+  unsigned version{context_.langOptions().OpenMPVersion};
+  auto doLoops{CollectAffectedDoLoops(x, version, &context_)};
+  if (!doLoops) {
+    return;
+  }
+  const parser::OmpDirectiveSpecification &spec{x.BeginDir()};
+  llvm::omp::Directive dirId{spec.DirId()};
+
+  // Collect symbols from DSA clauses on the construct. These symbols
+  // are the "host" versions of symbols inside the construct. The flags
+  // of interest are on the associated symbols.
+  std::map<const Symbol *, std::pair<parser::CharBlock, llvm::omp::Clause>> dsa;
+  for (auto &clause : spec.Clauses().v) {
+    llvm::omp::Clause clauseId{clause.Id()};
+    if (llvm::omp::isDataSharingAttributeClause(clauseId, version)) {
+      for (auto &object : parser::omp::GetOmpObjectList(clause)->v) {
+        if (auto *symbol{GetObjectSymbol(object)}) {
+          auto maybeSource{parser::omp::GetObjectSource(object)};
+          assert(maybeSource && "Expecting object source");
+          dsa.insert(
+              std::make_pair(symbol, std::make_pair(*maybeSource, clauseId)));
+        }
+      }
     }
   }
-}
 
-void OmpStructureChecker::CheckIterationVariableType(
-    const parser::OpenMPLoopConstruct &x) {
-  auto &body{std::get<parser::Block>(x.t)};
-  for (auto &construct : LoopRange(body, LoopRange::Step::Into)) {
-    // 'construct' can also be OpenMPLoopConstruct
-    if (auto *loop{parser::Unwrap<parser::DoConstruct>(construct)}) {
-      if (loop->IsDoNormal()) {
-        if (const parser::Name &iv{GetLoopIndex(loop)}; iv.symbol) {
-          const auto *type{iv.symbol->GetType()};
-          if (!type->IsNumeric(TypeCategory::Integer)) {
-            context_.Say(iv.source,
-                "The DO loop iteration variable must be of integer type"_err_en_US,
-                iv.ToString());
-          }
+  auto [depth, _]{GetAffectedNestDepthWithReason(spec, version)};
+  bool isLinearAllowed{false};
+  if (!depth || depth.value == 1) {
+    auto leafs{llvm::omp::getLeafConstructsOrSelf(dirId)};
+    isLinearAllowed = leafs.back() == llvm::omp::Directive::OMPD_simd;
+  }
+
+  std::vector<parser::Name> ivs;
+  for (const parser::DoConstruct *loop : *doLoops) {
+    for (auto &control : GetLoopControls(*loop)) {
+      if (control.iv.symbol) {
+        ivs.push_back(control.iv);
+      }
+    }
+  }
+
+  for (const parser::Name &iv : ivs) {
+    const auto *type{iv.symbol->GetType()};
+    if (!type->IsNumeric(TypeCategory::Integer)) {
+      context_.Say(iv.source,
+          "The DO loop iteration variable must be of integer type"_err_en_US,
+          iv.ToString());
+    }
+    const Symbol *host{GetHostSymbol(*iv.symbol)};
+    if (!host) {
+      continue;
+    }
+    if (host->test(Symbol::Flag::OmpThreadprivate)) {
+      context_.Say(iv.source,
+          "Loop iteration variable of an affected loop cannot be THREADPRIVATE"_err_en_US,
+          iv.ToString());
+    }
+    // Check conflict between a predetermined DSA and explicit DSA.
+    assert(iv.symbol->test(Symbol::Flag::OmpPreDetermined) &&
+        "Expecting affected iteration variable to have predetermined DSA");
+    if (iv.symbol->test(Symbol::Flag::OmpExplicit)) {
+      if (auto f{dsa.find(host)}; f != dsa.end()) {
+        llvm::omp::Clause id{f->second.second};
+        if (!llvm::omp::isAllowedClauseForDirective(dirId, id, version)) {
+          continue;
+        }
+        if (id == llvm::omp::Clause::OMPC_private ||
+            id == llvm::omp::Clause::OMPC_lastprivate) {
+          continue;
         }
+        if (id == llvm::omp::Clause::OMPC_linear && isLinearAllowed) {
+          continue;
+        }
+        context_
+            .Say(f->second.first,
+                "Loop iteration variable with a predetermined data sharing attribute cannot appear in a %s clause"_err_en_US,
+                parser::omp::GetUpperName(id, version))
+            .Attach(iv.source,
+                "'%s' is an iteration variable of an affected loop"_because_en_US,
+                iv.ToString());
       }
     }
   }
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 7bff7de2f8fbf..790c8c325c60f 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -4285,7 +4285,6 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) {
 
   CheckVarIsNotPartOfAnotherVar(GetContext().clauseSource, x.v, "FIRSTPRIVATE");
   CheckCrayPointee(x.v, "FIRSTPRIVATE");
-  CheckIsLoopIvPartOfClause(llvm::omp::Clause::OMPC_firstprivate, x.v);
 
   SymbolSourceMap currSymbols;
   GetSymbolsInObjectList(x.v, currSymbols);
@@ -4323,20 +4322,6 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Firstprivate &x) {
       currSymbols, dirClauseTriple, llvm::omp::Clause::OMPC_firstprivate);
 }
 
-void OmpStructureChecker::CheckIsLoopIvPartOfClause(
-    llvm::omp::Clause clause, const parser::OmpObjectList &ompObjectList) {
-  unsigned version{context_.langOptions().OpenMPVersion};
-  for (const auto &ompObject : ompObjectList.v) {
-    if (const parser::Name *name{parser::Unwrap<parser::Name>(ompObject)}) {
-      if (name->symbol == GetContext().loopIV) {
-        context_.Say(name->source,
-            "DO iteration variable %s is not allowed in %s clause."_err_en_US,
-            name->ToString(), parser::omp::GetUpperName(clause, version));
-      }
-    }
-  }
-}
-
 void OmpStructureChecker::Enter(const parser::OmpClause::Align &x) {
   CheckAllowedClause(llvm::omp::Clause::OMPC_align);
   if (const auto &v{GetIntValue(x.v.v)}) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index e75bb5da847a9..6634582708173 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -250,8 +250,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
       const omp::LoopSequence &nest);
   void CheckNestedConstruct(const parser::OpenMPLoopConstruct &x);
   const parser::Name GetLoopIndex(const parser::DoConstruct *x);
-  void SetLoopInfo(const parser::OpenMPLoopConstruct &x);
-  void CheckIterationVariableType(const parser::OpenMPLoopConstruct &x);
+  void CheckIterationVariables(const parser::OpenMPLoopConstruct &x);
   std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x);
   void CheckAssociatedLoopConstraints(const parser::OpenMPLoopConstruct &x);
   void CheckScanModifier(const parser::OmpClause::Reduction &x);
@@ -351,8 +350,6 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
       SymbolSourceMap &, const llvm::omp::Clause);
   void CheckPrivateSymbolsInOuterCxt(
       SymbolSourceMap &, DirectivesClauseTriple &, const llvm::omp::Clause);
-  void CheckIsLoopIvPartOfClause(
-      llvm::omp::Clause clause, const parser::OmpObjectList &ompObjectList);
   bool CheckTargetBlockOnlyTeams(const parser::Block &);
   void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
   void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 9c5f77ef03a27..e837f8dca19f6 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -207,6 +207,13 @@ bool IsWholeAssumedSizeArray(const parser::OmpObject &object) {
   return false;
 }
 
+const Symbol *GetHostSymbol(const Symbol &sym) {
+  if (auto *details{sym.detailsIf<HostAssocDetails>()}) {
+    return &details->symbol();
+  }
+  return nullptr;
+}
+
 bool IsMapEnteringType(parser::OmpMapType::Value type) {
   switch (type) {
   case parser::OmpMapType::Value::Alloc:
@@ -576,7 +583,7 @@ static bool IsTransformableLoop(const parser::ExecutionPartConstruct &epc) {
 }
 
 LoopControl::LoopControl(const parser::LoopControl::Bounds &x) {
-  iv = x.Name().thing.symbol;
+  iv = x.Name().thing;
   lbound = fromParserExpr(parser::UnwrapRef<parser::Expr>(x.Lower()));
   ubound = fromParserExpr(parser::UnwrapRef<parser::Expr>(x.Upper()));
   if (auto &inc{x.Step()}) {
@@ -586,7 +593,7 @@ LoopControl::LoopControl(const parser::LoopControl::Bounds &x) {
 
 LoopControl::LoopControl(const parser::ConcurrentControl &x) {
   auto &[name, lower, upper, inc]{x.t};
-  iv = name.symbol;
+  iv = name;
   lbound = fromParserExpr(parser::UnwrapRef<parser::Expr>(lower));
   ubound = fromParserExpr(parser::UnwrapRef<parser::Expr>(upper));
   if (inc) {
@@ -605,7 +612,8 @@ std::vector<LoopControl> GetLoopControls(const parser::DoConstruct &x) {
     controls.emplace_back(std::get<parser::LoopControl::Bounds>(control.u));
   } else if (x.IsDoConcurrent()) {
     const parser::LoopControl &control{*x.GetLoopControl()};
-    auto &header{parser::UnwrapRef<parser::ConcurrentHeader>(control)};
+    auto &concurrent{std::get<parser::LoopControl::Concurrent>(control.u)};
+    auto &header{std::get<parser::ConcurrentHeader>(concurrent.t)};
     for (auto &cc : std::get<std::list<parser::ConcurrentControl>>(header.t)) {
       controls.emplace_back(cc);
     }
@@ -1776,8 +1784,8 @@ WithReason<bool> LoopSequence::isRectangular(
   SymbolVector outerIVs;
   for (auto *sequence : llvm::reverse(outer)) {
     for (auto &control : sequence->getLoopControls()) {
-      if (control.iv) {
-        outerIVs.emplace_back(*control.iv);
+      if (control.iv.symbol) {
+        outerIVs.emplace_back(*control.iv.symbol);
       }
     }
   }
@@ -1785,7 +1793,7 @@ WithReason<bool> LoopSequence::isRectangular(
   WithReason<bool> result(true);
 
   for (auto &control : getLoopControls()) {
-    if (!control.iv || !control.lbound.value || !control.ubound.value) {
+    if (!control.iv.symbol || !control.lbound.value || !control.ubound.value) {
       continue;
     }
     CheckSymbolExprOverlap(result, outerIVs, *control.lbound.value,
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 22c2ca082ff22..d056917d849fd 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2114,16 +2114,6 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex(
     ivDSA = Symbol::Flag::OmpLastPrivate;
   }
 
-  auto checkThreadprivate{[&](const parser::Name &iv) {
-    if (const auto *details{iv.symbol->detailsIf<HostAssocDetails>()}) {
-      if (details->symbol().test(Symbol::Flag::OmpThreadprivate)) {
-        context_.Say(iv.source,
-            "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US,
-            iv.ToString());
-      }
-    }
-  }};
-
   Scope &scope{currScope()};
 
   if (auto doLoops{omp::CollectAffectedDoLoops(x, version, &context_)}) {
@@ -2132,9 +2122,7 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex(
       if (!iv || (iv->symbol && IsLocalInsideScope(*iv->symbol, scope))) {
         continue;
       }
-
       if (auto *symbol{ResolveOmp(*iv, ivDSA, scope)}) {
-        checkThreadprivate(*iv);
         SetSymbolDSA(*symbol, {Symbol::Flag::OmpPreDetermined, ivDSA});
         iv->symbol = symbol; // adjust the symbol within region
         AddToContextObjectWithDSA(*symbol, ivDSA);
diff --git a/flang/test/Parser/OpenMP/linear-clause.f90 b/flang/test/Parser/OpenMP/linear-clause.f90
index 5df37e0b3b028..45d288ce2077d 100644
--- a/flang/test/Parser/OpenMP/linear-clause.f90
+++ b/flang/test/Parser/OpenMP/linear-clause.f90
@@ -1,5 +1,5 @@
-!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=52 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
-!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=52 %s | FileCheck --check-prefix="PARSE-TREE" %s
+!RUN: %flang_fc1 -fdebug-unparse-no-sema -fopenmp -fopenmp-version=52 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree-no-sema -fopenmp -fopenmp-version=52 %s | FileCheck --check-prefix="PARSE-TREE" %s
 
 subroutine f00(x)
   integer :: x
@@ -12,7 +12,7 @@ subroutine f00(x)
 !UNPARSE: SUBROUTINE f00 (x)
 !UNPARSE:  INTEGER x
 !UNPARSE: !$OMP DO  LINEAR(x)
-!UNPARSE:  DO x=1_4,10_4
+!UNPARSE:  DO x=1,10
 !UNPARSE:  END DO
 !UNPARSE: !$OMP END DO
 !UNPARSE: END SUBROUTINE
@@ -35,8 +35,8 @@ subroutine f01(x)
 
 !UNPARSE: SUBROUTINE f01 (x)
 !UNPARSE:  INTEGER x
-!UNPARSE: !$OMP DO  LINEAR(x: 2_4)
-!UNPARSE:  DO x=1_4,10_4
+!UNPARSE: !$OMP DO  LINEAR(x: 2)
+!UNPARSE:  DO x=1,10
 !UNPARSE:  END DO
 !UNPARSE: !$OMP END DO
 !UNPARSE: END SUBROUTINE
@@ -45,8 +45,7 @@ subroutine f01(x)
 !PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = do
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Linear -> OmpLinearClause
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
-!PARSE-TREE: | | Modifier -> OmpStepSimpleModifier -> Scalar -> Integer -> Expr = '2_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | Modifier -> OmpStepSimpleModifier -> Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '2'
 !PARSE-TREE: | | bool = 'true'
 !PARSE-TREE: | Flags = {}
 !PARSE-TREE: DoConstruct
@@ -61,8 +60,8 @@ subroutine f02(x)
 
 !UNPARSE: SUBROUTINE f02 (x)
 !UNPARSE:  INTEGER x
-!UNPARSE: !$OMP DO  LINEAR(x: STEP(3_4))
-!UNPARSE:  DO x=1_4,10_4
+!UNPARSE: !$OMP DO  LINEAR(x: STEP(3))
+!UNPARSE:  DO x=1,10
 !UNPARSE:  END DO
 !UNPARSE: !$OMP END DO
 !UNPARSE: END SUBROUTINE
@@ -71,8 +70,7 @@ subroutine f02(x)
 !PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = do
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Linear -> OmpLinearClause
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
-!PARSE-TREE: | | Modifier -> OmpStepComplexModifier -> Scalar -> Integer -> Expr = '3_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '3'
+!PARSE-TREE: | | Modifier -> OmpStepComplexModifier -> Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '3'
 !PARSE-TREE: | | bool = 'true'
 !PARSE-TREE: | Flags = {}
 !PARSE-TREE: DoConstruct
@@ -102,7 +100,7 @@ subroutine f04(x)
 
 !UNPARSE: SUBROUTINE f04 (x)
 !UNPARSE:  INTEGER x
-!UNPARSE: !$OMP DECLARE SIMD LINEAR(x: UVAL, STEP(3_4))
+!UNPARSE: !$OMP DECLARE SIMD LINEAR(x: UVAL, STEP(3))
 !UNPARSE: END SUBROUTINE
 
 !PARSE-TREE: DeclarationConstruct -> SpecificationConstruct -> OpenMPDeclarativeConstruct -> OmpDeclareSimdDirective -> OmpDirectiveSpecification
@@ -110,7 +108,6 @@ subroutine f04(x)
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Linear -> OmpLinearClause
 !PARSE-TREE: | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 'x'
 !PARSE-TREE: | | Modifier -> OmpLinearModifier -> Value = Uval
-!PARSE-TREE: | | Modifier -> OmpStepComplexModifier -> Scalar -> Integer -> Expr = '3_4'
-!PARSE-TREE: | | | LiteralConstant -> IntLiteralConstant = '3'
+!PARSE-TREE: | | Modifier -> OmpStepComplexModifier -> Scalar -> Integer -> Expr -> LiteralConstant -> IntLiteralConstant = '3'
 !PARSE-TREE: | | bool = 'true'
 !PARSE-TREE: | Flags = {}
diff --git a/flang/test/Semantics/OpenMP/do01.f90 b/flang/test/Semantics/OpenMP/do01.f90
index 78c3ba38bc873..a06df1339774e 100644
--- a/flang/test/Semantics/OpenMP/do01.f90
+++ b/flang/test/Semantics/OpenMP/do01.f90
@@ -6,8 +6,9 @@
 program omp_do
   integer i, j, k
 
-  !ERROR: DO iteration variable i is not allowed in FIRSTPRIVATE clause.
+  !ERROR: Loop iteration variable with a predetermined data sharing attribute cannot appear in a FIRSTPRIVATE clause
   !$omp do firstprivate(k,i)
+  !BECAUSE: 'i' is an iteration variable of an affected loop
   do i = 1, 10
     do j = 1, 10
       print *, "Hello"
diff --git a/flang/test/Semantics/OpenMP/do04.f90 b/flang/test/Semantics/OpenMP/do04.f90
index 6690f4927f6a9..7c24ccac229c0 100644
--- a/flang/test/Semantics/OpenMP/do04.f90
+++ b/flang/test/Semantics/OpenMP/do04.f90
@@ -8,9 +8,9 @@ subroutine omp_do
   integer, save:: i, j, k,n
   !$omp  threadprivate(k,j,i)
   !$omp  do collapse(2)
-  !ERROR: Loop iteration variable i is not allowed in THREADPRIVATE.
+  !ERROR: Loop iteration variable of an affected loop cannot be THREADPRIVATE
   do i = 1, 10
-    !ERROR: Loop iteration variable j is not allowed in THREADPRIVATE.
+    !ERROR: Loop iteration variable of an affected loop cannot be THREADPRIVATE
     do j = 1, 10
       print *, "Hello"
     end do
@@ -22,7 +22,7 @@ subroutine omp_do1
   integer, save :: i, j, k
   !$omp  threadprivate(k,j,i)
   !$omp  do
-  !ERROR: Loop iteration variable i is not allowed in THREADPRIVATE.
+  !ERROR: Loop iteration variable of an affected loop cannot be THREADPRIVATE
   do i = 1, 10
     do j = 1, 10
       print *, "Hello"
@@ -40,7 +40,7 @@ subroutine omp_do2
   contains
   subroutine compute()
   !$omp  do ordered(1) collapse(1)
-  !ERROR: Loop iteration variable k is not allowed in THREADPRIVATE.
+  !ERROR: Loop iteration variable of an affected loop cannot be THREADPRIVATE
   foo: do k = 1, 10
     do i = 1, 10
       print *, "Hello"
@@ -58,7 +58,7 @@ subroutine omp_do3
   print *, "parallel"
   !$omp end parallel
   !$omp  do
-  !ERROR: Loop iteration variable i is not allowed in THREADPRIVATE.
+  !ERROR: Loop iteration variable of an affected loop cannot be THREADPRIVATE
   do i = 1, 10
     do j = 1, 10
       print *, "Hello"
@@ -82,7 +82,7 @@ end module usetp
 subroutine main
   use usetp
   !$omp  do
-  !ERROR: Loop iteration variable i is not allowed in THREADPRIVATE.
+  !ERROR: Loop iteration variable of an affected loop cannot be THREADPRIVATE
   do i = 1, 10
     do j = 1, 10
       print *, "Hello"
@@ -94,7 +94,7 @@ subroutine main
 subroutine main1
   use tp
   !$omp  do
-  !ERROR: Loop iteration variable j is not allowed in THREADPRIVATE.
+  !ERROR: Loop iteration variable of an affected loop cannot be THREADPRIVATE
   do j = 1, 10
     do i = 1, 10
       print *, "Hello"
diff --git a/flang/test/Semantics/OpenMP/do10.f90 b/flang/test/Semantics/OpenMP/do10.f90
index b609567c4d93d..1878864a4a5db 100644
--- a/flang/test/Semantics/OpenMP/do10.f90
+++ b/flang/test/Semantics/OpenMP/do10.f90
@@ -8,7 +8,6 @@ program omp_do
   !$omp do
   !ERROR: The DO loop iteration variable must be of integer type
   do i = 1, 10
-    !ERROR: The DO loop iteration variable must be of integer type
     do j = 1, 10
       print *, "it", i, j
     end do
@@ -18,9 +17,7 @@ program omp_do
   !ERROR: This construct requires a perfect nest of depth 3, but the associated nest is a perfect nest of depth 2
   !BECAUSE: COLLAPSE clause was specified with argument 3
   !$omp do collapse(3)
-  !ERROR: The DO loop iteration variable must be of integer type
   do i = 1, 10
-    !ERROR: The DO loop iteration variable must be of integer type
     do j = 1, 10
       print *, "it", i, j
     end do
diff --git a/flang/test/Semantics/OpenMP/linear-clause03.f90 b/flang/test/Semantics/OpenMP/linear-clause03.f90
index 124eb65701e26..7143db5927d46 100644
--- a/flang/test/Semantics/OpenMP/linear-clause03.f90
+++ b/flang/test/Semantics/OpenMP/linear-clause03.f90
@@ -15,7 +15,9 @@ subroutine f(x, y)
 subroutine g
   integer :: i
   !ERROR: Clause LINEAR is not allowed if clause ORDERED appears on the DO directive
+  !ERROR: Loop iteration variable with a predetermined data sharing attribute cannot appear in a LINEAR clause
   !$omp do ordered(1) linear(i)
+  !BECAUSE: 'i' is an iteration variable of an affected loop
   do i = 1, 10
   end do
 end
diff --git a/flang/test/Semantics/OpenMP/linear-iter.f90 b/flang/test/Semantics/OpenMP/linear-iter.f90
index a46a3411fab34..d810bc26c3606 100644
--- a/flang/test/Semantics/OpenMP/linear-iter.f90
+++ b/flang/test/Semantics/OpenMP/linear-iter.f90
@@ -76,8 +76,12 @@ SUBROUTINE LINEAR_BAD(N)
    !$omp end distribute simd
 
    !WARNING: `DISTRIBUTE` must be dynamically enclosed in a `TEAMS` region.
+   !ERROR: Loop iteration variable with a predetermined data sharing attribute cannot appear in a LINEAR clause
+   !ERROR: Loop iteration variable with a predetermined data sharing attribute cannot appear in a LINEAR clause
    !$omp distribute simd linear(i,j) collapse(2)
+   !BECAUSE: 'i' is an iteration variable of an affected loop
    do i = 1, N
+   !BECAUSE: 'j' is an iteration variable of an affected loop
       do j = 1, N
          a = 3.14
       enddo
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.h b/llvm/include/llvm/Frontend/OpenMP/OMP.h
index c7ad54ef3ed1b..c153707961d64 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.h
@@ -69,6 +69,33 @@ static constexpr inline bool isPrivatizingClause(Clause C, unsigned Version) {
   }
 }
 
+static constexpr inline bool isDataSharingAttributeClause(Clause C,
+                                                          unsigned Version) {
+  // The "Version" parameter is in case the result is version-depenent
+  // in the future.
+  (void)Version;
+  switch (C) {
+  case OMPC_detach:
+  case OMPC_firstprivate:
+  case OMPC_has_device_addr:
+  case OMPC_induction:
+  case OMPC_in_reduction:
+  case OMPC_is_device_ptr:
+  case OMPC_lastprivate:
+  case OMPC_linear:
+  case OMPC_private:
+  case OMPC_reduction:
+  case OMPC_shared:
+  case OMPC_task_reduction:
+  case OMPC_use_device_addr:
+  case OMPC_use_device_ptr:
+  case OMPC_uses_allocators:
+    return true;
+  default:
+    return false;
+  }
+}
+
 static constexpr unsigned FallbackVersion = 52;
 LLVM_ABI ArrayRef<unsigned> getOpenMPVersions();
 

>From 2f964578b2aff825e3eb2e42843461e15be1e0e5 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 30 Apr 2026 06:13:06 -0500
Subject: [PATCH 2/2] Remove loopIV from DirectiveContext

---
 flang/lib/Semantics/check-directive-structure.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/flang/lib/Semantics/check-directive-structure.h b/flang/lib/Semantics/check-directive-structure.h
index 46fbc5191b38a..8b4c3be76098e 100644
--- a/flang/lib/Semantics/check-directive-structure.h
+++ b/flang/lib/Semantics/check-directive-structure.h
@@ -231,11 +231,8 @@ class DirectiveStructureChecker : public virtual BaseChecker {
     std::list<C> actualClauses;
     std::list<C> endDirectiveClauses;
     std::list<C> crtGroup;
-    Symbol *loopIV{nullptr};
   };
 
-  void SetLoopIv(Symbol *symbol) { GetContext().loopIV = symbol; }
-
   // back() is the top of the stack
   DirectiveContext &GetContext() {
     CHECK(!dirContext_.empty());
@@ -260,7 +257,6 @@ class DirectiveStructureChecker : public virtual BaseChecker {
     GetContext().allowedExclusiveClauses = {};
     GetContext().requiredClauses = {};
     GetContext().clauseInfo = {};
-    GetContext().loopIV = {nullptr};
   }
 
   void SetContextDirectiveSource(const parser::CharBlock &directive) {



More information about the flang-commits mailing list