[flang-commits] [flang] [flang][OpenMP] Improve diagnosing invalid operators in DECLARE_REDUCTION (PR #232353)

Krzysztof Parzyszek via flang-commits flang-commits at lists.llvm.org
Sat Oct 24 04:41:37 PDT 2026


https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/232353

>From b9566443aed3a452aa253593eb28f1b2e0620862 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 15 Oct 2026 08:14:08 -0500
Subject: [PATCH 1/9] [flang][OpenMP] Improve diagnosing invalid operators in
 DECLARE_REDUCTION

Move the check out of the common MakeNameFromOperator function and
into OmpStructureChecker. Remove the no longer needed SemanticsContext
parameter in MakeNameFromOperator.

This will allow reusing at least some parts of the the name-mangling
code in DECLARE_INDUCTION.
---
 flang/include/flang/Semantics/tools.h         |  3 +-
 flang/lib/Lower/OpenMP/OpenMP.cpp             |  3 +-
 .../lib/Lower/Support/ReductionProcessor.cpp  |  2 +-
 flang/lib/Semantics/check-omp-structure.cpp   | 70 ++++++++++++++-----
 flang/lib/Semantics/check-omp-structure.h     |  6 +-
 flang/lib/Semantics/resolve-names.cpp         |  3 +-
 flang/lib/Semantics/tools.cpp                 |  8 +--
 .../OpenMP/declare-reduction-bad-operator.f90 |  4 +-
 flang/test/Semantics/OpenMP/in-reduction.f90  |  2 +-
 .../Semantics/OpenMP/reduction-subtract.f90   |  5 +-
 flang/test/Semantics/OpenMP/reduction01.f90   |  2 +-
 flang/test/Semantics/OpenMP/reduction14.f90   |  3 +-
 .../test/Semantics/OpenMP/task-reduction.f90  |  2 +-
 13 files changed, 72 insertions(+), 41 deletions(-)

diff --git a/flang/include/flang/Semantics/tools.h b/flang/include/flang/Semantics/tools.h
index bd2cb0780de9bd..eb8bc762aa1a06 100644
--- a/flang/include/flang/Semantics/tools.h
+++ b/flang/include/flang/Semantics/tools.h
@@ -791,8 +791,7 @@ bool HadUseError(SemanticsContext &, SourceName at, const Symbol *);
 bool AreSameModuleSymbol(const Symbol &, const Symbol &);
 
 parser::CharBlock MakeNameFromOperator(
-    const parser::DefinedOperator::IntrinsicOperator &op,
-    SemanticsContext &context);
+    const parser::DefinedOperator::IntrinsicOperator &op);
 parser::CharBlock MangleSpecialFunctions(parser::CharBlock name);
 std::string MangleDefinedOperator(parser::CharBlock name);
 
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 0c0ad4f158d908..1eadb4c4cf891f 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -6744,8 +6744,7 @@ static void genOpenMPDeclareReductionImpl(
                           parser::CharBlock mangledOpName =
                               semantics::MakeNameFromOperator(
                                   ReductionProcessor::toParserIntrinsicOperator(
-                                      intrOp),
-                                  semaCtx);
+                                      intrOp));
                           redSymPtr = semantics::omp::FindUserReductionSymbol(
                               converter.getCurrentScope(), mangledOpName);
                         }
diff --git a/flang/lib/Lower/Support/ReductionProcessor.cpp b/flang/lib/Lower/Support/ReductionProcessor.cpp
index 25f62cd8e0f0bf..f318b2d3c4f1b4 100644
--- a/flang/lib/Lower/Support/ReductionProcessor.cpp
+++ b/flang/lib/Lower/Support/ReductionProcessor.cpp
@@ -1207,7 +1207,7 @@ bool ReductionProcessor::processReductionArguments(
           const semantics::DeclTypeSpec *opVarType =
               reductionSymbols[idx]->GetUltimate().GetType();
           parser::CharBlock mangledOpName = semantics::MakeNameFromOperator(
-              toParserIntrinsicOperator(intrinsicOp), *semaCtx);
+              toParserIntrinsicOperator(intrinsicOp));
           if (const semantics::Symbol *userSym =
                   semantics::omp::FindUserReductionSymbol(
                       converter.getCurrentScope(), mangledOpName, opVarType)) {
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index e5ec85721fab39..862b2324e5360d 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -2802,17 +2802,32 @@ void OmpStructureChecker::Enter(const parser::OmpDeclareMapperDirective &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpDeclareReductionDirective &x) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+  std::string dirName{
+      GetUpperName(llvm::omp::Directive::OMPD_declare_reduction, version)};
+
   const parser::OmpArgumentList &args{x.v.Arguments()};
   if (args.v.size() != 1) {
     context_.Say(args.source,
-        "DECLARE_REDUCTION directive should have a single argument"_err_en_US);
+        "%s directive should have a single argument"_err_en_US, dirName);
     return;
   }
 
   const parser::OmpArgument &arg{args.v.front()};
-  if (!std::holds_alternative<parser::OmpReductionSpecifier>(arg.u)) {
+  if (auto *spec{std::get_if<parser::OmpReductionSpecifier>(&arg.u)}) {
+    auto &ident{std::get<parser::OmpReductionIdentifier>(spec->t)};
+    using IntrinsicOperator = parser::DefinedOperator::IntrinsicOperator;
+    if (auto *intOp{parser::Unwrap<IntrinsicOperator>(ident)}) {
+      if (!CheckReductionIntrinsicOperator(*intOp, arg.source)) {
+        context_.Say(arg.source,
+            "Invalid reduction intrinsic operator in %s directive"_err_en_US,
+            dirName);
+      }
+    }
+  } else {
     context_.Say(arg.source,
-        "The argument to the DECLARE_REDUCTION directive should be a reduction-specifier"_err_en_US);
+        "The argument to the %s directive should be a reduction-specifier"_err_en_US,
+        dirName);
   }
 }
 
@@ -4209,6 +4224,32 @@ void OmpStructureChecker::Enter(const parser::OmpClause::TaskReduction &x) {
   CheckReductionObjects(objects, llvm::omp::Clause::OMPC_task_reduction);
 }
 
+bool OmpStructureChecker::CheckReductionIntrinsicOperator(
+    parser::DefinedOperator::IntrinsicOperator op, parser::CharBlock source) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+  switch (op) {
+  case parser::DefinedOperator::IntrinsicOperator::Add:
+  case parser::DefinedOperator::IntrinsicOperator::Multiply:
+  case parser::DefinedOperator::IntrinsicOperator::AND:
+  case parser::DefinedOperator::IntrinsicOperator::OR:
+  case parser::DefinedOperator::IntrinsicOperator::EQV:
+  case parser::DefinedOperator::IntrinsicOperator::NEQV:
+    return true;
+  case parser::DefinedOperator::IntrinsicOperator::Subtract:
+    if (llvm::omp::Version v52(52); version >= v52) {
+      context_.Say(source,
+          "The minus reduction operator is deprecated since %s"_err_en_US,
+          ThisVersion(v52));
+    } else {
+      context_.Say(source,
+          "The minus operator is not supported as a reduction operator"_err_en_US);
+    }
+    return false;
+  default:
+    return false;
+  }
+}
+
 bool OmpStructureChecker::CheckReductionOperator(
     const parser::OmpReductionIdentifier &ident, parser::CharBlock source,
     llvm::omp::Clause clauseId) {
@@ -4217,22 +4258,13 @@ bool OmpStructureChecker::CheckReductionOperator(
   auto visitOperator{[&](const parser::DefinedOperator &dOpr) {
     if (const auto *intrinsicOp{
             std::get_if<parser::DefinedOperator::IntrinsicOperator>(&dOpr.u)}) {
-      switch (*intrinsicOp) {
-      case parser::DefinedOperator::IntrinsicOperator::Add:
-      case parser::DefinedOperator::IntrinsicOperator::Multiply:
-      case parser::DefinedOperator::IntrinsicOperator::AND:
-      case parser::DefinedOperator::IntrinsicOperator::OR:
-      case parser::DefinedOperator::IntrinsicOperator::EQV:
-      case parser::DefinedOperator::IntrinsicOperator::NEQV:
-        return true;
-      case parser::DefinedOperator::IntrinsicOperator::Subtract:
-        context_.Say(GetContext().clauseSource,
-            "The minus reduction operator is deprecated since OpenMP 5.2 and is not supported in the REDUCTION clause."_err_en_US,
-            ContextDirectiveAsFortran());
+      if (!CheckReductionIntrinsicOperator(*intrinsicOp, source)) {
+        context_.Say(source,
+            "Invalid reduction operator in %s clause"_err_en_US,
+            GetUpperName(clauseId, version));
         return false;
-      default:
-        break;
       }
+      return true;
     }
     // User-defined operators are OK if there has been a declared reduction
     // for that. We mangle those names to store the user details.
@@ -4358,7 +4390,7 @@ static std::optional<parser::CharBlock> MangledReductionIdentifier(
             if (const auto *intrinsicOp{
                     std::get_if<parser::DefinedOperator::IntrinsicOperator>(
                         &dOpr.u)}) {
-              return MakeNameFromOperator(*intrinsicOp, context);
+              return MakeNameFromOperator(*intrinsicOp);
             }
             if (const auto *definedOp{
                     std::get_if<parser::DefinedOpName>(&dOpr.u)};
@@ -4443,7 +4475,7 @@ static bool IsReductionAllowedForType(
         DIE("This should have been caught in CheckIntrinsicOperator");
         return false;
       }
-      parser::CharBlock name{MakeNameFromOperator(*intrinsicOp, context)};
+      parser::CharBlock name{MakeNameFromOperator(*intrinsicOp)};
       return CheckSymbolSupportsType(scope, name, type);
     } else if (const auto *definedOp{
                    std::get_if<parser::DefinedOpName>(&dOpr.u)}) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 9e9880172b2439..28fa7d9c5f6eb4 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -490,10 +490,12 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
       const std::optional<parser::OmpClauseList> &maybeClauses);
   void CheckCancellationNest(
       const parser::CharBlock &source, llvm::omp::Directive type);
-  void CheckReductionObjects(
-      const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
   void CheckSubstring(
       const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
+  void CheckReductionObjects(
+      const parser::OmpObjectList &objects, llvm::omp::Clause clauseId);
+  bool CheckReductionIntrinsicOperator(
+      parser::DefinedOperator::IntrinsicOperator op, parser::CharBlock source);
   bool CheckReductionOperator(const parser::OmpReductionIdentifier &ident,
       parser::CharBlock source, llvm::omp::Clause clauseId);
   void CheckReductionObjectTypes(const parser::OmpObjectList &objects,
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index 6dd69a7d210dbd..cc2f1e61fa0577 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -2179,8 +2179,7 @@ void OmpVisitor::ProcessReductionSpecifier(
       mangledName = context().SaveTempName(MangleDefinedOperator(name->source));
     } else {
       mangledName = MakeNameFromOperator(
-          std::get<parser::DefinedOperator::IntrinsicOperator>(defOp.u),
-          context());
+          std::get<parser::DefinedOperator::IntrinsicOperator>(defOp.u));
     }
   }
 
diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp
index ce5c72a481c78b..32aa55c5bd73ab 100644
--- a/flang/lib/Semantics/tools.cpp
+++ b/flang/lib/Semantics/tools.cpp
@@ -1995,8 +1995,7 @@ bool AreSameModuleSymbol(const Symbol &symbol, const Symbol &other) {
 }
 
 parser::CharBlock MakeNameFromOperator(
-    const parser::DefinedOperator::IntrinsicOperator &op,
-    SemanticsContext &context) {
+    const parser::DefinedOperator::IntrinsicOperator &op) {
   switch (op) {
   case parser::DefinedOperator::IntrinsicOperator::Multiply:
     return parser::CharBlock{"op.*", 4};
@@ -2015,7 +2014,6 @@ parser::CharBlock MakeNameFromOperator(
     return parser::CharBlock{"op.NEQV", 7};
 
   default:
-    context.Say("Unsupported operator in DECLARE REDUCTION"_err_en_US);
     return parser::CharBlock{"op.?", 4};
   }
 }
@@ -2031,8 +2029,8 @@ parser::CharBlock MangleSpecialFunctions(parser::CharBlock name) {
 }
 
 std::string MangleDefinedOperator(parser::CharBlock name) {
-  CHECK(name[0] == '.' && name[name.size() - 1] == '.');
-  return "op" + name.ToString();
+  CHECK(name.starts_with('.') && name.ends_with('.'));
+  return "op" + name.str();
 }
 
 std::string GetReductionFortranId(parser::CharBlock mangledName) {
diff --git a/flang/test/Semantics/OpenMP/declare-reduction-bad-operator.f90 b/flang/test/Semantics/OpenMP/declare-reduction-bad-operator.f90
index 1d1d2903a2780c..ce93b640f237bf 100644
--- a/flang/test/Semantics/OpenMP/declare-reduction-bad-operator.f90
+++ b/flang/test/Semantics/OpenMP/declare-reduction-bad-operator.f90
@@ -1,6 +1,6 @@
-! RUN: not %flang_fc1 -fdebug-dump-symbols -fopenmp -fopenmp-version=50 %s 2>&1 | FileCheck %s
+!RUN: %python %S/../test_errors.py %s %flang_fc1 %openmp_flags -fopenmp-version=50
 
 function func(n)
+!ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
     !$omp declare reduction(/:integer:omp_out=omp_out+omp_in)
-!CHECK: error: Unsupported operator in DECLARE REDUCTION
 end function func
diff --git a/flang/test/Semantics/OpenMP/in-reduction.f90 b/flang/test/Semantics/OpenMP/in-reduction.f90
index 0472774704334c..54d11028f8e45e 100644
--- a/flang/test/Semantics/OpenMP/in-reduction.f90
+++ b/flang/test/Semantics/OpenMP/in-reduction.f90
@@ -9,7 +9,7 @@ subroutine f00
 
 subroutine f01
   real :: x
-!ERROR: Invalid reduction operator in IN_REDUCTION clause.
+!ERROR: Invalid reduction operator in IN_REDUCTION clause
 !$omp target in_reduction(.not.: x)
 !$omp end target
 end
diff --git a/flang/test/Semantics/OpenMP/reduction-subtract.f90 b/flang/test/Semantics/OpenMP/reduction-subtract.f90
index d4034743a14dc7..129a5c6f076948 100644
--- a/flang/test/Semantics/OpenMP/reduction-subtract.f90
+++ b/flang/test/Semantics/OpenMP/reduction-subtract.f90
@@ -1,10 +1,11 @@
-! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=52
 ! OpenMP Version 5.2
 ! Minus operation is deprecated in reduction
 
 subroutine reduction_subtract
   integer :: x
-  !ERROR: The minus reduction operator is deprecated since OpenMP 5.2 and is not supported in the REDUCTION clause.
+  !ERROR: The minus reduction operator is deprecated since OpenMP v5.2
+  !ERROR: Invalid reduction operator in REDUCTION clause
   !$omp do reduction(-:x)
   do i=1, 100
     x = x - i
diff --git a/flang/test/Semantics/OpenMP/reduction01.f90 b/flang/test/Semantics/OpenMP/reduction01.f90
index 0e1a8a571c5847..d08b55c4b92141 100644
--- a/flang/test/Semantics/OpenMP/reduction01.f90
+++ b/flang/test/Semantics/OpenMP/reduction01.f90
@@ -5,7 +5,7 @@ program omp_reduction
   integer :: i
   integer :: k = 10
 
-  !ERROR: Invalid reduction operator in REDUCTION clause.
+  !ERROR: Invalid reduction operator in REDUCTION clause
   !$omp parallel do reduction(**:k)
   do i = 1, 10
     k = k ** 1
diff --git a/flang/test/Semantics/OpenMP/reduction14.f90 b/flang/test/Semantics/OpenMP/reduction14.f90
index 7d3f8d468f7652..f0b6ff283f2b06 100644
--- a/flang/test/Semantics/OpenMP/reduction14.f90
+++ b/flang/test/Semantics/OpenMP/reduction14.f90
@@ -23,7 +23,8 @@ program omp_reduction
   !$omp end parallel
 
   ! - is deprecated for all types
-  ! ERROR: The minus reduction operator is deprecated since OpenMP 5.2 and is not supported in the REDUCTION clause.
+  ! ERROR: The minus operator is not supported as a reduction operator
+  ! ERROR: Invalid reduction operator in REDUCTION clause
   !$omp parallel reduction(-:i,r,c,z,l)
   !$omp end parallel
 
diff --git a/flang/test/Semantics/OpenMP/task-reduction.f90 b/flang/test/Semantics/OpenMP/task-reduction.f90
index 7e43332d53fe1e..efbfa6ba199f5f 100644
--- a/flang/test/Semantics/OpenMP/task-reduction.f90
+++ b/flang/test/Semantics/OpenMP/task-reduction.f90
@@ -9,7 +9,7 @@ subroutine f00
 
 subroutine f01
   real :: x
-!ERROR: Invalid reduction operator in TASK_REDUCTION clause.
+!ERROR: Invalid reduction operator in TASK_REDUCTION clause
 !$omp taskgroup task_reduction(.not.: x)
 !$omp end taskgroup
 end

>From f31d0f5c01d992458d36b8f192dd37321811dd25 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Fri, 16 Oct 2026 09:58:27 -0500
Subject: [PATCH 2/9] Verify DECLARE_REDUCTION in METADIRECTIVE

---
 flang/lib/Semantics/check-omp-structure.cpp         |  9 +++++++--
 flang/lib/Semantics/check-omp-structure.h           |  1 +
 flang/lib/Semantics/check-omp-variant.cpp           | 13 +++++++++++++
 .../OpenMP/metadirective-declare-reduction.f90      |  9 +++++++++
 4 files changed, 30 insertions(+), 2 deletions(-)
 create mode 100644 flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90

diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 862b2324e5360d..fa5c4297bf9706 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -2801,12 +2801,13 @@ void OmpStructureChecker::Enter(const parser::OmpDeclareMapperDirective &x) {
   }
 }
 
-void OmpStructureChecker::Enter(const parser::OmpDeclareReductionDirective &x) {
+void OmpStructureChecker::CheckDeclareReduction(
+    const parser::OmpDirectiveSpecification &x) {
   llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   std::string dirName{
       GetUpperName(llvm::omp::Directive::OMPD_declare_reduction, version)};
 
-  const parser::OmpArgumentList &args{x.v.Arguments()};
+  const parser::OmpArgumentList &args{x.Arguments()};
   if (args.v.size() != 1) {
     context_.Say(args.source,
         "%s directive should have a single argument"_err_en_US, dirName);
@@ -2831,6 +2832,10 @@ void OmpStructureChecker::Enter(const parser::OmpDeclareReductionDirective &x) {
   }
 }
 
+void OmpStructureChecker::Enter(const parser::OmpDeclareReductionDirective &x) {
+  CheckDeclareReduction(x.v);
+}
+
 void OmpStructureChecker::Enter(const parser::OmpDeclareTargetDirective &x) {
   const parser::OmpDirectiveName &dirName{x.v.DirName()};
 
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 28fa7d9c5f6eb4..254bad141860f4 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -472,6 +472,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   void CheckIndividualAllocateDirective(
       const parser::OmpAllocateDirective &x, bool isExecutable);
   void CheckExecutableAllocateDirective(const parser::OmpAllocateDirective &x);
+  void CheckDeclareReduction(const parser::OmpDirectiveSpecification &x);
 
   void CheckUsesAllocatorsSpec(
       const parser::OmpUsesAllocatorsClause::AllocatorSpec &spec);
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 7add875c96d883..127a912a10485a 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -772,6 +772,19 @@ void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective &x) {
 }
 
 void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &) {
+  // Perform semantic checks on declarative variants.
+  if (!partStack_.empty() && partStack_.back() == PartKind::SpecificationPart) {
+    OmpVariantMatchContext matchContext{context_};
+    for (const MetadirectiveLoopVariant &variant : metadirectiveLoopVariants_) {
+      if (!MayVariantBeSelected(variant.selector, context_, matchContext)) {
+        continue;
+      }
+      llvm::omp::Directive dirId{variant.spec->DirId()};
+      if (dirId == llvm::omp::Directive::OMPD_declare_reduction) {
+        CheckDeclareReduction(*variant.spec);
+      }
+    }
+  }
   ExitDirectiveNest(MetadirectiveNest);
 }
 
diff --git a/flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90 b/flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90
new file mode 100644
index 00000000000000..24aa50b0b45cb0
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90
@@ -0,0 +1,9 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
+
+subroutine s
+  !$omp metadirective &
+  !$omp& when(implementation={vendor(llvm)}: &
+!ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
+  !$omp& declare reduction(/ : integer : omp_out = omp_out + omp_in))
+end subroutine
+

>From 64a6248b2a378b0baa424a43ee81595dd5cc3911 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 21 Oct 2026 08:58:19 -0500
Subject: [PATCH 3/9] Limit checks to current METADIRECTIVE

---
 flang/lib/Semantics/check-omp-variant.cpp | 24 ++++++++++++++++-------
 1 file changed, 17 insertions(+), 7 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 127a912a10485a..e90a81e0653ff2 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -771,17 +771,27 @@ void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective &x) {
   EnterDirectiveNest(MetadirectiveNest);
 }
 
-void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &) {
-  // Perform semantic checks on declarative variants.
+void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &x) {
+  // Perform semantic checks on declarative variants on this METADIRECTIVE.
   if (!partStack_.empty() && partStack_.back() == PartKind::SpecificationPart) {
     OmpVariantMatchContext matchContext{context_};
-    for (const MetadirectiveLoopVariant &variant : metadirectiveLoopVariants_) {
-      if (!MayVariantBeSelected(variant.selector, context_, matchContext)) {
+    for (const parser::OmpClause &clause : x.v.Clauses().v) {
+      auto *when{parser::Unwrap<parser::OmpWhenClause>(clause)};
+      if (!when) {
         continue;
       }
-      llvm::omp::Directive dirId{variant.spec->DirId()};
-      if (dirId == llvm::omp::Directive::OMPD_declare_reduction) {
-        CheckDeclareReduction(*variant.spec);
+      const auto &modifiers{OmpGetModifiers(*when)};
+      auto *selector{OmpGetUniqueModifier<parser::OmpContextSelector>(modifiers)};
+      if (!MayVariantBeSelected(selector, context_, matchContext)) {
+        continue;
+      }
+      using DirSpecType = common::Indirection<parser::OmpDirectiveSpecification>;
+      if (auto &maybeSpec{std::get<std::optional<DirSpecType>>(when->t)}) {
+        const parser::OmpDirectiveSpecification &spec{maybeSpec->value()};
+        llvm::omp::Directive dirId{spec.DirId()};
+        if (dirId == llvm::omp::Directive::OMPD_declare_reduction) {
+          CheckDeclareReduction(spec);
+        }
       }
     }
   }

>From 3232cd9a36b88813aec9abb25f1595c42976858d Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 21 Oct 2026 09:38:19 -0500
Subject: [PATCH 4/9] format

---
 flang/lib/Semantics/check-omp-variant.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index e90a81e0653ff2..ddc2f8ea061c7d 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -781,11 +781,13 @@ void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &x) {
         continue;
       }
       const auto &modifiers{OmpGetModifiers(*when)};
-      auto *selector{OmpGetUniqueModifier<parser::OmpContextSelector>(modifiers)};
+      auto *selector{
+          OmpGetUniqueModifier<parser::OmpContextSelector>(modifiers)};
       if (!MayVariantBeSelected(selector, context_, matchContext)) {
         continue;
       }
-      using DirSpecType = common::Indirection<parser::OmpDirectiveSpecification>;
+      using DirSpecType =
+          common::Indirection<parser::OmpDirectiveSpecification>;
       if (auto &maybeSpec{std::get<std::optional<DirSpecType>>(when->t)}) {
         const parser::OmpDirectiveSpecification &spec{maybeSpec->value()};
         llvm::omp::Directive dirId{spec.DirId()};

>From afe936b527203441ca2ae932426dc17c0855a8ac Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 22 Oct 2026 07:41:27 -0500
Subject: [PATCH 5/9] Verify selectorless clauses as well

---
 flang/include/flang/Semantics/openmp-utils.h  |  5 ++
 flang/lib/Semantics/check-omp-variant.cpp     | 24 +++-----
 flang/lib/Semantics/openmp-utils.cpp          | 55 ++++++++++++-------
 .../metadirective-declare-reduction.f90       | 10 +++-
 4 files changed, 57 insertions(+), 37 deletions(-)

diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 353b8b1cd6d1e2..70c3bdf91d9b70 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -280,6 +280,11 @@ struct MetadirectiveCandidateSet {
   const parser::OmpDirectiveSpecification *fallback{nullptr};
 };
 
+const parser::OmpContextSelector *GetContextSelector(
+    const parser::OmpClause &clause);
+const parser::OmpDirectiveSpecification *GetDirectiveVariant(
+    const parser::OmpClause &clause);
+
 /// Build the statically applicable candidates for a METADIRECTIVE.
 ///
 /// Returns std::nullopt when a selector is malformed or uses a feature that
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index ddc2f8ea061c7d..ae6a609fbdae6e 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -771,33 +771,27 @@ void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective &x) {
   EnterDirectiveNest(MetadirectiveNest);
 }
 
+
 void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &x) {
   // Perform semantic checks on declarative variants on this METADIRECTIVE.
   if (!partStack_.empty() && partStack_.back() == PartKind::SpecificationPart) {
     OmpVariantMatchContext matchContext{context_};
     for (const parser::OmpClause &clause : x.v.Clauses().v) {
-      auto *when{parser::Unwrap<parser::OmpWhenClause>(clause)};
-      if (!when) {
+      auto *variant{GetDirectiveVariant(clause)};
+      if (!variant) {
         continue;
       }
-      const auto &modifiers{OmpGetModifiers(*when)};
-      auto *selector{
-          OmpGetUniqueModifier<parser::OmpContextSelector>(modifiers)};
-      if (!MayVariantBeSelected(selector, context_, matchContext)) {
+      if (!MayVariantBeSelected(
+              GetContextSelector(clause), context_, matchContext)) {
         continue;
       }
-      using DirSpecType =
-          common::Indirection<parser::OmpDirectiveSpecification>;
-      if (auto &maybeSpec{std::get<std::optional<DirSpecType>>(when->t)}) {
-        const parser::OmpDirectiveSpecification &spec{maybeSpec->value()};
-        llvm::omp::Directive dirId{spec.DirId()};
-        if (dirId == llvm::omp::Directive::OMPD_declare_reduction) {
-          CheckDeclareReduction(spec);
-        }
+      llvm::omp::Directive dirId{variant->DirId()};
+      if (dirId == llvm::omp::Directive::OMPD_declare_reduction) {
+        CheckDeclareReduction(*variant);
       }
     }
+    ExitDirectiveNest(MetadirectiveNest);
   }
-  ExitDirectiveNest(MetadirectiveNest);
 }
 
 // Check a loop-associated metadirective's variants against the loop nest they
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 97a5684064f7b1..8560989cb126a7 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -2558,21 +2558,39 @@ std::optional<DynamicUserCondition> MakeVariantMatchInfo(
   return dynamicCond;
 }
 
+const parser::OmpContextSelector *GetContextSelector(
+    const parser::OmpClause &clause) {
+  if (auto *when{parser::Unwrap<parser::OmpWhenClause>(clause)}) {
+    const auto &modifiers{OmpGetModifiers(*when)};
+    return OmpGetUniqueModifier<parser::OmpContextSelector>(modifiers);
+  }
+  return nullptr;
+}
+
+const parser::OmpDirectiveSpecification *GetDirectiveVariant(
+    const parser::OmpClause &clause) {
+  using DirectiveSpecification =
+      common::Indirection<parser::OmpDirectiveSpecification>;
+  if (auto *when{parser::Unwrap<parser::OmpWhenClause>(clause)}) {
+    if (auto &spec{std::get<std::optional<DirectiveSpecification>>(when->t)}) {
+      return &spec->value();
+    }
+  } else if (auto *other{parser::Unwrap<parser::OmpOtherwiseClause>(clause)}) {
+    if (auto &spec{other->v}) {
+      return &spec->value();
+    }
+  } else if (auto *def{
+                 parser::Unwrap<parser::OmpDefaultVariantClause>(clause)}) {
+    return &def->v.value();
+  }
+  return nullptr;
+}
+
 std::optional<MetadirectiveCandidateSet> BuildMetadirectiveCandidateSet(
     const parser::OmpClauseList &clauses, SemanticsContext &context,
     const OmpVariantMatchContext &matchContext) {
   MetadirectiveCandidateSet result;
 
-  auto getContextSelector = [](const parser::OmpClause::When &whenClause)
-      -> const parser::modifier::OmpContextSelector * {
-    const auto &modifiers{std::get<0>(whenClause.v.t)};
-    if (!modifiers || modifiers->size() != 1) {
-      return nullptr;
-    }
-    return std::get_if<parser::modifier::OmpContextSelector>(
-        &modifiers->front().u);
-  };
-
   auto getVariant = [](const parser::OmpDirectiveSpecification &spec) {
     // Keep NOTHING with clauses, e.g. APPLY, so they are not silently dropped.
     return spec.DirId() == llvm::omp::Directive::OMPD_nothing &&
@@ -2581,26 +2599,23 @@ std::optional<MetadirectiveCandidateSet> BuildMetadirectiveCandidateSet(
         : &spec;
   };
 
-  auto getDirectiveVariant = [&](const parser::OmpClause::When &whenClause)
+  auto getDirectiveVariant = [&](const parser::OmpClause &clause)
       -> std::pair<const parser::OmpDirectiveSpecification *, bool> {
-    const auto &optionalSpec{std::get<1>(whenClause.v.t)};
-    if (!optionalSpec) {
-      return {nullptr, false};
-    }
-    return {getVariant(optionalSpec->value()), true};
+    auto *variant{GetDirectiveVariant(clause)};
+    return variant ? std::make_pair(getVariant(*variant), true) :
+            std::make_pair(nullptr, false);
   };
 
   for (const parser::OmpClause &clause : clauses.v) {
-    if (const auto *whenClause{
-            std::get_if<parser::OmpClause::When>(&clause.u)}) {
-      const auto *ctxSel{getContextSelector(*whenClause)};
+    if (clause.Id() == llvm::omp::Clause::OMPC_when) {
+      const auto *ctxSel{GetContextSelector(clause)};
       if (!ctxSel ||
           FindUnsupportedSelectorFeature(*ctxSel, context) !=
               UnsupportedSelectorFeature::None) {
         return std::nullopt;
       }
 
-      auto [spec, isExplicit]{getDirectiveVariant(*whenClause)};
+      auto [spec, isExplicit]{getDirectiveVariant(clause)};
       llvm::omp::VariantMatchInfo rawVMI;
       std::optional<DynamicUserCondition> dynamicCondition{
           MakeVariantMatchInfo(rawVMI, *ctxSel, context)};
diff --git a/flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90 b/flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90
index 24aa50b0b45cb0..1004dad02683de 100644
--- a/flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90
+++ b/flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90
@@ -1,9 +1,15 @@
 !RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
 
-subroutine s
+subroutine f01
   !$omp metadirective &
   !$omp& when(implementation={vendor(llvm)}: &
 !ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
   !$omp& declare reduction(/ : integer : omp_out = omp_out + omp_in))
-end subroutine
+end
 
+subroutine f02
+  !$omp metadirective &
+  !$omp& otherwise(
+!ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
+  !$omp& declare reduction(/ : integer : omp_out = omp_out + omp_in))
+end

>From 8e71df4b2c5f10b3f0ffd65724d1b50086b354a8 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 22 Oct 2026 09:32:26 -0500
Subject: [PATCH 6/9] format

---
 flang/lib/Semantics/check-omp-variant.cpp | 1 -
 flang/lib/Semantics/openmp-utils.cpp      | 4 ++--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index ae6a609fbdae6e..1146c6c22a3588 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -771,7 +771,6 @@ void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective &x) {
   EnterDirectiveNest(MetadirectiveNest);
 }
 
-
 void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &x) {
   // Perform semantic checks on declarative variants on this METADIRECTIVE.
   if (!partStack_.empty() && partStack_.back() == PartKind::SpecificationPart) {
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 8560989cb126a7..d62dad5619efa6 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -2602,8 +2602,8 @@ std::optional<MetadirectiveCandidateSet> BuildMetadirectiveCandidateSet(
   auto getDirectiveVariant = [&](const parser::OmpClause &clause)
       -> std::pair<const parser::OmpDirectiveSpecification *, bool> {
     auto *variant{GetDirectiveVariant(clause)};
-    return variant ? std::make_pair(getVariant(*variant), true) :
-            std::make_pair(nullptr, false);
+    return variant ? std::make_pair(getVariant(*variant), true)
+                   : std::make_pair(nullptr, false);
   };
 
   for (const parser::OmpClause &clause : clauses.v) {

>From 301e98a302cec217da7811a013c0abea9eb8a54b Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 22 Oct 2026 10:11:40 -0500
Subject: [PATCH 7/9] Add missing include

---
 flang/lib/Semantics/openmp-utils.cpp | 1 +
 1 file changed, 1 insertion(+)

diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index d62dad5619efa6..acff9440da3ea5 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -32,6 +32,7 @@
 #include "flang/Parser/parse-tree.h"
 #include "flang/Semantics/expression.h"
 #include "flang/Semantics/openmp-directive-sets.h"
+#include "flang/Semantics/openmp-modifiers.h"
 #include "flang/Semantics/scope.h"
 #include "flang/Semantics/semantics.h"
 #include "flang/Semantics/symbol.h"

>From 86d6f5e776b7346866cca39aa2a46612acc65b62 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Fri, 23 Oct 2026 08:27:52 -0500
Subject: [PATCH 8/9] Address review comments

---
 flang/lib/Semantics/check-omp-variant.cpp         |  2 +-
 .../metadirective-declare-reduction-v51.f90       | 15 +++++++++++++++
 ...90 => metadirective-declare-reduction-v52.f90} |  0
 3 files changed, 16 insertions(+), 1 deletion(-)
 create mode 100644 flang/test/Semantics/OpenMP/metadirective-declare-reduction-v51.f90
 rename flang/test/Semantics/OpenMP/{metadirective-declare-reduction.f90 => metadirective-declare-reduction-v52.f90} (100%)

diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 1146c6c22a3588..226000e7edeb0d 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -789,8 +789,8 @@ void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &x) {
         CheckDeclareReduction(*variant);
       }
     }
-    ExitDirectiveNest(MetadirectiveNest);
   }
+  ExitDirectiveNest(MetadirectiveNest);
 }
 
 // Check a loop-associated metadirective's variants against the loop nest they
diff --git a/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v51.f90 b/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v51.f90
new file mode 100644
index 00000000000000..ddf7adccddddba
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v51.f90
@@ -0,0 +1,15 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=51
+
+subroutine f01
+  !$omp metadirective &
+  !$omp& when(implementation={vendor(llvm)}: &
+!ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
+  !$omp& declare reduction(/ : integer : omp_out = omp_out + omp_in))
+end
+
+subroutine f02
+  !$omp metadirective &
+  !$omp& default(
+!ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
+  !$omp& declare reduction(/ : integer : omp_out = omp_out + omp_in))
+end
diff --git a/flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90 b/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v52.f90
similarity index 100%
rename from flang/test/Semantics/OpenMP/metadirective-declare-reduction.f90
rename to flang/test/Semantics/OpenMP/metadirective-declare-reduction-v52.f90

>From f87b59ae3f4fd558a9393f6825be217744292f31 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Sat, 24 Oct 2026 06:26:54 -0500
Subject: [PATCH 9/9] Treat ERROR AT(COMPILATION) as neutral

---
 flang/lib/Semantics/canonicalize-omp.cpp      | 38 ++++++++++++++-----
 .../Semantics/OpenMP/metadirective-common.f90 |  7 ++++
 .../metadirective-declare-reduction-v51.f90   | 20 ++++++++++
 .../metadirective-declare-reduction-v52.f90   | 20 ++++++++++
 4 files changed, 76 insertions(+), 9 deletions(-)

diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp
index 3a93ba33179421..85d916a111bb28 100644
--- a/flang/lib/Semantics/canonicalize-omp.cpp
+++ b/flang/lib/Semantics/canonicalize-omp.cpp
@@ -301,10 +301,28 @@ static const parser::OmpDirectiveSpecification *GetODS(
   return nullptr;
 }
 
-static bool IsConsideredExecutable(const parser::OmpDirectiveSpecification &x) {
+static std::optional<bool> IsConsideredExecutable(
+    const parser::OmpDirectiveSpecification &x) {
   switch (llvm::omp::getDirectiveCategory(x.DirId())) {
   case llvm::omp::Category::Executable:
+    return true;
   case llvm::omp::Category::Utility:
+    if (x.DirId() == llvm::omp::Directive::OMPD_nothing) {
+      return std::nullopt;
+    }
+    if (x.DirId() == llvm::omp::Directive::OMPD_error) {
+      for (const parser::OmpClause &clause : x.Clauses().v) {
+        if (auto *at = parser::Unwrap<parser::OmpAtClause>(clause)) {
+          switch (at->v) {
+            SWITCH_COVERS_ALL_CASES
+          case parser::OmpAtClause::ActionTime::Compilation:
+            return std::nullopt;
+          case parser::OmpAtClause::ActionTime::Execution:
+            return true;
+          }
+        }
+      }
+    }
     return true;
   default:
     return false;
@@ -314,22 +332,24 @@ static bool IsConsideredExecutable(const parser::OmpDirectiveSpecification &x) {
 static bool IsConsideredExecutable(
     const parser::OmpMetadirectiveDirective &md) {
   // NOTHING as a variant by itself should not decide whether the METADIRECTIVE
-  // as a whole executable or not. But when all variants are NOTHING then the
-  // METADIRECTIVE should be treated as executable.
-  bool something = false;
+  // as a whole is executable or not. But when all variants are NOTHING then
+  // the METADIRECTIVE should be treated as executable.
+  // The same applies to ERROR AT(COMPILATION).
+  bool nonExec = false;
   for (const parser::OmpClause &clause : md.v.Clauses().v) {
     if (auto *ods = GetODS(clause)) {
-      if (ods->DirId() != llvm::omp::Directive::OMPD_nothing) {
-        something = true;
-        if (IsConsideredExecutable(*ods)) {
+      if (auto exe = IsConsideredExecutable(*ods)) {
+        if (*exe) {
+          // If it's known to be executable, return true.
           return true;
         }
+        // Known to be non-executable.
+        nonExec = true;
       }
     }
     // If the directive specification is missing, it's NOTHING.
   }
-  // If everything is NOTHING, return true.
-  return !something;
+  return !nonExec;
 }
 
 static bool IsConsideredExecutable(
diff --git a/flang/test/Semantics/OpenMP/metadirective-common.f90 b/flang/test/Semantics/OpenMP/metadirective-common.f90
index 3d219e9d9495a9..4ea9a17d19eab1 100644
--- a/flang/test/Semantics/OpenMP/metadirective-common.f90
+++ b/flang/test/Semantics/OpenMP/metadirective-common.f90
@@ -45,3 +45,10 @@ subroutine f05
 !ERROR: 'context-selector' modifier is required
   !$omp & when(nothing)
 end
+
+subroutine f06
+  !$omp metadirective &
+  !$omp & otherwise(nothing)
+!ERROR: A NOWAIT clause may only appear on TASKWAIT if a DEPEND clause is present
+  !$omp taskwait nowait
+end
diff --git a/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v51.f90 b/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v51.f90
index ddf7adccddddba..8562069875ec9a 100644
--- a/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v51.f90
+++ b/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v51.f90
@@ -13,3 +13,23 @@ subroutine f02
 !ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
   !$omp& declare reduction(/ : integer : omp_out = omp_out + omp_in))
 end
+
+subroutine f03
+  type :: t
+    integer :: m
+  end type
+  !$omp begin metadirective default(parallel)
+  block
+! Ok
+    !$omp declare reduction(+ : t : omp_out%m = omp_out%m + omp_in%m)
+  end block
+  !$omp end metadirective
+end
+
+subroutine f04
+  !$omp metadirective &
+  !$omp& when (implementation={vendor(llvm)}: &
+!ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
+  !$omp& declare reduction(/ : integer : omp_out = omp_out + omp_in)) &
+  !$omp& default(error at(compilation))
+end
diff --git a/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v52.f90 b/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v52.f90
index 1004dad02683de..63ba1f08993e9d 100644
--- a/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v52.f90
+++ b/flang/test/Semantics/OpenMP/metadirective-declare-reduction-v52.f90
@@ -13,3 +13,23 @@ subroutine f02
 !ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
   !$omp& declare reduction(/ : integer : omp_out = omp_out + omp_in))
 end
+
+subroutine f03
+  type :: t
+    integer :: m
+  end type
+  !$omp begin metadirective otherwise(parallel)
+  block
+! Ok
+    !$omp declare reduction(+ : t : omp_out%m = omp_out%m + omp_in%m)
+  end block
+  !$omp end metadirective
+end
+
+subroutine f04
+  !$omp metadirective &
+  !$omp& when (implementation={vendor(llvm)}: &
+!ERROR: Invalid reduction intrinsic operator in DECLARE REDUCTION directive
+  !$omp& declare reduction(/ : integer : omp_out = omp_out + omp_in)) &
+  !$omp& otherwise(error at(compilation))
+end



More information about the flang-commits mailing list