[flang-commits] [flang] [FLANG][OpenMP] Handling pure directives in DO CONCURRENT (PR #216642)

via flang-commits flang-commits at lists.llvm.org
Mon Aug 31 21:24:26 PDT 2026


https://github.com/ShashwathiNavada updated https://github.com/llvm/llvm-project/pull/216642

>From 4b13bf0d1fb507723914c1fbfcc0aff5283924c0 Mon Sep 17 00:00:00 2001
From: Shashwathi N <nshashwa at pe34genoa.hpc.amslabs.hpecorp.net>
Date: Mon, 17 Aug 2026 00:57:19 -0500
Subject: [PATCH 1/4] [FLANG][OpenMP] Handling pure directives in DO CONCURRENt

---
 flang/docs/OpenMPSupport.md                   |  2 +-
 flang/lib/Semantics/check-omp-structure.cpp   | 52 +++++++++++++++----
 flang/lib/Semantics/check-omp-structure.h     |  4 ++
 .../OpenMP/do-concurrent-pure-version.f90     | 22 ++++++++
 .../Semantics/OpenMP/do-concurrent-pure.f90   | 48 +++++++++++++++++
 5 files changed, 117 insertions(+), 11 deletions(-)
 create mode 100644 flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90
 create mode 100644 flang/test/Semantics/OpenMP/do-concurrent-pure.f90

diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index 63c210ca6d966..1a9d92c9424a1 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -234,7 +234,7 @@ Parser/Semantics, MLIR, Lowering, or the OpenMPIRBuilder.
 | loop construct with DO CONCURRENT | <span class="progress">in progress</span> | | Experimental support is available and under active development. | [llvm/llvm-project#178138](https://github.com/llvm/llvm-project/pull/178138), [llvm/llvm-project#190990](https://github.com/llvm/llvm-project/pull/190990) |
 | optional argument for all clauses | <span class="part">partial</span> | | Semantics coverage exists across clause tests (for example `flang/test/Semantics/OpenMP/if-clause-50.f90`). | |
 | canonical loop sequences | <span class="part">partial</span> | | Related loop/transform coverage exists (`flang/test/Semantics/OpenMP/loop-transformation-construct01.f90`, `flang/test/Lower/OpenMP/loop-directive.f90`). | [llvm/llvm-project#161213](https://github.com/llvm/llvm-project/pull/161213), [llvm/llvm-project#168884](https://github.com/llvm/llvm-project/pull/168884), [llvm/llvm-project#170734](https://github.com/llvm/llvm-project/pull/170734), [llvm/llvm-project#170735](https://github.com/llvm/llvm-project/pull/170735) |
-| pure directives in DO CONCURRENT | <span class="none">unclaimed</span> | | Define exact PURE+DO CONCURRENT directive legality in semantics and add lowering tests proving accepted forms remain side-effect safe. | |
+| pure directives in DO CONCURRENT | <span class="part">partial</span> | | Semantics enforce that OpenMP directives inside a DO CONCURRENT body must have the "pure" property. | |
 | pure procedure support extended to more directives | <span class="good">done</span> | | Semantics extend the OpenMP 5.2 pure-procedure allow list with the newly added loop-transforming constructs and the scan directive. | [llvm/llvm-project#212676](https://github.com/llvm/llvm-project/pull/212676) |
 | extensions to depobj construct | <span class="none">unclaimed</span> | | Semantics and deprecation diagnostics exist for several depobj forms, but extension support remains incomplete in lowering (for example `flang/test/Lower/OpenMP/Todo/depobj-construct.f90`). Implement lowering for extension operands/modifiers and add non-TODO lowering tests. | |
 | extensions to atomic construct | <span class="part">partial</span> | | Atomic compare lowering is now available (`flang/test/Lower/OpenMP/atomic-compare.f90`), but fail/capture-related extension paths remain incomplete (`flang/test/Lower/OpenMP/Todo/atomic-compare-fail.f90`). | [llvm/llvm-project#184761](https://github.com/llvm/llvm-project/pull/184761) |
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 274efa64d03d0..204c2c49ef401 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -957,12 +957,8 @@ void OmpStructureChecker::CheckDirectiveDeprecation(
   // one another, but only the top-level directive should cause a warning.
 }
 
-void OmpStructureChecker::CheckDirectiveInPureProcedure(
-    parser::CharBlock source, llvm::omp::Directive id) {
-  const Scope &scope{context_.FindScope(source)};
-  if (!FindPureProcedureContaining(scope)) {
-    return;
-  }
+void OmpStructureChecker::CheckDirectivePureSince(
+    parser::CharBlock source, llvm::omp::Directive id, const char *where) {
   unsigned version{context_.langOptions().OpenMPVersion};
   // A directive's "pure" property is version-specific: pureSince is the
   // OpenMP version at which the directive gained that property.
@@ -972,13 +968,47 @@ void OmpStructureChecker::CheckDirectiveInPureProcedure(
   }
   if (pureSince != 0x7FFFFFFF) {
     context_.Say(source,
-        "The OpenMP directive '%s' is not allowed in a PURE procedure in %s, %s"_err_en_US,
-        parser::omp::GetUpperName(id, version), ThisVersion(version),
+        "The OpenMP directive '%s' is not allowed in %s in %s, %s"_err_en_US,
+        parser::omp::GetUpperName(id, version), where, ThisVersion(version),
         TryVersion(pureSince));
   } else {
     context_.Say(source,
-        "The OpenMP directive '%s' is not allowed in a PURE procedure"_err_en_US,
-        parser::omp::GetUpperName(id, version));
+        "The OpenMP directive '%s' is not allowed in %s"_err_en_US,
+        parser::omp::GetUpperName(id, version), where);
+  }
+}
+
+void OmpStructureChecker::CheckDirectiveInPureProcedure(
+    parser::CharBlock source, llvm::omp::Directive id) {
+  const Scope &scope{context_.FindScope(source)};
+  if (!FindPureProcedureContaining(scope)) {
+    return;
+  }
+  CheckDirectivePureSince(source, id, "a PURE procedure");
+}
+
+void OmpStructureChecker::CheckDirectiveInDoConcurrent(
+    parser::CharBlock source, llvm::omp::Directive id) {
+  // Look for any enclosing DO CONCURRENT, not just the nearest DO, since a
+  // plain DO nested inside DO CONCURRENT is still part of its body.
+  for (const LoopOrConstruct &c : llvm::reverse(constructStack_)) {
+    auto *doConstruct{std::get_if<const parser::DoConstruct *>(&c)};
+    if (!doConstruct || !(*doConstruct)->IsDoConcurrent()) {
+      continue;
+    }
+    unsigned version{context_.langOptions().OpenMPVersion};
+    if (!IsDoConcurrentLegal(version)) {
+      // Prior to OpenMP 6.0, no OpenMP directive, regardless of its "pure"
+      // property, was allowed inside a DO CONCURRENT construct.
+      context_.Say(source,
+          "The OpenMP directive '%s' is not allowed inside a DO CONCURRENT construct"_err_en_US,
+          parser::omp::GetUpperName(id, version));
+    } else {
+      // Starting with OpenMP 6.0, directives that have the "pure" property
+      // are permitted inside a DO CONCURRENT construct.
+      CheckDirectivePureSince(source, id, "a DO CONCURRENT construct");
+    }
+    return;
   }
 }
 
@@ -1331,6 +1361,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
   dirStack_.push_back(&GetOmpDirectiveSpecification(x));
   CheckDirectiveDeprecation(x);
   CheckDirectiveInPureProcedure(dirName.source, dirName.v);
+  CheckDirectiveInDoConcurrent(dirName.source, dirName.v);
 
   // Verify clauses
   common::visit(
@@ -1388,6 +1419,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
       llvm::iterator_range(std::list<parser::OmpClause>{}));
 
   CheckDirectiveInPureProcedure(dirName.source, dirName.v);
+  CheckDirectiveInDoConcurrent(dirName.source, dirName.v);
   EnterDirectiveNest(DeclarativeNest);
 }
 
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 5eb33133e1efd..000ef81786e81 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -348,8 +348,12 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   void CheckDirectiveSpelling(
       parser::CharBlock spelling, llvm::omp::Directive id);
   void CheckDirectiveDeprecation(const parser::OpenMPConstruct &x);
+  void CheckDirectivePureSince(parser::CharBlock source,
+      llvm::omp::Directive id, const char *where);
   void CheckDirectiveInPureProcedure(
       parser::CharBlock source, llvm::omp::Directive id);
+  void CheckDirectiveInDoConcurrent(
+      parser::CharBlock source, llvm::omp::Directive id);
   void CheckClauses(parser::OmpDirectiveName dirName,
       llvm::iterator_range<ClauseIterator> beginClauses,
       llvm::iterator_range<ClauseIterator> endClauses);
diff --git a/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90 b/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90
new file mode 100644
index 0000000000000..e23bfac8b8303
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90
@@ -0,0 +1,22 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=52
+
+! Prior to OpenMP 6.0, no OpenMP directive is allowed inside a DO CONCURRENT
+! construct, regardless of whether it would otherwise have the "pure"
+! property (e.g. SIMD, which has been "pure" since OpenMP 4.5).
+
+module m
+contains
+  subroutine do_concurrent_bad(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n)
+    integer :: i, j
+    do concurrent (i = 1:n)
+      !ERROR: The OpenMP directive 'SIMD' is not allowed inside a DO CONCURRENT construct
+      !$omp simd
+      do j = 1, n
+      end do
+      !ERROR: The OpenMP directive 'BARRIER' is not allowed inside a DO CONCURRENT construct
+      !$omp barrier
+    end do
+  end subroutine
+end module
diff --git a/flang/test/Semantics/OpenMP/do-concurrent-pure.f90 b/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
new file mode 100644
index 0000000000000..cefc68190d61c
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
@@ -0,0 +1,48 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=60
+
+! Starting with OpenMP 6.0, directives with the "pure" property are allowed
+! in a DO CONCURRENT construct; other directives are still rejected.
+
+module m
+contains
+  subroutine do_concurrent_valid(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n)
+    integer :: i
+    do concurrent (i = 1:n)
+      !$omp nothing
+      a(i) = a(i) + 1
+    end do
+  end subroutine
+
+  subroutine do_concurrent_bad(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n)
+    integer :: i
+    do concurrent (i = 1:n)
+      !ERROR: The OpenMP directive 'BARRIER' is not allowed in a DO CONCURRENT construct
+      !$omp barrier
+      !ERROR: The OpenMP directive 'PARALLEL' is not allowed in a DO CONCURRENT construct
+      !$omp parallel
+      !$omp end parallel
+      !ERROR: The OpenMP directive 'ATOMIC' is not allowed in a DO CONCURRENT construct
+      !$omp atomic
+      a(i) = a(i) + 1
+    end do
+  end subroutine
+
+  ! A plain DO nested in DO CONCURRENT is still part of its body.
+  subroutine do_concurrent_nested_do(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n)
+    integer :: i, j
+    do concurrent (i = 1:n)
+      do j = 1, n
+        !ERROR: The OpenMP directive 'TASK' is not allowed in a DO CONCURRENT construct
+        !$omp task
+        !$omp end task
+      end do
+      a(i) = a(i) + 1
+    end do
+  end subroutine
+end module

>From f608c4107540b7ce8e70af6be8f4375a6ccf4f5d Mon Sep 17 00:00:00 2001
From: Shashwathi N <nshashwa at pe34genoa.hpc.amslabs.hpecorp.net>
Date: Mon, 17 Aug 2026 01:06:01 -0500
Subject: [PATCH 2/4] Updated Doc

---
 flang/docs/OpenMPSupport.md               | 2 +-
 flang/lib/Semantics/check-omp-structure.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index 1a9d92c9424a1..0eed6e1265476 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -234,7 +234,7 @@ Parser/Semantics, MLIR, Lowering, or the OpenMPIRBuilder.
 | loop construct with DO CONCURRENT | <span class="progress">in progress</span> | | Experimental support is available and under active development. | [llvm/llvm-project#178138](https://github.com/llvm/llvm-project/pull/178138), [llvm/llvm-project#190990](https://github.com/llvm/llvm-project/pull/190990) |
 | optional argument for all clauses | <span class="part">partial</span> | | Semantics coverage exists across clause tests (for example `flang/test/Semantics/OpenMP/if-clause-50.f90`). | |
 | canonical loop sequences | <span class="part">partial</span> | | Related loop/transform coverage exists (`flang/test/Semantics/OpenMP/loop-transformation-construct01.f90`, `flang/test/Lower/OpenMP/loop-directive.f90`). | [llvm/llvm-project#161213](https://github.com/llvm/llvm-project/pull/161213), [llvm/llvm-project#168884](https://github.com/llvm/llvm-project/pull/168884), [llvm/llvm-project#170734](https://github.com/llvm/llvm-project/pull/170734), [llvm/llvm-project#170735](https://github.com/llvm/llvm-project/pull/170735) |
-| pure directives in DO CONCURRENT | <span class="part">partial</span> | | Semantics enforce that OpenMP directives inside a DO CONCURRENT body must have the "pure" property. | |
+| pure directives in DO CONCURRENT | <span class="good">done</span> | | Semantics enforce that OpenMP directives inside a DO CONCURRENT body must have the "pure" property. | [llvm/llvm-project#216642](https://github.com/llvm/llvm-project/pull/216642) |
 | pure procedure support extended to more directives | <span class="good">done</span> | | Semantics extend the OpenMP 5.2 pure-procedure allow list with the newly added loop-transforming constructs and the scan directive. | [llvm/llvm-project#212676](https://github.com/llvm/llvm-project/pull/212676) |
 | extensions to depobj construct | <span class="none">unclaimed</span> | | Semantics and deprecation diagnostics exist for several depobj forms, but extension support remains incomplete in lowering (for example `flang/test/Lower/OpenMP/Todo/depobj-construct.f90`). Implement lowering for extension operands/modifiers and add non-TODO lowering tests. | |
 | extensions to atomic construct | <span class="part">partial</span> | | Atomic compare lowering is now available (`flang/test/Lower/OpenMP/atomic-compare.f90`), but fail/capture-related extension paths remain incomplete (`flang/test/Lower/OpenMP/Todo/atomic-compare-fail.f90`). | [llvm/llvm-project#184761](https://github.com/llvm/llvm-project/pull/184761) |
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 000ef81786e81..92ab031758baf 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -348,8 +348,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   void CheckDirectiveSpelling(
       parser::CharBlock spelling, llvm::omp::Directive id);
   void CheckDirectiveDeprecation(const parser::OpenMPConstruct &x);
-  void CheckDirectivePureSince(parser::CharBlock source,
-      llvm::omp::Directive id, const char *where);
+  void CheckDirectivePureSince(
+      parser::CharBlock source, llvm::omp::Directive id, const char *where);
   void CheckDirectiveInPureProcedure(
       parser::CharBlock source, llvm::omp::Directive id);
   void CheckDirectiveInDoConcurrent(

>From 939dfd0abeaf92a623140483def3b0079546064e Mon Sep 17 00:00:00 2001
From: Shashwathi N <nshashwa at pe34genoa.hpc.amslabs.hpecorp.net>
Date: Thu, 20 Aug 2026 09:05:37 -0500
Subject: [PATCH 3/4] Suggested change

---
 flang/lib/Semantics/check-omp-structure.cpp   |  2 +-
 flang/lib/Semantics/check-omp-variant.cpp     |  4 ++
 .../OpenMP/do-concurrent-pure-version.f90     |  4 +-
 .../Semantics/OpenMP/do-concurrent-pure.f90   | 65 ++++++++++++++++++-
 4 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 204c2c49ef401..a988ae7921177 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -1001,7 +1001,7 @@ void OmpStructureChecker::CheckDirectiveInDoConcurrent(
       // Prior to OpenMP 6.0, no OpenMP directive, regardless of its "pure"
       // property, was allowed inside a DO CONCURRENT construct.
       context_.Say(source,
-          "The OpenMP directive '%s' is not allowed inside a DO CONCURRENT construct"_err_en_US,
+          "The OpenMP directive '%s' is not allowed in a DO CONCURRENT construct"_err_en_US,
           parser::omp::GetUpperName(id, version));
     } else {
       // Starting with OpenMP 6.0, directives that have the "pure" property
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 0f8daf84d25ec..6bd6fcbad6831 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -744,6 +744,10 @@ void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
   if (dirId != llvm::omp::Directive::OMPD_metadirective) {
     metadirectiveLoopVariants_.push_back(
         {currentWhenSelector_, &x, checkDefaultNoneInAssociatedLoop});
+    // METADIRECTIVE is "pure", but its selected variant may not be.
+    // Check each variant independently.
+    CheckDirectiveInPureProcedure(x.DirName().source, dirId);
+    CheckDirectiveInDoConcurrent(x.DirName().source, dirId);
   }
 }
 
diff --git a/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90 b/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90
index e23bfac8b8303..b4a85e61f2f64 100644
--- a/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90
+++ b/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90
@@ -11,11 +11,11 @@ subroutine do_concurrent_bad(a, n)
     integer, intent(inout) :: a(n)
     integer :: i, j
     do concurrent (i = 1:n)
-      !ERROR: The OpenMP directive 'SIMD' is not allowed inside a DO CONCURRENT construct
+      !ERROR: The OpenMP directive 'SIMD' is not allowed in a DO CONCURRENT construct
       !$omp simd
       do j = 1, n
       end do
-      !ERROR: The OpenMP directive 'BARRIER' is not allowed inside a DO CONCURRENT construct
+      !ERROR: The OpenMP directive 'BARRIER' is not allowed in a DO CONCURRENT construct
       !$omp barrier
     end do
   end subroutine
diff --git a/flang/test/Semantics/OpenMP/do-concurrent-pure.f90 b/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
index cefc68190d61c..c3449772fa56b 100644
--- a/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
+++ b/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
@@ -8,13 +8,76 @@ module m
   subroutine do_concurrent_valid(a, n)
     integer, intent(in) :: n
     integer, intent(inout) :: a(n)
-    integer :: i
+    integer :: i, j
     do concurrent (i = 1:n)
       !$omp nothing
+      !$omp simd
+      do j = 1, n
+        a(i) = a(i) + 1
+      end do
+    end do
+  end subroutine
+
+  ! A DO CONCURRENT nested in another DO CONCURRENT is checked against its
+  ! innermost enclosing DO CONCURRENT.
+  subroutine do_concurrent_nested_do_concurrent(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n, n)
+    integer :: i, j, k
+    do concurrent (i = 1:n)
+      do concurrent (j = 1:n)
+        !$omp simd
+        do k = 1, n
+          a(i, j) = a(i, j) + 1
+        end do
+        !ERROR: The OpenMP directive 'PARALLEL' is not allowed in a DO CONCURRENT construct
+        !$omp parallel
+        !$omp end parallel
+      end do
+    end do
+  end subroutine
+
+  ! A BLOCK construct nested in DO CONCURRENT is still part of its body.
+  subroutine do_concurrent_nested_block(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n)
+    integer :: i
+    do concurrent (i = 1:n)
+      block
+        !$omp nothing
+        !ERROR: The OpenMP directive 'CRITICAL' is not allowed in a DO CONCURRENT construct
+        !$omp critical
+        a(i) = a(i) + 1
+        !$omp end critical
+      end block
+    end do
+  end subroutine
+
+  ! A METADIRECTIVE only reflects its own "pure" property; whichever variant
+  ! it selects must be checked independently.
+  subroutine do_concurrent_metadirective(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n)
+    integer :: i
+    do concurrent (i = 1:n)
+      !$omp metadirective when(user={condition(.true.)}: nothing)
+      !$omp metadirective when(user={condition(.false.)}: nothing) otherwise(assume no_openmp)
       a(i) = a(i) + 1
     end do
   end subroutine
 
+  subroutine do_concurrent_metadirective_bad(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n)
+    integer :: i
+    do concurrent (i = 1:n)
+      !ERROR: The OpenMP directive 'PARALLEL' is not allowed in a DO CONCURRENT construct
+      !$omp metadirective when(user={condition(.true.)}: parallel)
+      block
+      end block
+    end do
+  end subroutine
+
   subroutine do_concurrent_bad(a, n)
     integer, intent(in) :: n
     integer, intent(inout) :: a(n)

>From 7ae76785b89e08557f6d0f78eae057d7be73795b Mon Sep 17 00:00:00 2001
From: Shashwathi N <shashwathinavada at gmail.com>
Date: Thu, 20 Aug 2026 10:28:56 -0500
Subject: [PATCH 4/4] Fix the regressin

---
 flang/lib/Semantics/check-omp-variant.cpp     | 20 +++++++++++---
 .../Semantics/OpenMP/do-concurrent-pure.f90   | 27 +++++++++++++++++++
 2 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 6bd6fcbad6831..2bc568fd168f6 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -744,10 +744,22 @@ void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
   if (dirId != llvm::omp::Directive::OMPD_metadirective) {
     metadirectiveLoopVariants_.push_back(
         {currentWhenSelector_, &x, checkDefaultNoneInAssociatedLoop});
-    // METADIRECTIVE is "pure", but its selected variant may not be.
-    // Check each variant independently.
-    CheckDirectiveInPureProcedure(x.DirName().source, dirId);
-    CheckDirectiveInDoConcurrent(x.DirName().source, dirId);
+    // Metadirective is "pure", but its selected variant may not be.
+    // Check the variant independently only when metadirective is legal;
+    // otherwise, the outer metadirective check already reports the error.
+    if (GetDirectiveNest(MetadirectiveNest)) {
+      unsigned version{context_.langOptions().OpenMPVersion};
+      if (version >= llvm::omp::getDirectivePureSince(
+                         llvm::omp::Directive::OMPD_metadirective)) {
+        CheckDirectiveInPureProcedure(x.DirName().source, dirId);
+      }
+      if (IsDoConcurrentLegal(version)) {
+        CheckDirectiveInDoConcurrent(x.DirName().source, dirId);
+      }
+    } else {
+      CheckDirectiveInPureProcedure(x.DirName().source, dirId);
+      CheckDirectiveInDoConcurrent(x.DirName().source, dirId);
+    }
   }
 }
 
diff --git a/flang/test/Semantics/OpenMP/do-concurrent-pure.f90 b/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
index c3449772fa56b..ed5211a816db5 100644
--- a/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
+++ b/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
@@ -78,6 +78,33 @@ subroutine do_concurrent_metadirective_bad(a, n)
     end do
   end subroutine
 
+  ! An APPLY clause's directive-variant must be pure too, independently of
+  ! its host directive (TILE, which is itself pure).
+  subroutine do_concurrent_apply(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n, n)
+    integer :: i, j
+    do concurrent (i = 1:n)
+      !$omp tile sizes(2) apply(grid: nothing)
+      do j = 1, n
+        a(i, j) = a(i, j) + 1
+      end do
+    end do
+  end subroutine
+
+  subroutine do_concurrent_apply_bad(a, n)
+    integer, intent(in) :: n
+    integer, intent(inout) :: a(n, n)
+    integer :: i, j
+    do concurrent (i = 1:n)
+      !ERROR: The OpenMP directive 'BARRIER' is not allowed in a DO CONCURRENT construct
+      !$omp tile sizes(2) apply(grid: barrier)
+      do j = 1, n
+        a(i, j) = a(i, j) + 1
+      end do
+    end do
+  end subroutine
+
   subroutine do_concurrent_bad(a, n)
     integer, intent(in) :: n
     integer, intent(inout) :: a(n)



More information about the flang-commits mailing list