[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