[flang-commits] [flang] [flang][OpenMP] Remove over-broad global scan for declare reduction symbols (PR #200329)

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


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

`CheckSymbolSupportsType` walked all module scopes in the global scope to
find reduction declarations. This incorrectly matched reductions from
modules that were never `USE`'d by the current scope, or reductions that
were excluded via `USE...ONLY`.

Now that #200328 added `GetUltimate()` resolution, `FindSymbol` correctly
resolves USE-associated reductions through their `UseDetails` chains. The
global scan workaround is no longer needed.

Depends on #200328.

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

Assisted-by: Claude Opus 4.6.

>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 1/2] [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)

>From 69d6a28bb3f8f33bd302e60f23d4ae404d0668dc Mon Sep 17 00:00:00 2001
From: "Matt P. Dziubinski" <matt-p.dziubinski at hpe.com>
Date: Thu, 28 May 2026 23:09:59 -0500
Subject: [PATCH 2/2] [flang][OpenMP] Remove over-broad global scan for declare
 reduction symbols

CheckSymbolSupportsType walked all module scopes in the global scope to
find reduction declarations. This incorrectly matched reductions from
modules that were never USE'd by the current scope, or reductions that
were excluded via USE...ONLY.

Now that the previous commit added GetUltimate() resolution, FindSymbol
correctly resolves USE-associated reductions through their UseDetails
chains. The global scan workaround is no longer needed.

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

Assisted-by: Claude Opus 4.6.
---
 flang/lib/Semantics/check-omp-structure.cpp   | 18 -----------
 .../declare-reduction-overbroad-lookup.f90    | 32 +++++++++++++++++++
 2 files changed, 32 insertions(+), 18 deletions(-)
 create mode 100644 flang/test/Semantics/OpenMP/declare-reduction-overbroad-lookup.f90

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index c450c3fbfeb43..96c4e84bd8963 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -3954,24 +3954,6 @@ static bool CheckSymbolSupportsType(const Scope &scope,
       return reductionDetails->SupportsType(type);
     }
   }
-  // Look through module scopes in the global scope.
-  // This covers reductions declared in a module and used via USE association.
-  const SemanticsContext &semCtx{scope.context()};
-  Scope &global = const_cast<SemanticsContext &>(semCtx).globalScope();
-  for (const Scope &child : global.children()) {
-    if (child.kind() == Scope::Kind::Module) {
-      if (const auto *symbol{child.FindSymbol(name)}) {
-        // Skip PRIVATE reductions that aren't visible in the current scope.
-        if (symbol->attrs().test(Attr::PRIVATE)) {
-          continue;
-        }
-        if (const auto *reductionDetails{
-                symbol->detailsIf<UserReductionDetails>()}) {
-          return reductionDetails->SupportsType(type);
-        }
-      }
-    }
-  }
   return false;
 }
 
diff --git a/flang/test/Semantics/OpenMP/declare-reduction-overbroad-lookup.f90 b/flang/test/Semantics/OpenMP/declare-reduction-overbroad-lookup.f90
new file mode 100644
index 0000000000000..1db247b4eaff5
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/declare-reduction-overbroad-lookup.f90
@@ -0,0 +1,32 @@
+! RUN: not %flang_fc1 -fopenmp -fopenmp-version=52 %s 2>&1 | FileCheck %s
+
+! Test that a declare reduction from a module that was not USE'd (or only
+! partially USE'd) is not incorrectly found during type checking.
+! Related: https://github.com/llvm/llvm-project/issues/200300
+
+module m_with_reduction
+  type :: t
+    integer :: val = 0
+  end type
+  !$omp declare reduction(+:t:omp_out%val=omp_out%val+omp_in%val) &
+  !$omp   initializer(omp_priv=t(0))
+end module
+
+! proxy re-exports only the type, not the reduction
+module m_proxy
+  use m_with_reduction, only: t
+end module
+
+program test_overbroad_lookup
+  use m_proxy
+  type(t) :: x
+  integer :: i
+  x = t(0)
+  !CHECK: error: The type of 'x' is incompatible with the reduction operator.
+  !$omp parallel do reduction(+:x)
+  do i = 1, 10
+    x%val = x%val + 1
+  end do
+  !$omp end parallel do
+  print *, x%val
+end program



More information about the flang-commits mailing list