[flang-commits] [flang] 2fce8c9 - [flang][OpenMP] Frontend support for BEGIN/END METADIRECTIVE (#194402)

via flang-commits flang-commits at lists.llvm.org
Tue Apr 28 13:55:22 PDT 2026


Author: Krzysztof Parzyszek
Date: 2026-04-28T15:55:16-05:00
New Revision: 2fce8c9d9e90d9697cb54907347396eebc1b5d61

URL: https://github.com/llvm/llvm-project/commit/2fce8c9d9e90d9697cb54907347396eebc1b5d61
DIFF: https://github.com/llvm/llvm-project/commit/2fce8c9d9e90d9697cb54907347396eebc1b5d61.diff

LOG: [flang][OpenMP] Frontend support for BEGIN/END METADIRECTIVE (#194402)

This implements parsing of BEGIN/END METADIRECTIVE, plus a minimal
semantic check for the association of a directive in a WHEN/OTHERWISE
clauses.

The same semantic checks for the context selectors apply here as in the
case of a standalone METADIRECTIVE.

Added: 
    flang/test/Lower/OpenMP/Todo/begin-metadirective.f90
    flang/test/Parser/OpenMP/begin-metadirective.f90
    flang/test/Semantics/OpenMP/begin-metadirective.f90

Modified: 
    flang/include/flang/Parser/dump-parse-tree.h
    flang/include/flang/Parser/parse-tree.h
    flang/lib/Lower/OpenMP/OpenMP.cpp
    flang/lib/Parser/openmp-parsers.cpp
    flang/lib/Parser/unparse.cpp
    flang/lib/Semantics/check-omp-metadirective.cpp
    flang/lib/Semantics/check-omp-structure.cpp
    flang/lib/Semantics/check-omp-structure.h
    flang/lib/Semantics/resolve-directives.cpp

Removed: 
    


################################################################################
diff  --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h
index 4bfeccfbe5c67..4c49f3e6e2417 100644
--- a/flang/include/flang/Parser/dump-parse-tree.h
+++ b/flang/include/flang/Parser/dump-parse-tree.h
@@ -586,6 +586,7 @@ class ParseTreeDumper {
   NODE_ENUM(OmpDefaultmapClause, ImplicitBehavior)
   NODE(parser, OmpDeleteModifier)
   NODE_ENUM(OmpDeleteModifier, Value)
+  NODE(parser, OmpDelimitedMetadirectiveDirective)
   NODE(parser, OmpDependClause)
   NODE(OmpDependClause, TaskDep)
   NODE(OmpDependClause::TaskDep, Modifier)

diff  --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h
index a07e47bf92bd1..28a565d7e27f8 100644
--- a/flang/include/flang/Parser/parse-tree.h
+++ b/flang/include/flang/Parser/parse-tree.h
@@ -5082,7 +5082,7 @@ struct OmpClauseList {
 // --- Directives and constructs
 
 struct OmpDirectiveSpecification {
-  ENUM_CLASS(Flag, DeprecatedSyntax, CrossesLabelDo)
+  ENUM_CLASS(Flag, DeprecatedSyntax, CrossesLabelDo, ExplicitBegin)
   using Flags = common::EnumSet<Flag, Flag_enumSize>;
 
   TUPLE_CLASS_BOILERPLATE(OmpDirectiveSpecification);
@@ -5133,6 +5133,11 @@ struct OmpMetadirectiveDirective {
       OmpMetadirectiveDirective, OmpDirectiveSpecification);
 };
 
+struct OmpDelimitedMetadirectiveDirective : public OmpBlockConstruct {
+  INHERITED_TUPLE_CLASS_BOILERPLATE(
+      OmpDelimitedMetadirectiveDirective, OmpBlockConstruct);
+};
+
 // Ref: [5.1:89-90], [5.2:216]
 //
 // nothing-directive ->
@@ -5468,7 +5473,7 @@ struct OpenMPConstruct {
       OpenMPSectionConstruct, OpenMPLoopConstruct, OmpBlockConstruct,
       OpenMPAtomicConstruct, OmpAllocateDirective, OpenMPDispatchConstruct,
       OpenMPUtilityConstruct, OpenMPAllocatorsConstruct, OpenMPAssumeConstruct,
-      OpenMPCriticalConstruct>
+      OpenMPCriticalConstruct, OmpDelimitedMetadirectiveDirective>
       u;
 };
 

diff  --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 3a2c5a3862bd1..6859f5b291342 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -4544,6 +4544,14 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
   lowerAtomic(converter, symTable, semaCtx, eval, construct);
 }
 
+static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
+                   semantics::SemanticsContext &semaCtx,
+                   lower::pft::Evaluation &eval,
+                   const parser::OmpDelimitedMetadirectiveDirective &meta) {
+  TODO(converter.getCurrentLocation(),
+       "OpenMP BEGIN/END METADIRECTIVE lowering");
+}
+
 static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
                    semantics::SemanticsContext &semaCtx,
                    lower::pft::Evaluation &eval,

diff  --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 153d7ad4717c8..f8ee77a275344 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -1924,16 +1924,15 @@ TYPE_PARSER(construct<OmpMetadirectiveDirective>(
         IsDirective(llvm::omp::Directive::OMPD_metadirective)) >=
     OmpDirectiveSpecificationParser{}))
 
-struct OmpBeginDirectiveParser {
+struct OmpDirectiveParser {
   using resultType = OmpDirectiveSpecification;
 
-  constexpr OmpBeginDirectiveParser(DirectiveSet dirs) : dirs_(dirs) {}
-  constexpr OmpBeginDirectiveParser(llvm::omp::Directive dir) {
-    dirs_.set(llvm::to_underlying(dir));
-  }
+  constexpr OmpDirectiveParser(DirectiveSet dirs) : dirs_(dirs) {}
+  constexpr OmpDirectiveParser(llvm::omp::Directive dir)
+      : dirs_({static_cast<unsigned>(llvm::to_underlying(dir))}) {}
 
   std::optional<resultType> Parse(ParseState &state) const {
-    auto &&p{predicated(Parser<OmpDirectiveName>{}, IsMemberOf(dirs_)) >=
+    auto p{predicated(Parser<OmpDirectiveName>{}, IsMemberOf(dirs_)) >=
         OmpDirectiveSpecificationParser{}};
     return p.Parse(state);
   }
@@ -1942,18 +1941,61 @@ struct OmpBeginDirectiveParser {
   DirectiveSet dirs_;
 };
 
-struct OmpEndDirectiveParser {
+// Parse the directive that begins a construct. In some cases the directive
+// has to be preceded with an explicit "BEGIN", in other cases the begin is
+// assumed to be implicit. This parser is invoked after the OpenMP sentinel
+// has been consumed.
+// Note: Even if OMPD_begin_somename exists, the directive(s) to parse should
+// use the non-begin id, i.e. OMPD_somename.
+struct OmpBeginDirectiveParser {
   using resultType = OmpDirectiveSpecification;
 
-  constexpr OmpEndDirectiveParser(DirectiveSet dirs) : dirs_(dirs) {}
-  constexpr OmpEndDirectiveParser(llvm::omp::Directive dir) {
-    dirs_.set(llvm::to_underlying(dir));
+  constexpr OmpBeginDirectiveParser(DirectiveSet dirs, bool implicit = true)
+      : dparser_(dirs), implicit_(implicit) {}
+  constexpr OmpBeginDirectiveParser(
+      llvm::omp::Directive dir, bool implicit = true)
+      : dparser_(dir), implicit_(implicit) {}
+
+  std::optional<resultType> Parse(ParseState &state) const {
+    if (implicit_) {
+      return dparser_.Parse(state);
+    }
+
+    if (auto &&beginToken{verbatim("BEGIN"_sptok).Parse(state)}) {
+      if (auto &&dirSpec{dparser_.Parse(state)}) {
+        // Extend the "source" on both the OmpDirectiveName and the
+        // OmpDirectiveNameSpecification.
+        CharBlock &nameSource{std::get<OmpDirectiveName>(dirSpec->t).source};
+        nameSource.ExtendToCover(beginToken->source);
+        dirSpec->source.ExtendToCover(beginToken->source);
+        std::get<OmpDirectiveSpecification::Flags>(dirSpec->t)
+            .set(OmpDirectiveSpecification::Flag::ExplicitBegin);
+        return std::move(*dirSpec);
+      }
+    }
+    return std::nullopt;
   }
 
+private:
+  OmpDirectiveParser dparser_;
+  bool implicit_;
+};
+
+// Parse the directive that end a construct. In all cases the directive
+// must be preceded with an explicit "END". This parser is invoked directly
+// from other construct parsers, so it must handle the OpenMP sentinel.
+// Note: Even if OMPD_end_somename exists, the directive(s) to parse should
+// use the non-end id, i.e. OMPD_somename.
+struct OmpEndDirectiveParser {
+  using resultType = OmpDirectiveSpecification;
+
+  constexpr OmpEndDirectiveParser(DirectiveSet dirs) : dparser_(dirs) {}
+  constexpr OmpEndDirectiveParser(llvm::omp::Directive dir) : dparser_(dir) {}
+
   std::optional<resultType> Parse(ParseState &state) const {
     if (startOmpLine.Parse(state)) {
       if (auto endToken{verbatim("END"_sptok).Parse(state)}) {
-        if (auto &&spec{OmpBeginDirectiveParser(dirs_).Parse(state)}) {
+        if (auto &&spec{dparser_.Parse(state)}) {
           // Extend the "source" on both the OmpDirectiveName and the
           // OmpDirectiveNameSpecification.
           CharBlock &nameSource{std::get<OmpDirectiveName>(spec->t).source};
@@ -1967,16 +2009,18 @@ struct OmpEndDirectiveParser {
   }
 
 private:
-  DirectiveSet dirs_;
+  OmpDirectiveParser dparser_;
 };
 
 struct OmpStatementConstructParser {
   using resultType = OmpBlockConstruct;
 
-  constexpr OmpStatementConstructParser(llvm::omp::Directive dir) : dir_(dir) {}
+  constexpr OmpStatementConstructParser(
+      llvm::omp::Directive dir, bool implicit = true)
+      : dir_(dir), implicit_(implicit) {}
 
   std::optional<resultType> Parse(ParseState &state) const {
-    if (auto begin{OmpBeginDirectiveParser(dir_).Parse(state)}) {
+    if (auto begin{OmpBeginDirectiveParser(dir_, implicit_).Parse(state)}) {
       Block body;
       if (auto stmt{attempt(validEPC).Parse(state)}) {
         body.emplace_back(std::move(*stmt));
@@ -1994,15 +2038,18 @@ struct OmpStatementConstructParser {
 
 private:
   llvm::omp::Directive dir_;
+  bool implicit_;
 };
 
 struct OmpBlockConstructParser {
   using resultType = OmpBlockConstruct;
 
-  constexpr OmpBlockConstructParser(llvm::omp::Directive dir) : dir_(dir) {}
+  constexpr OmpBlockConstructParser(
+      llvm::omp::Directive dir, bool implicit = true)
+      : dir_(dir), implicit_(implicit) {}
 
   std::optional<resultType> Parse(ParseState &state) const {
-    if (auto &&begin{OmpBeginDirectiveParser(dir_).Parse(state)}) {
+    if (auto &&begin{OmpBeginDirectiveParser(dir_, implicit_).Parse(state)}) {
       if (IsStandaloneOrdered(*begin)) {
         return std::nullopt;
       }
@@ -2030,19 +2077,21 @@ struct OmpBlockConstructParser {
 
 private:
   llvm::omp::Directive dir_;
+  bool implicit_;
 };
 
 struct OmpLoopConstructParser {
   using resultType = OpenMPLoopConstruct;
 
-  constexpr OmpLoopConstructParser(DirectiveSet dirs) : dirs_(dirs) {}
+  constexpr OmpLoopConstructParser(DirectiveSet dirs, bool implicit = true)
+      : dirs_(dirs), implicit_(implicit) {}
 
   std::optional<resultType> Parse(ParseState &state) const {
     auto ompLoopConstruct{asBlock(predicated(executionPartConstruct,
         [](auto &epc) { return Unwrap<OpenMPLoopConstruct>(epc); }))};
     auto loopItem{LoopNestParser{} || ompLoopConstruct};
 
-    if (auto &&begin{OmpBeginDirectiveParser(dirs_).Parse(state)}) {
+    if (auto &&begin{OmpBeginDirectiveParser(dirs_, implicit_).Parse(state)}) {
       auto loopDir{begin->DirId()};
       auto assoc{llvm::omp::getDirectiveAssociation(loopDir)};
       if (assoc == llvm::omp::Association::LoopNest) {
@@ -2078,6 +2127,7 @@ struct OmpLoopConstructParser {
 
 private:
   DirectiveSet dirs_;
+  bool implicit_;
 };
 
 struct OmpDeclarativeAllocateParser {
@@ -2457,6 +2507,10 @@ TYPE_PARSER( //
     MakeBlockConstruct(llvm::omp::Directive::OMPD_workdistribute))
 #undef MakeBlockConstruct
 
+TYPE_PARSER(sourced(
+    construct<OmpDelimitedMetadirectiveDirective>(OmpBlockConstructParser{
+        llvm::omp::Directive::OMPD_metadirective, /*implicit=*/false})))
+
 // OMP SECTIONS Directive
 static constexpr DirectiveSet GetSectionsDirectives() {
   using Directive = llvm::omp::Directive;
@@ -2493,8 +2547,12 @@ static bool IsExecutionPart(const OmpDirectiveName &name) {
   return name.IsExecutionPart();
 }
 
-TYPE_PARSER(construct<OpenMPExecDirective>(
-    startOmpLine >> predicated(Parser<OmpDirectiveName>{}, IsExecutionPart)))
+TYPE_PARSER(construct<OpenMPExecDirective>(startOmpLine >>
+    first( //
+        predicated(Parser<OmpDirectiveName>{}, IsExecutionPart),
+        // begin/end metadirective
+        predicated("BEGIN"_sptok >> Parser<OmpDirectiveName>{},
+            IsDirective(llvm::omp::Directive::OMPD_metadirective)))))
 
 TYPE_CONTEXT_PARSER("OpenMP construct"_en_US,
     startOmpLine >>
@@ -2512,7 +2570,9 @@ TYPE_CONTEXT_PARSER("OpenMP construct"_en_US,
                 construct<OpenMPConstruct>(Parser<OpenMPDispatchConstruct>{}),
                 construct<OpenMPConstruct>(Parser<OpenMPAllocatorsConstruct>{}),
                 construct<OpenMPConstruct>(Parser<OpenMPAssumeConstruct>{}),
-                construct<OpenMPConstruct>(Parser<OpenMPCriticalConstruct>{}))))
+                construct<OpenMPConstruct>(Parser<OpenMPCriticalConstruct>{}),
+                construct<OpenMPConstruct>(
+                    Parser<OmpDelimitedMetadirectiveDirective>{}))))
 
 static constexpr DirectiveSet GetLoopDirectives() {
   using Directive = llvm::omp::Directive;
@@ -2571,7 +2631,7 @@ static constexpr DirectiveSet GetAllDirectives() { //
 TYPE_PARSER(construct<OpenMPMisplacedEndDirective>(
     OmpEndDirectiveParser{GetAllDirectives()}))
 
-TYPE_PARSER( //
-    startOmpLine >> sourced(construct<OpenMPInvalidDirective>(
-                        !OmpDirectiveNameParser{} >> SkipTo<'\n'>{})))
+TYPE_PARSER(startOmpLine >>
+    sourced(construct<OpenMPInvalidDirective>(
+        maybe("BEGIN"_sptok) >> !OmpDirectiveNameParser{} >> SkipTo<'\n'>{})))
 } // namespace Fortran::parser

diff  --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index b980a51d3f249..ab0951821d321 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2201,6 +2201,10 @@ class UnparseVisitor {
   void Unparse(const OmpBeginDirective &x) {
     BeginOpenMP();
     Word("!$OMP ");
+    auto flags{std::get<OmpDirectiveSpecification::Flags>(x.t)};
+    if (flags.test(OmpDirectiveSpecification::Flag::ExplicitBegin)) {
+      Word("BEGIN ");
+    }
     Walk(static_cast<const OmpDirectiveSpecification &>(x));
     Put("\n");
     EndOpenMP();

diff  --git a/flang/lib/Semantics/check-omp-metadirective.cpp b/flang/lib/Semantics/check-omp-metadirective.cpp
index 499fb8452a154..c8c19e4ac7dac 100644
--- a/flang/lib/Semantics/check-omp-metadirective.cpp
+++ b/flang/lib/Semantics/check-omp-metadirective.cpp
@@ -534,6 +534,45 @@ void OmpStructureChecker::CheckTraitSimd(
   }
 }
 
+void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
+  // OmpDirectiveSpecification exists on its own only in clauses on
+  // METADIRECTIVE.
+  // In other cases it's a part of other constructs that handle directive
+  // context stack by themselves.
+  if (!GetDirectiveNest(MetadirectiveNest)) {
+    return;
+  }
+
+  llvm::omp::Directive dirId{x.DirId()};
+  if (const parser::OpenMPConstruct *meta{GetCurrentConstruct()}) {
+    if (parser::Unwrap<parser::OmpDelimitedMetadirectiveDirective>(meta->u)) {
+      unsigned version{context_.langOptions().OpenMPVersion};
+      switch (llvm::omp::getDirectiveAssociation(dirId)) {
+      case llvm::omp::Association::Block:
+      case llvm::omp::Association::LoopNest:
+      case llvm::omp::Association::LoopSeq:
+        break;
+      default:
+        if (dirId != llvm::omp::Directive::OMPD_nothing) {
+          context_.Say(x.DirName().source,
+              "A directive in BEGIN %s should have a corresponding end-directive"_err_en_US,
+              parser::omp::GetUpperName(
+                  llvm::omp::Directive::OMPD_metadirective, version));
+        }
+      }
+    }
+  }
+
+  PushContextAndClauseSets(
+      std::get<parser::OmpDirectiveName>(x.t).source, dirId);
+}
+
+void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &x) {
+  if (GetDirectiveNest(MetadirectiveNest)) {
+    dirContext_.pop_back();
+  }
+}
+
 void OmpStructureChecker::Enter(const parser::OmpMetadirectiveDirective &x) {
   EnterDirectiveNest(MetadirectiveNest);
   PushContextAndClauseSets(
@@ -545,4 +584,14 @@ void OmpStructureChecker::Leave(const parser::OmpMetadirectiveDirective &) {
   dirContext_.pop_back();
 }
 
+void OmpStructureChecker::Enter(
+    const parser::OmpDelimitedMetadirectiveDirective &x) {
+  PushContextAndClauseSets(x.source, llvm::omp::Directive::OMPD_metadirective);
+}
+
+void OmpStructureChecker::Leave(
+    const parser::OmpDelimitedMetadirectiveDirective &) {
+  dirContext_.pop_back();
+}
+
 } // namespace Fortran::semantics

diff  --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 114a098da15f6..7bff7de2f8fbf 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -864,22 +864,6 @@ void OmpStructureChecker::Enter(const parser::OmpClause::DynGroupprivate &x) {
   OmpVerifyModifiers(x.v, llvm::omp::OMPC_dyn_groupprivate, source, context_);
 }
 
-void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
-  // OmpDirectiveSpecification exists on its own only in METADIRECTIVE.
-  // In other cases it's a part of other constructs that handle directive
-  // context stack by themselves.
-  if (GetDirectiveNest(MetadirectiveNest)) {
-    PushContextAndClauseSets(
-        std::get<parser::OmpDirectiveName>(x.t).source, x.DirId());
-  }
-}
-
-void OmpStructureChecker::Leave(const parser::OmpDirectiveSpecification &) {
-  if (GetDirectiveNest(MetadirectiveNest)) {
-    dirContext_.pop_back();
-  }
-}
-
 template <typename Checker> struct DirectiveSpellingVisitor {
   using Directive = llvm::omp::Directive;
 
@@ -1396,12 +1380,27 @@ void OmpStructureChecker::ChecksOnOrderedAsBlock() {
   }
 }
 
-void OmpStructureChecker::Leave(const parser::OmpBeginDirective &) {
-  switch (GetContext().directive) {
+void OmpStructureChecker::Enter(const parser::OmpBeginDirective &x) {
+  switch (x.DirId()) {
+  case llvm::omp::Directive::OMPD_metadirective:
+    // Delimited METADIRECTIVE
+    EnterDirectiveNest(MetadirectiveNest);
+    break;
+  default:
+    break;
+  }
+}
+
+void OmpStructureChecker::Leave(const parser::OmpBeginDirective &x) {
+  switch (x.DirId()) {
   case llvm::omp::Directive::OMPD_ordered:
     // [5.1] 2.19.9 Ordered Construct Restriction
     ChecksOnOrderedAsBlock();
     break;
+  case llvm::omp::Directive::OMPD_metadirective:
+    // Delimited METADIRECTIVE
+    ExitDirectiveNest(MetadirectiveNest);
+    break;
   default:
     break;
   }
@@ -3433,7 +3432,7 @@ void OmpStructureChecker::Leave(const parser::OmpEndDirective &x) {
 // 2. Checks on clauses which fall under 'struct OmpClause' from parse-tree.h.
 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h.
 
-void OmpStructureChecker::Leave(const parser::OmpClauseList &) {
+void OmpStructureChecker::Leave(const parser::OmpClauseList &x) {
   unsigned version{context_.langOptions().OpenMPVersion};
 
   // 2.7.1 Loop Construct Restriction

diff  --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 5bccf3576923e..e75bb5da847a9 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -113,6 +113,7 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
   void Leave(const parser::OpenMPInteropConstruct &);
   void Enter(const parser::OmpBlockConstruct &);
   void Leave(const parser::OmpBlockConstruct &);
+  void Enter(const parser::OmpBeginDirective &);
   void Leave(const parser::OmpBeginDirective &);
   void Enter(const parser::OmpEndDirective &);
   void Leave(const parser::OmpEndDirective &);
@@ -175,6 +176,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
 
   void Enter(const parser::OmpMetadirectiveDirective &);
   void Leave(const parser::OmpMetadirectiveDirective &);
+  void Enter(const parser::OmpDelimitedMetadirectiveDirective &);
+  void Leave(const parser::OmpDelimitedMetadirectiveDirective &);
 
   void Enter(const parser::OmpContextSelector &);
   void Leave(const parser::OmpContextSelector &);

diff  --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index abfde0dafc29a..8712c361965cb 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -469,8 +469,19 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
   }
 
   template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
-  template <typename A> bool Pre(const A &) { return true; }
-  template <typename A> void Post(const A &) {}
+  // Normally the catch-all Pre/Post functions are templates taking
+  // "const T &". For a class D derived from B, and an explicit overload
+  // of Pre(const B &), a call to Pre(D) will select the template instead
+  // of the base clase overload.
+  // Force user-defined conversion from any const-reference, to make sure
+  // that the Pre(AbsorbAnyReference) and Post(AbsorbAnyReference) overloads
+  // will be worse than derived-to-base conversions. This will, for example,
+  // invoke Pre(const OmpBlockConstruct &) for directives derived from it.
+  struct AbsorbAnyReference {
+    template <typename T> AbsorbAnyReference(const T &) {}
+  };
+  bool Pre(AbsorbAnyReference) { return true; }
+  void Post(AbsorbAnyReference) {}
 
   bool Pre(const parser::SpecificationPart &) {
     partStack_.push_back(PartKind::SpecificationPart);

diff  --git a/flang/test/Lower/OpenMP/Todo/begin-metadirective.f90 b/flang/test/Lower/OpenMP/Todo/begin-metadirective.f90
new file mode 100644
index 0000000000000..ccc06ebc3007f
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/begin-metadirective.f90
@@ -0,0 +1,12 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
+
+! CHECK: not yet implemented: OpenMP BEGIN/END METADIRECTIVE lowering
+subroutine test_begin_metadirective
+  integer :: x
+  x = 0
+  !$omp begin metadirective &
+  !$omp & when(implementation={vendor(llvm)}: parallel) &
+  !$omp & otherwise(nothing)
+  x = 1
+  !$omp end metadirective
+end subroutine

diff  --git a/flang/test/Parser/OpenMP/begin-metadirective.f90 b/flang/test/Parser/OpenMP/begin-metadirective.f90
new file mode 100644
index 0000000000000..73b3678be4b10
--- /dev/null
+++ b/flang/test/Parser/OpenMP/begin-metadirective.f90
@@ -0,0 +1,80 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=52 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=52 %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine f00
+  !$omp begin metadirective
+  continue
+  !$omp end metadirective
+end
+
+!UNPARSE: SUBROUTINE f00
+!UNPARSE: !$OMP BEGIN METADIRECTIVE
+!UNPARSE:  CONTINUE
+!UNPARSE: !$OMP END METADIRECTIVE
+!UNPARSE: END SUBROUTINE
+
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OmpDelimitedMetadirectiveDirective
+!PARSE-TREE: | OmpBeginDirective
+!PARSE-TREE: | | OmpDirectiveName -> llvm::omp::Directive = metadirective
+!PARSE-TREE: | | OmpClauseList ->
+!PARSE-TREE: | | Flags = {ExplicitBegin}
+!PARSE-TREE: | Block
+!PARSE-TREE: | OmpEndDirective
+!PARSE-TREE: | | OmpDirectiveName -> llvm::omp::Directive = metadirective
+!PARSE-TREE: | | OmpClauseList ->
+!PARSE-TREE: | | Flags = {}
+
+subroutine f01(s)
+  integer :: i
+  integer :: s
+  s = 0
+  !$omp begin metadirective &
+  !$omp & when(user={condition(.true.)}: parallel do reduction(+: s)) &
+  !$omp & otherwise(do)
+  do i = 1, 10
+    s = s + i
+  end do
+  !$omp end metadirective
+end
+
+!UNPARSE: SUBROUTINE f01 (s)
+!UNPARSE:  INTEGER i
+!UNPARSE:  INTEGER s
+!UNPARSE:   s=0_4
+!UNPARSE: !$OMP BEGIN METADIRECTIVE WHEN(USER={CONDITION(.true._4)}: PARALLEL DO REDUCTION(+: s)&
+!UNPARSE: !$OMP&) OTHERWISE(DO)
+!UNPARSE:  DO i=1_4,10_4
+!UNPARSE:    s=s+i
+!UNPARSE:  END DO
+!UNPARSE: !$OMP END METADIRECTIVE
+!UNPARSE: END SUBROUTINE
+
+!PARSE-TREE: ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OmpDelimitedMetadirectiveDirective
+!PARSE-TREE: | OmpBeginDirective
+!PARSE-TREE: | | OmpDirectiveName -> llvm::omp::Directive = metadirective
+!PARSE-TREE: | | OmpClauseList -> OmpClause -> When -> OmpWhenClause
+!PARSE-TREE: | | | Modifier -> OmpContextSelectorSpecification -> OmpTraitSetSelector
+!PARSE-TREE: | | | | OmpTraitSetSelectorName -> Value = User
+!PARSE-TREE: | | | | OmpTraitSelector
+!PARSE-TREE: | | | | | OmpTraitSelectorName -> Value = Condition
+!PARSE-TREE: | | | | | Properties
+!PARSE-TREE: | | | | | | OmpTraitProperty -> Scalar -> Expr = '.true._4'
+!PARSE-TREE: | | | | | | | LiteralConstant -> LogicalLiteralConstant
+!PARSE-TREE: | | | | | | | | bool = 'true'
+!PARSE-TREE: | | | OmpDirectiveSpecification
+!PARSE-TREE: | | | | OmpDirectiveName -> llvm::omp::Directive = parallel do
+!PARSE-TREE: | | | | OmpClauseList -> OmpClause -> Reduction -> OmpReductionClause
+!PARSE-TREE: | | | | | Modifier -> OmpReductionIdentifier -> DefinedOperator -> IntrinsicOperator = Add
+!PARSE-TREE: | | | | | OmpObjectList -> OmpObject -> Designator -> DataRef -> Name = 's'
+!PARSE-TREE: | | | | Flags = {}
+!PARSE-TREE: | | OmpClause -> Otherwise -> OmpOtherwiseClause -> OmpDirectiveSpecification
+!PARSE-TREE: | | | OmpDirectiveName -> llvm::omp::Directive = do
+!PARSE-TREE: | | | OmpClauseList ->
+!PARSE-TREE: | | | Flags = {}
+!PARSE-TREE: | | Flags = {ExplicitBegin}
+!PARSE-TREE: | Block
+!PARSE-TREE: | OmpEndDirective
+!PARSE-TREE: | | OmpDirectiveName -> llvm::omp::Directive = metadirective
+!PARSE-TREE: | | OmpClauseList ->
+!PARSE-TREE: | | Flags = {}
+

diff  --git a/flang/test/Semantics/OpenMP/begin-metadirective.f90 b/flang/test/Semantics/OpenMP/begin-metadirective.f90
new file mode 100644
index 0000000000000..f557252362a07
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/begin-metadirective.f90
@@ -0,0 +1,8 @@
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=52
+
+subroutine f00
+  !ERROR: A directive in BEGIN METADIRECTIVE should have a corresponding end-directive
+  !$omp begin metadirective when(user={condition(.true.)}: taskwait)
+  continue
+  !$omp end metadirective
+end


        


More information about the flang-commits mailing list