[flang-commits] [flang] 4062618 - [flang][NFC] Unify OpenMP and OpenACC structure checker

via flang-commits flang-commits at lists.llvm.org
Wed Aug 5 11:25:57 PDT 2020


Author: Valentin Clement
Date: 2020-08-05T14:25:49-04:00
New Revision: 40626184cf95a08f8c62192fa19cd8a71a476206

URL: https://github.com/llvm/llvm-project/commit/40626184cf95a08f8c62192fa19cd8a71a476206
DIFF: https://github.com/llvm/llvm-project/commit/40626184cf95a08f8c62192fa19cd8a71a476206.diff

LOG: [flang][NFC] Unify OpenMP and OpenACC structure checker

This patch remove duplicated code between the check-omp-structure and the check-acc-structure
and unify it into a check-directive-structure templated class.

Reviewed By: kiranchandramohan, sscalpone, ichoyjx

Differential Revision: https://reviews.llvm.org/D85104

Added: 
    flang/lib/Semantics/check-directive-structure.h

Modified: 
    flang/lib/Semantics/check-acc-structure.cpp
    flang/lib/Semantics/check-acc-structure.h
    flang/lib/Semantics/check-omp-structure.cpp
    flang/lib/Semantics/check-omp-structure.h
    flang/test/Semantics/acc-clause-validity.f90
    flang/test/Semantics/omp-clause-validity01.f90
    flang/test/Semantics/omp-declarative-directive.f90
    llvm/test/TableGen/directive1.td
    llvm/test/TableGen/directive2.td
    llvm/utils/TableGen/DirectiveEmitter.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Semantics/check-acc-structure.cpp b/flang/lib/Semantics/check-acc-structure.cpp
index 974c9dc59abe..46ccc2b762f5 100644
--- a/flang/lib/Semantics/check-acc-structure.cpp
+++ b/flang/lib/Semantics/check-acc-structure.cpp
@@ -132,7 +132,7 @@ void AccStructureChecker::Leave(const parser::OpenACCBlockConstruct &x) {
   default:
     break;
   }
-  accContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void AccStructureChecker::CheckNoBranching(const parser::Block &block,
@@ -152,7 +152,7 @@ void AccStructureChecker::Leave(
     const parser::OpenACCStandaloneDeclarativeConstruct &) {
   // Restriction - 2075
   CheckAtLeastOneClause();
-  accContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) {
@@ -185,12 +185,7 @@ void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) {
   default:
     break;
   }
-  accContext_.pop_back();
-}
-
-std::string AccStructureChecker::ContextDirectiveAsFortran() {
-  return parser::ToUpperCaseLetters(
-      llvm::acc::getOpenACCDirectiveName(GetContext().directive).str());
+  dirContext_.pop_back();
 }
 
 void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) {
@@ -211,7 +206,7 @@ void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) {
         {llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector,
             llvm::acc::Clause::ACCC_worker});
   }
-  accContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) {
@@ -238,7 +233,7 @@ void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) {
   default:
     break;
   }
-  accContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) {
@@ -250,7 +245,7 @@ void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) {
   // Restriction - 2407-2408
   CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
       routineOnlyAllowedAfterDeviceTypeClauses);
-  accContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 // Clause checkers
@@ -348,154 +343,13 @@ void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) {
   }
 }
 
-void AccStructureChecker::CheckAllowed(llvm::acc::Clause clause) {
-  if (!GetContext().allowedClauses.test(clause) &&
-      !GetContext().allowedOnceClauses.test(clause) &&
-      !GetContext().allowedExclusiveClauses.test(clause) &&
-      !GetContext().requiredClauses.test(clause)) {
-    context_.Say(GetContext().clauseSource,
-        "%s clause is not allowed on the %s directive"_err_en_US,
-        parser::ToUpperCaseLetters(
-            llvm::acc::getOpenACCClauseName(clause).str()),
-        parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
-    return;
-  }
-  if ((GetContext().allowedOnceClauses.test(clause) ||
-          GetContext().allowedExclusiveClauses.test(clause)) &&
-      FindClause(clause)) {
-    context_.Say(GetContext().clauseSource,
-        "At most one %s clause can appear on the %s directive"_err_en_US,
-        parser::ToUpperCaseLetters(
-            llvm::acc::getOpenACCClauseName(clause).str()),
-        parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
-    return;
-  }
-  if (GetContext().allowedExclusiveClauses.test(clause)) {
-    std::vector<llvm::acc::Clause> others;
-    GetContext().allowedExclusiveClauses.IterateOverMembers(
-        [&](llvm::acc::Clause o) {
-          if (FindClause(o)) {
-            others.emplace_back(o);
-          }
-        });
-    for (const auto &e : others) {
-      context_.Say(GetContext().clauseSource,
-          "%s and %s clauses are mutually exclusive and may not appear on the "
-          "same %s directive"_err_en_US,
-          parser::ToUpperCaseLetters(
-              llvm::acc::getOpenACCClauseName(clause).str()),
-          parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(e).str()),
-          parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
-    }
-    if (!others.empty()) {
-      return;
-    }
-  }
-  SetContextClauseInfo(clause);
-  AddClauseToCrtContext(clause);
-}
-
-void AccStructureChecker::CheckOnlyAllowedAfter(
-    llvm::acc::Clause clause, AccClauseSet set) {
-  bool enforceCheck = false;
-  for (auto cl : GetContext().actualClauses) {
-    if (cl == clause) {
-      enforceCheck = true;
-      continue;
-    } else if (enforceCheck && !set.test(cl)) {
-      auto parserClause = GetContext().clauseInfo.find(cl);
-      context_.Say(parserClause->second->source,
-          "Clause %s is not allowed after clause %s on the %s "
-          "directive"_err_en_US,
-          parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()),
-          parser::ToUpperCaseLetters(
-              llvm::acc::getOpenACCClauseName(clause).str()),
-          ContextDirectiveAsFortran());
-    }
-  }
-}
-
-void AccStructureChecker::CheckRequireAtLeastOneOf() {
-  for (auto cl : GetContext().actualClauses) {
-    if (GetContext().requiredClauses.test(cl))
-      return;
-  }
-  // No clause matched in the actual clauses list
-  context_.Say(GetContext().directiveSource,
-      "At least one of %s clause must appear on the %s directive"_err_en_US,
-      ClauseSetToString(GetContext().requiredClauses),
-      ContextDirectiveAsFortran());
-}
-
-void AccStructureChecker::CheckAtLeastOneClause() {
-  if (GetContext().actualClauses.empty()) {
-    context_.Say(GetContext().directiveSource,
-        "At least one clause is required on the %s directive"_err_en_US,
-        ContextDirectiveAsFortran());
-  }
-}
-
-// Enforce restriction where clauses in the given set are not allowed if the
-// given clause appears.
-void AccStructureChecker::CheckNotAllowedIfClause(
-    llvm::acc::Clause clause, AccClauseSet set) {
-  if (std::find(GetContext().actualClauses.begin(),
-          GetContext().actualClauses.end(),
-          clause) == GetContext().actualClauses.end()) {
-    return; // Clause is not present
-  }
-
-  for (auto cl : GetContext().actualClauses) {
-    if (set.test(cl)) {
-      context_.Say(GetContext().directiveSource,
-          "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,
-          parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(cl).str()),
-          parser::ToUpperCaseLetters(
-              llvm::acc::getOpenACCClauseName(clause).str()),
-          ContextDirectiveAsFortran());
-    }
-  }
-}
-
-void AccStructureChecker::RequiresConstantPositiveParameter(
-    const llvm::acc::Clause &clause, const parser::ScalarIntConstantExpr &i) {
-  if (const auto v{GetIntValue(i)}) {
-    if (*v <= 0) {
-      context_.Say(GetContext().clauseSource,
-          "The parameter of the %s clause on the %s directive must be "
-          "a constant positive integer expression"_err_en_US,
-          parser::ToUpperCaseLetters(
-              llvm::acc::getOpenACCClauseName(clause).str()),
-          ContextDirectiveAsFortran());
-    }
-  }
-}
-
-void AccStructureChecker::OptionalConstantPositiveParameter(
-    const llvm::acc::Clause &clause,
-    const std::optional<parser::ScalarIntConstantExpr> &o) {
-  if (o != std::nullopt) {
-    RequiresConstantPositiveParameter(clause, o.value());
-  }
-}
-
-std::string AccStructureChecker::ClauseSetToString(const AccClauseSet set) {
-  std::string list;
-  set.IterateOverMembers([&](llvm::acc::Clause o) {
-    if (!list.empty())
-      list.append(", ");
-    list.append(
-        parser::ToUpperCaseLetters(llvm::acc::getOpenACCClauseName(o).str()));
-  });
-  return list;
+llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) {
+  return llvm::acc::getOpenACCClauseName(clause);
 }
 
-void AccStructureChecker::SayNotMatching(
-    const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
-  context_
-      .Say(endSource, "Unmatched %s directive"_err_en_US,
-          parser::ToUpperCaseLetters(endSource.ToString()))
-      .Attach(beginSource, "Does not match directive"_en_US);
+llvm::StringRef AccStructureChecker::getDirectiveName(
+    llvm::acc::Directive directive) {
+  return llvm::acc::getOpenACCDirectiveName(directive);
 }
 
 } // namespace Fortran::semantics

diff  --git a/flang/lib/Semantics/check-acc-structure.h b/flang/lib/Semantics/check-acc-structure.h
index fef12383952d..1ce6e9052b03 100644
--- a/flang/lib/Semantics/check-acc-structure.h
+++ b/flang/lib/Semantics/check-acc-structure.h
@@ -14,13 +14,12 @@
 #ifndef FORTRAN_SEMANTICS_CHECK_ACC_STRUCTURE_H_
 #define FORTRAN_SEMANTICS_CHECK_ACC_STRUCTURE_H_
 
+#include "check-directive-structure.h"
 #include "flang/Common/enum-set.h"
 #include "flang/Parser/parse-tree.h"
 #include "flang/Semantics/semantics.h"
 #include "llvm/Frontend/OpenACC/ACC.h.inc"
 
-#include <unordered_map>
-
 using AccDirectiveSet = Fortran::common::EnumSet<llvm::acc::Directive,
     llvm::acc::Directive_enumSize>;
 
@@ -32,9 +31,16 @@ using AccClauseSet =
 
 namespace Fortran::semantics {
 
-class AccStructureChecker : public virtual BaseChecker {
+class AccStructureChecker
+    : public DirectiveStructureChecker<llvm::acc::Directive, llvm::acc::Clause,
+          parser::AccClause, llvm::acc::Clause_enumSize> {
 public:
-  AccStructureChecker(SemanticsContext &context) : context_{context} {}
+  AccStructureChecker(SemanticsContext &context)
+      : DirectiveStructureChecker(context,
+#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
+#include "llvm/Frontend/OpenACC/ACC.cpp.inc"
+        ) {
+  }
 
   // Construct and directives
   void Enter(const parser::OpenACCBlockConstruct &);
@@ -100,103 +106,13 @@ class AccStructureChecker : public virtual BaseChecker {
   void Enter(const parser::AccClause::Write &);
 
 private:
-#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
-#include "llvm/Frontend/OpenACC/ACC.cpp.inc"
-
-  struct AccContext {
-    AccContext(parser::CharBlock source, llvm::acc::Directive d)
-        : directiveSource{source}, directive{d} {}
-
-    parser::CharBlock directiveSource{nullptr};
-    parser::CharBlock clauseSource{nullptr};
-    llvm::acc::Directive directive;
-    AccClauseSet allowedClauses{};
-    AccClauseSet allowedOnceClauses{};
-    AccClauseSet allowedExclusiveClauses{};
-    AccClauseSet requiredClauses{};
-
-    const parser::AccClause *clause{nullptr};
-    std::multimap<llvm::acc::Clause, const parser::AccClause *> clauseInfo;
-    std::list<llvm::acc::Clause> actualClauses;
-  };
-
-  // back() is the top of the stack
-  AccContext &GetContext() {
-    CHECK(!accContext_.empty());
-    return accContext_.back();
-  }
-
-  void SetContextClause(const parser::AccClause &clause) {
-    GetContext().clauseSource = clause.source;
-    GetContext().clause = &clause;
-  }
-
-  void SetContextClauseInfo(llvm::acc::Clause type) {
-    GetContext().clauseInfo.emplace(type, GetContext().clause);
-  }
-
-  void AddClauseToCrtContext(llvm::acc::Clause type) {
-    GetContext().actualClauses.push_back(type);
-  }
-
-  const parser::AccClause *FindClause(llvm::acc::Clause type) {
-    auto it{GetContext().clauseInfo.find(type)};
-    if (it != GetContext().clauseInfo.end()) {
-      return it->second;
-    }
-    return nullptr;
-  }
-
-  void PushContext(const parser::CharBlock &source, llvm::acc::Directive dir) {
-    accContext_.emplace_back(source, dir);
-  }
-
-  void SetClauseSets(llvm::acc::Directive dir) {
-    accContext_.back().allowedClauses = directiveClausesTable[dir].allowed;
-    accContext_.back().allowedOnceClauses =
-        directiveClausesTable[dir].allowedOnce;
-    accContext_.back().allowedExclusiveClauses =
-        directiveClausesTable[dir].allowedExclusive;
-    accContext_.back().requiredClauses =
-        directiveClausesTable[dir].requiredOneOf;
-  }
-  void PushContextAndClauseSets(
-      const parser::CharBlock &source, llvm::acc::Directive dir) {
-    PushContext(source, dir);
-    SetClauseSets(dir);
-  }
-
-  void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &);
-
-  template <typename B> void CheckMatching(const B &beginDir, const B &endDir) {
-    const auto &begin{beginDir.v};
-    const auto &end{endDir.v};
-    if (begin != end) {
-      SayNotMatching(beginDir.source, endDir.source);
-    }
-  }
-
-  // Check that only clauses in set are after the specific clauses.
-  void CheckOnlyAllowedAfter(llvm::acc::Clause clause, AccClauseSet set);
-  void CheckRequireAtLeastOneOf();
-  void CheckAllowed(llvm::acc::Clause clause);
-  void CheckAtLeastOneClause();
-  void CheckNotAllowedIfClause(llvm::acc::Clause clause, AccClauseSet set);
-  std::string ContextDirectiveAsFortran();
 
   void CheckNoBranching(const parser::Block &block,
       const llvm::acc::Directive directive,
       const parser::CharBlock &directiveSource) const;
 
-  void RequiresConstantPositiveParameter(
-      const llvm::acc::Clause &clause, const parser::ScalarIntConstantExpr &i);
-  void OptionalConstantPositiveParameter(const llvm::acc::Clause &clause,
-      const std::optional<parser::ScalarIntConstantExpr> &o);
-
-  SemanticsContext &context_;
-  std::vector<AccContext> accContext_; // used as a stack
-
-  std::string ClauseSetToString(const AccClauseSet set);
+  llvm::StringRef getClauseName(llvm::acc::Clause clause) override;
+  llvm::StringRef getDirectiveName(llvm::acc::Directive directive) override;
 };
 
 } // namespace Fortran::semantics

diff  --git a/flang/lib/Semantics/check-directive-structure.h b/flang/lib/Semantics/check-directive-structure.h
new file mode 100644
index 000000000000..a5de0564677c
--- /dev/null
+++ b/flang/lib/Semantics/check-directive-structure.h
@@ -0,0 +1,389 @@
+//===-- lib/Semantics/check-directive-structure.h ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// Directive structure validity checks common to OpenMP, OpenACC and other
+// directive language.
+
+#ifndef FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_
+#define FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_
+
+#include "flang/Common/enum-set.h"
+#include "flang/Semantics/semantics.h"
+#include "flang/Semantics/tools.h"
+
+#include <unordered_map>
+
+namespace Fortran::semantics {
+
+template <typename C, std::size_t ClauseEnumSize> struct DirectiveClauses {
+  const common::EnumSet<C, ClauseEnumSize> allowed;
+  const common::EnumSet<C, ClauseEnumSize> allowedOnce;
+  const common::EnumSet<C, ClauseEnumSize> allowedExclusive;
+  const common::EnumSet<C, ClauseEnumSize> requiredOneOf;
+};
+
+// Generic structure checker for directives/clauses language such as OpenMP
+// and OpenACC.
+// typename D is the directive enumeration.
+// tyepname C is the clause enumeration.
+// typename PC is the parser class defined in parse-tree.h for the clauses.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+class DirectiveStructureChecker : public virtual BaseChecker {
+public:
+  DirectiveStructureChecker(SemanticsContext &context,
+      std::unordered_map<D, DirectiveClauses<C, ClauseEnumSize>>
+          directiveClausesMap)
+      : context_{context}, directiveClausesMap_(directiveClausesMap) {}
+
+protected:
+  struct DirectiveContext {
+    DirectiveContext(parser::CharBlock source, D d)
+        : directiveSource{source}, directive{d} {}
+
+    parser::CharBlock directiveSource{nullptr};
+    parser::CharBlock clauseSource{nullptr};
+    D directive;
+    common::EnumSet<C, ClauseEnumSize> allowedClauses{};
+    common::EnumSet<C, ClauseEnumSize> allowedOnceClauses{};
+    common::EnumSet<C, ClauseEnumSize> allowedExclusiveClauses{};
+    common::EnumSet<C, ClauseEnumSize> requiredClauses{};
+
+    const PC *clause{nullptr};
+    std::multimap<C, const PC *> clauseInfo;
+    std::list<C> actualClauses;
+  };
+
+  // back() is the top of the stack
+  DirectiveContext &GetContext() {
+    CHECK(!dirContext_.empty());
+    return dirContext_.back();
+  }
+
+  void SetContextClause(const PC &clause) {
+    GetContext().clauseSource = clause.source;
+    GetContext().clause = &clause;
+  }
+
+  void ResetPartialContext(const parser::CharBlock &source) {
+    CHECK(!dirContext_.empty());
+    SetContextDirectiveSource(source);
+    GetContext().allowedClauses = {};
+    GetContext().allowedOnceClauses = {};
+    GetContext().allowedExclusiveClauses = {};
+    GetContext().requiredClauses = {};
+    GetContext().clauseInfo = {};
+  }
+
+  void SetContextDirectiveSource(const parser::CharBlock &directive) {
+    GetContext().directiveSource = directive;
+  }
+
+  void SetContextDirectiveEnum(D dir) { GetContext().directive = dir; }
+
+  void SetContextAllowed(const common::EnumSet<C, ClauseEnumSize> &allowed) {
+    GetContext().allowedClauses = allowed;
+  }
+
+  void SetContextAllowedOnce(
+      const common::EnumSet<C, ClauseEnumSize> &allowedOnce) {
+    GetContext().allowedOnceClauses = allowedOnce;
+  }
+
+  void SetContextAllowedExclusive(
+      const common::EnumSet<C, ClauseEnumSize> &allowedExclusive) {
+    GetContext().allowedExclusiveClauses = allowedExclusive;
+  }
+
+  void SetContextRequired(const common::EnumSet<C, ClauseEnumSize> &required) {
+    GetContext().requiredClauses = required;
+  }
+
+  void SetContextClauseInfo(C type) {
+    GetContext().clauseInfo.emplace(type, GetContext().clause);
+  }
+
+  void AddClauseToCrtContext(C type) {
+    GetContext().actualClauses.push_back(type);
+  }
+
+  const PC *FindClause(C type) {
+    auto it{GetContext().clauseInfo.find(type)};
+    if (it != GetContext().clauseInfo.end()) {
+      return it->second;
+    }
+    return nullptr;
+  }
+
+  void PushContext(const parser::CharBlock &source, D dir) {
+    dirContext_.emplace_back(source, dir);
+  }
+
+  bool CurrentDirectiveIsNested() { return dirContext_.size() > 0; };
+
+  void SetClauseSets(D dir) {
+    dirContext_.back().allowedClauses = directiveClausesMap_[dir].allowed;
+    dirContext_.back().allowedOnceClauses =
+        directiveClausesMap_[dir].allowedOnce;
+    dirContext_.back().allowedExclusiveClauses =
+        directiveClausesMap_[dir].allowedExclusive;
+    dirContext_.back().requiredClauses =
+        directiveClausesMap_[dir].requiredOneOf;
+  }
+  void PushContextAndClauseSets(const parser::CharBlock &source, D dir) {
+    PushContext(source, dir);
+    SetClauseSets(dir);
+  }
+
+  void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &);
+
+  template <typename B> void CheckMatching(const B &beginDir, const B &endDir) {
+    const auto &begin{beginDir.v};
+    const auto &end{endDir.v};
+    if (begin != end) {
+      SayNotMatching(beginDir.source, endDir.source);
+    }
+  }
+
+  // Check that only clauses in set are after the specific clauses.
+  void CheckOnlyAllowedAfter(C clause, common::EnumSet<C, ClauseEnumSize> set);
+
+  void CheckRequired(C clause);
+
+  void CheckRequireAtLeastOneOf();
+
+  void CheckAllowed(C clause);
+
+  void CheckAtLeastOneClause();
+
+  void CheckNotAllowedIfClause(
+      C clause, common::EnumSet<C, ClauseEnumSize> set);
+
+  std::string ContextDirectiveAsFortran();
+
+  void RequiresConstantPositiveParameter(
+      const C &clause, const parser::ScalarIntConstantExpr &i);
+
+  void RequiresPositiveParameter(
+      const C &clause, const parser::ScalarIntExpr &i);
+
+  void OptionalConstantPositiveParameter(
+      const C &clause, const std::optional<parser::ScalarIntConstantExpr> &o);
+
+  virtual llvm::StringRef getClauseName(C clause) { return ""; };
+
+  virtual llvm::StringRef getDirectiveName(D directive) { return ""; };
+
+  SemanticsContext &context_;
+  std::vector<DirectiveContext> dirContext_; // used as a stack
+  std::unordered_map<D, DirectiveClauses<C, ClauseEnumSize>>
+      directiveClausesMap_;
+
+  std::string ClauseSetToString(const common::EnumSet<C, ClauseEnumSize> set);
+};
+
+// Check that only clauses included in the given set are present after the given
+// clause.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckOnlyAllowedAfter(
+    C clause, common::EnumSet<C, ClauseEnumSize> set) {
+  bool enforceCheck = false;
+  for (auto cl : GetContext().actualClauses) {
+    if (cl == clause) {
+      enforceCheck = true;
+      continue;
+    } else if (enforceCheck && !set.test(cl)) {
+      auto parserClause = GetContext().clauseInfo.find(cl);
+      context_.Say(parserClause->second->source,
+          "Clause %s is not allowed after clause %s on the %s "
+          "directive"_err_en_US,
+          parser::ToUpperCaseLetters(getClauseName(cl).str()),
+          parser::ToUpperCaseLetters(getClauseName(clause).str()),
+          ContextDirectiveAsFortran());
+    }
+  }
+}
+
+// Check that at least one clause is attached to the directive.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC,
+    ClauseEnumSize>::CheckAtLeastOneClause() {
+  if (GetContext().actualClauses.empty()) {
+    context_.Say(GetContext().directiveSource,
+        "At least one clause is required on the %s directive"_err_en_US,
+        ContextDirectiveAsFortran());
+  }
+}
+
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+std::string
+DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::ClauseSetToString(
+    const common::EnumSet<C, ClauseEnumSize> set) {
+  std::string list;
+  set.IterateOverMembers([&](C o) {
+    if (!list.empty())
+      list.append(", ");
+    list.append(parser::ToUpperCaseLetters(getClauseName(o).str()));
+  });
+  return list;
+}
+
+// Check that at least one clause in the required set is present on the
+// directive.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC,
+    ClauseEnumSize>::CheckRequireAtLeastOneOf() {
+  for (auto cl : GetContext().actualClauses) {
+    if (GetContext().requiredClauses.test(cl))
+      return;
+  }
+  // No clause matched in the actual clauses list
+  context_.Say(GetContext().directiveSource,
+      "At least one of %s clause must appear on the %s directive"_err_en_US,
+      ClauseSetToString(GetContext().requiredClauses),
+      ContextDirectiveAsFortran());
+}
+
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+std::string DirectiveStructureChecker<D, C, PC,
+    ClauseEnumSize>::ContextDirectiveAsFortran() {
+  return parser::ToUpperCaseLetters(
+      getDirectiveName(GetContext().directive).str());
+}
+
+// Check that clauses present on the directive are allowed clauses.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckAllowed(
+    C clause) {
+  if (!GetContext().allowedClauses.test(clause) &&
+      !GetContext().allowedOnceClauses.test(clause) &&
+      !GetContext().allowedExclusiveClauses.test(clause) &&
+      !GetContext().requiredClauses.test(clause)) {
+    context_.Say(GetContext().clauseSource,
+        "%s clause is not allowed on the %s directive"_err_en_US,
+        parser::ToUpperCaseLetters(getClauseName(clause).str()),
+        parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
+    return;
+  }
+  if ((GetContext().allowedOnceClauses.test(clause) ||
+          GetContext().allowedExclusiveClauses.test(clause)) &&
+      FindClause(clause)) {
+    context_.Say(GetContext().clauseSource,
+        "At most one %s clause can appear on the %s directive"_err_en_US,
+        parser::ToUpperCaseLetters(getClauseName(clause).str()),
+        parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
+    return;
+  }
+  if (GetContext().allowedExclusiveClauses.test(clause)) {
+    std::vector<C> others;
+    GetContext().allowedExclusiveClauses.IterateOverMembers([&](C o) {
+      if (FindClause(o)) {
+        others.emplace_back(o);
+      }
+    });
+    for (const auto &e : others) {
+      context_.Say(GetContext().clauseSource,
+          "%s and %s clauses are mutually exclusive and may not appear on the "
+          "same %s directive"_err_en_US,
+          parser::ToUpperCaseLetters(getClauseName(clause).str()),
+          parser::ToUpperCaseLetters(getClauseName(e).str()),
+          parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
+    }
+    if (!others.empty()) {
+      return;
+    }
+  }
+  SetContextClauseInfo(clause);
+  AddClauseToCrtContext(clause);
+}
+
+// Enforce restriction where clauses in the given set are not allowed if the
+// given clause appears.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC,
+    ClauseEnumSize>::CheckNotAllowedIfClause(C clause,
+    common::EnumSet<C, ClauseEnumSize> set) {
+  if (std::find(GetContext().actualClauses.begin(),
+          GetContext().actualClauses.end(),
+          clause) == GetContext().actualClauses.end()) {
+    return; // Clause is not present
+  }
+
+  for (auto cl : GetContext().actualClauses) {
+    if (set.test(cl)) {
+      context_.Say(GetContext().directiveSource,
+          "Clause %s is not allowed if clause %s appears on the %s directive"_err_en_US,
+          parser::ToUpperCaseLetters(getClauseName(cl).str()),
+          parser::ToUpperCaseLetters(getClauseName(clause).str()),
+          ContextDirectiveAsFortran());
+    }
+  }
+}
+
+// Check the value of the clause is a constant positive integer.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC,
+    ClauseEnumSize>::RequiresConstantPositiveParameter(const C &clause,
+    const parser::ScalarIntConstantExpr &i) {
+  if (const auto v{GetIntValue(i)}) {
+    if (*v <= 0) {
+      context_.Say(GetContext().clauseSource,
+          "The parameter of the %s clause must be "
+          "a constant positive integer expression"_err_en_US,
+          parser::ToUpperCaseLetters(getClauseName(clause).str()));
+    }
+  }
+}
+
+// Check the value of the clause is a constant positive parameter.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC,
+    ClauseEnumSize>::OptionalConstantPositiveParameter(const C &clause,
+    const std::optional<parser::ScalarIntConstantExpr> &o) {
+  if (o != std::nullopt) {
+    RequiresConstantPositiveParameter(clause, o.value());
+  }
+}
+
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::SayNotMatching(
+    const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
+  context_
+      .Say(endSource, "Unmatched %s directive"_err_en_US,
+          parser::ToUpperCaseLetters(endSource.ToString()))
+      .Attach(beginSource, "Does not match directive"_en_US);
+}
+
+// Check that at least one of the required clauses is present on the directive.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::CheckRequired(C c) {
+  if (!FindClause(c)) {
+    context_.Say(GetContext().directiveSource,
+        "At least one %s clause must appear on the %s directive"_err_en_US,
+        parser::ToUpperCaseLetters(getClauseName(c).str()),
+        ContextDirectiveAsFortran());
+  }
+}
+
+// Check the value of the clause is a positive parameter.
+template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
+void DirectiveStructureChecker<D, C, PC,
+    ClauseEnumSize>::RequiresPositiveParameter(const C &clause,
+    const parser::ScalarIntExpr &i) {
+  if (const auto v{GetIntValue(i)}) {
+    if (*v <= 0) {
+      context_.Say(GetContext().clauseSource,
+          "The parameter of the %s clause must be "
+          "a positive integer expression"_err_en_US,
+          parser::ToUpperCaseLetters(getClauseName(clause).str()));
+    }
+  }
+}
+
+} // namespace Fortran::semantics
+
+#endif // FORTRAN_SEMANTICS_CHECK_DIRECTIVE_STRUCTURE_H_

diff  --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 725dfcd9ced8..b4e4ad46ef95 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -12,21 +12,6 @@
 
 namespace Fortran::semantics {
 
-std::string OmpStructureChecker::ContextDirectiveAsFortran() {
-  auto dir = llvm::omp::getOpenMPDirectiveName(GetContext().directive).str();
-  std::transform(dir.begin(), dir.end(), dir.begin(),
-      [](unsigned char c) { return std::toupper(c); });
-  return dir;
-}
-
-void OmpStructureChecker::SayNotMatching(
-    const parser::CharBlock &beginSource, const parser::CharBlock &endSource) {
-  context_
-      .Say(endSource, "Unmatched %s directive"_err_en_US,
-          parser::ToUpperCaseLetters(endSource.ToString()))
-      .Attach(beginSource, "Does not match directive"_en_US);
-}
-
 bool OmpStructureChecker::HasInvalidWorksharingNesting(
     const parser::CharBlock &source, const OmpDirectiveSet &set) {
   // set contains all the invalid closely nested directives
@@ -41,85 +26,6 @@ bool OmpStructureChecker::HasInvalidWorksharingNesting(
   return false;
 }
 
-void OmpStructureChecker::CheckAllowed(llvm::omp::Clause type) {
-  if (!GetContext().allowedClauses.test(type) &&
-      !GetContext().allowedOnceClauses.test(type) &&
-      !GetContext().allowedExclusiveClauses.test(type) &&
-      !GetContext().requiredClauses.test(type)) {
-    context_.Say(GetContext().clauseSource,
-        "%s clause is not allowed on the %s directive"_err_en_US,
-        parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(type).str()),
-        parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
-    return;
-  }
-  if ((GetContext().allowedOnceClauses.test(type) ||
-          GetContext().allowedExclusiveClauses.test(type)) &&
-      FindClause(type)) {
-    context_.Say(GetContext().clauseSource,
-        "At most one %s clause can appear on the %s directive"_err_en_US,
-        parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(type).str()),
-        parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
-    return;
-  }
-  if (GetContext().allowedExclusiveClauses.test(type)) {
-    std::vector<llvm::omp::Clause> others;
-    GetContext().allowedExclusiveClauses.IterateOverMembers(
-        [&](llvm::omp::Clause o) {
-          if (FindClause(o)) {
-            others.emplace_back(o);
-          }
-        });
-    for (const auto &e : others) {
-      context_.Say(GetContext().clauseSource,
-          "%s and %s are mutually exclusive and may not appear on the "
-          "same %s directive"_err_en_US,
-          parser::ToUpperCaseLetters(
-              llvm::omp::getOpenMPClauseName(type).str()),
-          parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(e).str()),
-          parser::ToUpperCaseLetters(GetContext().directiveSource.ToString()));
-    }
-    if (!others.empty()) {
-      return;
-    }
-  }
-  SetContextClauseInfo(type);
-}
-
-void OmpStructureChecker::CheckRequired(llvm::omp::Clause c) {
-  if (!FindClause(c)) {
-    context_.Say(GetContext().directiveSource,
-        "At least one %s clause must appear on the %s directive"_err_en_US,
-        parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(c).str()),
-        ContextDirectiveAsFortran());
-  }
-}
-
-void OmpStructureChecker::RequiresConstantPositiveParameter(
-    const llvm::omp::Clause &clause, const parser::ScalarIntConstantExpr &i) {
-  if (const auto v{GetIntValue(i)}) {
-    if (*v <= 0) {
-      context_.Say(GetContext().clauseSource,
-          "The parameter of the %s clause must be "
-          "a constant positive integer expression"_err_en_US,
-          parser::ToUpperCaseLetters(
-              llvm::omp::getOpenMPClauseName(clause).str()));
-    }
-  }
-}
-
-void OmpStructureChecker::RequiresPositiveParameter(
-    const llvm::omp::Clause &clause, const parser::ScalarIntExpr &i) {
-  if (const auto v{GetIntValue(i)}) {
-    if (*v <= 0) {
-      context_.Say(GetContext().clauseSource,
-          "The parameter of the %s clause must be "
-          "a positive integer expression"_err_en_US,
-          parser::ToUpperCaseLetters(
-              llvm::omp::getOpenMPClauseName(clause).str()));
-    }
-  }
-}
-
 void OmpStructureChecker::Enter(const parser::OpenMPConstruct &) {
   // 2.8.1 TODO: Simd Construct with Ordered Construct Nesting check
 }
@@ -131,7 +37,10 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
   // check matching, End directive is optional
   if (const auto &endLoopDir{
           std::get<std::optional<parser::OmpEndLoopDirective>>(x.t)}) {
-    CheckMatching<parser::OmpLoopDirective>(beginLoopDir, *endLoopDir);
+    const auto &endDir{
+        std::get<parser::OmpLoopDirective>(endLoopDir.value().t)};
+
+    CheckMatching<parser::OmpLoopDirective>(beginDir, endDir);
   }
 
   if (beginDir.v != llvm::omp::Directive::OMPD_do) {
@@ -162,7 +71,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
 }
 
 void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
-  ompContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
@@ -184,28 +93,31 @@ void OmpStructureChecker::Enter(const parser::OmpEndLoopDirective &x) {
 void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
   const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
   const auto &endBlockDir{std::get<parser::OmpEndBlockDirective>(x.t)};
-  const auto &beginDir{
-      CheckMatching<parser::OmpBlockDirective>(beginBlockDir, endBlockDir)};
+  const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
+  const auto &endDir{std::get<parser::OmpBlockDirective>(endBlockDir.t)};
+  CheckMatching<parser::OmpBlockDirective>(beginDir, endDir);
 
   PushContextAndClauseSets(beginDir.source, beginDir.v);
 }
 
 void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
-  ompContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void OmpStructureChecker::Enter(const parser::OpenMPSectionsConstruct &x) {
   const auto &beginSectionsDir{
       std::get<parser::OmpBeginSectionsDirective>(x.t)};
   const auto &endSectionsDir{std::get<parser::OmpEndSectionsDirective>(x.t)};
-  const auto &beginDir{CheckMatching<parser::OmpSectionsDirective>(
-      beginSectionsDir, endSectionsDir)};
+  const auto &beginDir{
+      std::get<parser::OmpSectionsDirective>(beginSectionsDir.t)};
+  const auto &endDir{std::get<parser::OmpSectionsDirective>(endSectionsDir.t)};
+  CheckMatching<parser::OmpSectionsDirective>(beginDir, endDir);
 
   PushContextAndClauseSets(beginDir.source, beginDir.v);
 }
 
 void OmpStructureChecker::Leave(const parser::OpenMPSectionsConstruct &) {
-  ompContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void OmpStructureChecker::Enter(const parser::OmpEndSectionsDirective &x) {
@@ -229,7 +141,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPDeclareSimdConstruct &x) {
 }
 
 void OmpStructureChecker::Leave(const parser::OpenMPDeclareSimdConstruct &) {
-  ompContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
@@ -243,7 +155,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPDeclareTargetConstruct &x) {
 }
 
 void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &) {
-  ompContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void OmpStructureChecker::Enter(
@@ -254,7 +166,7 @@ void OmpStructureChecker::Enter(
 
 void OmpStructureChecker::Leave(
     const parser::OpenMPSimpleStandaloneConstruct &) {
-  ompContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
@@ -263,7 +175,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPFlushConstruct &x) {
 }
 
 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &) {
-  ompContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
@@ -272,7 +184,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
 }
 
 void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
-  ompContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void OmpStructureChecker::Enter(
@@ -284,7 +196,7 @@ void OmpStructureChecker::Enter(
 
 void OmpStructureChecker::Leave(
     const parser::OpenMPCancellationPointConstruct &) {
-  ompContext_.pop_back();
+  dirContext_.pop_back();
 }
 
 void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
@@ -701,4 +613,14 @@ void OmpStructureChecker::Enter(const parser::OmpScheduleClause &x) {
     }
   }
 }
+
+llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
+  return llvm::omp::getOpenMPClauseName(clause);
+}
+
+llvm::StringRef OmpStructureChecker::getDirectiveName(
+    llvm::omp::Directive directive) {
+  return llvm::omp::getOpenMPDirectiveName(directive);
+}
+
 } // namespace Fortran::semantics

diff  --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 7fe78a792f19..4dc318429890 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -14,13 +14,12 @@
 #ifndef FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
 #define FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_
 
+#include "check-directive-structure.h"
 #include "flang/Common/enum-set.h"
 #include "flang/Parser/parse-tree.h"
 #include "flang/Semantics/semantics.h"
 #include "llvm/Frontend/OpenMP/OMPConstants.h"
 
-#include <unordered_map>
-
 using OmpDirectiveSet = Fortran::common::EnumSet<llvm::omp::Directive,
     llvm::omp::Directive_enumSize>;
 
@@ -79,9 +78,16 @@ static OmpDirectiveSet taskGeneratingSet{
 
 namespace Fortran::semantics {
 
-class OmpStructureChecker : public virtual BaseChecker {
+class OmpStructureChecker
+    : public DirectiveStructureChecker<llvm::omp::Directive, llvm::omp::Clause,
+          parser::OmpClause, llvm::omp::Clause_enumSize> {
 public:
-  OmpStructureChecker(SemanticsContext &context) : context_{context} {}
+  OmpStructureChecker(SemanticsContext &context)
+      : DirectiveStructureChecker(context,
+#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
+#include "llvm/Frontend/OpenMP/OMP.cpp.inc"
+        ) {
+  }
 
   void Enter(const parser::OpenMPConstruct &);
   void Enter(const parser::OpenMPLoopConstruct &);
@@ -156,116 +162,16 @@ class OmpStructureChecker : public virtual BaseChecker {
   void Enter(const parser::OmpScheduleClause &);
 
 private:
-#define GEN_FLANG_DIRECTIVE_CLAUSE_MAP
-#include "llvm/Frontend/OpenMP/OMP.cpp.inc"
 
-  struct OmpContext {
-    OmpContext(parser::CharBlock source, llvm::omp::Directive d)
-        : directiveSource{source}, directive{d} {}
-    parser::CharBlock directiveSource{nullptr};
-    parser::CharBlock clauseSource{nullptr};
-    llvm::omp::Directive directive;
-    OmpClauseSet allowedClauses{};
-    OmpClauseSet allowedOnceClauses{};
-    OmpClauseSet allowedExclusiveClauses{};
-    OmpClauseSet requiredClauses{};
-
-    const parser::OmpClause *clause{nullptr};
-    std::multimap<llvm::omp::Clause, const parser::OmpClause *> clauseInfo;
-  };
-  // back() is the top of the stack
-  OmpContext &GetContext() {
-    CHECK(!ompContext_.empty());
-    return ompContext_.back();
-  }
-  // reset source location, check information, and
-  // collected information for END directive
-  void ResetPartialContext(const parser::CharBlock &source) {
-    CHECK(!ompContext_.empty());
-    SetContextDirectiveSource(source);
-    GetContext().allowedClauses = {};
-    GetContext().allowedOnceClauses = {};
-    GetContext().allowedExclusiveClauses = {};
-    GetContext().requiredClauses = {};
-    GetContext().clauseInfo = {};
-  }
-  void SetContextDirectiveSource(const parser::CharBlock &directive) {
-    GetContext().directiveSource = directive;
-  }
-  void SetContextClause(const parser::OmpClause &clause) {
-    GetContext().clauseSource = clause.source;
-    GetContext().clause = &clause;
-  }
-  void SetContextDirectiveEnum(llvm::omp::Directive dir) {
-    GetContext().directive = dir;
-  }
-  void SetContextAllowed(const OmpClauseSet &allowed) {
-    GetContext().allowedClauses = allowed;
-  }
-  void SetContextAllowedOnce(const OmpClauseSet &allowedOnce) {
-    GetContext().allowedOnceClauses = allowedOnce;
-  }
-  void SetContextAllowedExclusive(const OmpClauseSet &allowedExclusive) {
-    GetContext().allowedExclusiveClauses = allowedExclusive;
-  }
-  void SetContextRequired(const OmpClauseSet &required) {
-    GetContext().requiredClauses = required;
-  }
-  void SetContextClauseInfo(llvm::omp::Clause type) {
-    GetContext().clauseInfo.emplace(type, GetContext().clause);
-  }
-  const parser::OmpClause *FindClause(llvm::omp::Clause type) {
-    auto it{GetContext().clauseInfo.find(type)};
-    if (it != GetContext().clauseInfo.end()) {
-      return it->second;
-    }
-    return nullptr;
-  }
-  void PushContext(const parser::CharBlock &source, llvm::omp::Directive dir) {
-    ompContext_.emplace_back(source, dir);
-  }
-  void SetClauseSets(llvm::omp::Directive dir) {
-    ompContext_.back().allowedClauses = directiveClausesTable[dir].allowed;
-    ompContext_.back().allowedOnceClauses =
-        directiveClausesTable[dir].allowedOnce;
-    ompContext_.back().allowedExclusiveClauses =
-        directiveClausesTable[dir].allowedExclusive;
-    ompContext_.back().requiredClauses =
-        directiveClausesTable[dir].requiredOneOf;
-  }
-  void PushContextAndClauseSets(
-      const parser::CharBlock &source, llvm::omp::Directive dir) {
-    PushContext(source, dir);
-    SetClauseSets(dir);
-  }
-  void RequiresConstantPositiveParameter(
-      const llvm::omp::Clause &clause, const parser::ScalarIntConstantExpr &i);
-  void RequiresPositiveParameter(
-      const llvm::omp::Clause &clause, const parser::ScalarIntExpr &i);
-
-  bool CurrentDirectiveIsNested() { return ompContext_.size() > 0; };
   bool HasInvalidWorksharingNesting(
       const parser::CharBlock &, const OmpDirectiveSet &);
-  void CheckAllowed(llvm::omp::Clause);
-  void CheckRequired(llvm::omp::Clause);
-  std::string ContextDirectiveAsFortran();
-  void SayNotMatching(const parser::CharBlock &, const parser::CharBlock &);
-  template <typename A, typename B, typename C>
-  const A &CheckMatching(const B &beginDir, const C &endDir) {
-    const A &begin{std::get<A>(beginDir.t)};
-    const A &end{std::get<A>(endDir.t)};
-    if (begin.v != end.v) {
-      SayNotMatching(begin.source, end.source);
-    }
-    return begin;
-  }
 
   // specific clause related
   bool ScheduleModifierHasType(const parser::OmpScheduleClause &,
       const parser::OmpScheduleModifierType::ModType &);
 
-  SemanticsContext &context_;
-  std::vector<OmpContext> ompContext_; // used as a stack
+  llvm::StringRef getClauseName(llvm::omp::Clause clause) override;
+  llvm::StringRef getDirectiveName(llvm::omp::Directive directive) override;
 };
 } // namespace Fortran::semantics
 #endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_

diff  --git a/flang/test/Semantics/acc-clause-validity.f90 b/flang/test/Semantics/acc-clause-validity.f90
index a8aefad384b1..75a0efa87d35 100644
--- a/flang/test/Semantics/acc-clause-validity.f90
+++ b/flang/test/Semantics/acc-clause-validity.f90
@@ -82,7 +82,7 @@ program openacc_clause_validity
   !$acc end parallel
 
   !$acc parallel
-  !ERROR: The parameter of the COLLAPSE clause on the LOOP directive must be a constant positive integer expression
+  !ERROR: The parameter of the COLLAPSE clause must be a constant positive integer expression
   !$acc loop collapse(-1)
   do i = 1, N
     do j = 1, N

diff  --git a/flang/test/Semantics/omp-clause-validity01.f90 b/flang/test/Semantics/omp-clause-validity01.f90
index 75050bdc06b5..d1889979527b 100644
--- a/flang/test/Semantics/omp-clause-validity01.f90
+++ b/flang/test/Semantics/omp-clause-validity01.f90
@@ -364,7 +364,7 @@
      a = 3.14
   enddo
 
-  !ERROR: GRAINSIZE and NUM_TASKS are mutually exclusive and may not appear on the same TASKLOOP directive
+  !ERROR: GRAINSIZE and NUM_TASKS clauses are mutually exclusive and may not appear on the same TASKLOOP directive
   !$omp taskloop num_tasks(3) grainsize(2)
   do i = 1,N
      a = 3.14
@@ -468,7 +468,7 @@
   !ERROR: Unmatched END TASKLOOP directive
   !$omp end taskloop
 
-  !ERROR: GRAINSIZE and NUM_TASKS are mutually exclusive and may not appear on the same TASKLOOP SIMD directive
+  !ERROR: GRAINSIZE and NUM_TASKS clauses are mutually exclusive and may not appear on the same TASKLOOP SIMD directive
   !$omp taskloop simd num_tasks(3) grainsize(2)
   do i = 1,N
      a = 3.14

diff  --git a/flang/test/Semantics/omp-declarative-directive.f90 b/flang/test/Semantics/omp-declarative-directive.f90
index 782cee7c4446..e732cec61f32 100644
--- a/flang/test/Semantics/omp-declarative-directive.f90
+++ b/flang/test/Semantics/omp-declarative-directive.f90
@@ -28,7 +28,7 @@ subroutine declare_simd_2
   use m1
   procedure (sub) sub1
   !ERROR: Internal: no symbol found for 'sub1'
-  !ERROR: NOTINBRANCH and INBRANCH are mutually exclusive and may not appear on the same DECLARE SIMD directive
+  !ERROR: NOTINBRANCH and INBRANCH clauses are mutually exclusive and may not appear on the same DECLARE SIMD directive
   !$omp declare simd(sub1) inbranch notinbranch
   procedure (sub), pointer::p
   p=>sub1

diff  --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td
index 49df4e67b53d..905d6e28c4f9 100644
--- a/llvm/test/TableGen/directive1.td
+++ b/llvm/test/TableGen/directive1.td
@@ -161,15 +161,7 @@ def TDL_DirA : Directive<"dira"> {
 // GEN-NEXT:  #ifdef GEN_FLANG_DIRECTIVE_CLAUSE_MAP
 // GEN-NEXT:  #undef GEN_FLANG_DIRECTIVE_CLAUSE_MAP
 // GEN-EMPTY:
-// GEN-NEXT:  struct TdlDirectiveClauses {
-// GEN-NEXT:    const  allowed;
-// GEN-NEXT:    const  allowedOnce;
-// GEN-NEXT:    const  allowedExclusive;
-// GEN-NEXT:    const  requiredOneOf;
-// GEN-NEXT:  };
-// GEN-EMPTY:
-// GEN-NEXT:  std::unordered_map<llvm::tdl::Directive, TdlDirectiveClauses>
-// GEN-NEXT:      directiveClausesTable = {
+// GEN-NEXT:  {
 // GEN-NEXT:    {llvm::tdl::Directive::TDLD_dira,
 // GEN-NEXT:      {
 // GEN-NEXT:        llvm::tdl::allowedClauses_TDLD_dira,
@@ -178,7 +170,7 @@ def TDL_DirA : Directive<"dira"> {
 // GEN-NEXT:        llvm::tdl::requiredClauses_TDLD_dira,
 // GEN-NEXT:      }
 // GEN-NEXT:    },
-// GEN-NEXT:  };
+// GEN-NEXT:  }
 // GEN-EMPTY:
 // GEN-NEXT:  #endif // GEN_FLANG_DIRECTIVE_CLAUSE_MAP
 

diff  --git a/llvm/test/TableGen/directive2.td b/llvm/test/TableGen/directive2.td
index e585e11496ef..02f9d4ec40e9 100644
--- a/llvm/test/TableGen/directive2.td
+++ b/llvm/test/TableGen/directive2.td
@@ -152,15 +152,7 @@ def TDL_DirA : Directive<"dira"> {
 // GEN-NEXT: #ifdef GEN_FLANG_DIRECTIVE_CLAUSE_MAP
 // GEN-NEXT: #undef GEN_FLANG_DIRECTIVE_CLAUSE_MAP
 // GEN-EMPTY:
-// GEN-NEXT: struct TdlDirectiveClauses {
-// GEN-NEXT:   const  allowed;
-// GEN-NEXT:   const  allowedOnce;
-// GEN-NEXT:   const  allowedExclusive;
-// GEN-NEXT:   const  requiredOneOf;
-// GEN-NEXT: };
-// GEN-EMPTY:
-// GEN-NEXT: std::unordered_map<llvm::tdl::Directive, TdlDirectiveClauses>
-// GEN-NEXT:     directiveClausesTable = {
+// GEN-NEXT: {
 // GEN-NEXT:   {llvm::tdl::Directive::TDLD_dira,
 // GEN-NEXT:     {
 // GEN-NEXT:       llvm::tdl::allowedClauses_TDLD_dira,
@@ -169,6 +161,6 @@ def TDL_DirA : Directive<"dira"> {
 // GEN-NEXT:       llvm::tdl::requiredClauses_TDLD_dira,
 // GEN-NEXT:     }
 // GEN-NEXT:   },
-// GEN-NEXT: };
+// GEN-NEXT: }
 // GEN-EMPTY:
 // GEN-NEXT: #endif // GEN_FLANG_DIRECTIVE_CLAUSE_MAP

diff  --git a/llvm/utils/TableGen/DirectiveEmitter.cpp b/llvm/utils/TableGen/DirectiveEmitter.cpp
index 0e7f17c35415..60c80eb09d73 100644
--- a/llvm/utils/TableGen/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/DirectiveEmitter.cpp
@@ -465,18 +465,7 @@ void GenerateDirectiveClauseMap(const std::vector<Record *> &Directives,
   IfDefScope Scope("GEN_FLANG_DIRECTIVE_CLAUSE_MAP", OS);
 
   OS << "\n";
-  OS << "struct " << DirLang.getName() << "DirectiveClauses {\n";
-  OS << "  const " << DirLang.getClauseEnumSetClass() << " allowed;\n";
-  OS << "  const " << DirLang.getClauseEnumSetClass() << " allowedOnce;\n";
-  OS << "  const " << DirLang.getClauseEnumSetClass() << " allowedExclusive;\n";
-  OS << "  const " << DirLang.getClauseEnumSetClass() << " requiredOneOf;\n";
-  OS << "};\n";
-
-  OS << "\n";
-
-  OS << "std::unordered_map<llvm::" << DirLang.getCppNamespace()
-     << "::Directive, " << DirLang.getName() << "DirectiveClauses>\n";
-  OS << "    directiveClausesTable = {\n";
+  OS << "{\n";
 
   for (const auto &D : Directives) {
     Directive Dir{D};
@@ -497,7 +486,7 @@ void GenerateDirectiveClauseMap(const std::vector<Record *> &Directives,
     OS << "  },\n";
   }
 
-  OS << "};\n";
+  OS << "}\n";
 }
 
 // Generate the implemenation section for the enumeration in the directive


        


More information about the flang-commits mailing list