[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:22:26 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-openmp

Author: Matt (MattPD)

<details>
<summary>Changes</summary>

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.


---
Full diff: https://github.com/llvm/llvm-project/pull/207492.diff


4 Files Affected:

- (modified) flang/lib/Semantics/mod-file.cpp (+15) 
- (modified) flang/lib/Semantics/resolve-names-utils.cpp (+5) 
- (modified) flang/lib/Semantics/resolve-names-utils.h (+11) 
- (added) flang/test/Semantics/OpenMP/declare-reduction-operator-modfile-reexport.f90 (+86) 


``````````diff
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

``````````

</details>


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


More information about the flang-commits mailing list