[flang-commits] [flang] [flang][OpenACC] Recover from repeated and conflicting loop modes (PR #234430)

Andre Kuhlenschmidt via flang-commits flang-commits at lists.llvm.org
Thu Oct 22 12:45:55 PDT 2026


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

>From 5e3e3e25339bc170669815472295ed975fbe45e1 Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Fri, 16 Oct 2026 17:24:51 -0700
Subject: [PATCH 1/2] [flang][OpenACC] Recover from repeated and conflicting
 loop modes

Accept repeated SEQ or INDEPENDENT clauses on standalone and combined loop
constructs with one openacc-usage warning, retaining the first clause.
When both modes appear, warn once and discard all occurrences so lowering
uses the same default mode as a directive with neither clause.

Normalize the clauses before structure checking and lowering. Preserve
existing diagnostics when AUTO or DEVICE_TYPE clauses are present.
Document the compatibility extension and test diagnostics, warning controls,
clause preservation, and the resulting loop modes. Keep recovery independent
of loop-control form; the semantic DO WHILE test records that decision.
---
 flang/docs/OpenACC-extensions.md              |   8 ++
 flang/lib/Semantics/rewrite-parse-tree.cpp    |  65 ++++++++++
 .../OpenACC/acc-loop-conflicting-modes.f90    |  58 +++++++++
 .../Lower/OpenACC/acc-loop-repeated-modes.f90 | 118 +++++++++++++++++
 .../acc-loop-conflicting-modes-errors.f90     |  38 ++++++
 .../OpenACC/acc-loop-conflicting-modes.f90    | 120 ++++++++++++++++++
 .../OpenACC/acc-loop-repeated-modes.f90       |  92 ++++++++++++++
 7 files changed, 499 insertions(+)
 create mode 100644 flang/test/Lower/OpenACC/acc-loop-conflicting-modes.f90
 create mode 100644 flang/test/Lower/OpenACC/acc-loop-repeated-modes.f90
 create mode 100644 flang/test/Semantics/OpenACC/acc-loop-conflicting-modes-errors.f90
 create mode 100644 flang/test/Semantics/OpenACC/acc-loop-conflicting-modes.f90
 create mode 100644 flang/test/Semantics/OpenACC/acc-loop-repeated-modes.f90

diff --git a/flang/docs/OpenACC-extensions.md b/flang/docs/OpenACC-extensions.md
index aa2987686628b1..e0760900f9af39 100644
--- a/flang/docs/OpenACC-extensions.md
+++ b/flang/docs/OpenACC-extensions.md
@@ -23,6 +23,14 @@ These extensions require no flag.
 * An `!$acc routine` with no parallelism clause is treated as if the `seq`
   clause were present.
 * `!$acc end loop` does not trigger a parsing error and is silently ignored.
+* On `loop` and combined loop directives without `auto` or `device_type`
+  clauses, repeated or conflicting `seq` and `independent` clauses are accepted
+  with a warning. Repetitions of a single mode are ignored, retaining one copy
+  of that clause. If both modes appear, all occurrences of both clauses are
+  ignored, including repeated clauses, and the loop uses the same default
+  parallelism mode as a directive with neither clause. One warning is emitted
+  per affected directive, enabled by default (`-Wopenacc-usage`; suppress with
+  `-Wno-openacc-usage`).
 * The restriction on required clauses for `!$acc data` is emitted as a
   portability warning rather than an error, matching the behavior of other
   compilers.
diff --git a/flang/lib/Semantics/rewrite-parse-tree.cpp b/flang/lib/Semantics/rewrite-parse-tree.cpp
index 9eb2e8fd8ec8fd..03b2e98c84f6b0 100644
--- a/flang/lib/Semantics/rewrite-parse-tree.cpp
+++ b/flang/lib/Semantics/rewrite-parse-tree.cpp
@@ -19,6 +19,7 @@
 #include "flang/Semantics/symbol.h"
 #include "flang/Semantics/tools.h"
 #include <list>
+#include <utility>
 
 namespace Fortran::semantics {
 
@@ -63,6 +64,8 @@ class RewriteMutator {
   void Post(parser::ReadStmt &);
   void Post(parser::WriteStmt &);
   void Post(parser::AccObjectList &);
+  void Post(parser::AccBeginLoopDirective &);
+  void Post(parser::AccBeginCombinedDirective &);
 
   // Name resolution yet implemented:
   // TODO: Can some/all of these now be enabled?
@@ -93,6 +96,7 @@ class RewriteMutator {
   void FixMisparsedStmtFuncs(parser::SpecificationPart &, parser::Block &);
   void OpenMPSimdOnly(parser::Block &, bool);
   void OpenMPSimdOnly(parser::SpecificationPart &);
+  void NormalizeAccLoopClauses(parser::AccClauseList &, llvm::acc::Directive);
 
   SemanticsContext &context_;
   bool errorOnUnresolvedName_{true};
@@ -528,6 +532,67 @@ void RewriteMutator::Post(parser::AccObjectList &x) {
   });
 }
 
+// Recover from repeated or conflicting loop modes before structure checking
+// and lowering. Repeated clauses of one mode retain that mode.
+// Merely downgrading the exclusivity diagnostic would leave INDEPENDENT in the
+// parse tree and incorrectly promise parallelism to lowering. Discard both
+// modes so that the enclosing compute construct determines the loop's default
+// mode.
+void RewriteMutator::NormalizeAccLoopClauses(
+    parser::AccClauseList &clauses, llvm::acc::Directive directive) {
+  unsigned seq{0}, independent{0};
+  for (const auto &clause : clauses.v) {
+    if (std::holds_alternative<parser::AccClause::Seq>(clause.u)) {
+      ++seq;
+    } else if (std::holds_alternative<parser::AccClause::Independent>(
+                   clause.u)) {
+      ++independent;
+    } else if (std::holds_alternative<parser::AccClause::Auto>(clause.u) ||
+        std::holds_alternative<parser::AccClause::DeviceType>(clause.u)) {
+      // Preserve existing diagnostics for other mode conflicts and device
+      // groups. Recovery here only handles SEQ/INDEPENDENT modes.
+      return;
+    }
+  }
+  if (seq && independent) {
+    context_.Warn(common::UsageWarning::OpenAccUsage, clauses.source,
+        "SEQ and INDEPENDENT clauses on the %s directive are ignored because they are mutually exclusive"_warn_en_US,
+        parser::ToUpperCaseLetters(
+            llvm::acc::getOpenACCDirectiveName(directive).str()));
+    clauses.v.remove_if([](const parser::AccClause &clause) {
+      return std::holds_alternative<parser::AccClause::Seq>(clause.u) ||
+          std::holds_alternative<parser::AccClause::Independent>(clause.u);
+    });
+    return;
+  }
+  if (seq < 2 && independent < 2) {
+    return;
+  }
+  context_.Warn(common::UsageWarning::OpenAccUsage, clauses.source,
+      "Repeated %s clauses on the %s directive are ignored"_warn_en_US,
+      seq ? "SEQ" : "INDEPENDENT",
+      parser::ToUpperCaseLetters(
+          llvm::acc::getOpenACCDirectiveName(directive).str()));
+  bool seen{false};
+  clauses.v.remove_if([&](const parser::AccClause &clause) {
+    if (std::holds_alternative<parser::AccClause::Seq>(clause.u) ||
+        std::holds_alternative<parser::AccClause::Independent>(clause.u)) {
+      return std::exchange(seen, true);
+    }
+    return false;
+  });
+}
+
+void RewriteMutator::Post(parser::AccBeginLoopDirective &x) {
+  NormalizeAccLoopClauses(std::get<parser::AccClauseList>(x.t),
+      std::get<parser::AccLoopDirective>(x.t).v);
+}
+
+void RewriteMutator::Post(parser::AccBeginCombinedDirective &x) {
+  NormalizeAccLoopClauses(std::get<parser::AccClauseList>(x.t),
+      std::get<parser::AccCombinedDirective>(x.t).v);
+}
+
 bool RewriteParseTree(SemanticsContext &context, parser::Program &program) {
   RewriteMutator mutator{context};
   parser::Walk(program, mutator);
diff --git a/flang/test/Lower/OpenACC/acc-loop-conflicting-modes.f90 b/flang/test/Lower/OpenACC/acc-loop-conflicting-modes.f90
new file mode 100644
index 00000000000000..ef82a931131f8c
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-loop-conflicting-modes.f90
@@ -0,0 +1,58 @@
+! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
+
+! The recovered loop must have the same inferred mode as a plain loop.
+! In a kernels region, this is AUTO, not INDEPENDENT or SEQ.
+subroutine recovered_modes(a)
+  real :: a(10)
+  integer :: i
+  !$acc kernels
+  !$acc loop seq independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc loop independent seq
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc loop seq seq independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc loop independent seq independent seq independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc loop
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc end kernels
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrecovered_modes
+! CHECK: acc.kernels
+! CHECK: acc.loop
+! CHECK: } inclusiveUpperbound(array<i1: true>) auto_{{ *$}}
+! CHECK: acc.loop
+! CHECK: } inclusiveUpperbound(array<i1: true>) auto_{{ *$}}
+! CHECK: acc.loop
+! CHECK: } inclusiveUpperbound(array<i1: true>) auto_{{ *$}}
+! CHECK: acc.loop
+! CHECK: } inclusiveUpperbound(array<i1: true>) auto_{{ *$}}
+! CHECK: acc.loop
+! CHECK: } inclusiveUpperbound(array<i1: true>) auto_{{ *$}}
+
+! Unrelated scheduling clauses must survive recovery on combined directives.
+subroutine recovered_gang(a)
+  real :: a(10)
+  integer :: i
+  !$acc parallel loop gang seq independent seq independent
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrecovered_gang
+! CHECK: acc.parallel
+! CHECK: acc.loop combined(parallel) gang
+! CHECK: } inclusiveUpperbound(array<i1: true>) independent{{ *$}}
diff --git a/flang/test/Lower/OpenACC/acc-loop-repeated-modes.f90 b/flang/test/Lower/OpenACC/acc-loop-repeated-modes.f90
new file mode 100644
index 00000000000000..930eb53b8493d9
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-loop-repeated-modes.f90
@@ -0,0 +1,118 @@
+! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
+
+! Repeated clauses must retain their mode, rather than fall back to AUTO.
+subroutine repeated_seq_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc kernels
+  !$acc loop seq private(i) seq seq
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc end kernels
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrepeated_seq_loop
+! CHECK: acc.loop
+! CHECK-SAME: private(
+! CHECK: } inclusiveUpperbound(array<i1: true>) seq{{ *$}}
+
+subroutine repeated_seq_parallel_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc parallel loop seq private(i) seq seq
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrepeated_seq_parallel_loop
+! CHECK: acc.loop
+! CHECK-SAME: private(
+! CHECK: } inclusiveUpperbound(array<i1: true>) seq{{ *$}}
+
+subroutine repeated_seq_kernels_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc kernels loop seq private(i) seq seq
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrepeated_seq_kernels_loop
+! CHECK: acc.loop
+! CHECK-SAME: private(
+! CHECK: } inclusiveUpperbound(array<i1: true>) seq{{ *$}}
+
+subroutine repeated_seq_serial_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc serial loop seq private(i) seq seq
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrepeated_seq_serial_loop
+! CHECK: acc.loop
+! CHECK-SAME: private(
+! CHECK: } inclusiveUpperbound(array<i1: true>) seq{{ *$}}
+
+subroutine repeated_independent_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc kernels
+  !$acc loop independent private(i) independent independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc end kernels
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrepeated_independent_loop
+! CHECK: acc.loop
+! CHECK-SAME: private(
+! CHECK: } inclusiveUpperbound(array<i1: true>) independent{{ *$}}
+
+subroutine repeated_independent_parallel_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc parallel loop independent private(i) independent independent
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrepeated_independent_parallel_loop
+! CHECK: acc.loop
+! CHECK-SAME: private(
+! CHECK: } inclusiveUpperbound(array<i1: true>) independent{{ *$}}
+
+subroutine repeated_independent_kernels_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc kernels loop independent private(i) independent independent
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrepeated_independent_kernels_loop
+! CHECK: acc.loop
+! CHECK-SAME: private(
+! CHECK: } inclusiveUpperbound(array<i1: true>) independent{{ *$}}
+
+subroutine repeated_independent_serial_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc serial loop independent private(i) independent independent
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+! CHECK-LABEL: func.func @_QPrepeated_independent_serial_loop
+! CHECK: acc.loop
+! CHECK-SAME: private(
+! CHECK: } inclusiveUpperbound(array<i1: true>) independent{{ *$}}
diff --git a/flang/test/Semantics/OpenACC/acc-loop-conflicting-modes-errors.f90 b/flang/test/Semantics/OpenACC/acc-loop-conflicting-modes-errors.f90
new file mode 100644
index 00000000000000..418122d62375f1
--- /dev/null
+++ b/flang/test/Semantics/OpenACC/acc-loop-conflicting-modes-errors.f90
@@ -0,0 +1,38 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenacc
+
+! Recovery excludes AUTO and DEVICE_TYPE groups. Preserve their diagnostics.
+subroutine unrecovered_modes(a)
+  real :: a(10)
+  integer :: i
+  !$acc kernels
+  !ERROR: AUTO and SEQ clauses are mutually exclusive and may not appear on the same LOOP directive
+  !$acc loop seq auto
+  do i = 1, 10
+    a(i) = i
+  end do
+
+  !ERROR: AUTO and SEQ clauses are mutually exclusive and may not appear on the same LOOP directive
+  !ERROR: INDEPENDENT and SEQ clauses are mutually exclusive and may not appear on the same LOOP directive
+  !$acc loop seq auto independent
+  do i = 1, 10
+    a(i) = i
+  end do
+
+  !ERROR: At most one INDEPENDENT clause can appear on the LOOP directive
+  !$acc loop device_type(nvidia) independent independent
+  do i = 1, 10
+    a(i) = i
+  end do
+
+  !ERROR: INDEPENDENT and SEQ clauses are mutually exclusive and may not appear on the same LOOP directive
+  !$acc loop seq device_type(nvidia) independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc end kernels
+end subroutine
+
+subroutine unrecovered_routine
+  !ERROR: INDEPENDENT clause is not allowed on the ROUTINE SEQ INDEPENDENT directive
+  !$acc routine seq independent
+end subroutine
diff --git a/flang/test/Semantics/OpenACC/acc-loop-conflicting-modes.f90 b/flang/test/Semantics/OpenACC/acc-loop-conflicting-modes.f90
new file mode 100644
index 00000000000000..af5e0ba5444232
--- /dev/null
+++ b/flang/test/Semantics/OpenACC/acc-loop-conflicting-modes.f90
@@ -0,0 +1,120 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenacc -Werror
+! RUN: %flang_fc1 -fopenacc -fsyntax-only %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -fopenacc -fsyntax-only -Werror -w %s
+! RUN: %flang_fc1 -fopenacc -fsyntax-only -Werror -Wno-openacc-usage %s
+
+! Conflicting SEQ and INDEPENDENT modes are both ignored, regardless of order.
+! CHECK-COUNT-11: warning: SEQ and INDEPENDENT clauses on the
+! CHECK-NOT: warning:
+! CHECK-NOT: error:
+subroutine conflicting_modes(a)
+  real :: a(10)
+  integer :: i
+
+  !$acc kernels
+  !WARNING: SEQ and INDEPENDENT clauses on the LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc loop seq independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc end kernels
+
+  !$acc kernels
+  !WARNING: SEQ and INDEPENDENT clauses on the LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc loop seq seq independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !WARNING: SEQ and INDEPENDENT clauses on the LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc loop independent independent seq
+  do i = 1, 10
+    a(i) = i
+  end do
+  !WARNING: SEQ and INDEPENDENT clauses on the LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc loop seq independent seq independent seq independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc end kernels
+
+  ! Removing SEQ must also avoid a spurious conflict with GANG.
+  !WARNING: SEQ and INDEPENDENT clauses on the PARALLEL LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc parallel loop independent seq gang private(i) seq independent
+  do i = 1, 10
+    a(i) = i
+  end do
+
+  !WARNING: SEQ and INDEPENDENT clauses on the KERNELS LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc kernels loop seq independent
+  do i = 1, 10
+    a(i) = i
+  end do
+
+  !WARNING: SEQ and INDEPENDENT clauses on the SERIAL LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc serial loop independent seq
+  do i = 1, 10
+    a(i) = i
+  end do
+
+  !$acc kernels
+  !$acc loop seq
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc loop independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc loop auto
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc end kernels
+end subroutine
+
+! Recovery deliberately does not depend on the associated DO's loop control.
+! DO WHILE is already accepted by semantics with a plain LOOP directive, even
+! though OpenACC lowering currently crashes on that form. That separate lowering
+! bug must not prevent the same clause recovery here. Keep this a semantics-only
+! test: it checks the recovery decision without requiring the lowering bug to be
+! fixed or preserving the crash as expected behavior.
+subroutine conflicting_modes_while(a)
+  real :: a(10)
+  integer :: i
+
+  i = 0
+  !$acc kernels
+  !$acc loop
+  do while (i < 10)
+    i = i + 1
+    a(i) = i
+  end do
+  !WARNING: SEQ and INDEPENDENT clauses on the LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc loop seq independent seq independent
+  do while (i > 0)
+    a(i) = i
+    i = i - 1
+  end do
+  !$acc end kernels
+
+  !WARNING: SEQ and INDEPENDENT clauses on the PARALLEL LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc parallel loop independent seq
+  do while (i < 10)
+    i = i + 1
+    a(i) = i
+  end do
+
+  !WARNING: SEQ and INDEPENDENT clauses on the KERNELS LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc kernels loop seq independent
+  do while (i > 0)
+    a(i) = i
+    i = i - 1
+  end do
+
+  !WARNING: SEQ and INDEPENDENT clauses on the SERIAL LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+  !$acc serial loop independent seq independent seq
+  do while (i < 10)
+    i = i + 1
+    a(i) = i
+  end do
+end subroutine
diff --git a/flang/test/Semantics/OpenACC/acc-loop-repeated-modes.f90 b/flang/test/Semantics/OpenACC/acc-loop-repeated-modes.f90
new file mode 100644
index 00000000000000..4b8848289d4306
--- /dev/null
+++ b/flang/test/Semantics/OpenACC/acc-loop-repeated-modes.f90
@@ -0,0 +1,92 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenacc -Werror
+! RUN: %flang_fc1 -fopenacc -fsyntax-only %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -fopenacc -fsyntax-only -Werror -w %s
+! RUN: %flang_fc1 -fopenacc -fsyntax-only -Werror -Wno-openacc-usage %s
+
+! Repetitions retain one clause and emit one warning per directive.
+! CHECK-COUNT-8: warning: Repeated
+! CHECK-NOT: warning:
+! CHECK-NOT: error:
+subroutine repeated_seq_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc kernels
+  !WARNING: Repeated SEQ clauses on the LOOP directive are ignored [-Wopenacc-usage]
+  !$acc loop seq private(i) seq seq
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc end kernels
+end subroutine
+
+subroutine repeated_seq_parallel_loop(a)
+  real :: a(10)
+  integer :: i
+  !WARNING: Repeated SEQ clauses on the PARALLEL LOOP directive are ignored [-Wopenacc-usage]
+  !$acc parallel loop seq private(i) seq seq
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+subroutine repeated_seq_kernels_loop(a)
+  real :: a(10)
+  integer :: i
+  !WARNING: Repeated SEQ clauses on the KERNELS LOOP directive are ignored [-Wopenacc-usage]
+  !$acc kernels loop seq private(i) seq seq
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+subroutine repeated_seq_serial_loop(a)
+  real :: a(10)
+  integer :: i
+  !WARNING: Repeated SEQ clauses on the SERIAL LOOP directive are ignored [-Wopenacc-usage]
+  !$acc serial loop seq private(i) seq seq
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+subroutine repeated_independent_loop(a)
+  real :: a(10)
+  integer :: i
+  !$acc kernels
+  !WARNING: Repeated INDEPENDENT clauses on the LOOP directive are ignored [-Wopenacc-usage]
+  !$acc loop independent private(i) independent independent
+  do i = 1, 10
+    a(i) = i
+  end do
+  !$acc end kernels
+end subroutine
+
+subroutine repeated_independent_parallel_loop(a)
+  real :: a(10)
+  integer :: i
+  !WARNING: Repeated INDEPENDENT clauses on the PARALLEL LOOP directive are ignored [-Wopenacc-usage]
+  !$acc parallel loop independent private(i) independent independent
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+subroutine repeated_independent_kernels_loop(a)
+  real :: a(10)
+  integer :: i
+  !WARNING: Repeated INDEPENDENT clauses on the KERNELS LOOP directive are ignored [-Wopenacc-usage]
+  !$acc kernels loop independent private(i) independent independent
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine
+
+subroutine repeated_independent_serial_loop(a)
+  real :: a(10)
+  integer :: i
+  !WARNING: Repeated INDEPENDENT clauses on the SERIAL LOOP directive are ignored [-Wopenacc-usage]
+  !$acc serial loop independent private(i) independent independent
+  do i = 1, 10
+    a(i) = i
+  end do
+end subroutine

>From d1c4cc6b417198461dbaef1363275546371879eb Mon Sep 17 00:00:00 2001
From: Andre Kuhlenschmidt <akuhlenschmi at nvidia.com>
Date: Tue, 20 Oct 2026 11:38:02 -0700
Subject: [PATCH 2/2] [flang][OpenACC] Preserve loop recovery warnings in
 submodules

Emit loop mode recovery warnings for executable source even when an
ancestor module scope was loaded from a module file. Keep the existing
OpenACC warning controls and add a separate-compilation regression for
mixed and repeated modes on standalone and combined loops.
---
 flang/lib/Semantics/rewrite-parse-tree.cpp    | 10 +-
 .../OpenACC/acc-loop-modes-submodule.f90      | 96 +++++++++++++++++++
 2 files changed, 104 insertions(+), 2 deletions(-)
 create mode 100644 flang/test/Semantics/OpenACC/acc-loop-modes-submodule.f90

diff --git a/flang/lib/Semantics/rewrite-parse-tree.cpp b/flang/lib/Semantics/rewrite-parse-tree.cpp
index 03b2e98c84f6b0..b597f89a030cfe 100644
--- a/flang/lib/Semantics/rewrite-parse-tree.cpp
+++ b/flang/lib/Semantics/rewrite-parse-tree.cpp
@@ -554,8 +554,12 @@ void RewriteMutator::NormalizeAccLoopClauses(
       return;
     }
   }
+  // These executable directives belong to the source program, even when an
+  // ancestor scope was read from a module file for a separate submodule.
   if (seq && independent) {
-    context_.Warn(common::UsageWarning::OpenAccUsage, clauses.source,
+    context_.messages().Warn(/*isInModuleFile=*/false,
+        context_.languageFeatures(), common::UsageWarning::OpenAccUsage,
+        clauses.source,
         "SEQ and INDEPENDENT clauses on the %s directive are ignored because they are mutually exclusive"_warn_en_US,
         parser::ToUpperCaseLetters(
             llvm::acc::getOpenACCDirectiveName(directive).str()));
@@ -568,7 +572,9 @@ void RewriteMutator::NormalizeAccLoopClauses(
   if (seq < 2 && independent < 2) {
     return;
   }
-  context_.Warn(common::UsageWarning::OpenAccUsage, clauses.source,
+  context_.messages().Warn(/*isInModuleFile=*/false,
+      context_.languageFeatures(), common::UsageWarning::OpenAccUsage,
+      clauses.source,
       "Repeated %s clauses on the %s directive are ignored"_warn_en_US,
       seq ? "SEQ" : "INDEPENDENT",
       parser::ToUpperCaseLetters(
diff --git a/flang/test/Semantics/OpenACC/acc-loop-modes-submodule.f90 b/flang/test/Semantics/OpenACC/acc-loop-modes-submodule.f90
new file mode 100644
index 00000000000000..e4939911015064
--- /dev/null
+++ b/flang/test/Semantics/OpenACC/acc-loop-modes-submodule.f90
@@ -0,0 +1,96 @@
+! RUN: split-file %s %t
+! RUN: %flang_fc1 -fsyntax-only -J%t %t/parent.f90
+! RUN: %flang_fc1 -fopenacc -fsyntax-only -J%t %t/child.f90 2>&1 | FileCheck %s --implicit-check-not='warning:'
+! RUN: not %flang_fc1 -fopenacc -fsyntax-only -Werror -J%t %t/child.f90 2>&1 | FileCheck %s --implicit-check-not='warning:'
+! RUN: %flang_fc1 -fopenacc -fsyntax-only -Werror -w -J%t %t/child.f90 2>&1 | FileCheck %s --check-prefix=QUIET --allow-empty
+! RUN: %flang_fc1 -fopenacc -fsyntax-only -Werror -Wno-openacc-usage -J%t %t/child.f90 2>&1 | FileCheck %s --check-prefix=QUIET --allow-empty
+! RUN: not %flang_fc1 -fopenacc -fsyntax-only -Werror -Wno-openacc-usage -Wopenacc-usage -J%t %t/child.f90 2>&1 | FileCheck %s --implicit-check-not='warning:'
+
+! Recovery warnings refer to executable submodule source even when its parent
+! scope was imported from a .mod file. Check both warning sites on standalone
+! and combined loops, including warning severity and suppression.
+! QUIET-NOT: warning:
+! QUIET-NOT: error:
+
+!--- parent.f90
+module loop_modes_parent
+  interface
+    module subroutine work(a)
+      real :: a(8)
+    end subroutine
+  end interface
+end module
+
+!--- child.f90
+submodule(loop_modes_parent) loop_modes_child
+contains
+  module procedure work
+    integer :: i
+    !$acc kernels
+    !$acc loop seq seq
+    do i = 1, 8
+      a(i) = i
+    end do
+    !$acc loop independent independent
+    do i = 1, 8
+      a(i) = i
+    end do
+    !$acc loop seq independent
+    do i = 1, 8
+      a(i) = i
+    end do
+    !$acc end kernels
+
+    !$acc parallel loop seq seq
+    do i = 1, 8
+      a(i) = i
+    end do
+    !$acc parallel loop independent independent
+    do i = 1, 8
+      a(i) = i
+    end do
+    !$acc parallel loop seq independent
+    do i = 1, 8
+      a(i) = i
+    end do
+
+    !$acc kernels loop seq seq
+    do i = 1, 8
+      a(i) = i
+    end do
+    !$acc kernels loop independent independent
+    do i = 1, 8
+      a(i) = i
+    end do
+    !$acc kernels loop seq independent
+    do i = 1, 8
+      a(i) = i
+    end do
+
+    !$acc serial loop seq seq
+    do i = 1, 8
+      a(i) = i
+    end do
+    !$acc serial loop independent independent
+    do i = 1, 8
+      a(i) = i
+    end do
+    !$acc serial loop seq independent
+    do i = 1, 8
+      a(i) = i
+    end do
+  end procedure
+end submodule
+
+! CHECK: warning: Repeated SEQ clauses on the LOOP directive are ignored [-Wopenacc-usage]
+! CHECK: warning: Repeated INDEPENDENT clauses on the LOOP directive are ignored [-Wopenacc-usage]
+! CHECK: warning: SEQ and INDEPENDENT clauses on the LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+! CHECK: warning: Repeated SEQ clauses on the PARALLEL LOOP directive are ignored [-Wopenacc-usage]
+! CHECK: warning: Repeated INDEPENDENT clauses on the PARALLEL LOOP directive are ignored [-Wopenacc-usage]
+! CHECK: warning: SEQ and INDEPENDENT clauses on the PARALLEL LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+! CHECK: warning: Repeated SEQ clauses on the KERNELS LOOP directive are ignored [-Wopenacc-usage]
+! CHECK: warning: Repeated INDEPENDENT clauses on the KERNELS LOOP directive are ignored [-Wopenacc-usage]
+! CHECK: warning: SEQ and INDEPENDENT clauses on the KERNELS LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]
+! CHECK: warning: Repeated SEQ clauses on the SERIAL LOOP directive are ignored [-Wopenacc-usage]
+! CHECK: warning: Repeated INDEPENDENT clauses on the SERIAL LOOP directive are ignored [-Wopenacc-usage]
+! CHECK: warning: SEQ and INDEPENDENT clauses on the SERIAL LOOP directive are ignored because they are mutually exclusive [-Wopenacc-usage]



More information about the flang-commits mailing list