[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
Sat Jul 4 00:21:47 PDT 2026
https://github.com/MattPD created https://github.com/llvm/llvm-project/pull/207492
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."), 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 use-only emission for a use-associated reduction symbol. The reduction is re-exported implicitly with its operator, which is emitted, and the reduction resolver recovers it from there (FindUserReductionSymbol / SearchOperatorReduction).
The new 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.
>From e85f47e8c105898f87e03cfd68568702a6d96d66 Mon Sep 17 00:00:00 2001
From: "Matt P. Dziubinski" <matt-p.dziubinski at hpe.com>
Date: Thu, 2 Jul 2026 22:29:33 -0500
Subject: [PATCH] [flang][OpenMP] Do not emit a use-only item for a reduction
symbol 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."), 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 use-only emission for a use-associated reduction symbol. The reduction is
re-exported implicitly with its operator, which is emitted, and the reduction
resolver recovers it from there (FindUserReductionSymbol / SearchOperatorReduction).
The new 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 | 15 ++++
flang/lib/Semantics/resolve-names-utils.cpp | 5 ++
flang/lib/Semantics/resolve-names-utils.h | 11 +++
...re-reduction-operator-modfile-reexport.f90 | 86 +++++++++++++++++++
4 files changed, 117 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..b1d47f44a06cc 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -890,6 +890,21 @@ void ModFileWriter::PutGeneric(const Symbol &symbol) {
}
void ModFileWriter::PutUse(const Symbol &symbol) {
+ // A user-defined operator declare reduction has an internal mangled
+ // symbol name ("op.<operator>.") that is not valid Fortran and cannot be
+ // emitted as a "use,only:" item: doing so produces a module file that fails
+ // to re-parse (both for a plain re-export and for an embedded module in a
+ // hermetic module file). Such a reduction is re-exported implicitly with its
+ // operator, which is emitted, and the reduction resolver recovers it from
+ // there (FindUserReductionSymbol / SearchOperatorReduction), so skip its use
+ // item. A reduction named by a plain identifier ("myred") has a valid name
+ // and is emitted normally; an intrinsic-operator or special-intrinsic
+ // reduction has no re-exported operator to recover through and is left to the
+ // normal path.
+ if (symbol.GetUltimate().has<UserReductionDetails>() &&
+ IsMangledDefinedOperatorReductionName(symbol.GetUltimate().name())) {
+ return;
+ }
auto &details{symbol.get<UseDetails>()};
auto &use{details.symbol()};
const Symbol &module{GetUsedModule(details)};
diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index 03d5df73a9d79..93a53515dabf9 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -987,4 +987,9 @@ std::string GetReductionFortranId(const parser::CharBlock &mangledName) {
return suffix.str();
}
+bool IsMangledDefinedOperatorReductionName(const parser::CharBlock &name) {
+ llvm::StringRef ref{name.begin(), name.size()};
+ return ref.starts_with("op.") && ref.ends_with(".");
+}
+
} // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/resolve-names-utils.h b/flang/lib/Semantics/resolve-names-utils.h
index a1bac72e45ca1..e31056da7b855 100644
--- a/flang/lib/Semantics/resolve-names-utils.h
+++ b/flang/lib/Semantics/resolve-names-utils.h
@@ -152,6 +152,17 @@ parser::CharBlock MakeNameFromOperator(
parser::CharBlock MangleSpecialFunctions(const parser::CharBlock &name);
std::string MangleDefinedOperator(const parser::CharBlock &name);
+// True iff `name` is the mangled name of a user-defined-operator declare
+// reduction (as produced by MangleDefinedOperator, e.g. "op.remote."): it
+// starts with "op." and ends with ".". This is the only reduction kind whose
+// symbol name is not valid Fortran yet is recoverable through its re-exported
+// operator interface, so the module-file writer omits its (unwritable) use-only
+// item and relies on the operator to carry it. Intrinsic-operator ("op.+") and
+// special-intrinsic ("op.max") reductions do not match (no trailing dot); plain
+// named reductions ("myred") do not match (no "op." prefix, since a valid
+// identifier cannot contain '.').
+bool IsMangledDefinedOperatorReductionName(const parser::CharBlock &name);
+
// Map a mangled declare reduction name (e.g., "op.+", "op.combine.",
// "op.max") back to the Fortran identifier used as the scope key for the
// corresponding operator or procedure (e.g., "operator(+)", ".combine.",
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..dd42760781464
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/declare-reduction-operator-modfile-reexport.f90
@@ -0,0 +1,86 @@
+! 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.") 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. 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 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 operator is re-exported; the mangled reduction symbol is not.
+! OPMOD: use rx_base,only:operator(.remote.)
+! OPMOD-NOT: only:op.remote.
+! 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
More information about the flang-commits
mailing list