[flang-commits] [flang] [llvm] [Flang][OpenMP] add Apply clause for OpenMP Flang (PR #204225)

Ferran Toda via flang-commits flang-commits at lists.llvm.org
Fri Jun 19 07:16:37 PDT 2026


https://github.com/NouTimbaler updated https://github.com/llvm/llvm-project/pull/204225

>From 1b8fe1def9fc0160d4b2d660b13662af78943f35 Mon Sep 17 00:00:00 2001
From: Ferran Toda <ferran.todacasaban at bsc.es>
Date: Tue, 16 Jun 2026 17:42:05 +0000
Subject: [PATCH 1/5] apply parsing and basic checks

---
 flang/include/flang/Parser/dump-parse-tree.h  |   8 +
 flang/include/flang/Parser/parse-tree.h       |  29 ++++
 flang/lib/Parser/openmp-parsers.cpp           |  17 +++
 flang/lib/Parser/unparse.cpp                  |  10 ++
 flang/lib/Semantics/check-omp-loop.cpp        |  45 ++++++
 flang/lib/Semantics/check-omp-structure.h     |   6 +
 flang/lib/Semantics/check-omp-variant.cpp     |   4 +-
 flang/test/Parser/OpenMP/apply01.f90          | 137 ++++++++++++++++++
 flang/test/Parser/OpenMP/apply02.f90          | 114 +++++++++++++++
 flang/test/Semantics/OpenMP/apply01.f90       |  34 +++++
 flang/test/Semantics/OpenMP/apply02.f90       |  34 +++++
 .../llvm/Frontend/Directive/DirectiveBase.td  |  24 +++
 llvm/include/llvm/Frontend/OpenMP/OMP.td      |  55 +++++++
 llvm/include/llvm/TableGen/DirectiveEmitter.h |  12 ++
 .../utils/TableGen/Basic/DirectiveEmitter.cpp |  60 +++++++-
 15 files changed, 586 insertions(+), 3 deletions(-)
 create mode 100644 flang/test/Parser/OpenMP/apply01.f90
 create mode 100644 flang/test/Parser/OpenMP/apply02.f90
 create mode 100644 flang/test/Semantics/OpenMP/apply01.f90
 create mode 100644 flang/test/Semantics/OpenMP/apply02.f90

diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 1205101c21fcf..b3e674603d4fa 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -556,6 +556,14 @@ class ParseTreeDumper {
   NODE_ENUM(OmpAlwaysModifier, Value)
   NODE(parser, OmpAppendArgsClause)
   NODE(OmpAppendArgsClause, OmpAppendOp)
+  NODE(parser, OmpLoopModifier)
+
+  static std::string GetNodeName(const llvm::omp::LoopModifier &x) {
+    return llvm::Twine(
+        "llvm::omp::LoopModifier = ", llvm::omp::getOpenMPLoopModifierName(x))
+        .str();
+  }
+  NODE(parser, OmpApplyClause)
   NODE(parser, OmpArgument)
   NODE(parser, OmpArgumentList)
   NODE(parser, OmpAssumeDirective)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index ee6288539395c..ea05c03d009f7 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4353,6 +4353,24 @@ struct OmpxHoldModifier {
   WRAPPER_CLASS_BOILERPLATE(OmpxHoldModifier, Value);
 };
 
+// Ref: [6.0:372-373]
+//
+// loop-modifier ->
+//    FLATTENED |
+//    FUSED | GRID | IDENTITY |
+//    INTERCHANGED |
+//    INTRATILE | OFFSETS |
+//    REVERSED | SPLIT |
+//    UNROLLED
+//    [( ScalarIntConstantExpr-list )]
+struct OmpLoopModifier {
+  TUPLE_CLASS_BOILERPLATE(OmpLoopModifier);
+  std::tuple<llvm::omp::LoopModifier,
+      std::optional<std::list<ScalarIntConstantExpr>>>
+      t;
+  CharBlock source;
+};
+
 // context-selector
 using OmpContextSelector = traits::OmpContextSelectorSpecification;
 } // namespace modifier
@@ -4480,6 +4498,17 @@ struct OmpContainsClause {
   WRAPPER_CLASS_BOILERPLATE(OmpContainsClause, OmpDirectiveList);
 };
 
+// Ref: [6.0:372-373]
+//
+// apply-clause ->
+//    APPLY( [loop-modifier :] directive-specification-list )
+struct OmpApplyClause {
+  TUPLE_CLASS_BOILERPLATE(OmpApplyClause);
+  std::tuple<std::optional<OmpLoopModifier>,
+      std::list<OmpDirectiveSpecification>>
+      t;
+};
+
 // Ref: [4.5:46-50], [5.0:74-78], [5.1:92-96], [5.2:109]
 //
 // When used as a data-sharing clause:
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 7016c688a572d..aedca5e582100 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -1101,6 +1101,18 @@ TYPE_PARSER(construct<OmpAdjustArgsClause::OmpAdjustOp>(
     "NEED_DEVICE_PTR" >>
         pure(OmpAdjustArgsClause::OmpAdjustOp::Value::Need_Device_Ptr)))
 
+TYPE_PARSER(sourced(construct<OmpLoopModifier>(
+    "FUSED" >> pure(llvm::omp::LoopModifier::OMPLM_fused) ||
+        "GRID" >> pure(llvm::omp::LoopModifier::OMPLM_grid) ||
+        "IDENTITY" >> pure(llvm::omp::LoopModifier::OMPLM_identity) ||
+        "INTERCHANGED" >> pure(llvm::omp::LoopModifier::OMPLM_interchanged) ||
+        "INTRATILE" >> pure(llvm::omp::LoopModifier::OMPLM_intratile) ||
+        "OFFSETS" >> pure(llvm::omp::LoopModifier::OMPLM_offsets) ||
+        "REVERSED" >> pure(llvm::omp::LoopModifier::OMPLM_reversed) ||
+        "SPLIT" >> pure(llvm::omp::LoopModifier::OMPLM_split) ||
+        "UNROLLED" >> pure(llvm::omp::LoopModifier::OMPLM_unrolled),
+    maybe(parenthesized(nonemptyList(scalarIntConstantExpr))))))
+
 // --- Parsers for clauses --------------------------------------------
 
 // Declaration of the ODS parser. This type must be complete for some of
@@ -1143,6 +1155,9 @@ TYPE_PARSER(construct<OmpAffinityClause>(
     maybe(nonemptyList(Parser<OmpAffinityClause::Modifier>{}) / ":"),
     Parser<OmpObjectList>{}))
 
+TYPE_PARSER(construct<OmpApplyClause>(maybe(Parser<OmpLoopModifier>{} / ":"),
+    nonemptyList(OmpDirectiveSpecificationParser(/*allowCommas=*/false))))
+
 // 2.4 Requires construct [OpenMP 5.0]
 //        atomic-default-mem-order-clause ->
 //                               acq_rel
@@ -1457,6 +1472,8 @@ TYPE_PARSER( //
                       parenthesized(Parser<OmpAllocateClause>{}))) ||
     "APPEND_ARGS" >> construct<OmpClause>(construct<OmpClause::AppendArgs>(
                          parenthesized(Parser<OmpAppendArgsClause>{}))) ||
+    "APPLY" >> construct<OmpClause>(construct<OmpClause::Apply>(
+                   parenthesized(Parser<OmpApplyClause>{}))) ||
     "ALLOCATOR" >> construct<OmpClause>(construct<OmpClause::Allocator>(
                        parenthesized(scalarIntExpr))) ||
     "AT" >> construct<OmpClause>(construct<OmpClause::At>(
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 42f042e470e81..e1b02f4c70e8e 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2213,6 +2213,16 @@ class UnparseVisitor {
   }
   void Unparse(const OmpAppendArgsClause &x) { Walk(x.v, ","); }
   void Unparse(const OmpArgumentList &x) { Walk(x.v, ", "); }
+  void Unparse(const OmpLoopModifier &x) {
+    Word(llvm::omp::getOpenMPLoopModifierName(
+        std::get<llvm::omp::LoopModifier>(x.t)));
+    Walk("(", std::get<std::optional<std::list<ScalarIntConstantExpr>>>(x.t),
+        ")");
+  }
+  void Unparse(const OmpApplyClause &x) {
+    Walk(std::get<std::optional<OmpLoopModifier>>(x.t), ": ");
+    Walk(std::get<std::list<OmpDirectiveSpecification>>(x.t));
+  }
   void Unparse(const OmpAttachModifier &x) {
     Word("ATTACH(");
     Walk(x.v);
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 689e3a0da89ca..99778afb4938e 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -879,6 +879,51 @@ void OmpStructureChecker::Enter(const parser::DoConstruct &x) {
   constructStack_.push_back(&x);
 }
 
+void OmpStructureChecker::Enter(const parser::OmpLoopModifier &x) {
+  DirectiveContext &dirCtx = GetContext();
+  llvm::omp::Directive dir{dirCtx.directive};
+  unsigned version{context_.langOptions().OpenMPVersion};
+  auto &m{std::get<llvm::omp::LoopModifier>(x.t)};
+  if (!llvm::omp::isAllowedLoopModifier(dir, m)) {
+    llvm::StringRef name = llvm::omp::getOpenMPLoopModifierName(m);
+    context_.Say(x.source,
+        "%s modifier is not allowed on %s directive"_err_en_US,
+        parser::ToUpperCaseLetters(name),
+        parser::omp::GetUpperName(dir, version));
+  }
+  if (const auto &il{
+          std::get<std::optional<std::list<parser::ScalarIntConstantExpr>>>(
+              x.t)}) {
+    int64_t last = -1;
+    for (auto &i : il.value()) {
+      if (const auto v{GetIntValue(i)}) {
+        if (*v <= 0) {
+          context_.Say(x.source,
+              "The loop modifier indexes of the %s clause must be constant positive integer expressions"_err_en_US,
+              parser::ToUpperCaseLetters(
+                  getClauseName(llvm::omp::Clause::OMPC_apply).str()));
+        } else if (*v <= last) {
+          context_.Say(x.source,
+              "The loop modifier indexes of the %s clause must be in ascending order"_err_en_US,
+              parser::ToUpperCaseLetters(
+                  getClauseName(llvm::omp::Clause::OMPC_apply).str()));
+        } else {
+          last = *v;
+        }
+      }
+    }
+  }
+}
+
+void OmpStructureChecker::Enter(const parser::OmpApplyClause &x) {
+  EnterDirectiveNest(ApplyNest);
+  CheckAllowedClause(llvm::omp::Clause::OMPC_apply);
+}
+
+void OmpStructureChecker::Leave(const parser::OmpApplyClause &x) {
+  ExitDirectiveNest(ApplyNest);
+}
+
 void OmpStructureChecker::Leave(const parser::DoConstruct &x) {
   assert(!constructStack_.empty() && "Expecting non-empty construct stack");
 #ifndef NDEBUG
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 9fca5ff0f5fca..ea358430c1e2d 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -173,6 +173,11 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   void Enter(const parser::OmpContextSelector &);
   void Leave(const parser::OmpContextSelector &);
 
+  void Enter(const parser::OmpLoopModifier &);
+
+  void Enter(const parser::OmpApplyClause &);
+  void Leave(const parser::OmpApplyClause &);
+
   template <typename A> void Enter(const parser::Statement<A> &);
   void Leave(const parser::GotoStmt &);
   void Leave(const parser::ComputedGotoStmt &);
@@ -399,6 +404,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
 
   bool deviceConstructFound_{false};
   enum directiveNestType : int {
+    ApplyNest,
     SIMDNest,
     TargetBlockOnlyTeams,
     TargetNest,
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 8ab160496b64a..1832acf687c24 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -549,7 +549,7 @@ void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
   // METADIRECTIVE.
   // In other cases it's a part of other constructs that handle directive
   // context stack by themselves.
-  if (!GetDirectiveNest(MetadirectiveNest)) {
+  if (!GetDirectiveNest(MetadirectiveNest) && !GetDirectiveNest(ApplyNest)) {
     return;
   }
 
@@ -578,7 +578,7 @@ void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
 }
 
 void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &x) {
-  if (GetDirectiveNest(MetadirectiveNest)) {
+  if (GetDirectiveNest(MetadirectiveNest) || GetDirectiveNest(ApplyNest)) {
     dirContext_.pop_back();
   }
 }
diff --git a/flang/test/Parser/OpenMP/apply01.f90 b/flang/test/Parser/OpenMP/apply01.f90
new file mode 100644
index 0000000000000..c1c5d4be2d0eb
--- /dev/null
+++ b/flang/test/Parser/OpenMP/apply01.f90
@@ -0,0 +1,137 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine apply_1(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp interchange apply(nothing, reverse)
+  do i = 1,10
+    do j = 1,10
+      x = x + 1
+    end do
+  end do
+end
+
+!UNPARSE: !$OMP INTERCHANGE APPLY(NOTHING, REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:   DO j=1_4,10_4
+!UNPARSE:    x=x+1_4
+!UNPARSE:   END DO
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = nothing
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+subroutine apply_modifier(x)
+  implicit none
+  integer :: x, i
+
+  !$omp tile sizes(2) apply(grid: reverse)
+  do i = 1, 10
+    x = x + 1
+  end do
+end
+
+!UNPARSE: !$OMP TILE SIZES(2_4) APPLY(GRID: REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:    x=x+1_4
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = tile
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> Expr = '2_4'
+!PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = grid
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+subroutine apply_2_clauses(x)
+  implicit none
+  integer :: x, i
+
+  !$omp tile sizes(2) apply(intratile: unroll) apply(grid: reverse)
+  do i = 1, 10
+    x = x + 1
+  end do
+end
+
+!UNPARSE: !$OMP TILE SIZES(2_4) APPLY(INTRATILE: UNROLL) APPLY(GRID: REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:    x=x+1_4
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = tile
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> Expr = '2_4'
+!PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = intratile
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = unroll
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+!PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = grid
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+
+subroutine apply_inside_apply(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp fuse apply(fused: tile sizes(2) apply(grid: reverse))
+  do i = 1, 10
+    x = x + 1
+  end do
+  do j = 1, 10
+    x = x - 1
+  end do
+  !$omp end fuse
+end
+
+!UNPARSE: !$OMP FUSE APPLY(FUSED: TILE SIZES(2_4) APPLY(GRID: REVERSE))
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:    x=x+1_4
+!UNPARSE:  END DO
+!UNPARSE:  DO j=1_4,10_4
+!UNPARSE:    x=x-1_4
+!UNPARSE:  END DO
+!UNPARSE: !$OMP END FUSE
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = fuse
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = fused
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = tile
+!PARSE-TREE: | | | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> Expr = '2_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | | | OmpLoopModifier
+!PARSE-TREE: | | | | | llvm::omp::LoopModifier = grid
+!PARSE-TREE: | | | | OmpDirectiveSpecification
+!PARSE-TREE: | | | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | | | OmpClauseList ->
+!PARSE-TREE: | | | | | Flags = {}
+!PARSE-TREE: | | | Flags = {}
+
diff --git a/flang/test/Parser/OpenMP/apply02.f90 b/flang/test/Parser/OpenMP/apply02.f90
new file mode 100644
index 0000000000000..803f0ac73d25a
--- /dev/null
+++ b/flang/test/Parser/OpenMP/apply02.f90
@@ -0,0 +1,114 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine apply_all(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp interchange apply(interchanged(1,2): nothing, reverse)
+  do i = 1,10
+    do j = 1,10
+      x = x + 1
+    end do
+  end do
+end
+
+!UNPARSE: !$OMP INTERCHANGE APPLY(INTERCHANGED(1_4, 2_4): NOTHING, REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:   DO j=1_4,10_4
+!UNPARSE:     x=x+1_4
+!UNPARSE:   END DO
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = interchanged
+!PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '1_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '2_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = nothing
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+subroutine apply_one(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp interchange apply(interchanged(2): reverse)
+  do i = 1,10
+    do j = 1,10
+      x = x + 1
+    end do
+  end do
+end
+
+!UNPARSE: !$OMP INTERCHANGE APPLY(INTERCHANGED(2_4): REVERSE)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:   DO j=1_4,10_4
+!UNPARSE:     x=x+1_4
+!UNPARSE:   END DO
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = interchanged
+!PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '2_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+
+subroutine apply_inside_apply(x)
+  implicit none
+  integer :: x, i, j
+
+  !$omp tile sizes(2,2) apply(grid(1): interchange apply(interchanged(2): reverse))
+  do i = 1,10
+    do j = 1,10
+      x = x + 1
+    end do
+  end do
+end
+
+!UNPARSE: !$OMP TILE SIZES(2_4,2_4) APPLY(GRID(1_4): INTERCHANGE APPLY(INTERCHANGED(2_4): REVERSE))
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:   DO j=1_4,10_4
+!UNPARSE:     x=x+1_4
+!UNPARSE:   END DO
+!UNPARSE:  END DO
+
+!PARSE-TREE: OmpBeginDirective
+!PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = tile
+!PARSE-TREE: | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> Expr = '2_4'
+!PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | Scalar -> Integer -> Expr = '2_4'
+!PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | | llvm::omp::LoopModifier = grid
+!PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '1_4'
+!PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: | | OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: | | | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
+!PARSE-TREE: | | | | OmpLoopModifier
+!PARSE-TREE: | | | | | llvm::omp::LoopModifier = interchanged
+!PARSE-TREE: | | | | | Scalar -> Integer -> Constant -> Expr = '2_4'
+!PARSE-TREE: | | | | | | LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: | | | | OmpDirectiveSpecification
+!PARSE-TREE: | | | | | OmpDirectiveName -> llvm::omp::Directive = reverse
+!PARSE-TREE: | | | | | OmpClauseList ->
+!PARSE-TREE: | | | | | Flags = {}
+!PARSE-TREE: | | | Flags = {}
+
diff --git a/flang/test/Semantics/OpenMP/apply01.f90 b/flang/test/Semantics/OpenMP/apply01.f90
new file mode 100644
index 0000000000000..f146c4e7730d8
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/apply01.f90
@@ -0,0 +1,34 @@
+! Testing the Semantics of tile
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+
+subroutine wrong_modifier(x)
+  implicit none
+  integer i, j, x
+
+  !ERROR: INTERCHANGED modifier is not allowed on TILE directive
+  !$omp tile sizes(2,2) apply(interchanged: nothing, reverse)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+  !ERROR: INTERCHANGED modifier is not allowed on TILE directive
+  !ERROR: FUSED modifier is not allowed on TILE directive
+  !$omp tile sizes(2,2) apply(interchanged: nothing, reverse) apply(fused: reverse)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+  !ERROR: INTRATILE modifier is not allowed on UNROLL directive
+  !$omp tile sizes(2,2) apply(grid: nothing, unroll partial(2) apply(intratile: reverse))
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+end subroutine
+
diff --git a/flang/test/Semantics/OpenMP/apply02.f90 b/flang/test/Semantics/OpenMP/apply02.f90
new file mode 100644
index 0000000000000..81fc436f96fe6
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/apply02.f90
@@ -0,0 +1,34 @@
+! Testing the Semantics of tile
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+
+subroutine wrong_modifier(x)
+  implicit none
+  integer i, j, x
+
+  !ERROR: Must be a constant value
+  !$omp tile sizes(2,2) apply(grid(x): unroll)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+  !ERROR: The loop modifier indexes of the APPLY clause must be constant positive integer expressions
+  !$omp tile sizes(2,2) apply(grid(-1): unroll)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+  !ERROR: The loop modifier indexes of the APPLY clause must be in ascending order
+  !$omp tile sizes(2,2) apply(grid(2,1): unroll, nothing)
+  do i = 1, 10
+    do j = 1, 10
+      x = x + 1
+    end do
+  end do
+
+end subroutine
+
diff --git a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
index 2aa0649479023..56d5457481bc3 100644
--- a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
+++ b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
@@ -33,6 +33,10 @@ class DirectiveLanguage {
   // enum.
   string clausePrefix = "";
 
+  // Optional prefix used for the generation of the enumerator in the
+  // LoopModifier enum.
+  string loopModifierPrefix = "";
+
   // Make the enum values available in the namespace. This allows us to
   // write something like Enum_X if we have a `using namespace cppNamespace`.
   bit makeEnumAvailableInNamespace = false;
@@ -201,6 +205,23 @@ class SourceLanguage<string n> {
 def L_C : SourceLanguage<"C"> {}
 def L_Fortran : SourceLanguage<"Fortran"> {}
 
+// Loop Modifier for Apply clause
+class LoopModifier<list<Spelling> ss> {
+  // Spellings of the loop modifer.
+  list<Spelling> spellings = ss;
+}
+
+def LM_Flattened: LoopModifier<[Spelling<"flattened">]> {}
+def LM_Fused: LoopModifier<[Spelling<"fused">]> {}
+def LM_Grid: LoopModifier<[Spelling<"grid">]> {}
+def LM_Identity: LoopModifier<[Spelling<"identity">]> {}
+def LM_Interchanged: LoopModifier<[Spelling<"interchanged">]> {}
+def LM_Intratile: LoopModifier<[Spelling<"intratile">]> {}
+def LM_Offsets: LoopModifier<[Spelling<"offsets">]> {}
+def LM_Reversed: LoopModifier<[Spelling<"reversed">]> {}
+def LM_Split: LoopModifier<[Spelling<"split">]> {}
+def LM_Unrolled: LoopModifier<[Spelling<"unrolled">]> {}
+
 // Information about a specific directive.
 class Directive<list<Spelling> ss> {
   // Spellings of the directive.
@@ -235,6 +256,9 @@ class Directive<list<Spelling> ss> {
   // The category of the directive.
   Category category = ?;
 
+  // List of allowed loop modifiers for the apply clause.
+  list<LoopModifier> allowedLoopModifiers = [];
+
   // The languages that allow this directive. Default: all languages.
   list<SourceLanguage> languages = [L_C, L_Fortran];
 }
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index e1e66df72dfc5..11a0354ba54f6 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -21,6 +21,7 @@ def OpenMP : DirectiveLanguage {
   let cppNamespace = "omp"; // final namespace will be llvm::omp
   let directivePrefix = "OMPD_";
   let clausePrefix = "OMPC_";
+  let loopModifierPrefix = "OMPLM_";
   let makeEnumAvailableInNamespace = true;
   let enableBitmaskEnumInNamespace = true;
   let clauseEnumSetClass = "OmpClauseSet";
@@ -66,6 +67,7 @@ def OMPC_Allocator : Clause<[Spelling<"allocator">]> {
   let flangClass = "ScalarIntExpr";
 }
 def OMPC_Apply : Clause<[Spelling<"apply">]> {
+  let flangClass = "OmpApplyClause";
 }
 def OMPC_AppendArgs : Clause<[Spelling<"append_args">]> {
   let flangClass = "OmpAppendArgsClause";
@@ -939,6 +941,9 @@ def OMP_Flatten : Directive<[Spelling<"flatten">]> {
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Flattened,
+  ];
 }
 def OMP_Flush : Directive<[Spelling<"flush">]> {
   let allowedOnceClauses = [
@@ -980,19 +985,31 @@ def OMP_Groupprivate : Directive<[Spelling<"groupprivate">]> {
   let languages = [L_C, L_Fortran];
 }
 def OMP_Fuse : Directive<[Spelling<"fuse">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Depth, 61>,
     VersionedClause<OMPC_LoopRange, 60>,
   ];
   let association = AS_LoopSeq;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Fused,
+  ];
 }
 def OMP_Interchange : Directive<[Spelling<"interchange">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Permutation>,
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Interchanged,
+  ];
 }
 def OMP_interop : Directive<[Spelling<"interop">]> {
   let allowedClauses = [
@@ -1043,8 +1060,14 @@ def OMP_Metadirective : Directive<[Spelling<"metadirective">]> {
   let category = CA_Meta;
 }
 def OMP_Nothing : Directive<[Spelling<"nothing">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let association = AS_None;
   let category = CA_Utility;
+  let allowedLoopModifiers = [
+    LM_Identity,
+  ];
 }
 def OMP_Ordered : Directive<[Spelling<"ordered">]> {
   let allowedClauses = [
@@ -1100,8 +1123,14 @@ def OMP_Requires : Directive<[Spelling<"requires">]> {
   let category = CA_Informational;
 }
 def OMP_Reverse : Directive<[Spelling<"reverse">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Reversed,
+  ];
 }
 def OMP_Scan : Directive<[Spelling<"scan">]> {
   let allowedClauses = [
@@ -1409,6 +1438,9 @@ def OMP_ThreadPrivate : Directive<[Spelling<"threadprivate">]> {
   let category = CA_Declarative;
 }
 def OMP_Tile : Directive<[Spelling<"tile">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Sizes, 51>,
   ];
@@ -1417,15 +1449,29 @@ def OMP_Tile : Directive<[Spelling<"tile">]> {
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Grid,
+    LM_Intratile,
+  ];
 }
 def OMP_Stripe : Directive<[Spelling<"stripe">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Sizes, 60>,
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Grid,
+    LM_Offsets,
+  ];
 }
 def OMP_Split : Directive<[Spelling<"split">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Counts, 60>,
   ];
@@ -1434,6 +1480,9 @@ def OMP_Split : Directive<[Spelling<"split">]> {
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Split,
+  ];
 }
 def OMP_Unknown : Directive<[Spelling<"unknown">]> {
   let isDefault = true;
@@ -1441,12 +1490,18 @@ def OMP_Unknown : Directive<[Spelling<"unknown">]> {
   let category = CA_Utility;
 }
 def OMP_Unroll : Directive<[Spelling<"unroll">]> {
+  let allowedClauses = [
+    VersionedClause<OMPC_Apply, 60>,
+  ];
   let allowedOnceClauses = [
     VersionedClause<OMPC_Full, 51>,
     VersionedClause<OMPC_Partial, 51>,
   ];
   let association = AS_LoopNest;
   let category = CA_Executable;
+  let allowedLoopModifiers = [
+    LM_Unrolled,
+  ];
 }
 def OMP_Workshare : Directive<[Spelling<"workshare">]> {
   let allowedOnceClauses = [
diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 2080f75eb8cfc..d111f666ff31a 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -48,6 +48,10 @@ class DirectiveLanguage {
     return Def->getValueAsString("clausePrefix");
   }
 
+  StringRef getLoopModifierPrefix() const {
+    return Def->getValueAsString("loopModifierPrefix");
+  }
+
   StringRef getClauseEnumSetClass() const {
     return Def->getValueAsString("clauseEnumSetClass");
   }
@@ -84,6 +88,10 @@ class DirectiveLanguage {
     return Records.getAllDerivedDefinitions("Clause");
   }
 
+  ArrayRef<const Record *> getLoopModifiers() const {
+    return Records.getAllDerivedDefinitions("LoopModifier");
+  }
+
   bool HasValidityErrors() const;
 
 private:
@@ -262,6 +270,10 @@ class Directive : public BaseRecord {
     return Def->getValueAsListOfDefs("languages");
   }
 
+  std::vector<const Record *> getAllowedLoopModifiers() const {
+    return Def->getValueAsListOfDefs("allowedLoopModifiers");
+  }
+
   // Clang uses a different format for names of its directives enum.
   std::string getClangAccSpelling() const {
     StringRef Name = getSpellingForIdentifier();
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index e091cda694f99..07157fc23a9ab 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -311,6 +311,11 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) {
                       DirLang.getClausePrefix(),
                       DirLang.hasMakeEnumAvailableInNamespace());
 
+    // Emit LoopModifier
+    generateEnumClass(DirLang.getLoopModifiers(), OS, "LoopModifier",
+                      DirLang.getLoopModifierPrefix(),
+                      DirLang.hasMakeEnumAvailableInNamespace());
+
     // Emit ClauseVals enumeration
     std::string EnumHelperFuncs;
     generateClauseEnumVal(DirLang.getClauses(), OS, DirLang, EnumHelperFuncs);
@@ -354,11 +359,16 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) {
     OS << "\n";
     OS << "constexpr std::size_t getMaxLeafCount() { return "
        << getMaxLeafCount(DirLang) << "; }\n";
+    OS << "LLVM_ABI bool isAllowedLoopModifier(Directive D, LoopModifier "
+          "LM);\n";
+    OS << "LLVM_ABI StringRef get" << Lang
+       << "LoopModifierName(LoopModifier LM, unsigned Ver = 0);\n";
     OS << EnumHelperFuncs;
   } // close DirLangNS
 
   // These specializations need to be in ::llvm.
-  for (StringRef Enum : {"Association", "Category", "Directive", "Clause"}) {
+  for (StringRef Enum :
+       {"Association", "Category", "Directive", "Clause", "LoopModifier"}) {
     OS << "\n";
     OS << "template <> struct enum_iteration_traits<"
        << DirLang.getCppNamespace() << "::" << Enum << "> {\n";
@@ -909,6 +919,47 @@ static void generateGetDirectiveLanguages(const DirectiveLanguage &DirLang,
   OS << "}\n";
 }
 
+// Generate the isAllowedLoopModifier function implementation.
+static void generateIsAllowedLoopModifier(const DirectiveLanguage &DirLang,
+                                          raw_ostream &OS) {
+  std::string Qual = getQualifier(DirLang);
+
+  OS << "\n";
+  OS << "bool " << Qual << "isAllowedLoopModifier(" << Qual << "Directive D, "
+     << Qual << "LoopModifier LM) {\n";
+  OS << "  assert(unsigned(D) <= Directive_enumSize);\n";
+
+  OS << "  switch (D) {\n";
+
+  StringRef DPrefix = DirLang.getDirectivePrefix();
+  StringRef LMPrefix = DirLang.getLoopModifierPrefix();
+  for (const Record *R : DirLang.getDirectives()) {
+    Directive Dir(R);
+    OS << "    case " << getIdentifierName(R, DPrefix) << ":\n";
+    if (Dir.getAllowedLoopModifiers().empty()) {
+      OS << "      return false;\n";
+    } else {
+      OS << "      switch (LM) {\n";
+
+      for (const Record *LMR : Dir.getAllowedLoopModifiers()) {
+        std::string Name = getIdentifierName(LMR, LMPrefix);
+        OS << "        case LoopModifier::" << Name << ":\n";
+        OS << "          return true;\n";
+      }
+
+      OS << "        default:\n";
+      OS << "          return false;\n";
+      OS << "      }\n"; // End of modifier switch
+    }
+    OS << "      break;\n";
+  }
+
+  OS << "  }\n"; // End of directives switch
+  OS << "  llvm_unreachable(\"Invalid " << DirLang.getName()
+     << " Directive kind\");\n";
+  OS << "}\n"; // End of function isAllowedLoopModifier
+}
+
 // Generate a simple enum set with the give clauses.
 static void generateClauseSet(ArrayRef<const Record *> VerClauses,
                               raw_ostream &OS, StringRef ClauseSetPrefix,
@@ -1360,6 +1411,13 @@ void emitDirectivesBasicImpl(const DirectiveLanguage &DirLang,
   // isAllowedClauseForDirective(Directive D, Clause C, unsigned Version)
   generateIsAllowedClause(DirLang, OS);
 
+  // isAllowedLoopModifier(Directive D, LoopModifier LM)
+  generateIsAllowedLoopModifier(DirLang, OS);
+
+  // getLoopModifierName(LoopModifier Kind)
+  generateGetName(DirLang.getLoopModifiers(), OS, "LoopModifier", DirLang,
+                  DirLang.getLoopModifierPrefix());
+
   // Leaf table for getLeafConstructs, etc.
   emitLeafTable(DirLang, OS, "LeafConstructTable");
 }

>From 1c4fb4e98a1c069b9b1cb4815223ae3d8eba90b5 Mon Sep 17 00:00:00 2001
From: Ferran Toda <ferran.todacasaban at bsc.es>
Date: Wed, 17 Jun 2026 14:05:23 +0000
Subject: [PATCH 2/5] remove lang name from function

---
 flang/include/flang/Parser/dump-parse-tree.h  |  2 +-
 flang/lib/Parser/unparse.cpp                  |  4 ++--
 flang/lib/Semantics/check-omp-loop.cpp        |  2 +-
 .../utils/TableGen/Basic/DirectiveEmitter.cpp | 22 ++++++++++---------
 4 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index b3e674603d4fa..d895b9bb53d70 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -560,7 +560,7 @@ class ParseTreeDumper {
 
   static std::string GetNodeName(const llvm::omp::LoopModifier &x) {
     return llvm::Twine(
-        "llvm::omp::LoopModifier = ", llvm::omp::getOpenMPLoopModifierName(x))
+        "llvm::omp::LoopModifier = ", llvm::omp::getLoopModifierName(x))
         .str();
   }
   NODE(parser, OmpApplyClause)
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index e1b02f4c70e8e..05b9de704d5b6 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2214,8 +2214,8 @@ class UnparseVisitor {
   void Unparse(const OmpAppendArgsClause &x) { Walk(x.v, ","); }
   void Unparse(const OmpArgumentList &x) { Walk(x.v, ", "); }
   void Unparse(const OmpLoopModifier &x) {
-    Word(llvm::omp::getOpenMPLoopModifierName(
-        std::get<llvm::omp::LoopModifier>(x.t)));
+    Word(
+        llvm::omp::getLoopModifierName(std::get<llvm::omp::LoopModifier>(x.t)));
     Walk("(", std::get<std::optional<std::list<ScalarIntConstantExpr>>>(x.t),
         ")");
   }
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 99778afb4938e..009ea6acfd6e6 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -885,7 +885,7 @@ void OmpStructureChecker::Enter(const parser::OmpLoopModifier &x) {
   unsigned version{context_.langOptions().OpenMPVersion};
   auto &m{std::get<llvm::omp::LoopModifier>(x.t)};
   if (!llvm::omp::isAllowedLoopModifier(dir, m)) {
-    llvm::StringRef name = llvm::omp::getOpenMPLoopModifierName(m);
+    llvm::StringRef name = llvm::omp::getLoopModifierName(m);
     context_.Say(x.source,
         "%s modifier is not allowed on %s directive"_err_en_US,
         parser::ToUpperCaseLetters(name),
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index 07157fc23a9ab..20fb878f08ec6 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -361,8 +361,8 @@ static void emitDirectivesDecl(const RecordKeeper &Records, raw_ostream &OS) {
        << getMaxLeafCount(DirLang) << "; }\n";
     OS << "LLVM_ABI bool isAllowedLoopModifier(Directive D, LoopModifier "
           "LM);\n";
-    OS << "LLVM_ABI StringRef get" << Lang
-       << "LoopModifierName(LoopModifier LM, unsigned Ver = 0);\n";
+    OS << "LLVM_ABI StringRef getLoopModifierName(LoopModifier LM, unsigned "
+          "Ver = 0);\n";
     OS << EnumHelperFuncs;
   } // close DirLangNS
 
@@ -394,12 +394,11 @@ orderSpellings(ArrayRef<Spelling::Value> Spellings) {
 // Generate function implementation for get<Enum>Name(StringRef Str)
 static void generateGetName(ArrayRef<const Record *> Records, raw_ostream &OS,
                             StringRef Enum, const DirectiveLanguage &DirLang,
-                            StringRef Prefix) {
-  StringRef Lang = DirLang.getName();
+                            StringRef LangName, StringRef Prefix) {
   std::string Qual = getQualifier(DirLang);
   OS << "\n";
-  OS << "llvm::StringRef " << Qual << "get" << Lang << Enum << "Name(" << Qual
-     << Enum << " Kind, unsigned Version) {\n";
+  OS << "llvm::StringRef " << Qual << "get" << LangName << Enum << "Name("
+     << Qual << Enum << " Kind, unsigned Version) {\n";
   OS << "  switch (Kind) {\n";
   for (const Record *R : Records) {
     BaseRecord Rec(R);
@@ -426,7 +425,8 @@ static void generateGetName(ArrayRef<const Record *> Records, raw_ostream &OS,
     }
   }
   OS << "  }\n"; // switch
-  OS << "  llvm_unreachable(\"Invalid " << Lang << " " << Enum << " kind\");\n";
+  OS << "  llvm_unreachable(\"Invalid " << LangName << " " << Enum
+     << " kind\");\n";
   OS << "}\n";
 }
 
@@ -1395,14 +1395,16 @@ void emitDirectivesBasicImpl(const DirectiveLanguage &DirLang,
                   /*ImplicitAsUnknown=*/false);
 
   // getDirectiveName(Directive Kind)
-  generateGetName(DirLang.getDirectives(), OS, "Directive", DirLang, DPrefix);
+  generateGetName(DirLang.getDirectives(), OS, "Directive", DirLang,
+                  DirLang.getName(), DPrefix);
 
   // getClauseKind(StringRef Str)
   generateGetKind(DirLang.getClauses(), OS, "Clause", DirLang, CPrefix,
                   /*ImplicitAsUnknown=*/true);
 
   // getClauseName(Clause Kind)
-  generateGetName(DirLang.getClauses(), OS, "Clause", DirLang, CPrefix);
+  generateGetName(DirLang.getClauses(), OS, "Clause", DirLang,
+                  DirLang.getName(), CPrefix);
 
   // <enumClauseValue> get<enumClauseValue>(StringRef Str) ; string -> value
   // StringRef get<enumClauseValue>Name(<enumClauseValue>) ; value -> string
@@ -1415,7 +1417,7 @@ void emitDirectivesBasicImpl(const DirectiveLanguage &DirLang,
   generateIsAllowedLoopModifier(DirLang, OS);
 
   // getLoopModifierName(LoopModifier Kind)
-  generateGetName(DirLang.getLoopModifiers(), OS, "LoopModifier", DirLang,
+  generateGetName(DirLang.getLoopModifiers(), OS, "LoopModifier", DirLang, "",
                   DirLang.getLoopModifierPrefix());
 
   // Leaf table for getLeafConstructs, etc.

>From ff7686d1f09b567455049bb7d313c9c295d7c3b4 Mon Sep 17 00:00:00 2001
From: Ferran Toda <ferran.todacasaban at bsc.es>
Date: Thu, 18 Jun 2026 15:30:49 +0000
Subject: [PATCH 3/5] using modifier mechanisms

---
 flang/include/flang/Parser/dump-parse-tree.h  |  1 +
 flang/include/flang/Parser/parse-tree.h       | 39 ++++++++++---------
 .../flang/Semantics/openmp-modifiers.h        |  3 +-
 flang/lib/Parser/openmp-parsers.cpp           |  4 +-
 flang/lib/Parser/unparse.cpp                  |  3 +-
 flang/lib/Semantics/check-omp-loop.cpp        |  5 ++-
 flang/lib/Semantics/check-omp-structure.cpp   |  1 -
 flang/lib/Semantics/check-omp-structure.h     |  3 +-
 flang/lib/Semantics/openmp-modifiers.cpp      | 16 ++++++++
 flang/test/Parser/OpenMP/apply01.f90          | 10 ++---
 flang/test/Parser/OpenMP/apply02.f90          |  8 ++--
 11 files changed, 57 insertions(+), 36 deletions(-)

diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index d895b9bb53d70..ef8680ad93d8d 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -564,6 +564,7 @@ class ParseTreeDumper {
         .str();
   }
   NODE(parser, OmpApplyClause)
+  NODE(OmpApplyClause, Modifier)
   NODE(parser, OmpArgument)
   NODE(parser, OmpArgumentList)
   NODE(parser, OmpAssumeDirective)
diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index ea05c03d009f7..9fa9cb7cd2772 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4150,6 +4150,24 @@ struct OmpLinearModifier {
   WRAPPER_CLASS_BOILERPLATE(OmpLinearModifier, Value);
 };
 
+// Ref: [6.0:372-373]
+//
+// loop-modifier ->
+//    FLATTENED |
+//    FUSED | GRID | IDENTITY |
+//    INTERCHANGED |
+//    INTRATILE | OFFSETS |
+//    REVERSED | SPLIT |
+//    UNROLLED
+//    [( ScalarIntConstantExpr-list )]
+struct OmpLoopModifier {
+  TUPLE_CLASS_BOILERPLATE(OmpLoopModifier);
+  std::tuple<llvm::omp::LoopModifier,
+      std::optional<std::list<ScalarIntConstantExpr>>>
+      t;
+  CharBlock source;
+};
+
 // Ref: [5.1:100-104], [5.2:277], [6.0:452-453]
 //
 // lower-bound ->
@@ -4353,24 +4371,6 @@ struct OmpxHoldModifier {
   WRAPPER_CLASS_BOILERPLATE(OmpxHoldModifier, Value);
 };
 
-// Ref: [6.0:372-373]
-//
-// loop-modifier ->
-//    FLATTENED |
-//    FUSED | GRID | IDENTITY |
-//    INTERCHANGED |
-//    INTRATILE | OFFSETS |
-//    REVERSED | SPLIT |
-//    UNROLLED
-//    [( ScalarIntConstantExpr-list )]
-struct OmpLoopModifier {
-  TUPLE_CLASS_BOILERPLATE(OmpLoopModifier);
-  std::tuple<llvm::omp::LoopModifier,
-      std::optional<std::list<ScalarIntConstantExpr>>>
-      t;
-  CharBlock source;
-};
-
 // context-selector
 using OmpContextSelector = traits::OmpContextSelectorSpecification;
 } // namespace modifier
@@ -4504,7 +4504,8 @@ struct OmpContainsClause {
 //    APPLY( [loop-modifier :] directive-specification-list )
 struct OmpApplyClause {
   TUPLE_CLASS_BOILERPLATE(OmpApplyClause);
-  std::tuple<std::optional<OmpLoopModifier>,
+  MODIFIER_BOILERPLATE(OmpLoopModifier);
+  std::tuple<MODIFIERS(),
       std::list<OmpDirectiveSpecification>>
       t;
 };
diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
index 8dcac9b179924..1e549c288925b 100644
--- a/flang/include/flang/Semantics/openmp-modifiers.h
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -44,7 +44,7 @@ namespace Fortran::semantics {
 // or post-modifier (i.e. item: modifier). The default is pre-.
 // Add an additional property that reflects the type of modifier.
 
-ENUM_CLASS(OmpProperty, Required, Unique, Exclusive, Ultimate, Post)
+ENUM_CLASS(OmpProperty, Required, Unique, Exclusive, Ultimate, Post, Optional)
 using OmpProperties = common::EnumSet<OmpProperty, OmpProperty_enumSize>;
 using OmpClauses =
     common::EnumSet<llvm::omp::Clause, llvm::omp::Clause_enumSize>;
@@ -90,6 +90,7 @@ DECLARE_DESCRIPTOR(parser::OmpInteropType);
 DECLARE_DESCRIPTOR(parser::OmpIterator);
 DECLARE_DESCRIPTOR(parser::OmpLastprivateModifier);
 DECLARE_DESCRIPTOR(parser::OmpLinearModifier);
+DECLARE_DESCRIPTOR(parser::OmpLoopModifier);
 DECLARE_DESCRIPTOR(parser::OmpLowerBound);
 DECLARE_DESCRIPTOR(parser::OmpMapper);
 DECLARE_DESCRIPTOR(parser::OmpMapType);
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index aedca5e582100..4615067af7d80 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -1101,6 +1101,8 @@ TYPE_PARSER(construct<OmpAdjustArgsClause::OmpAdjustOp>(
     "NEED_DEVICE_PTR" >>
         pure(OmpAdjustArgsClause::OmpAdjustOp::Value::Need_Device_Ptr)))
 
+TYPE_PARSER(construct<OmpApplyClause::Modifier>(Parser<OmpLoopModifier>{}))
+
 TYPE_PARSER(sourced(construct<OmpLoopModifier>(
     "FUSED" >> pure(llvm::omp::LoopModifier::OMPLM_fused) ||
         "GRID" >> pure(llvm::omp::LoopModifier::OMPLM_grid) ||
@@ -1155,7 +1157,7 @@ TYPE_PARSER(construct<OmpAffinityClause>(
     maybe(nonemptyList(Parser<OmpAffinityClause::Modifier>{}) / ":"),
     Parser<OmpObjectList>{}))
 
-TYPE_PARSER(construct<OmpApplyClause>(maybe(Parser<OmpLoopModifier>{} / ":"),
+TYPE_PARSER(construct<OmpApplyClause>(maybe(nonemptyList(Parser<OmpApplyClause::Modifier>{} / ":")),
     nonemptyList(OmpDirectiveSpecificationParser(/*allowCommas=*/false))))
 
 // 2.4 Requires construct [OpenMP 5.0]
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 05b9de704d5b6..2b15666962a67 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2220,7 +2220,8 @@ class UnparseVisitor {
         ")");
   }
   void Unparse(const OmpApplyClause &x) {
-    Walk(std::get<std::optional<OmpLoopModifier>>(x.t), ": ");
+    using Modifier = OmpApplyClause::Modifier;
+    Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
     Walk(std::get<std::list<OmpDirectiveSpecification>>(x.t));
   }
   void Unparse(const OmpAttachModifier &x) {
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 009ea6acfd6e6..95d55c7b94c71 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -915,12 +915,13 @@ void OmpStructureChecker::Enter(const parser::OmpLoopModifier &x) {
   }
 }
 
-void OmpStructureChecker::Enter(const parser::OmpApplyClause &x) {
+void OmpStructureChecker::Enter(const parser::OmpClause::Apply &x) {
   EnterDirectiveNest(ApplyNest);
   CheckAllowedClause(llvm::omp::Clause::OMPC_apply);
+  OmpVerifyModifiers(x.v, llvm::omp::Clause::OMPC_apply, GetContext().clauseSource, context_);
 }
 
-void OmpStructureChecker::Leave(const parser::OmpApplyClause &x) {
+void OmpStructureChecker::Leave(const parser::OmpClause::Apply &x) {
   ExitDirectiveNest(ApplyNest);
 }
 
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 7c531ae0046ae..be959bd3d5f5f 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5972,7 +5972,6 @@ CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel)
 CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire)
 CHECK_SIMPLE_CLAUSE(AdjustArgs, OMPC_adjust_args)
 CHECK_SIMPLE_CLAUSE(AppendArgs, OMPC_append_args)
-CHECK_SIMPLE_CLAUSE(Apply, OMPC_apply)
 CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind)
 CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture)
 CHECK_SIMPLE_CLAUSE(Collector, OMPC_collector)
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index ea358430c1e2d..a9aec01af354e 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -175,8 +175,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
 
   void Enter(const parser::OmpLoopModifier &);
 
-  void Enter(const parser::OmpApplyClause &);
-  void Leave(const parser::OmpApplyClause &);
+  void Leave(const parser::OmpClause::Apply &);
 
   template <typename A> void Enter(const parser::Statement<A> &);
   void Leave(const parser::GotoStmt &);
diff --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp
index b6a6d476ceec6..c261240480f63 100644
--- a/flang/lib/Semantics/openmp-modifiers.cpp
+++ b/flang/lib/Semantics/openmp-modifiers.cpp
@@ -456,6 +456,22 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLinearModifier>() {
   return desc;
 }
 
+template <>
+const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLoopModifier>() {
+  static const OmpModifierDescriptor desc{
+      /*name=*/"loop-modifier",
+      /*props=*/
+      {
+          {60, {OmpProperty::Optional}},
+      },
+      /*clauses=*/
+      {
+          {60, {Clause::OMPC_apply}},
+      },
+  };
+  return desc;
+}
+
 template <>
 const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLowerBound>() {
   static const OmpModifierDescriptor desc{
diff --git a/flang/test/Parser/OpenMP/apply01.f90 b/flang/test/Parser/OpenMP/apply01.f90
index c1c5d4be2d0eb..555ef769e22b3 100644
--- a/flang/test/Parser/OpenMP/apply01.f90
+++ b/flang/test/Parser/OpenMP/apply01.f90
@@ -52,7 +52,7 @@ subroutine apply_modifier(x)
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> Expr = '2_4'
 !PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
 !PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
-!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
 !PARSE-TREE: | | | llvm::omp::LoopModifier = grid
 !PARSE-TREE: | | OmpDirectiveSpecification
 !PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
@@ -79,14 +79,14 @@ subroutine apply_2_clauses(x)
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> Expr = '2_4'
 !PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
 !PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
-!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
 !PARSE-TREE: | | | llvm::omp::LoopModifier = intratile
 !PARSE-TREE: | | OmpDirectiveSpecification
 !PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = unroll
 !PARSE-TREE: | | | OmpClauseList ->
 !PARSE-TREE: | | | Flags = {}
 !PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
-!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
 !PARSE-TREE: | | | llvm::omp::LoopModifier = grid
 !PARSE-TREE: | | OmpDirectiveSpecification
 !PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = reverse
@@ -120,14 +120,14 @@ subroutine apply_inside_apply(x)
 !PARSE-TREE: OmpBeginDirective
 !PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = fuse
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
-!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
 !PARSE-TREE: | | | llvm::omp::LoopModifier = fused
 !PARSE-TREE: | | OmpDirectiveSpecification
 !PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = tile
 !PARSE-TREE: | | | OmpClauseList -> OmpClause -> Sizes -> Scalar -> Integer -> Expr = '2_4'
 !PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '2'
 !PARSE-TREE: | | | OmpClause -> Apply -> OmpApplyClause
-!PARSE-TREE: | | | | OmpLoopModifier
+!PARSE-TREE: | | | | Modifier -> OmpLoopModifier
 !PARSE-TREE: | | | | | llvm::omp::LoopModifier = grid
 !PARSE-TREE: | | | | OmpDirectiveSpecification
 !PARSE-TREE: | | | | | OmpDirectiveName -> llvm::omp::Directive = reverse
diff --git a/flang/test/Parser/OpenMP/apply02.f90 b/flang/test/Parser/OpenMP/apply02.f90
index 803f0ac73d25a..e2160796bf40e 100644
--- a/flang/test/Parser/OpenMP/apply02.f90
+++ b/flang/test/Parser/OpenMP/apply02.f90
@@ -23,7 +23,7 @@ subroutine apply_all(x)
 !PARSE-TREE: OmpBeginDirective
 !PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = interchange
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
-!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
 !PARSE-TREE: | | | llvm::omp::LoopModifier = interchanged
 !PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '1_4'
 !PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '1'
@@ -60,7 +60,7 @@ subroutine apply_one(x)
 !PARSE-TREE: OmpBeginDirective
 !PARSE-TREE: | OmpDirectiveName -> llvm::omp::Directive = interchange
 !PARSE-TREE: | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
-!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
 !PARSE-TREE: | | | llvm::omp::LoopModifier = interchanged
 !PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '2_4'
 !PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '2'
@@ -95,14 +95,14 @@ subroutine apply_inside_apply(x)
 !PARSE-TREE: | Scalar -> Integer -> Expr = '2_4'
 !PARSE-TREE: | | LiteralConstant -> IntLiteralConstant = '2'
 !PARSE-TREE: | OmpClause -> Apply -> OmpApplyClause
-!PARSE-TREE: | | OmpLoopModifier
+!PARSE-TREE: | | Modifier -> OmpLoopModifier
 !PARSE-TREE: | | | llvm::omp::LoopModifier = grid
 !PARSE-TREE: | | | Scalar -> Integer -> Constant -> Expr = '1_4'
 !PARSE-TREE: | | | | LiteralConstant -> IntLiteralConstant = '1'
 !PARSE-TREE: | | OmpDirectiveSpecification
 !PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = interchange
 !PARSE-TREE: | | | OmpClauseList -> OmpClause -> Apply -> OmpApplyClause
-!PARSE-TREE: | | | | OmpLoopModifier
+!PARSE-TREE: | | | | Modifier -> OmpLoopModifier
 !PARSE-TREE: | | | | | llvm::omp::LoopModifier = interchanged
 !PARSE-TREE: | | | | | Scalar -> Integer -> Constant -> Expr = '2_4'
 !PARSE-TREE: | | | | | | LiteralConstant -> IntLiteralConstant = '2'

>From 71bd484d147b5bcf4c673a183b34450b2dd46e18 Mon Sep 17 00:00:00 2001
From: Ferran Toda <ferran.todacasaban at bsc.es>
Date: Fri, 19 Jun 2026 14:14:35 +0000
Subject: [PATCH 4/5] format

---
 flang/include/flang/Parser/parse-tree.h | 4 +---
 flang/lib/Parser/openmp-parsers.cpp     | 3 ++-
 flang/lib/Semantics/check-omp-loop.cpp  | 3 ++-
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index 9fa9cb7cd2772..8c398fa1c4606 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -4505,9 +4505,7 @@ struct OmpContainsClause {
 struct OmpApplyClause {
   TUPLE_CLASS_BOILERPLATE(OmpApplyClause);
   MODIFIER_BOILERPLATE(OmpLoopModifier);
-  std::tuple<MODIFIERS(),
-      std::list<OmpDirectiveSpecification>>
-      t;
+  std::tuple<MODIFIERS(), std::list<OmpDirectiveSpecification>> t;
 };
 
 // Ref: [4.5:46-50], [5.0:74-78], [5.1:92-96], [5.2:109]
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 4615067af7d80..05b4d5cf46f79 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -1157,7 +1157,8 @@ TYPE_PARSER(construct<OmpAffinityClause>(
     maybe(nonemptyList(Parser<OmpAffinityClause::Modifier>{}) / ":"),
     Parser<OmpObjectList>{}))
 
-TYPE_PARSER(construct<OmpApplyClause>(maybe(nonemptyList(Parser<OmpApplyClause::Modifier>{} / ":")),
+TYPE_PARSER(construct<OmpApplyClause>(
+    maybe(nonemptyList(Parser<OmpApplyClause::Modifier>{} / ":")),
     nonemptyList(OmpDirectiveSpecificationParser(/*allowCommas=*/false))))
 
 // 2.4 Requires construct [OpenMP 5.0]
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 95d55c7b94c71..b4bfa6bae3db6 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -918,7 +918,8 @@ void OmpStructureChecker::Enter(const parser::OmpLoopModifier &x) {
 void OmpStructureChecker::Enter(const parser::OmpClause::Apply &x) {
   EnterDirectiveNest(ApplyNest);
   CheckAllowedClause(llvm::omp::Clause::OMPC_apply);
-  OmpVerifyModifiers(x.v, llvm::omp::Clause::OMPC_apply, GetContext().clauseSource, context_);
+  OmpVerifyModifiers(
+      x.v, llvm::omp::Clause::OMPC_apply, GetContext().clauseSource, context_);
 }
 
 void OmpStructureChecker::Leave(const parser::OmpClause::Apply &x) {

>From ea08d4e7d84b20ae5260e5963c692e1d7b247e49 Mon Sep 17 00:00:00 2001
From: Ferran Toda <ferran.todacasaban at bsc.es>
Date: Fri, 19 Jun 2026 14:15:41 +0000
Subject: [PATCH 5/5] remove optional property

---
 flang/include/flang/Semantics/openmp-modifiers.h | 2 +-
 flang/lib/Semantics/openmp-modifiers.cpp         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
index 1e549c288925b..66a4f3319c4d1 100644
--- a/flang/include/flang/Semantics/openmp-modifiers.h
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -44,7 +44,7 @@ namespace Fortran::semantics {
 // or post-modifier (i.e. item: modifier). The default is pre-.
 // Add an additional property that reflects the type of modifier.
 
-ENUM_CLASS(OmpProperty, Required, Unique, Exclusive, Ultimate, Post, Optional)
+ENUM_CLASS(OmpProperty, Required, Unique, Exclusive, Ultimate, Post)
 using OmpProperties = common::EnumSet<OmpProperty, OmpProperty_enumSize>;
 using OmpClauses =
     common::EnumSet<llvm::omp::Clause, llvm::omp::Clause_enumSize>;
diff --git a/flang/lib/Semantics/openmp-modifiers.cpp b/flang/lib/Semantics/openmp-modifiers.cpp
index c261240480f63..fe8e087cd5a94 100644
--- a/flang/lib/Semantics/openmp-modifiers.cpp
+++ b/flang/lib/Semantics/openmp-modifiers.cpp
@@ -462,7 +462,7 @@ const OmpModifierDescriptor &OmpGetDescriptor<parser::OmpLoopModifier>() {
       /*name=*/"loop-modifier",
       /*props=*/
       {
-          {60, {OmpProperty::Optional}},
+          {60, {}},
       },
       /*clauses=*/
       {



More information about the flang-commits mailing list