[llvm-branch-commits] [flang] [flang][OpenMP] Switch clause verification to descriptor-based (PR #229527)

Krzysztof Parzyszek via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Oct 6 12:49:39 PDT 2026


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

>From 84f57585dd07c54351d323ad5575f9e1f0ee3320 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 9 Sep 2026 14:33:55 -0500
Subject: [PATCH] [flang][OpenMP] Switch clause verification to
 descriptor-based

Delete all the scattered pieces of clause verification that are now
replaced by the unified handling.
---
 flang/lib/Parser/openmp-utils.cpp             |   4 +
 flang/lib/Semantics/check-omp-atomic.cpp      |  31 --
 flang/lib/Semantics/check-omp-loop.cpp        |  12 +
 flang/lib/Semantics/check-omp-structure.cpp   | 330 +++---------------
 flang/lib/Semantics/check-omp-structure.h     |  32 +-
 flang/lib/Semantics/check-omp-syntax.cpp      | 299 ++++++++++++++++
 flang/lib/Semantics/check-omp-variant.cpp     |   2 -
 flang/lib/Semantics/openmp-utils.cpp          |   5 +-
 .../test/Lower/OpenMP/target-kernel-types.f90 |   2 +-
 flang/test/Semantics/OpenMP/atomic.f90        |   2 +-
 flang/test/Semantics/OpenMP/atomic05.f90      |  10 +-
 flang/test/Semantics/OpenMP/cancel.f90        |   8 +-
 .../Semantics/OpenMP/clause-validity01.f90    |  15 +-
 .../Semantics/OpenMP/clause-validity02.f90    |  14 +-
 .../Semantics/OpenMP/combined-constructs.f90  |   4 +-
 .../OpenMP/declarative-directive01.f90        |   4 +-
 .../Semantics/OpenMP/declare-target01.f90     |  13 +
 .../Semantics/OpenMP/declare-target02.f90     |  15 +
 .../Semantics/OpenMP/declare-target06.f90     |   1 +
 .../OpenMP/declare-variant-match.f90          |   2 +-
 .../Semantics/OpenMP/depobj-construct-v50.f90 |   2 +-
 flang/test/Semantics/OpenMP/detach02.f90      |   2 +-
 .../Semantics/OpenMP/device-constructs.f90    |   2 +-
 flang/test/Semantics/OpenMP/flush02.f90       |  16 +-
 flang/test/Semantics/OpenMP/flush03.f90       |   1 +
 flang/test/Semantics/OpenMP/if-clause-45.f90  |   9 +-
 flang/test/Semantics/OpenMP/if-clause-50.f90  |   5 +-
 .../Semantics/OpenMP/interop-construct.f90    |   2 +-
 .../loop-transformation-construct01.f90       |  10 +-
 flang/test/Semantics/OpenMP/ordered03.f90     |   2 +-
 flang/test/Semantics/OpenMP/requires01.f90    |   1 +
 flang/test/Semantics/OpenMP/requires04.f90    |   1 +
 flang/test/Semantics/OpenMP/requires05.f90    |   1 +
 flang/test/Semantics/OpenMP/scan1.f90         |   7 +-
 flang/test/Semantics/OpenMP/single03.f90      |   6 +-
 flang/test/Semantics/OpenMP/single04.f90      |  10 +-
 .../test/Semantics/OpenMP/target-update01.f90 |   4 +-
 flang/test/Semantics/OpenMP/task-45.f90       |   2 +-
 flang/test/Semantics/OpenMP/tile01.f90        |   2 +-
 .../test/Semantics/OpenMP/unroll-clauses.f90  |   2 +-
 .../Semantics/OpenMP/workdistribute04.f90     |   2 +-
 flang/test/Semantics/indirect02.f90           |   4 +-
 42 files changed, 515 insertions(+), 383 deletions(-)

diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index 1910d765e22d02..5387735f823fd4 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -150,6 +150,10 @@ const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
 
 std::string GetUpperName(
     llvm::omp::Clause id, llvm::omp::Version version, bool annotate) {
+  if (id == llvm::omp::Clause::OMPC_cancellation_construct_type) {
+    return "cancel-directive-name";
+  }
+
   llvm::StringRef annot("");
   if (annotate) {
     switch (id) {
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index 7e8a3ffef8909c..22a1435fc0363c 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -1631,40 +1631,9 @@ void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) {
       llvm::omp::Clause::OMPC_acquire, llvm::omp::Clause::OMPC_relaxed,
       llvm::omp::Clause::OMPC_release, llvm::omp::Clause::OMPC_seq_cst};
 
-  auto checkExclusive{[&](llvm::ArrayRef<llvm::omp::Clause> group,
-                          std::string_view name,
-                          const parser::OmpClauseList &clauses) {
-    const parser::OmpClause *present{nullptr};
-    for (const parser::OmpClause &clause : clauses.v) {
-      llvm::omp::Clause id{clause.Id()};
-      if (!llvm::is_contained(group, id)) {
-        continue;
-      }
-      if (present == nullptr) {
-        present = &clause;
-        continue;
-      } else if (id == present->Id()) {
-        // Ignore repetitions of the same clause, those will be diagnosed
-        // separately.
-        continue;
-      }
-      parser::MessageFormattedText txt(
-          "At most one clause from the '%s' group is allowed on ATOMIC construct"_err_en_US,
-          name.data());
-      parser::Message message(clause.source, txt);
-      message.Attach(present->source,
-          "Previous clause from this group provided here"_en_US);
-      context_.Say(std::move(message));
-      return;
-    }
-  }};
-
   const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()};
   llvm::omp::Clause kind{x.GetKind()};
 
-  checkExclusive(atomic, "atomic", dirSpec.Clauses());
-  checkExclusive(memoryOrder, "memory-order", dirSpec.Clauses());
-
   checkIncompatibleMemoryOrderClause(context_, x, atomic, memoryOrder);
 
   // OpenMP 5.2 [15.8.3] extended-atomic Clauses: acq_rel and release cannot
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 5165ca5f70d078..94ed87a6f05e57 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -842,6 +842,7 @@ void OmpStructureChecker::CheckDoacross(
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
   llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+  auto leafs{llvm::omp::getLeafConstructsOrSelf(dirStack_.back()->DirId())};
   parser::CharBlock source{GetContext().clauseSource};
   std::string clauseName{
       parser::omp::GetUpperName(llvm::omp::Clause::OMPC_ordered, version)};
@@ -872,6 +873,17 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
       }
     }
   }
+
+  // ORDERED is not allowed on DISTRIBUTE, so if it is the only leaf,
+  // a diagnostic has already been emitted.
+  if (leafs.size() > 1 &&
+      llvm::is_contained(leafs, llvm::omp::OMPD_distribute)) {
+    context_.Say(GetContext().clauseSource,
+        "%s clause is not allowed on a compound directive with %s as a constituent"_err_en_US,
+        clauseName,
+        parser::omp::GetUpperName(
+            llvm::omp::Directive::OMPD_distribute, version));
+  }
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 6193b297b62fb5..6f43d250954790 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -529,7 +529,6 @@ class OmpUnitedTaskDesignatorChecker {
 
 bool OmpStructureChecker::IsAllowedClause(llvm::omp::Clause clauseId) {
   // Do not do clause checks while processing METADIRECTIVE.
-  // See comment in CheckAllowedClause.
   if (GetDirectiveNest(ContextSelectorNest) > 0) {
     return true;
   }
@@ -537,81 +536,6 @@ bool OmpStructureChecker::IsAllowedClause(llvm::omp::Clause clauseId) {
       context_.langOptions().getOpenMPVersion(), &context_);
 }
 
-static llvm::omp::Version AllowedInFutureVersion(llvm::omp::Clause clauseId,
-    llvm::omp::Directive dirId, llvm::omp::Version version,
-    SemanticsContext *semaCtx) {
-  for (llvm::omp::Version v : llvm::omp::getOpenMPVersions()) {
-    if (v <= version) {
-      continue;
-    }
-    if (llvm::omp::isAllowedClauseForDirective(dirId, clauseId, v)) {
-      return v;
-    }
-  }
-  return llvm::omp::Version();
-}
-
-bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clauseId,
-    parser::CharBlock clauseSource, llvm::omp::Directive dirId) {
-  // Do not do clause checks while processing METADIRECTIVE.
-  // Context selectors can contain clauses that are not given as a part
-  // of a construct, but as trait properties. Testing whether they are
-  // valid or not is deferred to the checks of the context selectors.
-  // As it stands now, these clauses would appear as if they were present
-  // on METADIRECTIVE, leading to incorrect diagnostics.
-  if (GetDirectiveNest(ContextSelectorNest) > 0) {
-    return true;
-  }
-
-  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
-
-  // Don't consult the overrides here. Checking the overrides would suppress
-  // repeated warnings for multiple occurrences of the same scenario (which
-  // may be desirable), but it would also suppress repeated errors with
-  // -Werror (which is undesirable).
-  if (!llvm::omp::isAllowedClauseForDirective(dirId, clauseId, version)) {
-    if (auto allowedInVersion{
-            AllowedInFutureVersion(clauseId, dirId, version, &context_)}) {
-      context_.Warn(common::UsageWarning::OpenMPFuture, clauseSource,
-          "%s clause is not allowed on %s directive in %s, %s"_warn_en_US,
-          GetUpperName(clauseId, version), GetUpperName(dirId, version),
-          ThisVersion(version), TryVersion(allowedInVersion));
-      SetAllowedClauseOverride(clauseId, dirId, allowedInVersion);
-    } else {
-      context_.Say(clauseSource,
-          "%s clause is not allowed on %s directive"_err_en_US,
-          GetUpperName(clauseId, version), GetUpperName(dirId, version));
-    }
-    return false;
-  }
-
-  return true;
-}
-
-// Mark clauseId as allowed on dirId. If dirId is a compound directive,
-// identify all leafs that allow the clause in version "since" or later,
-// and mark the clause as allowed on these leafs as well.
-// If dirId is not a compound directive, the "since" argument is ignored.
-void OmpStructureChecker::SetAllowedClauseOverride(llvm::omp::Clause clauseId,
-    llvm::omp::Directive dirId, llvm::omp::Version since) {
-  SemanticOverrides &overrides{context_.GetOmpSemanticOverrides()};
-  overrides.allowedClauses[clauseId].set(dirId);
-
-  auto leafs{llvm::omp::getLeafConstructsOrSelf(dirId)};
-  if (leafs.size() > 1) {
-    llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
-    assert(since > version && "\"since\" should be a future version");
-    for (llvm::omp::Directive leaf : leafs) {
-      if (auto allowedInVersion{
-              AllowedInFutureVersion(clauseId, leaf, version, &context_)}) {
-        if (allowedInVersion <= since) {
-          overrides.allowedClauses[clauseId].set(leaf);
-        }
-      }
-    }
-  }
-}
-
 void OmpStructureChecker::AnalyzeObject(const parser::OmpObject &object) {
   if (GetCommonBlockFromObj(object) ||
       std::holds_alternative<parser::OmpObject::Invalid>(object.u)) {
@@ -1136,144 +1060,25 @@ void OmpStructureChecker::CheckDirectiveInDoConcurrent(parser::CharBlock source,
   }
 }
 
-std::pair<const parser::OmpClause *, const parser::OmpClause *>
-OmpStructureChecker::FindMutuallyExclusiveClauses(llvm::omp::Clauses exclusive,
-    const std::vector<const parser::OmpClause *> &clauses) {
-  const parser::OmpClause *first{nullptr};
-  for (const parser::OmpClause *clause : clauses) {
-    llvm::omp::Clause clauseId{clause->Id()};
-    if (!exclusive.test(clauseId)) {
-      continue;
-    }
-    if (first) {
-      llvm::omp::Clause firstId{first->Id()};
-      if (clauseId < firstId) {
-        return std::make_pair(clause, first);
-      } else if (clauseId > firstId) {
-        return std::make_pair(first, clause);
-      }
-    } else {
-      first = clause;
-    }
-  }
-  return std::make_pair(nullptr, nullptr);
-}
-
-void OmpStructureChecker::CheckClauses(parser::OmpDirectiveName dirName,
+void OmpStructureChecker::AddClauses(
     llvm::iterator_range<ClauseIterator> beginClauses,
     llvm::iterator_range<ClauseIterator> endClauses) {
-  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
-  llvm::omp::Directive dirId{dirName.v};
-  std::vector<const parser::OmpClause *> allClauses;
-
+  // Record the clauses in the directive context.
   auto addClause{[&](const parser::OmpClause &clause) {
     llvm::omp::Clause clauseId{clause.Id()};
     AddClauseToCrtContext(clauseId);
     SetContextClause(clause);
     SetContextClauseInfo(clauseId);
-    allClauses.push_back(&clause);
   }};
 
   for (const parser::OmpClause &clause : beginClauses) {
     addClause(clause);
   }
   for (const parser::OmpClause &clause : endClauses) {
-    llvm::omp::Clause clauseId{clause.Id()};
-    if (llvm::omp::isEndClause(clauseId)) {
+    if (llvm::omp::isEndClause(clause.Id())) {
       addClause(clause);
-    } else {
-      context_.Say(clause.source,
-          "%s clause is not allowed on an end-directive"_err_en_US,
-          GetUpperName(clauseId, version));
     }
   }
-
-  llvm::omp::Clauses notAllowed;
-
-  for (const parser::OmpClause *clause : allClauses) {
-    llvm::omp::Clause clauseId{clause->Id()};
-    switch (clauseId) {
-    // Special cases.
-    case llvm::omp::Clause::OMPC_cancellation_construct_type:
-    case llvm::omp::Clause::OMPC_ompx_bare:
-      continue;
-    default:
-      break;
-    }
-    if (!CheckAllowedClause(clauseId, clause->source, dirId)) {
-      notAllowed.set(clauseId);
-    }
-  }
-  auto newEnd{llvm::remove_if(allClauses, [&](const parser::OmpClause *clause) {
-    return notAllowed.test(clause->Id());
-  })};
-  allClauses.erase(newEnd, allClauses.end());
-
-  std::multimap<llvm::omp::Clause, parser::CharBlock> present;
-  // Exclusive clauses aren't necessarily unique, but there is no way
-  // to specify a clause in both sets right now, and all clauses currently
-  // listed as exclusive also happen to be unique.
-  llvm::omp::Clauses uniqueSet{//
-      directiveClausesMap_[dirId].allowedOnce |
-      directiveClausesMap_[dirId].allowedExclusive};
-
-  for (const parser::OmpClause *clause : allClauses) {
-    llvm::omp::Clause clauseId{clause->Id()};
-    // Special case.
-    if (clauseId == llvm::omp::Clause::OMPC_cancellation_construct_type) {
-      continue;
-    }
-    auto range{llvm::make_range(present.equal_range(clauseId))};
-    if (uniqueSet.test(clauseId)) {
-      // Only report any repeated clause once.
-      if (std::distance(range.begin(), range.end()) == 1) {
-        std::string clauseName{GetUpperName(clauseId, version)};
-        context_
-            .Say(clause->source,
-                "At most one %s clause can appear on %s directive"_err_en_US,
-                clauseName, GetUpperName(dirId, version))
-            .Attach(range.begin()->second,
-                "%s clause was first specified here"_en_US, clauseName);
-      }
-    }
-    present.insert(std::make_pair(clauseId, clause->source));
-  }
-
-  bool requiredPresent{false};
-  // Prepare the requiredSet relevant to the current OpenMP version.
-  llvm::omp::Clauses requiredSet;
-  for (llvm::omp::Clause id : directiveClausesMap_[dirId].requiredOneOf) {
-    // Do not report overridden clauses as required.
-    if (llvm::omp::isAllowedClauseForDirective(dirId, id, version)) {
-      requiredSet.set(id);
-    }
-  }
-  for (llvm::omp::Clause id : requiredSet) {
-    if (!requiredPresent && present.count(id) != 0) {
-      requiredPresent = true;
-    }
-  }
-
-  if (!requiredPresent && !requiredSet.empty()) {
-    context_.Say(dirName.source,
-        "At least one of %s %s must appear on %s directive"_err_en_US,
-        ClauseSetToString(requiredSet),
-        requiredSet.count() == 1 ? "clause" : "clauses",
-        GetUpperName(dirName.v, version));
-  }
-
-  auto pair{FindMutuallyExclusiveClauses(
-      directiveClausesMap_[dirId].allowedExclusive, allClauses)};
-  if (pair.first && pair.second) {
-    std::string firstName{GetUpperName(pair.first->Id(), version)};
-    std::string secondName{GetUpperName(pair.second->Id(), version)};
-    context_
-        .Say(pair.second->source,
-            "%s and %s clauses are mutually exclusive and may not appear on the same %s directive"_err_en_US,
-            firstName, secondName, GetUpperName(dirId, version))
-        .Attach(pair.first->source, "%s clause was specified here"_en_US,
-            firstName);
-  }
 }
 
 void OmpStructureChecker::CheckMultipleOccurrence(
@@ -1489,14 +1294,14 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
             std::is_base_of_v<parser::OmpBlockConstruct, TypeS> ||
             std::is_same_v<parser::OpenMPSectionsConstruct, TypeS>) {
           if (auto &endSpec{s.EndDir()}) {
-            CheckClauses(dirName,
-                llvm::iterator_range(s.BeginDir().Clauses().v),
+            VerifyClauseSyntax(&s.BeginDir(), &*endSpec);
+            AddClauses(llvm::iterator_range(s.BeginDir().Clauses().v),
                 llvm::iterator_range(endSpec->Clauses().v));
             return;
           }
         }
-        CheckClauses(dirName,
-            llvm::iterator_range(dirStack_.back()->Clauses().v),
+        VerifyClauseSyntax(dirStack_.back(), nullptr);
+        AddClauses(llvm::iterator_range(dirStack_.back()->Clauses().v),
             llvm::iterator_range(std::list<parser::OmpClause>{}));
       },
       x.u);
@@ -1533,7 +1338,8 @@ void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
   PushContextAndClauseSets(dirName.source, dirName.v);
   dirStack_.push_back(&GetOmpDirectiveSpecification(x));
 
-  CheckClauses(dirName, llvm::iterator_range(dirStack_.back()->Clauses().v),
+  VerifyClauseSyntax(dirStack_.back(), nullptr);
+  AddClauses(llvm::iterator_range(dirStack_.back()->Clauses().v),
       llvm::iterator_range(std::list<parser::OmpClause>{}));
 
   CheckDirectiveInPureProcedure(dirName.source, dirName.v, *dirStack_.back());
@@ -1793,34 +1599,24 @@ void OmpStructureChecker::CheckSingleConstruct(
   const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
   llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   SymbolSourceMap copyPrivateSyms;
-  parser::CharBlock nowaitSource1, nowaitSource2;
 
-  auto catchCopyPrivateNowaitClauses =
-      [&](const parser::OmpDirectiveSpecification &spec,
-          parser::CharBlock &nowaitSource) {
+  auto collectCopyprivateSymbols =
+      [&](const parser::OmpDirectiveSpecification &spec) {
         for (auto &clause : spec.Clauses().v) {
           llvm::omp::Clause clauseId{clause.Id()};
           if (clauseId == llvm::omp::Clause::OMPC_copyprivate) {
             GetSymbolsInObjectList(*GetOmpObjectList(clause), copyPrivateSyms);
-          } else if (clauseId == llvm::omp::Clause::OMPC_nowait) {
-            if (nowaitSource.empty()) {
-              nowaitSource = clause.source;
-            }
           }
         }
       };
 
-  catchCopyPrivateNowaitClauses(beginSpec, nowaitSource1);
+  collectCopyprivateSymbols(beginSpec);
   if (auto &endSpec{x.EndDir()}) {
-    catchCopyPrivateNowaitClauses(*endSpec, nowaitSource2);
+    collectCopyprivateSymbols(*endSpec);
   }
 
-  std::string nowaitName{//
-      GetUpperName(llvm::omp::Clause::OMPC_nowait, version)};
   std::string copyPrivateName{
       GetUpperName(llvm::omp::Clause::OMPC_copyprivate, version)};
-  std::string singleName{
-      GetUpperName(llvm::omp::Directive::OMPD_single, version)};
 
   std::pair<const Symbol *, parser::CharBlock> last{nullptr, {}};
   bool reported{false};
@@ -1840,15 +1636,6 @@ void OmpStructureChecker::CheckSingleConstruct(
     }
     last = std::make_pair(symbol, source);
   }
-
-  if (version <= 52 && !copyPrivateSyms.empty() &&
-      (!nowaitSource1.empty() || !nowaitSource2.empty())) {
-    parser::CharBlock source{
-        !nowaitSource1.empty() ? nowaitSource1 : nowaitSource2};
-    context_.Say(source,
-        "%s clause must not be used with %s clause on %s directive"_err_en_US,
-        nowaitName, copyPrivateName, singleName);
-  }
 }
 
 void OmpStructureChecker::CheckMasterNesting(
@@ -2339,8 +2126,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) {
     }
   }
   if (clauses.v.size() != 1) {
-    context_.Say(
-        x.source, "The DEPOBJ construct requires a single clause"_err_en_US);
     return;
   }
 
@@ -3060,10 +2845,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPAllocatorsConstruct &x) {
 
 void OmpStructureChecker::CheckScan(
     const parser::OpenMPSimpleStandaloneConstruct &x) {
-  if (x.v.Clauses().v.size() != 1) {
-    context_.Say(x.source,
-        "Exactly one of EXCLUSIVE or INCLUSIVE clause is expected"_err_en_US);
-  }
   if (!CurrentDirectiveIsNested() ||
       !llvm::omp::scanParentAllowedSet.test(GetContextParent().directive)) {
     context_.Say(x.source,
@@ -3194,11 +2975,6 @@ void OmpStructureChecker::CheckTargetUpdate() {
   const parser::OmpClause *toWrapper{FindClause(llvm::omp::Clause::OMPC_to)};
   const parser::OmpClause *fromWrapper{
       FindClause(llvm::omp::Clause::OMPC_from)};
-  if (!toWrapper && !fromWrapper) {
-    context_.Say(GetContext().directiveSource,
-        "At least one motion-clause (TO/FROM) must be specified on "
-        "TARGET UPDATE construct."_err_en_US);
-  }
   if (toWrapper && fromWrapper) {
     SymbolSourceMap toSymbols, fromSymbols;
     GetSymbolsInObjectList(*GetOmpObjectList(*fromWrapper), fromSymbols);
@@ -3466,7 +3242,13 @@ void OmpStructureChecker::Leave(
 }
 
 void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   auto &flushList{std::get<std::optional<parser::OmpArgumentList>>(x.v.t)};
+  std::string flushName{
+      GetUpperName(llvm::omp::Directive::OMPD_flush, version)};
+
+  auto &mdesc{llvm::omp::getDescriptor(llvm::omp::ClauseSet::MemoryOrder)};
+  llvm::omp::Clauses memOrder{mdesc.getClauses(version)};
 
   auto isVariableListItemOrCommonBlock{[](const Symbol &sym) {
     return IsVariableListItem(sym) ||
@@ -3482,20 +3264,31 @@ void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
       }
     }
 
-    if (FindClause(llvm::omp::Clause::OMPC_acquire) ||
-        FindClause(llvm::omp::Clause::OMPC_release) ||
-        FindClause(llvm::omp::Clause::OMPC_acq_rel)) {
-      context_.Say(flushList->source,
-          "If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items must not be specified on the FLUSH directive"_err_en_US);
+    for (const parser::OmpClause &clause : x.v.Clauses().v) {
+      if (memOrder.test(clause.Id())) {
+        context_.Say(flushList->source,
+            "If a '%s' clause is specified, list items must not be specified on the %s directive"_err_en_US,
+            mdesc.getName(), flushName);
+        break;
+      }
+    }
+  }
+
+  for (const parser::OmpClause &clause : x.v.Clauses().v) {
+    llvm::omp::Clause clauseId{clause.Id()};
+    if (clauseId == llvm::omp::Clause::OMPC_relaxed) {
+      context_.Say(clause.source, "'%s' cannot be %s on %s directive"_err_en_US,
+          mdesc.getName(),
+          GetUpperName(llvm::omp::Clause::OMPC_relaxed, version), flushName);
     }
   }
 
-  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   if (version >= 52) {
     auto &flags{std::get<parser::OmpDirectiveSpecification::Flags>(x.v.t)};
     if (flags.test(parser::OmpDirectiveSpecification::Flag::DeprecatedSyntax)) {
       context_.Say(x.source,
-          "The syntax \"FLUSH clause (object, ...)\" has been deprecated, use \"FLUSH(object, ...) clause\" instead"_warn_en_US);
+          "The syntax \"FLUSH clause (object, ...)\" has been deprecated, use \"%s(object, ...) clause\" instead"_warn_en_US,
+          flushName);
     }
   }
 }
@@ -3638,9 +3431,9 @@ void OmpStructureChecker::Enter(
 
   if (dir != llvm::omp::Directive::OMPD_cancel &&
       dir != llvm::omp::Directive::OMPD_cancellation_point) {
-    // Do not call CheckAllowed/CheckAllowedClause, because in case of an error
-    // it will print "CANCELLATION_CONSTRUCT_TYPE" as the clause name instead
-    // of the contained construct name.
+    // Do not call CheckAllowed because in case of an error it will print
+    // "CANCELLATION_CONSTRUCT_TYPE" as the clause name instead of the
+    // contained construct name.
     context_.Say(dirName.source, "%s cannot follow %s"_err_en_US,
         parser::omp::GetUpperName(dirName.v, version),
         parser::omp::GetUpperName(dir, version));
@@ -3689,9 +3482,6 @@ std::optional<llvm::omp::Directive> OmpStructureChecker::GetCancelType(
         parser::OmpClause::CancellationConstructType;
     if (auto *cctype{std::get_if<CancellationConstructType>(&clause.u)}) {
       if (cancelee) {
-        context_.Say(cancelSource,
-            "Multiple cancel-directive-name clauses are not allowed on %s construct"_err_en_US,
-            cancelName);
         return std::nullopt;
       }
       cancelee = std::get<parser::OmpDirectiveName>(cctype->v.t).v;
@@ -3699,12 +3489,8 @@ std::optional<llvm::omp::Directive> OmpStructureChecker::GetCancelType(
   }
 
   if (!cancelee) {
-    context_.Say(cancelSource,
-        "Missing cancel-directive-name clause on the %s construct"_err_en_US,
-        cancelName);
     return std::nullopt;
   }
-
   return cancelee;
 }
 
@@ -3914,11 +3700,7 @@ void OmpStructureChecker::Leave(const parser::OmpClauseList &x) {
 
   if (GetContext().directive == llvm::omp::Directive::OMPD_task) {
     if (auto *detachClause{FindClause(llvm::omp::Clause::OMPC_detach)}) {
-      if (version < 52) {
-        // OpenMP 5.0: 2.10.1 Task construct restrictions
-        CheckNotAllowedIfClause(llvm::omp::Clause::OMPC_detach,
-            {llvm::omp::Clause::OMPC_mergeable});
-      } else if (version >= 52) {
+      if (version >= 52) {
         // OpenMP 5.2: 12.5.2 Detach construct restrictions
         if (FindClause(llvm::omp::Clause::OMPC_final)) {
           context_.Say(GetContext().clauseSource,
@@ -5407,10 +5189,21 @@ void OmpStructureChecker::CheckLastprivateModifier(
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &x) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   SymbolSourceMap currSymbols;
   GetSymbolsInObjectList(x.v, currSymbols);
   CheckCopyingPolymorphicAllocatable(
       currSymbols, llvm::omp::Clause::OMPC_copyin);
+
+  // COPYIN is not allowed on TARGET, so if it is the only leaf,
+  // a diagnostic has already been emitted.
+  auto leafs{llvm::omp::getLeafConstructsOrSelf(dirStack_.back()->DirId())};
+  if (leafs.size() > 1 && llvm::is_contained(leafs, llvm::omp::OMPD_target)) {
+    context_.Say(GetContext().clauseSource,
+        "%s clause is not allowed on a compound directive with %s as a constituent"_err_en_US,
+        GetUpperName(llvm::omp::Clause::OMPC_copyin, version),
+        GetUpperName(llvm::omp::Directive::OMPD_target, version));
+  }
 }
 
 void OmpStructureChecker::CheckStructureComponent(
@@ -5961,9 +5754,9 @@ void OmpStructureChecker::Enter(const parser::OmpClause::To &x) {
 
 void OmpStructureChecker::Enter(const parser::OmpClause::OmpxBare &x) {
   llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
-  // Don't call CheckAllowedClause, because it allows "ompx_bare" on
-  // a non-combined "target" directive (for reasons of splitting combined
-  // directives). In source code it's only allowed on "target teams".
+  // The OMPX_BARE clause is allowed in both TARGET and TEAMS for the
+  // purposes of construct decomposition. In source code it's only allowed
+  // on TARGET TEAMS.
   if (GetContext().directive != llvm::omp::Directive::OMPD_target_teams) {
     context_.Say(GetContext().clauseSource,
         "%s clause is only allowed on combined TARGET TEAMS"_err_en_US,
@@ -6326,10 +6119,11 @@ void OmpStructureChecker::CheckWorkdistributeBlockStmts(
     const parser::Block &block, parser::CharBlock source) {
   llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::Version since(60);
-  if (version < since)
+  if (version < since) {
     context_.Say(source,
         "WORKDISTRIBUTE construct is not allowed in %s, %s"_err_en_US,
         ThisVersion(version), TryVersion(since));
+  }
 
   OmpWorkdistributeBlockChecker ompWorkdistributeBlockChecker{context_, source};
 
@@ -6490,7 +6284,6 @@ void OmpStructureChecker::Enter(const parser::OmpClause::ThreadLimit &x) {
 void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
   bool isDependClauseOccurred{false};
   bool hasInitClause{false};
-  bool hasActionClause{false};
   int targetCount{0}, targetSyncCount{0};
   std::set<std::string> interopVarNames;
   // An interop-var must be a scalar variable of integer type. Reject
@@ -6573,7 +6366,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
         common::visitors{
             [&](const parser::OmpClause::Init &initClause) {
               hasInitClause = true;
-              hasActionClause = true;
               auto &modifiers{OmpGetModifiers(initClause.v)};
               auto &&interopTypeModifier{
                   OmpGetRepeatableModifier<parser::OmpInteropType>(modifiers)};
@@ -6635,7 +6427,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
               isDependClauseOccurred = true;
             },
             [&](const parser::OmpClause::Destroy &destroyClause) {
-              hasActionClause = true;
               if (!destroyClause.v) {
                 context_.Say(GetContext().directiveSource,
                     "The DESTROY clause on an INTEROP construct must specify an interop variable"_err_en_US);
@@ -6649,7 +6440,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
               }
             },
             [&](const parser::OmpClause::Use &useClause) {
-              hasActionClause = true;
               const auto *interopVar{
                   parser::Unwrap<parser::OmpObject>(useClause.v)};
               CheckTypeParamInquiry(
@@ -6663,12 +6453,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
         },
         clause.u);
   }
-  // At least one action-clause (init, use, or destroy) must appear on an
-  // interop construct; a construct with only e.g. device or depend is invalid.
-  if (!hasActionClause) {
-    context_.Say(GetContext().directiveSource,
-        "At least one action-clause (INIT, USE, or DESTROY) must appear on the INTEROP construct"_err_en_US);
-  }
   if (targetCount > 1 || targetSyncCount > 1) {
     context_.Say(GetContext().directiveSource,
         "Each interop-type may be specified at most once."_err_en_US);
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 17f531a3508b7e..c283f511f63b13 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -68,7 +68,8 @@ template <typename ElemTy, typename SetsSetTy> struct AppliedElementInfo {
 
 using AppliedModifierInfo =
     AppliedElementInfo<llvm::omp::Modifier, llvm::omp::ModifierSets>;
-using AppliedModifier = AppliedModifierInfo::ElementTy;
+using AppliedClauseInfo =
+    AppliedElementInfo<llvm::omp::Clause, llvm::omp::ClauseSets>;
 
 // Mapping from 'Symbol' to 'Source' to keep track of the variables
 // used in multiple clauses
@@ -357,6 +358,21 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
       const parser::OmpTraitSetSelector &, const parser::OmpTraitSelector &);
 
   // check-omp-syntax.cpp
+  bool VerifyClauseVersion(
+      parser::OmpDirectiveName dirName, const AppliedClauseInfo &info);
+  bool VerifyClauseRequired(
+      parser::OmpDirectiveName dirName, const AppliedClauseInfo &info);
+  bool VerifyClauseUnique(
+      parser::OmpDirectiveName dirName, const AppliedClauseInfo &info);
+  bool VerifyClauseExclusive(
+      parser::OmpDirectiveName dirName, const AppliedClauseInfo &info);
+  bool VerifyClauseUltimate(
+      parser::OmpDirectiveName dirName, const AppliedClauseInfo &info);
+  bool VerifyClauseSyntax(
+      parser::OmpDirectiveName dirName, const AppliedClauseInfo &info);
+  void VerifyClauseSyntax(const parser::OmpDirectiveSpecification *beginSpec,
+      const parser::OmpDirectiveSpecification *endDir);
+
   bool VerifyModifierVersion(parser::omp::WithSource<llvm::omp::Clause> clause,
       const AppliedModifierInfo &info);
   bool VerifyModifierRequired(parser::omp::WithSource<llvm::omp::Clause> clause,
@@ -372,14 +388,14 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
       const AppliedModifierInfo &info);
   void VerifyModifierSyntax(const parser::OmpClause &x);
 
+  void SetAllowedClauseOverride(llvm::omp::Clause clauseId,
+      llvm::omp::Directive dirId,
+      llvm::omp::Version since = llvm::omp::Version());
+
   // check-omp-structure.cpp
   using ClauseIterator =
       decltype(std::declval<const parser::OmpClauseList>().v.begin());
   bool IsAllowedClause(llvm::omp::Clause clauseId);
-  bool CheckAllowedClause(llvm::omp::Clause clauseId,
-      parser::CharBlock clauseSource, llvm::omp::Directive dirId);
-  void SetAllowedClauseOverride(llvm::omp::Clause clauseId,
-      llvm::omp::Directive dirId, llvm::omp::Version since);
   void CheckArgumentObjectKind(const parser::OmpClause &x);
   void CheckDirectiveSpelling(
       parser::CharBlock spelling, llvm::omp::Directive id);
@@ -391,13 +407,9 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
       llvm::omp::Directive id, const parser::OmpDirectiveSpecification &spec);
   void CheckDirectiveInDoConcurrent(parser::CharBlock source,
       llvm::omp::Directive id, const parser::OmpDirectiveSpecification &spec);
-  void CheckClauses(parser::OmpDirectiveName dirName,
-      llvm::iterator_range<ClauseIterator> beginClauses,
+  void AddClauses(llvm::iterator_range<ClauseIterator> beginClauses,
       llvm::iterator_range<ClauseIterator> endClauses);
   void AnalyzeObject(const parser::OmpObject &object);
-  std::pair<const parser::OmpClause *, const parser::OmpClause *>
-  FindMutuallyExclusiveClauses(llvm::omp::Clauses exclusive,
-      const std::vector<const parser::OmpClause *> &clauses);
 
   const parser::OpenMPConstruct *GetCurrentConstruct() const;
   void CheckSourceLabel(const parser::Label &);
diff --git a/flang/lib/Semantics/check-omp-syntax.cpp b/flang/lib/Semantics/check-omp-syntax.cpp
index 37585d3a0e5389..784ad8c623b64a 100644
--- a/flang/lib/Semantics/check-omp-syntax.cpp
+++ b/flang/lib/Semantics/check-omp-syntax.cpp
@@ -63,6 +63,19 @@ static llvm::omp::Version NextVersion(llvm::omp::Version version) {
   return llvm::omp::Version();
 }
 
+static std::string EnumSetToString(
+    llvm::omp::Clauses set, llvm::omp::Version version) {
+  llvm::SmallVector<std::string> names;
+  for (llvm::omp::Clause c : set) {
+    names.emplace_back(GetUpperName(c, version));
+  }
+  if (names.size() == 1) {
+    return names.front();
+  }
+  return llvm::join(llvm::ArrayRef(names).drop_back(), ", ") + " or " +
+      names.back();
+}
+
 static std::string EnumSetToString(
     llvm::omp::Modifiers set, llvm::omp::Version version) {
   llvm::SmallVector<std::string> names;
@@ -76,6 +89,19 @@ static std::string EnumSetToString(
       names.back();
 }
 
+static std::string OneOfClauses(
+    llvm::omp::ClauseSet set, llvm::omp::Version version) {
+  auto &sdesc{llvm::omp::getDescriptor(set)};
+  llvm::omp::Clauses members{sdesc.getClauses(version)};
+
+  if (size_t count{members.size()}; count == 1) {
+    return EnumSetToString(members, version) + " clause";
+  } else if (count > 1) {
+    return "One of " + EnumSetToString(members, version) + " clauses";
+  }
+  return "";
+}
+
 static std::string OneOfModifiers(
     llvm::omp::ModifierSet set, llvm::omp::Version version) {
   auto &sdesc{llvm::omp::getDescriptor(set)};
@@ -323,6 +349,250 @@ static ResultTy VerifyUltimate(
   return result;
 }
 
+bool OmpStructureChecker::VerifyClauseVersion(
+    parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+  bool isCancel{dirName.v == llvm::omp::Directive::OMPD_cancel ||
+      dirName.v == llvm::omp::Directive::OMPD_cancellation_point};
+
+  auto result = VerifyVersions(info, dirName.v, version);
+
+  for (auto &[c, svr] : result) {
+    std::string cname{GetUpperName(c, version)};
+    std::string dname{GetUpperName(dirName.v, version)};
+    llvm::omp::Version since(svr.second.Min);
+    llvm::omp::Version until(svr.second.Max);
+
+    // Cancellation construct type clauses are directive names. They are
+    // only allowed on CANCEL and CANCELLATION_POINT directives. They may
+    // appear as byproducts of parsing an invalid directive name,
+    // e.g. SECTIONS PARALLEL, where SECTIONS will be the directive name,
+    // and PARALLEL will be a cancellation-construct-type clause.
+    // This may cause confusing error messages to be emitted, so deal with
+    // these cases separately.
+    if (!isCancel && c == llvm::omp::Clause::OMPC_cancellation_construct_type) {
+      context_.Say(svr.first, "%s cannot follow %s"_err_en_US,
+          parser::ToUpperCaseLetters(svr.first.ToString()), dname);
+      continue;
+    }
+
+    if (since == ~0u && until == 0u) {
+      context_.Say(svr.first,
+          "%s clause is not allowed on %s directive"_err_en_US, cname, dname);
+    } else if (since != ~0u && version < since) {
+      context_.Warn(common::UsageWarning::OpenMPFuture, svr.first,
+          "%s clause is not allowed on %s directive in %s, %s"_warn_en_US,
+          cname, dname, omp::ThisVersion(version), omp::TryVersion(since));
+      SetAllowedClauseOverride(c, dirName.v, since);
+    } else if (until != 0u && version > until) {
+      context_.Warn(common::UsageWarning::OpenMPDeprecated, svr.first,
+          "%s clause is no longer allowed on %s directive since %s"_warn_en_US,
+          cname, dname, omp::ThisVersion(NextVersion(until)));
+      SetAllowedClauseOverride(c, dirName.v);
+    }
+  }
+
+  return result.empty();
+}
+
+// In OpenMP 6.0+ the COMBINER clause is required on DECLARE_REDUCTION,
+// even though the old syntax (with the combiner expression inside the
+// directive argument) is still allowed.
+static bool missingCombiner(llvm::omp::Directive d, llvm::omp::Clause c) {
+  return d == llvm::omp::Directive::OMPD_declare_reduction &&
+      c == llvm::omp::Clause::OMPC_combiner;
+}
+
+bool OmpStructureChecker::VerifyClauseRequired(
+    parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+  auto result = VerifyRequired(info, dirName.v, version);
+
+  for (llvm::omp::Clause c : result.first) {
+    // Exceptions:
+    if (version >= 60 && missingCombiner(dirName.v, c)) {
+      continue;
+    }
+
+    context_.Say(dirName.source,
+        "%s clause is required on %s directive"_err_en_US,
+        GetUpperName(c, version), GetUpperName(dirName.v, version));
+  }
+
+  for (llvm::omp::ClauseSet s : result.second) {
+    // If the group is required, at least one clause from that group must
+    // be present.
+    // Note: The tricky part is that when a directive accepts a clause
+    // group, it may still have restrictions that exclude some members of
+    // that group. For example FLUSH accepts memory-order group, but not the
+    // RELAXED clause (note that the memory-order group is not "required").
+    // If such a restriction applied to a required group, we don't want to say
+    //   Directive XYZ requires one of FOO, BAR or BAZ clauses
+    // and then
+    //   BAZ clause is not allowed on XYZ directive
+    // This hasn't happened yet, but may happen in the future.
+    if (s != llvm::omp::ClauseSet::CancelDirectiveName) {
+      context_.Say(dirName.source, "%s is required on %s directive"_err_en_US,
+          OneOfClauses(s, version), GetUpperName(dirName.v, version));
+    } else {
+      // cancel-directive-name is somewhat special: the ClauseSet doesn't
+      // contain any actual clauses. Moreover, the clauses are cancellable
+      // directive names and have no separate definitions. They are encoded
+      // as directive ids inside OmpCancellationConstructTypeClause with the
+      // id OMPC_cancellation_construct_type.
+      context_.Say(dirName.source,
+          "One of '%s' clauses is required on %s directive"_err_en_US,
+          llvm::omp::getDescriptor(s).getName().str(),
+          GetUpperName(dirName.v, version));
+    }
+  }
+
+  return result.first.empty() && result.second.empty();
+}
+
+bool OmpStructureChecker::VerifyClauseUnique(
+    parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+  auto result = VerifyUnique(info, dirName.v, version);
+
+  for (auto [id, where] : result) {
+    context_
+        .Say(where.second,
+            "At most one %s clause can appear on %s directive"_err_en_US,
+            GetUpperName(id, version), GetUpperName(dirName.v, version))
+        .Attach(where.first, "previous occurrence of this clause"_en_US);
+  }
+  return result.empty();
+}
+
+bool OmpStructureChecker::VerifyClauseExclusive(
+    parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+  auto resultExcl = VerifyExclusive(info, dirName.v, version);
+
+  for (auto [id, wrong] : resultExcl) {
+    auto [otherId, source, otherSource] = wrong;
+    context_
+        .Say(source,
+            "%s clause cannot be specified together with a clause of a different type"_err_en_US,
+            GetUpperName(id, version))
+        .Attach(otherSource, "%s provided here"_en_US,
+            GetUpperName(otherId, version));
+  }
+
+  auto resultMut = VerifyMutuallyExclusive(info, dirName.v, version);
+
+  for (auto [id, wrong] : resultMut) {
+    auto [otherId, setId, source, otherSource] = wrong;
+    auto thisName{GetUpperName(id, version)};
+    std::string annot;
+    if (llvm::omp::isClauseGroup(setId)) {
+      auto &sdesc{llvm::omp::getDescriptor(setId)};
+      annot = " as members of '" + sdesc.getName().str() + "' clause group";
+    }
+    context_
+        .Say(otherSource,
+            "%s and %s clauses are mutually exclusive%s"_err_en_US,
+            GetUpperName(otherId, version), thisName, annot)
+        .Attach(source, "%s clause specified here"_en_US, thisName);
+  }
+
+  return resultExcl.empty() && resultMut.empty();
+}
+
+bool OmpStructureChecker::VerifyClauseUltimate(
+    parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+  auto result = VerifyUltimate(info, dirName.v, version, /*last=*/true);
+
+  for (auto [id, where] : result) {
+    context_.Say(where, "%s should be the last clause"_err_en_US,
+        GetUpperName(id, version));
+  }
+
+  return result.empty();
+}
+
+// Collect the information about clauses specified on the given directive.
+// If a clause is allowed on this directive in "version", store the list of
+// clause sets that the directive allows in "version" in AppliedClause.
+// If a clause is not allowed on this directive in "version", but is allowed
+// on it in another version v, store the list of clause sets that the directive
+// allows in v.
+// In either case, store the applied version in AppliedClause.
+// If the clause is not allowed in any version, the applied version will
+// be the default (i.e. 0) and no sets will be stored.
+AppliedClauseInfo GetAppliedClauses(
+    const parser::OmpDirectiveSpecification *beginSpec,
+    const parser::OmpDirectiveSpecification *endSpec,
+    llvm::omp::Version version) {
+  using AppliedClause = AppliedClauseInfo::ElementTy;
+  AppliedClauseInfo info;
+  llvm::omp::Directive dirId{beginSpec->DirId()};
+  auto &ddesc{llvm::omp::getDescriptor(dirId)};
+
+  auto addClauses = [&](const parser::OmpClauseList &clauses) {
+    for (auto &clause : clauses.v) {
+      auto &am{info.elements.emplace_back(AppliedClause{})};
+      am.id = WithSource{clause.Id(), clause.source};
+      am.version = GetClosestVersion(
+          descriptor::GetVersionRangeForElement(am.id.value, dirId), version);
+      if (am.version) {
+        for (auto s : ddesc.getClauseSets(am.version)) {
+          auto &sdesc{llvm::omp::getDescriptor(s)};
+          if (sdesc.getClauses(am.version).test(am.id.value)) {
+            am.sets.set(s);
+          }
+        }
+      }
+    }
+  };
+
+  addClauses(DEREF(beginSpec).Clauses());
+  if (endSpec) {
+    addClauses(endSpec->Clauses());
+  }
+
+  return info;
+}
+
+bool OmpStructureChecker::VerifyClauseSyntax(
+    parser::OmpDirectiveName dirName, const AppliedClauseInfo &info) {
+  bool valid[]{
+      VerifyClauseVersion(dirName, info),
+      VerifyClauseRequired(dirName, info),
+      VerifyClauseUnique(dirName, info),
+      VerifyClauseExclusive(dirName, info),
+      VerifyClauseUltimate(dirName, info),
+  };
+
+  return llvm::all_of(valid, [](bool x) { return x; });
+}
+
+void OmpStructureChecker::VerifyClauseSyntax(
+    const parser::OmpDirectiveSpecification *beginSpec,
+    const parser::OmpDirectiveSpecification *endSpec) {
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+
+  if (endSpec) {
+    for (const parser::OmpClause &clause : endSpec->Clauses().v) {
+      llvm::omp::Clause id{clause.Id()};
+      auto &desc{llvm::omp::getDescriptor(id)};
+      if (!desc.getProperties(version).test(llvm::omp::Property::EndClause)) {
+        context_.Say(clause.source,
+            "%s clause is not allowed on an end-directive"_err_en_US,
+            GetUpperName(id, version));
+      }
+    }
+  }
+  VerifyClauseSyntax(
+      beginSpec->DirName(), GetAppliedClauses(beginSpec, endSpec, version));
+}
+
 bool OmpStructureChecker::VerifyModifierVersion(
     WithSource<llvm::omp::Clause> clause, const AppliedModifierInfo &info) {
   // Verify that the specified modifiers are allowed in this version.
@@ -580,4 +850,33 @@ void OmpStructureChecker::VerifyModifierSyntax(const parser::OmpClause &x) {
     break;
   }
 }
+
+// Mark clauseId as allowed on dirId.
+// * If dirId is a compound directive and "since" is a valid version,
+//   identify all leafs that allow the clause in version "since" or later,
+//   and mark the clause as allowed on these leafs as well.
+//   This is intended for allowing a "future case" in the current version.
+// * If dirId is a compound directive and "since" is not a valid version
+//   (i.e. !since is true) then mark the clause as allowed on all leafs
+//   that allow it in _some_ version. This is intended for allowing
+//   "deprecated cases".
+// * If dirId is not a compound directive the "since" parameter is ignored.
+void OmpStructureChecker::SetAllowedClauseOverride(llvm::omp::Clause clauseId,
+    llvm::omp::Directive dirId, llvm::omp::Version since) {
+  omp::SemanticOverrides &overrides{context_.GetOmpSemanticOverrides()};
+  overrides.allowedClauses[clauseId].set(dirId);
+
+  auto leafs{llvm::omp::getLeafConstructsOrSelf(dirId)};
+  if (leafs.size() > 1) {
+    llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
+    assert(
+        (!since || since > version) && "\"since\" should be a future version");
+    for (llvm::omp::Directive leaf : leafs) {
+      auto range{descriptor::GetVersionRangeForElement(clauseId, leaf)};
+      if (range.isValid() && (!since || since >= range.Min)) {
+        overrides.allowedClauses[clauseId].set(leaf);
+      }
+    }
+  }
+}
 } // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 7add875c96d883..ab981f688893ad 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -1170,8 +1170,6 @@ void OmpStructureChecker::CheckOmpDeclareVariantDirective(
   const parser::traits::OmpContextSelectorSpecification *matchSelector{
       getMatchClauseContextSelector(spec)};
   if (!matchSelector) {
-    context_.Say(x.source,
-        "DECLARE_VARIANT directive requires a MATCH clause"_err_en_US);
     return;
   }
 
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 16c265ce39690d..0f8f37739a2a84 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -43,6 +43,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Frontend/OpenMP/OMPContext.h"
+#include "llvm/Frontend/OpenMP/OMPDescriptors.h"
 
 #include <array>
 #include <cinttypes>
@@ -76,7 +77,9 @@ bool IsClauseAllowedOnDirective(llvm::omp::Clause clauseId,
       return true;
     }
   }
-  return llvm::omp::isAllowedClauseForDirective(dirId, clauseId, version);
+
+  const auto &desc{llvm::omp::getDescriptor(dirId)};
+  return descriptor::GetAllowedElements(desc, version).test(clauseId);
 }
 
 bool IsClauseAllowedOnDirective(llvm::omp::Clause clauseId,
diff --git a/flang/test/Lower/OpenMP/target-kernel-types.f90 b/flang/test/Lower/OpenMP/target-kernel-types.f90
index 50d715ea61486a..2799540116b3fb 100644
--- a/flang/test/Lower/OpenMP/target-kernel-types.f90
+++ b/flang/test/Lower/OpenMP/target-kernel-types.f90
@@ -1,4 +1,4 @@
-! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-assume-teams-oversubscription -fopenmp-assume-threads-oversubscription -o - %s | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=50 -fopenmp-assume-teams-oversubscription -fopenmp-assume-threads-oversubscription -o - %s | FileCheck %s
 
 ! ------------------------------------------------------------------------------
 ! GENERIC KERNELS
diff --git a/flang/test/Semantics/OpenMP/atomic.f90 b/flang/test/Semantics/OpenMP/atomic.f90
index 773c866e8c731c..d54262e3b8e519 100644
--- a/flang/test/Semantics/OpenMP/atomic.f90
+++ b/flang/test/Semantics/OpenMP/atomic.f90
@@ -35,7 +35,7 @@
   a = a + 1
   !$omp end atomic
 
-  !ERROR: At most one clause from the 'atomic' group is allowed on ATOMIC construct
+  !ERROR: READ and WRITE clauses are mutually exclusive as members of 'atomic' clause group
   !$omp atomic read write
   !ERROR: Atomic expression a+1._4 should be a variable
   a = a + 1
diff --git a/flang/test/Semantics/OpenMP/atomic05.f90 b/flang/test/Semantics/OpenMP/atomic05.f90
index 1c67b9354cf2c7..a1d8b19bbad963 100644
--- a/flang/test/Semantics/OpenMP/atomic05.f90
+++ b/flang/test/Semantics/OpenMP/atomic05.f90
@@ -5,20 +5,20 @@
 program OmpAtomic
     integer :: g, x
 
-    !ERROR: At most one clause from the 'memory-order' group is allowed on ATOMIC construct
+    !ERROR: RELAXED and SEQ_CST clauses are mutually exclusive as members of 'memory-order' clause group
     !$omp atomic relaxed, seq_cst
         x = x + 1
-    !ERROR: At most one clause from the 'memory-order' group is allowed on ATOMIC construct
+    !ERROR: SEQ_CST and RELAXED clauses are mutually exclusive as members of 'memory-order' clause group
     !$omp atomic read seq_cst, relaxed
         x = g
-    !ERROR: At most one clause from the 'memory-order' group is allowed on ATOMIC construct
+    !ERROR: RELAXED and RELEASE clauses are mutually exclusive as members of 'memory-order' clause group
     !$omp atomic write relaxed, release
         x = 2 * 4
-    !ERROR: At most one clause from the 'memory-order' group is allowed on ATOMIC construct
+    !ERROR: RELEASE and SEQ_CST clauses are mutually exclusive as members of 'memory-order' clause group
     !$omp atomic update release, seq_cst
     !ERROR: This is not a valid ATOMIC UPDATE operation
         x = 10
-    !ERROR: At most one clause from the 'memory-order' group is allowed on ATOMIC construct
+    !ERROR: RELEASE and SEQ_CST clauses are mutually exclusive as members of 'memory-order' clause group
     !$omp atomic capture release, seq_cst
         x = g
         g = x * 10
diff --git a/flang/test/Semantics/OpenMP/cancel.f90 b/flang/test/Semantics/OpenMP/cancel.f90
index 6147d581c4e69e..24c23fcbe42475 100644
--- a/flang/test/Semantics/OpenMP/cancel.f90
+++ b/flang/test/Semantics/OpenMP/cancel.f90
@@ -2,28 +2,28 @@
 
 subroutine f00
 !$omp parallel
-!ERROR: Missing cancel-directive-name clause on the CANCEL construct
+!ERROR: cancel-directive-name clause is required on CANCEL directive
 !$omp cancel
 !$omp end parallel
 end
 
 subroutine f01
 !$omp parallel
-!ERROR: Multiple cancel-directive-name clauses are not allowed on CANCEL construct
+!ERROR: At most one cancel-directive-name clause can appear on CANCEL directive
 !$omp cancel parallel parallel
 !$omp end parallel
 end
 
 subroutine f02
 !$omp parallel
-!ERROR: Missing cancel-directive-name clause on the CANCELLATION POINT construct
+!ERROR: cancel-directive-name clause is required on CANCELLATION POINT directive
 !$omp cancellation point
 !$omp end parallel
 end
 
 subroutine f03
 !$omp parallel
-!ERROR: Multiple cancel-directive-name clauses are not allowed on CANCELLATION POINT construct
+!ERROR: At most one cancel-directive-name clause can appear on CANCELLATION POINT directive
 !$omp cancellation point parallel parallel
 !$omp end parallel
 end
diff --git a/flang/test/Semantics/OpenMP/clause-validity01.f90 b/flang/test/Semantics/OpenMP/clause-validity01.f90
index 62c45818822c68..861d07ec4c59ed 100644
--- a/flang/test/Semantics/OpenMP/clause-validity01.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity01.f90
@@ -84,7 +84,8 @@
   end do
   !$omp end target
 
-  !ERROR: ALLOCATE clause is not allowed on TARGET DATA directive
+  !ERROR: ALLOCATE clause is not allowed on TARGET DATA directive in OpenMP v5.2, try -fopenmp-version=60 [-Wopenmp-future]
+  !ERROR: The ALLOCATE clause requires that 'b' must be listed in a private data-sharing attribute clause on the same directive
   !$omp target data map(from: b) allocate(b)
   do i = 1, N
      z = 2
@@ -286,6 +287,7 @@
   !$omp section
   c = 1
   d = 2
+  !ERROR: NUM_THREADS clause is not allowed on SECTIONS directive
   !ERROR: NUM_THREADS clause is not allowed on an end-directive
   !$omp end sections num_threads(4)
 
@@ -324,7 +326,6 @@
   !$omp end sections
 
   !$omp parallel sections
-  !ERROR: NOWAIT clause is not allowed on PARALLEL SECTIONS directive
   !$omp end parallel sections nowait
 
 ! 2.7.3 single-clause -> private-clause |
@@ -335,7 +336,7 @@
   !$omp parallel
   b = 1
   !ERROR: LASTPRIVATE clause is not allowed on SINGLE directive
-  !ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
+  !ERROR: NOWAIT and COPYPRIVATE clauses are mutually exclusive
   !$omp single private(a) lastprivate(c) nowait
   a = 3.14
   !ERROR: COPYPRIVATE variable 'a' may not appear on a PRIVATE or FIRSTPRIVATE clause on a SINGLE construct
@@ -470,7 +471,7 @@
      a = 3.14
   enddo
 
-  !ERROR: GRAINSIZE and NUM_TASKS clauses are mutually exclusive and may not appear on the same TASKLOOP directive
+  !ERROR: NUM_TASKS and GRAINSIZE clauses are mutually exclusive
   !$omp taskloop num_tasks(3) grainsize(2)
   do i = 1,N
      a = 3.14
@@ -523,12 +524,12 @@
   !WARNING: The syntax "FLUSH clause (object, ...)" has been deprecated, use "FLUSH(object, ...) clause" instead
   !$omp flush acquire
   !WARNING: The syntax "FLUSH clause (object, ...)" has been deprecated, use "FLUSH(object, ...) clause" instead
-  !ERROR: If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items must not be specified on the FLUSH directive
+  !ERROR: If a 'memory-order' clause is specified, list items must not be specified on the FLUSH directive
   !$omp flush release (c)
   !WARNING: The syntax "FLUSH clause (object, ...)" has been deprecated, use "FLUSH(object, ...) clause" instead
   !$omp flush seq_cst
   !WARNING: The syntax "FLUSH clause (object, ...)" has been deprecated, use "FLUSH(object, ...) clause" instead
-  !ERROR: RELAXED clause is not allowed on FLUSH directive
+  !ERROR: 'memory-order' cannot be RELAXED on FLUSH directive
   !$omp flush relaxed
 
 ! 2.13.2 critical Construct
@@ -584,7 +585,7 @@
   !ERROR: Misplaced OpenMP end-directive
   !$omp end taskloop
 
-  !ERROR: GRAINSIZE and NUM_TASKS clauses are mutually exclusive and may not appear on the same TASKLOOP SIMD directive
+  !ERROR: NUM_TASKS and GRAINSIZE clauses are mutually exclusive
   !$omp taskloop simd num_tasks(3) grainsize(2)
   do i = 1,N
      a = 3.14
diff --git a/flang/test/Semantics/OpenMP/clause-validity02.f90 b/flang/test/Semantics/OpenMP/clause-validity02.f90
index 1906df9d0eb477..b9c84797cbf742 100644
--- a/flang/test/Semantics/OpenMP/clause-validity02.f90
+++ b/flang/test/Semantics/OpenMP/clause-validity02.f90
@@ -3,8 +3,20 @@
 subroutine bad_in_45(h_ptr)
   integer, pointer :: h_ptr
   !ERROR: USE_DEVICE_ADDR clause is not allowed on TARGET DATA directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
-  !ERROR: At least one of MAP, USE_DEVICE_PTR clauses must appear on TARGET DATA directive
+  !ERROR: MAP clause is required on TARGET DATA directive
   !$omp target data use_device_addr(h_ptr)
   !$omp end target data
 end
 
+subroutine f02(N)
+  integer :: N
+  integer :: i, j
+  real :: a
+  !ERROR: ORDERED clause with an argument is not allowed on a compound directive with SIMD as a constituent
+  !$omp do simd ordered(2)
+  do i = 1, N
+     do j = 1, N
+        a = 3.14
+     enddo
+  enddo
+end
diff --git a/flang/test/Semantics/OpenMP/combined-constructs.f90 b/flang/test/Semantics/OpenMP/combined-constructs.f90
index c01998b1fa1b00..560ecb10332965 100644
--- a/flang/test/Semantics/OpenMP/combined-constructs.f90
+++ b/flang/test/Semantics/OpenMP/combined-constructs.f90
@@ -53,7 +53,7 @@ program main
   enddo
   !$omp end target parallel
 
-  !ERROR: COPYIN clause is not allowed on TARGET PARALLEL directive
+  !ERROR: COPYIN clause is not allowed on a compound directive with TARGET as a constituent
   !ERROR: Non-THREADPRIVATE object 'a' in COPYIN clause
   !$omp target parallel copyin(a)
   do i = 1, N
@@ -100,7 +100,7 @@ program main
   enddo
   !$omp end target parallel do
 
-  !ERROR: COPYIN clause is not allowed on TARGET PARALLEL DO directive
+  !ERROR: COPYIN clause is not allowed on a compound directive with TARGET as a constituent
   !ERROR: Non-THREADPRIVATE object 'a' in COPYIN clause
   !$omp target parallel do copyin(a)
   do i = 1, N
diff --git a/flang/test/Semantics/OpenMP/declarative-directive01.f90 b/flang/test/Semantics/OpenMP/declarative-directive01.f90
index 9e0612b5c8b53b..42b667e1f7afdf 100644
--- a/flang/test/Semantics/OpenMP/declarative-directive01.f90
+++ b/flang/test/Semantics/OpenMP/declarative-directive01.f90
@@ -38,7 +38,7 @@ end module m1
 subroutine declare_simd_2
   use m1
   procedure (sub) sub1
-  !ERROR: INBRANCH and NOTINBRANCH clauses are mutually exclusive and may not appear on the same DECLARE SIMD directive
+  !ERROR: INBRANCH and NOTINBRANCH clauses are mutually exclusive as members of 'branch' clause group
   !$omp declare simd(sub1) inbranch notinbranch
   procedure (sub), pointer::p
   p=>sub1
@@ -60,6 +60,7 @@ subroutine foo
     !WARNING: The entity with PARAMETER attribute is used in a DECLARE TARGET directive [-Wopenmp-usage]
     !WARNING: The entity with PARAMETER attribute is used in a DECLARE TARGET directive [-Wopenmp-usage]
     !$omp declare target (foo, N, M)
+    !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
     !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
     !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
     !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
@@ -69,6 +70,7 @@ subroutine foo
     !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
     !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
     !$omp declare target enter(Q, S) link(R)
+    !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
     !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
     !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
     !ERROR: MAP clause is not allowed on DECLARE TARGET directive
diff --git a/flang/test/Semantics/OpenMP/declare-target01.f90 b/flang/test/Semantics/OpenMP/declare-target01.f90
index b498cf649126da..b48cfb5b98de0e 100644
--- a/flang/test/Semantics/OpenMP/declare-target01.f90
+++ b/flang/test/Semantics/OpenMP/declare-target01.f90
@@ -48,16 +48,19 @@ module declare_target01
   !ERROR: A type parameter inquiry cannot appear on the DECLARE TARGET directive
   !$omp declare target (y%KIND)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (my_var)
 
   !$omp declare target enter (my_var)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (my_var) device_type(host)
 
   !$omp declare target enter (my_var) device_type(host)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !ERROR: A structure component cannot appear on the DECLARE TARGET directive
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (my_var%t_i)
@@ -65,6 +68,7 @@ module declare_target01
   !ERROR: A structure component cannot appear on the DECLARE TARGET directive
   !$omp declare target enter (my_var%t_i)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !ERROR: A structure component cannot appear on the DECLARE TARGET directive
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (my_var%t_arr)
@@ -72,6 +76,7 @@ module declare_target01
   !ERROR: A structure component cannot appear on the DECLARE TARGET directive
   !$omp declare target enter (my_var%t_arr)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !ERROR: Type parameter inquiry is not allowed as a list item on TO clause
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (my_var%kind_param)
@@ -79,6 +84,7 @@ module declare_target01
   !ERROR: Type parameter inquiry is not allowed as a list item on ENTER clause
   !$omp declare target enter (my_var%kind_param)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !ERROR: Type parameter inquiry is not allowed as a list item on TO clause
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (my_var%len_param)
@@ -86,16 +92,19 @@ module declare_target01
   !ERROR: Type parameter inquiry is not allowed as a list item on ENTER clause
   !$omp declare target enter (my_var%len_param)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (arr)
 
   !$omp declare target enter (arr)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (arr) device_type(nohost)
 
   !$omp declare target enter (arr) device_type(nohost)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !ERROR: An array element cannot appear on the DECLARE TARGET directive
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (arr(1))
@@ -103,6 +112,7 @@ module declare_target01
   !ERROR: An array element cannot appear on the DECLARE TARGET directive
   !$omp declare target enter (arr(1))
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !ERROR: An array element cannot appear on the DECLARE TARGET directive
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (arr(1:2))
@@ -110,6 +120,7 @@ module declare_target01
   !ERROR: An array element cannot appear on the DECLARE TARGET directive
   !$omp declare target enter (arr(1:2))
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !ERROR: Type parameter inquiry is not allowed as a list item on TO clause
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (x%KIND)
@@ -117,6 +128,7 @@ module declare_target01
   !ERROR: Type parameter inquiry is not allowed as a list item on ENTER clause
   !$omp declare target enter (x%KIND)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !ERROR: Type parameter inquiry is not allowed as a list item on TO clause
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (w%LEN)
@@ -124,6 +136,7 @@ module declare_target01
   !ERROR: Type parameter inquiry is not allowed as a list item on ENTER clause
   !$omp declare target enter (w%LEN)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !ERROR: Type parameter inquiry is not allowed as a list item on TO clause
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (y%KIND)
diff --git a/flang/test/Semantics/OpenMP/declare-target02.f90 b/flang/test/Semantics/OpenMP/declare-target02.f90
index db9e51a197b2c0..1157a8747cfc47 100644
--- a/flang/test/Semantics/OpenMP/declare-target02.f90
+++ b/flang/test/Semantics/OpenMP/declare-target02.f90
@@ -16,16 +16,19 @@ program declare_target02
   !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
   !$omp declare target (a1)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (arr1_to)
 
   !$omp declare target enter (arr1_to)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (blk1_to)
 
   !$omp declare target enter (blk1_to)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
   !$omp declare target to (a1_to)
@@ -44,6 +47,7 @@ program declare_target02
   !ERROR: A variable in a DECLARE TARGET directive cannot appear in an EQUIVALENCE statement
   !$omp declare target (eq_a)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !ERROR: A variable in a DECLARE TARGET directive cannot appear in an EQUIVALENCE statement
   !$omp declare target to (eq_a)
@@ -57,6 +61,7 @@ program declare_target02
   !ERROR: A variable in a DECLARE TARGET directive cannot appear in an EQUIVALENCE statement
   !$omp declare target (eq_c)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !ERROR: A variable in a DECLARE TARGET directive cannot appear in an EQUIVALENCE statement
   !$omp declare target to (eq_c)
@@ -87,6 +92,7 @@ subroutine func()
     !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
     !$omp declare target (a3)
 
+    !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
     !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
     !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
     !$omp declare target to (arr2_to)
@@ -94,11 +100,13 @@ subroutine func()
     !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
     !$omp declare target enter (arr2_to)
 
+    !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
     !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
     !$omp declare target to (arr3_to)
 
     !$omp declare target enter (arr3_to)
 
+    !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
     !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
     !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
     !$omp declare target to (a2_to)
@@ -106,6 +114,7 @@ subroutine func()
     !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
     !$omp declare target enter (a2_to)
 
+    !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
     !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
     !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
     !$omp declare target to (a3_to)
@@ -137,15 +146,18 @@ module mod4
   !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
   !$omp declare target (a4)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (arr4_to)
 
   !$omp declare target enter (arr4_to)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to (blk4_to)
   !$omp declare target enter (blk4_to)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
   !$omp declare target to (a4_to)
@@ -174,6 +186,7 @@ subroutine func5()
   !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
   !$omp declare target (a5)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
   !$omp declare target to (arr5_to)
@@ -181,6 +194,7 @@ subroutine func5()
   !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
   !$omp declare target enter (arr5_to)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
   !$omp declare target to (blk5_to)
@@ -188,6 +202,7 @@ subroutine func5()
   !ERROR: A variable that appears in a DECLARE TARGET directive must be declared in the scope of a module or have the SAVE attribute, either explicitly or implicitly
   !$omp declare target enter (blk5_to)
 
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !ERROR: A variable in a DECLARE TARGET directive cannot be an element of a common block
   !$omp declare target to (a5_to)
diff --git a/flang/test/Semantics/OpenMP/declare-target06.f90 b/flang/test/Semantics/OpenMP/declare-target06.f90
index 5dc4b8042ce1dd..5341aa73267de6 100644
--- a/flang/test/Semantics/OpenMP/declare-target06.f90
+++ b/flang/test/Semantics/OpenMP/declare-target06.f90
@@ -12,6 +12,7 @@ module test_0
 !ERROR: No explicit type declared for 'no_implicit_materialization_2'
 !$omp declare target link(no_implicit_materialization_2)
 
+!WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
 !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
 !ERROR: No explicit type declared for 'no_implicit_materialization_3'
 !$omp declare target to(no_implicit_materialization_3)
diff --git a/flang/test/Semantics/OpenMP/declare-variant-match.f90 b/flang/test/Semantics/OpenMP/declare-variant-match.f90
index 73558dc171b692..5cd82aaa36044b 100644
--- a/flang/test/Semantics/OpenMP/declare-variant-match.f90
+++ b/flang/test/Semantics/OpenMP/declare-variant-match.f90
@@ -97,7 +97,7 @@ subroutine sub
 end subroutine
 
 subroutine f08
-!ERROR: DECLARE_VARIANT directive requires a MATCH clause
+!ERROR: MATCH clause is required on DECLARE VARIANT directive
   !$omp declare variant (sub:vsub)
 contains
   subroutine vsub
diff --git a/flang/test/Semantics/OpenMP/depobj-construct-v50.f90 b/flang/test/Semantics/OpenMP/depobj-construct-v50.f90
index ce030a1082bde3..1fc0760ba50928 100644
--- a/flang/test/Semantics/OpenMP/depobj-construct-v50.f90
+++ b/flang/test/Semantics/OpenMP/depobj-construct-v50.f90
@@ -40,6 +40,6 @@ subroutine f05
 
 subroutine f06
   integer :: obj
-!ERROR: The DEPOBJ construct requires a single clause
+!ERROR: UPDATE (depend-objects) and DESTROY clauses are mutually exclusive
   !$omp depobj(obj) update(in) destroy
 end
diff --git a/flang/test/Semantics/OpenMP/detach02.f90 b/flang/test/Semantics/OpenMP/detach02.f90
index f0e53b92edf6d0..811e4a5de3dd61 100644
--- a/flang/test/Semantics/OpenMP/detach02.f90
+++ b/flang/test/Semantics/OpenMP/detach02.f90
@@ -14,7 +14,7 @@ program detach02
        x = x + 1
     !$omp end task
 
-    !ERROR: Clause MERGEABLE is not allowed if clause DETACH appears on the TASK directive
+    !ERROR: DETACH and MERGEABLE clauses are mutually exclusive
     !$omp task detach(event_01) mergeable
         x = x + 1
     !$omp end task
diff --git a/flang/test/Semantics/OpenMP/device-constructs.f90 b/flang/test/Semantics/OpenMP/device-constructs.f90
index b74304222c8351..7e950751fea030 100644
--- a/flang/test/Semantics/OpenMP/device-constructs.f90
+++ b/flang/test/Semantics/OpenMP/device-constructs.f90
@@ -144,7 +144,7 @@ program main
    cptr = c_null_ptr
   !$omp end target data
 
-  !ERROR: At least one of MAP, USE_DEVICE_ADDR, USE_DEVICE_PTR clauses must appear on TARGET DATA directive
+  !ERROR: One of MAP, USE_DEVICE_ADDR or USE_DEVICE_PTR clauses is required on TARGET DATA directive
   !$omp target data device(0)
   do i = 1, N
      a = 3.14d0
diff --git a/flang/test/Semantics/OpenMP/flush02.f90 b/flang/test/Semantics/OpenMP/flush02.f90
index db0cc12398b970..dbb33d4de03636 100644
--- a/flang/test/Semantics/OpenMP/flush02.f90
+++ b/flang/test/Semantics/OpenMP/flush02.f90
@@ -28,7 +28,7 @@
   if (omp_get_thread_num() == 1) THEN
     ! Not allowed clauses.
     !$omp flush seq_cst
-    !ERROR: RELAXED clause is not allowed on FLUSH directive
+    !ERROR: 'memory-order' cannot be RELAXED on FLUSH directive
     !$omp flush relaxed
 
     ! Not allowed more than once.
@@ -39,7 +39,7 @@
     !ERROR: At most one ACQUIRE clause can appear on FLUSH directive
     !$omp flush acquire acquire
 
-    ! Mix of allowed and not allowed.
+    !ERROR: SEQ_CST and ACQUIRE clauses are mutually exclusive as members of 'memory-order' clause group
     !$omp flush seq_cst acquire
   END IF
 
@@ -57,21 +57,21 @@
 
   array = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)
   if (omp_get_thread_num() == 3) THEN
-    !ERROR: If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items must not be specified on the FLUSH directive
+    !ERROR: If a 'memory-order' clause is specified, list items must not be specified on the FLUSH directive
     !$omp flush acq_rel (array)
-    !ERROR: If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items must not be specified on the FLUSH directive
+    !ERROR: If a 'memory-order' clause is specified, list items must not be specified on the FLUSH directive
     !$omp flush acq_rel (array, a, i)
 
     array = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)
-    !ERROR: If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items must not be specified on the FLUSH directive
+    !ERROR: If a 'memory-order' clause is specified, list items must not be specified on the FLUSH directive
     !$omp flush release (array)
-    !ERROR: If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items must not be specified on the FLUSH directive
+    !ERROR: If a 'memory-order' clause is specified, list items must not be specified on the FLUSH directive
     !$omp flush release (array, a)
 
     array = (/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/)
-    !ERROR: If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items must not be specified on the FLUSH directive
+    !ERROR: If a 'memory-order' clause is specified, list items must not be specified on the FLUSH directive
     !$omp flush acquire (array)
-    !ERROR: If memory-order-clause is RELEASE, ACQUIRE, or ACQ_REL, list items must not be specified on the FLUSH directive
+    !ERROR: If a 'memory-order' clause is specified, list items must not be specified on the FLUSH directive
     !$omp flush acquire (array, a, structObj%rr)
   END IF
   !$omp end parallel
diff --git a/flang/test/Semantics/OpenMP/flush03.f90 b/flang/test/Semantics/OpenMP/flush03.f90
index 82c5bef5f5b8d6..d92555c341d502 100644
--- a/flang/test/Semantics/OpenMP/flush03.f90
+++ b/flang/test/Semantics/OpenMP/flush03.f90
@@ -2,6 +2,7 @@
 
 subroutine f00(x)
   integer :: x
+!ERROR: If a 'memory-order' clause is specified, list items must not be specified on the FLUSH directive
 !ERROR: The syntax "FLUSH clause (object, ...)" has been deprecated, use "FLUSH(object, ...) clause" instead
   !$omp flush seq_cst (x)
 end
diff --git a/flang/test/Semantics/OpenMP/if-clause-45.f90 b/flang/test/Semantics/OpenMP/if-clause-45.f90
index 1ed2fcf8c0ae1a..5b34090d41d903 100644
--- a/flang/test/Semantics/OpenMP/if-clause-45.f90
+++ b/flang/test/Semantics/OpenMP/if-clause-45.f90
@@ -77,7 +77,6 @@ program main
   end do
   !$omp end distribute simd
 
-  !ERROR: IF clause is not allowed on DISTRIBUTE SIMD directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
   !ERROR: IF clause is not allowed on DISTRIBUTE SIMD directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
   !ERROR: SIMD is not allowed as 'directive-name-modifier' in OpenMP v4.5, try -fopenmp-version=50
   !$omp distribute simd if(.true.) if(simd: .false.)
@@ -109,7 +108,6 @@ program main
   end do
   !$omp end do simd
 
-  !ERROR: IF clause is not allowed on DO SIMD directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
   !ERROR: IF clause is not allowed on DO SIMD directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
   !ERROR: SIMD is not allowed as 'directive-name-modifier' in OpenMP v4.5, try -fopenmp-version=50
   !$omp do simd if(.true.) if(simd: .false.)
@@ -236,7 +234,6 @@ program main
   end do
   !$omp end simd
 
-  !ERROR: IF clause is not allowed on SIMD directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
   !ERROR: IF clause is not allowed on SIMD directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
   !ERROR: SIMD is not allowed as 'directive-name-modifier' in OpenMP v4.5, try -fopenmp-version=50
   !$omp simd if(.true.) if(simd: .false.)
@@ -569,7 +566,6 @@ program main
   !$omp teams if(target: .true.)
   !$omp end teams
 
-  !ERROR: IF clause is not allowed on TEAMS directive in OpenMP v4.5, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: IF clause is not allowed on TEAMS directive in OpenMP v4.5, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: TEAMS is not allowed as 'directive-name-modifier' in OpenMP v4.5, try -fopenmp-version=52
   !$omp teams if(.true.) if(teams: .false.)
@@ -578,23 +574,27 @@ program main
   ! ----------------------------------------------------------------------------
   ! TEAMS DISTRIBUTE
   ! ----------------------------------------------------------------------------
+  !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE directive in OpenMP v4.5, try -fopenmp-version=52 [-Wopenmp-future]
   !$omp teams distribute if(.true.)
   do i = 1, 10
   end do
   !$omp end teams distribute
 
+  !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE directive in OpenMP v4.5, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: TEAMS is not allowed as 'directive-name-modifier' in OpenMP v4.5, try -fopenmp-version=52
   !$omp teams distribute if(teams: .true.)
   do i = 1, 10
   end do
   !$omp end teams distribute
 
+  !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE directive in OpenMP v4.5, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: TARGET is not a constituent of the TEAMS DISTRIBUTE directive
   !$omp teams distribute if(target: .true.)
   do i = 1, 10
   end do
   !$omp end teams distribute
 
+  !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE directive in OpenMP v4.5, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: TEAMS is not allowed as 'directive-name-modifier' in OpenMP v4.5, try -fopenmp-version=52
   !$omp teams distribute if(.true.) if(teams: .true.)
   do i = 1, 10
@@ -652,7 +652,6 @@ program main
   end do
   !$omp end teams distribute simd
 
-  !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE SIMD directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
   !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE SIMD directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
   !ERROR: TEAMS is not allowed as 'directive-name-modifier' in OpenMP v4.5, try -fopenmp-version=52
   !ERROR: SIMD is not allowed as 'directive-name-modifier' in OpenMP v4.5, try -fopenmp-version=50
diff --git a/flang/test/Semantics/OpenMP/if-clause-50.f90 b/flang/test/Semantics/OpenMP/if-clause-50.f90
index 651b62d6a43448..94cd8d3133519a 100644
--- a/flang/test/Semantics/OpenMP/if-clause-50.f90
+++ b/flang/test/Semantics/OpenMP/if-clause-50.f90
@@ -544,7 +544,6 @@ program main
   !$omp teams if(target: .true.)
   !$omp end teams
 
-  !ERROR: IF clause is not allowed on TEAMS directive in OpenMP v5.0, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: IF clause is not allowed on TEAMS directive in OpenMP v5.0, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: TEAMS is not allowed as 'directive-name-modifier' in OpenMP v5.0, try -fopenmp-version=52
   !$omp teams if(.true.) if(teams: .false.)
@@ -553,23 +552,27 @@ program main
   ! ----------------------------------------------------------------------------
   ! TEAMS DISTRIBUTE
   ! ----------------------------------------------------------------------------
+  !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE directive in OpenMP v5.0, try -fopenmp-version=52 [-Wopenmp-future]
   !$omp teams distribute if(.true.)
   do i = 1, 10
   end do
   !$omp end teams distribute
 
+  !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE directive in OpenMP v5.0, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: TEAMS is not allowed as 'directive-name-modifier' in OpenMP v5.0, try -fopenmp-version=52
   !$omp teams distribute if(teams: .true.)
   do i = 1, 10
   end do
   !$omp end teams distribute
 
+  !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE directive in OpenMP v5.0, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: TARGET is not a constituent of the TEAMS DISTRIBUTE directive
   !$omp teams distribute if(target: .true.)
   do i = 1, 10
   end do
   !$omp end teams distribute
 
+  !ERROR: IF clause is not allowed on TEAMS DISTRIBUTE directive in OpenMP v5.0, try -fopenmp-version=52 [-Wopenmp-future]
   !ERROR: TEAMS is not allowed as 'directive-name-modifier' in OpenMP v5.0, try -fopenmp-version=52
   !$omp teams distribute if(.true.) if(teams: .true.)
   do i = 1, 10
diff --git a/flang/test/Semantics/OpenMP/interop-construct.f90 b/flang/test/Semantics/OpenMP/interop-construct.f90
index 69484d77b05e11..7f3cc5b3b03b95 100644
--- a/flang/test/Semantics/OpenMP/interop-construct.f90
+++ b/flang/test/Semantics/OpenMP/interop-construct.f90
@@ -154,7 +154,7 @@ END SUBROUTINE test_interop_17
 ! only a device (or other non-action) clause is invalid.
 SUBROUTINE test_interop_18(dev)
   INTEGER :: dev
-  !ERROR: At least one action-clause (INIT, USE, or DESTROY) must appear on the INTEROP construct
+  !ERROR: One of DESTROY, INIT or USE clauses is required on INTEROP directive
   !$OMP INTEROP DEVICE(dev)
   PRINT *, 'pass'
 END SUBROUTINE test_interop_18
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
index 10affbb4fd4746..5369f1cf7a7cc4 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
@@ -19,7 +19,7 @@ subroutine loop_transformation_construct2
   integer :: v(i)
 
   !$omp do
-  !ERROR: At least one of SIZES clause must appear on TILE directive
+  !ERROR: SIZES clause is required on TILE directive
   !$omp tile
   do x = 1, i
     v(x) = v(x) * 2
@@ -54,7 +54,7 @@ subroutine loop_transformation_construct4
     v(x) = v(x) * 2
   end do
   !ERROR: This construct should contain a DO-loop or a loop-nest-generating construct
-  !ERROR: At least one of SIZES clause must appear on TILE directive
+  !ERROR: SIZES clause is required on TILE directive
   !$omp tile
 end subroutine
 
@@ -66,7 +66,7 @@ subroutine loop_transformation_construct5
 
   !$omp do
   !ERROR: This construct requires a canonical loop nest
-  !ERROR: At least one of SIZES clause must appear on TILE directive
+  !ERROR: SIZES clause is required on TILE directive
   !$omp tile
   !BECAUSE: Fully unrolled loop does not result in a loop nest
   !$omp unroll full
@@ -83,7 +83,7 @@ subroutine loop_transformation_construct6
 
   !$omp do
   !ERROR: This construct requires a canonical loop nest
-  !ERROR: At least one of SIZES clause must appear on TILE directive
+  !ERROR: SIZES clause is required on TILE directive
   !$omp tile
   !BECAUSE: Fully unrolled loop does not result in a loop nest
   !$omp unroll
@@ -99,7 +99,7 @@ subroutine loop_transformation_construct7
   integer :: v(i)
 
   !$omp do
-  !ERROR: At least one of SIZES clause must appear on TILE directive
+  !ERROR: SIZES clause is required on TILE directive
   !$omp tile
   !$omp unroll partial(2)
   do x = 1, i
diff --git a/flang/test/Semantics/OpenMP/ordered03.f90 b/flang/test/Semantics/OpenMP/ordered03.f90
index ca5e3bf151b415..90e053ed58b6ef 100644
--- a/flang/test/Semantics/OpenMP/ordered03.f90
+++ b/flang/test/Semantics/OpenMP/ordered03.f90
@@ -52,7 +52,7 @@ subroutine sub1()
   end do
   !$omp end target parallel do
 
-  !ERROR: ORDERED clause is not allowed on TARGET TEAMS DISTRIBUTE PARALLEL DO directive
+  !ERROR: ORDERED clause is not allowed on a compound directive with DISTRIBUTE as a constituent
   !$omp target teams distribute parallel do ordered(1)
   do i = 1, N
     !ERROR: An ORDERED construct with the DEPEND clause must be closely nested in a worksharing-loop (or parallel worksharing-loop) construct with ORDERED clause with a parameter
diff --git a/flang/test/Semantics/OpenMP/requires01.f90 b/flang/test/Semantics/OpenMP/requires01.f90
index 9153f7ddf91b77..717ac9fff588bb 100644
--- a/flang/test/Semantics/OpenMP/requires01.f90
+++ b/flang/test/Semantics/OpenMP/requires01.f90
@@ -3,6 +3,7 @@
 !WARNING: REVERSE_OFFLOAD clause is not supported and will be ignored
 !$omp requires reverse_offload unified_shared_memory
 
+!ERROR: One of ATOMIC_DEFAULT_MEM_ORDER, DYNAMIC_ALLOCATORS, REVERSE_OFFLOAD, UNIFIED_ADDRESS or UNIFIED_SHARED_MEMORY clauses is required on REQUIRES directive
 !ERROR: NOWAIT clause is not allowed on REQUIRES directive
 !$omp requires nowait
 end
diff --git a/flang/test/Semantics/OpenMP/requires04.f90 b/flang/test/Semantics/OpenMP/requires04.f90
index e392564c523d53..70767817a8fae4 100644
--- a/flang/test/Semantics/OpenMP/requires04.f90
+++ b/flang/test/Semantics/OpenMP/requires04.f90
@@ -7,6 +7,7 @@
 
 subroutine f
   integer, save :: x
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to(x) device_type(nohost)
   !$omp declare target enter(x) device_type(nohost)
diff --git a/flang/test/Semantics/OpenMP/requires05.f90 b/flang/test/Semantics/OpenMP/requires05.f90
index 31f0b29edddda3..fb428410c73e2a 100644
--- a/flang/test/Semantics/OpenMP/requires05.f90
+++ b/flang/test/Semantics/OpenMP/requires05.f90
@@ -6,6 +6,7 @@
 ! clause and no device_type.
 
 subroutine f
+  !WARNING: TO clause is no longer allowed on DECLARE TARGET directive since OpenMP v5.2 [-Wopenmp-deprecated]
   !WARNING: The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead. [-Wopenmp-deprecated]
   !$omp declare target to(f)
   !$omp declare target enter(f)
diff --git a/flang/test/Semantics/OpenMP/scan1.f90 b/flang/test/Semantics/OpenMP/scan1.f90
index e6b1ccf695fff9..8138bea60cf85f 100644
--- a/flang/test/Semantics/OpenMP/scan1.f90
+++ b/flang/test/Semantics/OpenMP/scan1.f90
@@ -9,25 +9,26 @@ subroutine test_scan()
  !$omp parallel do simd
  do k = 1, n
  !ERROR: UNTIED clause is not allowed on SCAN directive
+ !ERROR: One of EXCLUSIVE or INCLUSIVE clauses is required on SCAN directive
    !$omp scan untied
  end do
 
  !$omp parallel do simd
  do k = 1, n
- !ERROR: Exactly one of EXCLUSIVE or INCLUSIVE clause is expected
+ !ERROR: One of EXCLUSIVE or INCLUSIVE clauses is required on SCAN directive
    !$omp scan
  end do
 
 !$omp parallel do simd reduction(inscan,+: x, y)
  do k = 1, n
- !ERROR: Exactly one of EXCLUSIVE or INCLUSIVE clause is expected
+ !ERROR: INCLUSIVE and EXCLUSIVE clauses are mutually exclusive
    !$omp scan inclusive(x) exclusive(y)
  end do
 
 !ERROR: List item y must appear in EXCLUSIVE or INCLUSIVE clause of an enclosed SCAN directive
 !$omp parallel do simd reduction(inscan,+: x, y)
  do k = 1, n
- !ERROR: Exactly one of EXCLUSIVE or INCLUSIVE clause is expected
+ !ERROR: INCLUSIVE and EXCLUSIVE clauses are mutually exclusive
  !ERROR: List item z must appear in REDUCTION clause with the INSCAN modifier of the parent directive
    !$omp scan inclusive(x) exclusive(z)
  end do
diff --git a/flang/test/Semantics/OpenMP/single03.f90 b/flang/test/Semantics/OpenMP/single03.f90
index d082be4a142e47..601e45f776426a 100644
--- a/flang/test/Semantics/OpenMP/single03.f90
+++ b/flang/test/Semantics/OpenMP/single03.f90
@@ -6,7 +6,7 @@ subroutine omp_single
     i = 10; j = 11
 
     !ERROR: COPYPRIVATE variable 'i' is not PRIVATE or THREADPRIVATE in outer context
-    !ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
+    !ERROR: COPYPRIVATE and NOWAIT clauses are mutually exclusive
     !$omp single copyprivate(i) nowait
         print *, "omp single", i
     !$omp end single
@@ -18,16 +18,16 @@ subroutine omp_single
     !$omp end parallel
 
     !$omp parallel
-        !ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
+        !ERROR: NOWAIT and COPYPRIVATE clauses are mutually exclusive
         !$omp single nowait
             print *, "omp single", i
         !ERROR: COPYPRIVATE variable 'i' is not PRIVATE or THREADPRIVATE in outer context
         !$omp end single copyprivate(i)
 
+        !ERROR: COPYPRIVATE and NOWAIT clauses are mutually exclusive
         !ERROR: COPYPRIVATE variable 'i' is not PRIVATE or THREADPRIVATE in outer context
         !$omp single copyprivate(i)
             print *, "omp single", i
-        !ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
         !$omp end single nowait
 
         !ERROR: COPYPRIVATE variable 'j' may not appear on a PRIVATE or FIRSTPRIVATE clause on a SINGLE construct
diff --git a/flang/test/Semantics/OpenMP/single04.f90 b/flang/test/Semantics/OpenMP/single04.f90
index 1eac263c95764b..328f80557af4d2 100644
--- a/flang/test/Semantics/OpenMP/single04.f90
+++ b/flang/test/Semantics/OpenMP/single04.f90
@@ -21,7 +21,7 @@ program single
     ! Invalid testcases
     !$omp single
         print *, x
-    !ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
+    !ERROR: COPYPRIVATE and NOWAIT clauses are mutually exclusive
     !$omp end single copyprivate(x) nowait
 
     !ERROR: 'x' appears more than once in a COPYPRIVATE clause
@@ -44,20 +44,20 @@ program single
     !ERROR: At most one NOWAIT clause can appear on SINGLE directive
     !$omp end single nowait nowait
 
-    !ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
+    !ERROR: COPYPRIVATE and NOWAIT clauses are mutually exclusive
     !$omp single copyprivate(x) nowait
         print *, x
     !ERROR: 'x' appears more than once in a COPYPRIVATE clause
     !ERROR: At most one NOWAIT clause can appear on SINGLE directive
     !$omp end single copyprivate(x) nowait
 
+    !ERROR: COPYPRIVATE and NOWAIT clauses are mutually exclusive
     !$omp single copyprivate(x)
         print *, x
     !ERROR: 'x' appears more than once in a COPYPRIVATE clause
-    !ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
     !$omp end single copyprivate(x) nowait
 
-    !ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
+    !ERROR: COPYPRIVATE and NOWAIT clauses are mutually exclusive
     !$omp single copyprivate(x, y) nowait
         print *, x
     !ERROR: 'x' appears more than once in a COPYPRIVATE clause
@@ -65,7 +65,7 @@ program single
     !ERROR: At most one NOWAIT clause can appear on SINGLE directive
     !$omp end single copyprivate(x, z) copyprivate(z) nowait
 
-    !ERROR: NOWAIT clause must not be used with COPYPRIVATE clause on SINGLE directive
+    !ERROR: COPYPRIVATE and NOWAIT clauses are mutually exclusive
     !$omp single copyprivate(x) nowait copyprivate(y) copyprivate(z)
         print *, x
     !ERROR: 'x' appears more than once in a COPYPRIVATE clause
diff --git a/flang/test/Semantics/OpenMP/target-update01.f90 b/flang/test/Semantics/OpenMP/target-update01.f90
index a7a12b96e85454..a69e628f357907 100644
--- a/flang/test/Semantics/OpenMP/target-update01.f90
+++ b/flang/test/Semantics/OpenMP/target-update01.f90
@@ -2,10 +2,10 @@
 
 subroutine foo(x)
   integer :: x
-  !ERROR: At least one motion-clause (TO/FROM) must be specified on TARGET UPDATE construct.
+  !ERROR: One of FROM or TO clauses is required on TARGET UPDATE directive
   !$omp target update
 
-  !ERROR: At least one motion-clause (TO/FROM) must be specified on TARGET UPDATE construct.
+  !ERROR: One of FROM or TO clauses is required on TARGET UPDATE directive
   !$omp target update nowait
 
   !$omp target update to(x) nowait
diff --git a/flang/test/Semantics/OpenMP/task-45.f90 b/flang/test/Semantics/OpenMP/task-45.f90
index 64b7cf765f8d2d..7c6e7a6e8c3ff1 100644
--- a/flang/test/Semantics/OpenMP/task-45.f90
+++ b/flang/test/Semantics/OpenMP/task-45.f90
@@ -7,7 +7,7 @@
 subroutine f
   use omp_lib, only: omp_event_handle_kind
   integer(kind=omp_event_handle_kind) :: e
-  !ERROR: Clause MERGEABLE is not allowed if clause DETACH appears on the TASK directive
+  !ERROR: MERGEABLE and DETACH clauses are mutually exclusive
   !WARNING: DETACH clause is not allowed on TASK directive in OpenMP v4.5, try -fopenmp-version=50 [-Wopenmp-future]
   !$omp task mergeable detach(e)
   !$omp end task
diff --git a/flang/test/Semantics/OpenMP/tile01.f90 b/flang/test/Semantics/OpenMP/tile01.f90
index 2e98586bef8fc8..dc1b5b9d48a4fe 100644
--- a/flang/test/Semantics/OpenMP/tile01.f90
+++ b/flang/test/Semantics/OpenMP/tile01.f90
@@ -6,7 +6,7 @@ subroutine missing_sizes
   implicit none
   integer i
 
-  !ERROR: At least one of SIZES clause must appear on TILE directive
+  !ERROR: SIZES clause is required on TILE directive
   !$omp tile
   do i = 1, 42
     print *, i
diff --git a/flang/test/Semantics/OpenMP/unroll-clauses.f90 b/flang/test/Semantics/OpenMP/unroll-clauses.f90
index 68dd47ce29b921..f524058c8fef29 100644
--- a/flang/test/Semantics/OpenMP/unroll-clauses.f90
+++ b/flang/test/Semantics/OpenMP/unroll-clauses.f90
@@ -8,7 +8,7 @@ subroutine unroll_clauses
   integer :: i
   integer :: v(n)
 
-  !ERROR: FULL and PARTIAL clauses are mutually exclusive and may not appear on the same UNROLL directive
+  !ERROR: FULL and PARTIAL clauses are mutually exclusive
   !$omp unroll full partial(2)
   do i = 1, n
     v(i) = i
diff --git a/flang/test/Semantics/OpenMP/workdistribute04.f90 b/flang/test/Semantics/OpenMP/workdistribute04.f90
index d407e8a073ae40..bb6d635f833c29 100644
--- a/flang/test/Semantics/OpenMP/workdistribute04.f90
+++ b/flang/test/Semantics/OpenMP/workdistribute04.f90
@@ -1,4 +1,4 @@
-! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=50
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=50 -Werror -Wno-experimental-option
 ! OpenMP Version 6.0
 ! workdistribute Construct
 ! Unsuported OpenMP version
diff --git a/flang/test/Semantics/indirect02.f90 b/flang/test/Semantics/indirect02.f90
index 3fae39f1c22813..65135615d30c79 100644
--- a/flang/test/Semantics/indirect02.f90
+++ b/flang/test/Semantics/indirect02.f90
@@ -1,6 +1,6 @@
 ! This test checks the lowering of OpenMP Indirect Clause when used with the Declare Target directive
 
-! RUN: not %flang -fopenmp -fopenmp-version=50 %s 2>&1 | FileCheck %s --check-prefix="CHECK-50"
+! RUN: not %flang -fopenmp -fopenmp-version=50 %s 2>&1 | FileCheck %s --check-prefixes="CHECK-50","CHECK-52"
 ! RUN: not %flang -fopenmp -fopenmp-version=52 %s 2>&1 | FileCheck %s --check-prefix="CHECK-52"
 
 module functions
@@ -14,7 +14,7 @@ function func() result(i)
 
 contains
   function func1() result(i)
-    !CHECK-50: INDIRECT clause is not allowed on DECLARE TARGET directive in OpenMP v5.0, try -fopenmp-version=51
+    !CHECK-50: INDIRECT clause is not allowed on DECLARE TARGET directive in OpenMP v5.0, try -fopenmp-version=51 [-Wopenmp-future]
     !CHECK-52: not yet implemented: Unhandled clause INDIRECT in DECLARE TARGET construct
     !$omp declare target enter(func1) indirect(.true.)
     character(1) :: i



More information about the llvm-branch-commits mailing list