[flang-commits] [flang] [flang][OpenMP] Fix USE-associated declare reduction symbol resolution (PR #200328)

via flang-commits flang-commits at lists.llvm.org
Thu May 28 22:07:20 PDT 2026


https://github.com/MattPD created https://github.com/llvm/llvm-project/pull/200328

When a declare reduction is accessed via USE association, the symbol in
the consuming scope has `UseDetails` rather than `UserReductionDetails`.
Calling `detailsIf<UserReductionDetails>()` directly on such a symbol
returns nullptr. This causes two distinct failure modes:

1. **Operator/identifier validation** (`CheckReductionOperator`): named
   reductions and defined operators are rejected with "Invalid reduction
   identifier" or "Invalid reduction operator".

2. **Type validation** (`CheckSymbolSupportsType`, `IsReductionAllowedForType`):
   the type compatibility check cannot find `UserReductionDetails` for the
   reduction, producing "The type of 'x' is incompatible with the
   reduction operator".

This bug has existed since `UserReductionDetails` was introduced (June
2025). Only intrinsic operator reductions (like `+`) worked via USE,
because they bypass the operator validation check and their type checking
was handled by a global module scan workaround (added Feb 2026).

**Fix:** Add `GetUltimate()` at 4 locations in `check-omp-structure.cpp` to resolve
through `UseDetails` chains before checking for `UserReductionDetails`:
two in `CheckReductionOperator` (validation paths 1 and 2 above), one in
`CheckSymbolSupportsType`, and one in `IsReductionAllowedForType`.

Fixes https://github.com/llvm/llvm-project/issues/184932

>From bea2a8a66330c344cbd04f3b079ed3738e3e2b22 Mon Sep 17 00:00:00 2001
From: "Matt P. Dziubinski" <matt-p.dziubinski at hpe.com>
Date: Thu, 28 May 2026 23:09:23 -0500
Subject: [PATCH] [flang][OpenMP] Fix USE-associated declare reduction symbol
 resolution

When a declare reduction is accessed via USE association, the symbol in
the consuming scope has UseDetails rather than UserReductionDetails.
Calling detailsIf<UserReductionDetails>() directly on such a symbol
returns nullptr. This causes two distinct failure modes:

1. Operator/identifier validation (CheckReductionOperator): named
   reductions and defined operators are rejected with "Invalid reduction
   identifier" or "Invalid reduction operator".

2. Type validation (CheckSymbolSupportsType, IsReductionAllowedForType):
   the type compatibility check cannot find UserReductionDetails for the
   reduction, producing "The type of 'x' is incompatible with the
   reduction operator".

This bug has existed since UserReductionDetails was introduced (June
2025). Only intrinsic operator reductions (like +) worked via USE,
because they bypass the operator validation check and their type checking
was handled by a global module scan workaround (added Feb 2026).

Add GetUltimate() at 4 locations in check-omp-structure.cpp to resolve
through UseDetails chains before checking for UserReductionDetails:
two in CheckReductionOperator (validation paths 1a and 1b above), one in
CheckSymbolSupportsType, and one in IsReductionAllowedForType.

Fixes https://github.com/llvm/llvm-project/issues/184932

Assisted-by: Claude Opus 4.6.
---
 flang/lib/Semantics/check-omp-structure.cpp   |  9 +-
 .../declare-reduction-use-assoc-named.f90     | 82 +++++++++++++++++++
 2 files changed, 87 insertions(+), 4 deletions(-)
 create mode 100644 flang/test/Semantics/OpenMP/declare-reduction-use-assoc-named.f90

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index ff41f49d88b32..c450c3fbfeb43 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -3847,7 +3847,7 @@ bool OmpStructureChecker::CheckReductionOperator(
       std::string mangled{MangleDefinedOperator(definedOp->v.symbol->name())};
       const Scope &scope{definedOp->v.symbol->owner()};
       if (const Symbol *symbol{scope.FindSymbol(mangled)}) {
-        if (symbol->detailsIf<UserReductionDetails>()) {
+        if (symbol->GetUltimate().detailsIf<UserReductionDetails>()) {
           return true;
         }
       }
@@ -3865,7 +3865,7 @@ bool OmpStructureChecker::CheckReductionOperator(
       valid =
           llvm::is_contained({"max", "min", "iand", "ior", "ieor"}, realName);
       if (!valid) {
-        valid = name->symbol->detailsIf<UserReductionDetails>();
+        valid = name->symbol->GetUltimate().detailsIf<UserReductionDetails>();
       }
     }
     if (!valid) {
@@ -3948,8 +3948,9 @@ void OmpStructureChecker::CheckReductionObjects(
 static bool CheckSymbolSupportsType(const Scope &scope,
     const parser::CharBlock &name, const DeclTypeSpec &type) {
   if (const auto *symbol{scope.FindSymbol(name)}) {
+    const auto &ultimate{symbol->GetUltimate()};
     if (const auto *reductionDetails{
-            symbol->detailsIf<UserReductionDetails>()}) {
+            ultimate.detailsIf<UserReductionDetails>()}) {
       return reductionDetails->SupportsType(type);
     }
   }
@@ -4063,7 +4064,7 @@ static bool IsReductionAllowedForType(
       // if the symbol has UserReductionDetails, and if so, the type is
       // supported.
       if (const auto *reductionDetails{
-              name->symbol->detailsIf<UserReductionDetails>()}) {
+              name->symbol->GetUltimate().detailsIf<UserReductionDetails>()}) {
         return reductionDetails->SupportsType(type);
       }
 
diff --git a/flang/test/Semantics/OpenMP/declare-reduction-use-assoc-named.f90 b/flang/test/Semantics/OpenMP/declare-reduction-use-assoc-named.f90
new file mode 100644
index 0000000000000..2b7b3864bdef2
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/declare-reduction-use-assoc-named.f90
@@ -0,0 +1,82 @@
+! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -fsyntax-only %s
+! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -fdebug-dump-symbols %s 2>&1 | FileCheck %s
+
+! Test that USE-associated named reductions and user-defined operator
+! reductions are correctly resolved through UseDetails.
+
+module m_named_reduction
+  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
+
+module m_defined_op_reduction
+  type :: dt
+    real :: x = 0.0
+  end type
+  interface operator(.combine.)
+    module procedure combine_fn
+  end interface
+  !$omp declare reduction(.combine.:dt:omp_out%x=omp_out%x+omp_in%x) &
+  !$omp   initializer(omp_priv=dt(0.0))
+contains
+  type(dt) function combine_fn(a, b)
+    type(dt), intent(in) :: a, b
+    combine_fn%x = a%x + b%x
+  end function
+end module
+
+program test_use_assoc_reductions
+  use m_named_reduction
+  use m_defined_op_reduction
+  type(t) :: x
+  type(dt) :: y
+  integer :: i
+  x = t(0)
+  y = dt(0.0)
+  ! Both should compile without error: reductions are accessible via USE.
+  !$omp parallel do reduction(myred:x)
+  do i = 1, 10
+    x%val = x%val + 1
+  end do
+  !$omp end parallel do
+  !$omp parallel do reduction(.combine.:y)
+  do i = 1, 10
+    y%x = y%x + 1.0
+  end do
+  !$omp end parallel do
+  print *, x%val, y%x
+end program
+
+! Test defined operator with external interface via USE (issue #184932 pattern).
+! Uses !$omp parallel (not parallel do) to cover that variant.
+module m_external_op
+  type :: ty
+    integer :: ii
+  end type
+  interface operator(.x.)
+    function h(a, b)
+      import :: ty
+      type(ty), intent(in) :: a, b
+    end function
+  end interface
+  !$omp declare reduction(.x.:ty:omp_out=ty(1)) initializer(omp_priv=ty(0))
+end module
+
+subroutine test_external_op_reduction
+  use m_external_op
+  type(ty) :: v
+  v = ty(0)
+  !$omp parallel reduction(.x.:v)
+  v = ty(1)
+  !$omp end parallel
+end subroutine
+
+!CHECK: Module scope: m_named_reduction
+!CHECK: myred, PUBLIC: UserReductionDetails TYPE(t)
+!CHECK: Module scope: m_defined_op_reduction
+!CHECK: op.combine., PUBLIC: UserReductionDetails TYPE(dt)
+!CHECK: Module scope: m_external_op
+!CHECK: op.x., PUBLIC: UserReductionDetails TYPE(ty)



More information about the flang-commits mailing list