[flang-commits] [flang] [flang][OpenMP] Do not emit a use-only item for a reduction symbol in a module file (PR #207492)

via flang-commits flang-commits at lists.llvm.org
Mon Jul 6 10:49:05 PDT 2026


https://github.com/MattPD updated https://github.com/llvm/llvm-project/pull/207492

>From 08fea82f987108faee96289cb26920876067f59c Mon Sep 17 00:00:00 2001
From: "Matt P. Dziubinski" <matt-p.dziubinski at hpe.com>
Date: Mon, 6 Jul 2026 12:14:35 -0500
Subject: [PATCH] [flang][OpenMP] Do not emit a use-only item for an operator
 reduction in a module file

Stop writing a module file that cannot be re-parsed (reading it crashes) when a
module re-exports a USE-associated user-defined operator declare reduction.

Such a reduction is represented by an internal symbol whose name is the mangled
operator (e.g. "op.remote." or "op.+"), which is not valid Fortran. The
module-file writer emitted it as a `use MODULE, only: op.remote.` item, which the
reader cannot parse. This affected both a plain re-export facade and an embedded
module in a hermetic module file.

Skip the use-only item for a reduction whose operator is itself re-exported (a
defined operator, or an intrinsic operator with a user interface): the reduction
is re-exported implicitly with that operator, which is emitted, and the reduction
resolver recovers it from there (FindUserReductionSymbol). This covers a
derived-type `operator(+)` reduction as well as a defined operator like
`.remote.`.

The test checks the re-exporting module file round-trips and that a consumer
reading it resolves the reduction through the operator.

Assisted-by: Claude Opus 4.8, GPT-5.5.
---
 flang/lib/Semantics/mod-file.cpp              |  22 +++
 ...re-reduction-operator-modfile-reexport.f90 | 150 ++++++++++++++++++
 2 files changed, 172 insertions(+)
 create mode 100644 flang/test/Semantics/OpenMP/declare-reduction-operator-modfile-reexport.f90

diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index fd1b1caa7fce1..d66d2b79bc7c5 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -890,6 +890,28 @@ void ModFileWriter::PutGeneric(const Symbol &symbol) {
 }
 
 void ModFileWriter::PutUse(const Symbol &symbol) {
+  // A declare reduction named by an operator has an internal mangled symbol name
+  // ("op.remote.", "op.+") that is not valid Fortran and cannot be emitted as a
+  // "use,only:" item (that module file could not be re-parsed, and reading it
+  // crashed, both for a plain re-export and for an embedded module in a hermetic
+  // module file). Its operator is itself re-exported as a valid item, and the
+  // resolver recovers the reduction through it (FindUserReductionSymbol /
+  // SearchOperatorReduction), keeping one shared reduction symbol, so skip the
+  // reduction's own use item. This covers a defined operator (which always has a
+  // mandatory "interface operator(.x.)") and an intrinsic operator with a user
+  // "interface operator(+)". A reduction named by a plain identifier ("myred")
+  // has a valid name and is emitted normally below. An operator-less reduction
+  // (a special function, or an intrinsic operator on an intrinsic type) has no
+  // operator to recover through and is left to the normal path.
+  const Symbol &ultimate{symbol.GetUltimate()};
+  if (ultimate.has<UserReductionDetails>()) {
+    std::string opId{GetReductionFortranId(ultimate.name())};
+    const Symbol *opSym{
+        opId.empty() ? nullptr : ultimate.owner().FindSymbol(opId)};
+    if (opSym && opSym->GetUltimate().has<GenericDetails>()) {
+      return;
+    }
+  }
   auto &details{symbol.get<UseDetails>()};
   auto &use{details.symbol()};
   const Symbol &module{GetUsedModule(details)};
diff --git a/flang/test/Semantics/OpenMP/declare-reduction-operator-modfile-reexport.f90 b/flang/test/Semantics/OpenMP/declare-reduction-operator-modfile-reexport.f90
new file mode 100644
index 0000000000000..35b1756b2a3ef
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/declare-reduction-operator-modfile-reexport.f90
@@ -0,0 +1,150 @@
+! RUN: rm -rf %t && split-file %s %t && cd %t
+! A module that uses another module's user-defined declare reduction re-exports
+! it. A reduction named by an operator has an internal mangled symbol name
+! ("op.remote.", "op.+") that is not valid Fortran, so it must not be written as
+! a "use,only:" item (that module file could not be re-parsed and reading it
+! crashed); it is recovered through the re-exported operator instead. This holds
+! for both a defined operator (".remote.") and an intrinsic operator ("+") with a
+! user "interface operator(+)", including when a consumer reaches the reduction
+! through both the base and the facade. A reduction named by a plain identifier
+! ("myred") has a valid name and is re-exported as a normal "use,only:" item.
+
+! RUN: %flang_fc1 -fsyntax-only -fopenmp rx_base.f90
+! RUN: %flang_fc1 -fsyntax-only -fopenmp rx_wrap.f90
+! RUN: FileCheck --check-prefix=OPMOD --input-file=rx_wrap.mod %s
+! RUN: %flang_fc1 -fsyntax-only -fopenmp rx_use.f90
+
+! RUN: %flang_fc1 -fsyntax-only -fopenmp io_base.f90
+! RUN: %flang_fc1 -fsyntax-only -fopenmp io_wrap.f90
+! RUN: FileCheck --check-prefix=IOPMOD --input-file=io_wrap.mod %s
+! RUN: %flang_fc1 -fsyntax-only -fopenmp io_use.f90
+! RUN: %flang_fc1 -fsyntax-only -fopenmp io_dual.f90
+
+! RUN: %flang_fc1 -fsyntax-only -fopenmp nm_base.f90
+! RUN: %flang_fc1 -fsyntax-only -fopenmp nm_wrap.f90
+! RUN: FileCheck --check-prefix=NMMOD --input-file=nm_wrap.mod %s
+! RUN: %flang_fc1 -fsyntax-only -fopenmp nm_use.f90
+
+! The defined operator is re-exported; the mangled reduction symbol is not.
+! OPMOD: use rx_base,only:operator(.remote.)
+! OPMOD-NOT: only:op.remote.
+! The intrinsic operator is re-exported; the mangled reduction symbol is not, and
+! the directive is not re-emitted (no facade-owned duplicate reduction).
+! IOPMOD: use io_base,only:operator(+)
+! IOPMOD-NOT: only:op.+
+! IOPMOD-NOT: DECLARE REDUCTION
+! The plainly-named reduction is re-exported as a normal use item.
+! NMMOD: use nm_base,only:myred
+
+!--- rx_base.f90
+module rx_base
+  type :: t
+    integer :: val = 0
+  end type
+  interface operator(.remote.)
+    module procedure add_t
+  end interface
+  !$omp declare reduction(.remote.:t:omp_out%val=omp_out%val+omp_in%val) &
+  !$omp   initializer(omp_priv=t(0))
+contains
+  type(t) function add_t(a, b)
+    type(t), intent(in) :: a, b
+    add_t%val = a%val + b%val
+  end function
+end module
+
+!--- rx_wrap.f90
+module rx_wrap
+  use rx_base
+end module
+
+!--- rx_use.f90
+program main
+  use rx_wrap, only: t, operator(.local.) => operator(.remote.)
+  type(t) :: x
+  integer :: i
+  x = t(0)
+  !$omp parallel do reduction(.local.:x)
+  do i = 1, 100
+    x%val = x%val + 1
+  end do
+  print *, x%val
+end program
+
+!--- nm_base.f90
+module nm_base
+  type :: t
+    integer :: val = 0
+  end type
+  !$omp declare reduction(myred:t:omp_out%val=omp_out%val+omp_in%val) &
+  !$omp   initializer(omp_priv=t(0))
+end module
+
+!--- nm_wrap.f90
+module nm_wrap
+  use nm_base
+end module
+
+!--- nm_use.f90
+program main
+  use nm_wrap
+  type(t) :: x
+  integer :: i
+  x = t(0)
+  !$omp parallel do reduction(myred:x)
+  do i = 1, 100
+    x%val = x%val + 1
+  end do
+  print *, x%val
+end program
+
+!--- io_base.f90
+module io_base
+  type :: t
+    integer :: val = 0
+  end type
+  interface operator(+)
+    module procedure add_t
+  end interface
+  !$omp declare reduction(+:t:omp_out=omp_out+omp_in) &
+  !$omp   initializer(omp_priv=t(0))
+contains
+  type(t) function add_t(a, b)
+    type(t), intent(in) :: a, b
+    add_t%val = a%val + b%val
+  end function
+end module
+
+!--- io_wrap.f90
+module io_wrap
+  use io_base
+end module
+
+!--- io_use.f90
+program main
+  use io_wrap
+  type(t) :: x
+  integer :: i
+  x = t(0)
+  !$omp parallel do reduction(+:x)
+  do i = 1, 100
+    x%val = x%val + 1
+  end do
+  print *, x%val
+end program
+
+!--- io_dual.f90
+! Reaching the reduction through both the base and the facade must bind one
+! reduction, not a facade-owned duplicate.
+program main
+  use io_base
+  use io_wrap
+  type(t) :: x
+  integer :: i
+  x = t(0)
+  !$omp parallel do reduction(+:x)
+  do i = 1, 100
+    x%val = x%val + 1
+  end do
+  print *, x%val
+end program



More information about the flang-commits mailing list