[flang-commits] [flang] [flang][OpenMP] Diagnose task reduction array sections and elements (PR #215997)

via flang-commits flang-commits at lists.llvm.org
Tue Aug 25 12:18:42 PDT 2026


================
@@ -183,6 +180,137 @@ static bool hasPrivatizedArrayElementReduction(
   return false;
 }
 
+static bool
+hasPartialArrayReductionObject(llvm::ArrayRef<Object> reductionObjects,
+                               semantics::SemanticsContext &semaCtx) {
+  for (const Object &object : reductionObjects) {
+    if (!object.ref() || isWholeArraySection(object, semaCtx))
+      continue;
+    if (evaluate::IsArraySection(*object.ref()))
+      return true;
+  }
+  return false;
+}
+
+static void checkTaskModifierPartialArrayReduction(
+    mlir::Location loc, semantics::SemanticsContext &semaCtx,
+    mlir::omp::ReductionModifierAttr reductionMod,
+    llvm::ArrayRef<Object> reductionObjects) {
+  if (reductionMod &&
+      reductionMod.getValue() == mlir::omp::ReductionModifier::task &&
+      hasPartialArrayReductionObject(reductionObjects, semaCtx))
+    TODO(loc, "REDUCTION with TASK modifier of a partial array section");
+}
+
+static bool isArrayElementReductionObject(const Object &object) {
+  return object.ref() && object.ref()->Rank() == 0 &&
+         evaluate::IsArrayElement(*object.ref(), /*intoSubstring=*/false);
+}
+
+static bool
+hasArrayElementReductionObject(llvm::ArrayRef<Object> reductionObjects) {
+  return llvm::any_of(reductionObjects, isArrayElementReductionObject);
+}
+
+static bool isUserDefinedReductionOperator(
+    const clause::ReductionOperator &reductionOperator, const Object &object,
+    lower::AbstractConverter &converter, semantics::SemanticsContext &semaCtx) {
+  const semantics::Symbol *objectSymbol = object.sym();
+  const semantics::DeclTypeSpec *objectType =
+      objectSymbol ? objectSymbol->GetUltimate().GetType() : nullptr;
+  if (!objectType)
+    return false;
+
+  return common::visit(
+      common::visitors{
+          [&](const clause::DefinedOperator &definedOperator) {
+            return common::visit(
+                common::visitors{
+                    [&](const clause::DefinedOperator::IntrinsicOperator &op) {
+                      using IntrinsicOperator =
+                          clause::DefinedOperator::IntrinsicOperator;
+                      switch (op) {
+                      case IntrinsicOperator::Add:
+                      case IntrinsicOperator::Multiply:
+                      case IntrinsicOperator::AND:
+                      case IntrinsicOperator::OR:
+                      case IntrinsicOperator::EQV:
+                      case IntrinsicOperator::NEQV:
+                        break;
+                      default:
+                        return false;
+                      }
+
+                      parser::CharBlock mangledName =
+                          semantics::omp::MangledIntrinsicOperatorReductionName(
+                              ReductionProcessor::toParserIntrinsicOperator(op),
+                              semaCtx);
+                      return semantics::omp::FindUserReductionSymbol(
+                                 converter.getCurrentScope(), mangledName,
+                                 objectType) != nullptr;
+                    },
+                    [&](const clause::DefinedOperator::DefinedOpName &op) {
+                      const semantics::Symbol *operatorSymbol = op.v.sym();
+                      return operatorSymbol &&
+                             semantics::omp::FindOperatorUserReductionSymbol(
+                                 converter.getCurrentScope(), *operatorSymbol,
+                                 objectType);
+                    },
+                },
+                definedOperator.u);
+          },
+          [&](const clause::ProcedureDesignator &procedureDesignator) {
+            const semantics::Symbol *symbol = procedureDesignator.v.sym();
+            return (symbol &&
+                    symbol->GetUltimate()
+                        .detailsIf<semantics::UserReductionDetails>()) ||
+                   ReductionProcessor::findUserDefinedReductionForIntrinsic(
+                       converter.getCurrentScope(), procedureDesignator,
+                       objectType) != nullptr;
+          },
+      },
+      reductionOperator.u);
+}
+
+template <typename ReductionClause>
+static bool
+hasUserDefinedArrayElementReduction(const List<Clause> &clauses,
+                                    lower::AbstractConverter &converter,
+                                    semantics::SemanticsContext &semaCtx) {
+  bool found = false;
+  ClauseFinder::findRepeatableClause<ReductionClause>(
+      clauses,
+      [&](const ReductionClause &reductionClause, const parser::CharBlock &) {
+        if (found)
+          return;
+
+        const auto &reductionOperators =
+            std::get<clause::ReductionOperatorList>(reductionClause.t);
+        assert(reductionOperators.size() == 1 &&
+               "expected one reduction operator");
+        const ObjectList &objects = std::get<ObjectList>(reductionClause.t);
+        found = llvm::any_of(objects, [&](const Object &object) {
+          return isArrayElementReductionObject(object) &&
+                 isUserDefinedReductionOperator(reductionOperators.front(),
+                                                object, converter, semaCtx);
+        });
+      });
+  return found;
+}
+
+static void checkTaskModifierUserDefinedArrayElementReduction(
+    mlir::Location loc, lower::AbstractConverter &converter,
+    semantics::SemanticsContext &semaCtx,
+    mlir::omp::ReductionModifierAttr reductionMod,
+    const List<Clause> &clauses) {
+  if (reductionMod &&
+      reductionMod.getValue() == mlir::omp::ReductionModifier::task &&
----------------
MattPD wrote:

Without the `task` modifier, a user-defined reduction on an array element covers the whole array:

```fortran
subroutine element(a)
  integer :: a(4)
  !$omp declare reduction(myred : integer : omp_out = omp_out + omp_in) &
  !$omp& initializer(omp_priv = 1)
  !$omp parallel reduction(myred : a(2))
  a(2) = a(2) + 1
  !$omp end parallel
end
```

This compiles, and the generated reduction initializes and combines all four elements, although the clause names only `a(2)`. The `task`, `taskgroup`, and `taskloop` constructs already diagnose a user-defined reduction on an array element when the clause carries no modifier.

Could `parallel`, `do`, `sections`, and `scope` apply the same check without requiring the `task` modifier?

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


More information about the flang-commits mailing list