[llvm-branch-commits] [flang] [llvm] [flang][OpenMP] Switch OpenMP version from unsigned to llvm::omp::Version (PR #219822)

Krzysztof Parzyszek via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Sep 1 08:03:25 PDT 2026


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

>From 6e960abd894c4497180c03f5b9369e707dadbc6a Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Fri, 28 Aug 2026 15:46:48 -0500
Subject: [PATCH 1/3] [flang][OpenMP] Switch OpenMP version from unsigned to
 llvm::omp::Version

Also make llvm::omp::Version converting constructor/operstor explicit.
---
 flang/include/flang/Parser/openmp-utils.h     |   4 +-
 .../flang/Semantics/openmp-modifiers.h        |  20 +-
 flang/include/flang/Semantics/openmp-utils.h  |  38 ++--
 flang/include/flang/Semantics/symbol.h        |   4 +-
 flang/include/flang/Support/LangOptions.h     |   5 +
 flang/lib/Frontend/CompilerInvocation.cpp     |  11 +-
 flang/lib/Frontend/FrontendActions.cpp        |   5 +-
 flang/lib/Lower/OpenMP/Atomic.cpp             |  10 +-
 flang/lib/Lower/OpenMP/ClauseProcessor.cpp    |   6 +-
 flang/lib/Lower/OpenMP/ClauseProcessor.h      |   2 +-
 .../lib/Lower/OpenMP/DataSharingProcessor.cpp |   6 +-
 flang/lib/Lower/OpenMP/DataSharingProcessor.h |   6 +-
 flang/lib/Lower/OpenMP/Decomposer.cpp         |   6 +-
 flang/lib/Lower/OpenMP/OpenMP.cpp             |   6 +-
 flang/lib/Lower/PFTBuilder.cpp                |   2 +-
 flang/lib/Parser/openmp-parsers.cpp           |  10 +-
 flang/lib/Parser/openmp-utils.cpp             |   4 +-
 flang/lib/Parser/unparse.cpp                  |   4 +-
 flang/lib/Semantics/check-omp-atomic.cpp      |   2 +-
 flang/lib/Semantics/check-omp-loop.cpp        |  12 +-
 flang/lib/Semantics/check-omp-structure.cpp   | 179 +++++++++---------
 flang/lib/Semantics/check-omp-variant.cpp     |  10 +-
 flang/lib/Semantics/mod-file.cpp              |   4 +-
 flang/lib/Semantics/openmp-utils.cpp          |  35 ++--
 flang/lib/Semantics/resolve-directives.cpp    |  14 +-
 flang/unittests/Semantics/OpenMPUtils.cpp     |   9 +-
 .../include/llvm/Frontend/OpenMP/OMPVersion.h |   7 +-
 27 files changed, 218 insertions(+), 203 deletions(-)

diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index 9f47689906035..cd19ed1d72f0d 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -142,8 +142,8 @@ template <typename T> OmpDirectiveName GetOmpDirectiveName(const T &x) {
   return detail::DirectiveNameScope::GetOmpDirectiveName(x);
 }
 
-std::string GetUpperName(llvm::omp::Clause id, unsigned version);
-std::string GetUpperName(llvm::omp::Directive id, unsigned version);
+std::string GetUpperName(llvm::omp::Clause id, llvm::omp::Version version);
+std::string GetUpperName(llvm::omp::Directive id, llvm::omp::Version version);
 
 const OpenMPDeclarativeConstruct *GetOmp(const DeclarationConstruct &x);
 const OpenMPConstruct *GetOmp(const ExecutionPartConstruct &x);
diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
index a199e55639ea8..ef92cd397dd89 100644
--- a/flang/include/flang/Semantics/openmp-modifiers.h
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -295,7 +295,7 @@ bool verifyVersions(const std::optional<std::list<UnionTy>> &modifiers,
   if (!modifiers) {
     return true;
   }
-  unsigned version{semaCtx.langOptions().OpenMPVersion};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
   bool result{true};
   for (auto &m : *modifiers) {
     const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor(m)};
@@ -304,8 +304,8 @@ bool verifyVersions(const std::optional<std::list<UnionTy>> &modifiers,
     }
     // Find the next higher version that allows this modifier on this clause.
     const auto &versions{desc.getVersions()};
-    unsigned since{~0u}, until{0u};
-    for (unsigned v : versions) {
+    llvm::omp::Version since(~0u), until(0u);
+    for (llvm::omp::Version v : versions) {
       if (desc.getClauses(v).test(id)) {
         if (v < version) {
           until = std::max(until, v);
@@ -314,20 +314,20 @@ bool verifyVersions(const std::optional<std::list<UnionTy>> &modifiers,
         }
       }
     }
-    if (since == ~0u && until == 0u) {
+    if (since == ~0 && until == 0) {
       // This shouldn't really happen, but have it just in case.
       semaCtx.Say(m.source,
           "'%s' modifier is not supported on %s clause"_err_en_US,
           desc.getName().str(),
           parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)));
-    } else if (since != ~0u && version < since) {
+    } else if (since != ~0 && version < since) {
       semaCtx.Say(m.source,
           "'%s' modifier is not supported in %s on %s clause, %s"_warn_en_US,
           desc.getName().str(), omp::ThisVersion(version),
           parser::ToUpperCaseLetters(llvm::omp::getOpenMPClauseName(id)),
           omp::TryVersion(since));
       result = false;
-    } else if (until != 0u && version > until) {
+    } else if (until != 0 && version > until) {
       semaCtx.Say(m.source,
           "'%s' modifier is no longer supported in %s on %s clause"_warn_en_US,
           desc.getName().str(), omp::ThisVersion(version),
@@ -346,7 +346,7 @@ template <typename SpecificTy, typename UnionTy>
 bool verifyIfRequired(const SpecificTy *,
     const std::optional<std::list<UnionTy>> &modifiers,
     parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
-  unsigned version{semaCtx.langOptions().OpenMPVersion};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
   const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor<SpecificTy>()};
   if (!desc.getProperties(version).test(llvm::omp::Property::Required)) {
     // If the modifier is not required, there is nothing to do.
@@ -400,7 +400,7 @@ bool verifyIfUnique(const SpecificTy *,
   // `specific` is the location of the modifier of type SpecificTy.
   assert(specific != end && "`specific` must be a valid location");
 
-  unsigned version{semaCtx.langOptions().OpenMPVersion};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
   const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor<SpecificTy>()};
   // Ultimate implies Unique.
   if (!desc.getProperties(version).test(llvm::omp::Property::Unique) &&
@@ -449,7 +449,7 @@ bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
   if (!modifiers || modifiers->size() <= 1) {
     return true;
   }
-  unsigned version{semaCtx.langOptions().OpenMPVersion};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
   bool result{true};
   auto first{modifiers->cbegin()};
   auto last{std::prev(modifiers->cend())};
@@ -498,7 +498,7 @@ bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
   if (!modifiers || modifiers->size() <= 1) {
     return true;
   }
-  unsigned version{semaCtx.langOptions().OpenMPVersion};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
   const UnionTy &front{modifiers->front()};
   const llvm::omp::descriptor::Modifier &frontDesc{OmpGetDescriptor(front)};
 
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index de3d532c7f79a..eab005c2a08ac 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -25,6 +25,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Frontend/OpenMP/OMPContext.h"
+#include "llvm/Frontend/OpenMP/OMPVersion.h"
 
 #include <memory>
 #include <optional>
@@ -69,8 +70,8 @@ struct SourcedActionStmt
 SourcedActionStmt GetActionStmt(const parser::ExecutionPartConstruct *x);
 SourcedActionStmt GetActionStmt(const parser::Block &block);
 
-std::string ThisVersion(unsigned version);
-std::string TryVersion(unsigned version);
+std::string ThisVersion(llvm::omp::Version version);
+std::string TryVersion(llvm::omp::Version version);
 
 const Symbol *GetObjectSymbol(
     const parser::OmpObject &object, bool ultimate = false);
@@ -275,7 +276,7 @@ enum struct ListItemKind : uint32_t {
 };
 
 std::optional<ListItemKind> GetArgumentListItemKind(
-    llvm::omp::Clause clause, unsigned version);
+    llvm::omp::Clause clause, llvm::omp::Version version);
 
 bool IsLoopTransforming(llvm::omp::Directive dir);
 bool HasDataEnvironment(llvm::omp::Directive dir);
@@ -297,7 +298,7 @@ struct OmpErrorArgs {
 /// MESSAGE clause values.
 OmpErrorArgs GetErrorDirectiveArgs(const parser::OmpErrorDirective &errDir);
 
-inline bool IsDoConcurrentLegal(unsigned version) {
+inline bool IsDoConcurrentLegal(llvm::omp::Version version) {
   // DO CONCURRENT is allowed (as an alternative to a Canonical Loop Nest)
   // in OpenMP 6.0+.
   return version >= 60;
@@ -358,33 +359,33 @@ template <typename T> struct WithReason {
 
 WithReason<int64_t> GetArgumentValueWithReason(
     const parser::OmpDirectiveSpecification &spec, llvm::omp::Clause clauseId,
-    unsigned version, SemanticsContext *semaCtx = nullptr);
+    llvm::omp::Version version, SemanticsContext *semaCtx = nullptr);
 WithReason<int64_t> GetNumArgumentsWithReason(
     const parser::OmpDirectiveSpecification &spec, llvm::omp::Clause clauseId,
-    unsigned version, SemanticsContext *semaCtx = nullptr);
+    llvm::omp::Version version, SemanticsContext *semaCtx = nullptr);
 WithReason<int64_t> GetHeightWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx = nullptr);
 
 /// Return the depth of the affected nest(s):
 ///   {affected-depth, must-be-perfect-nest}.
 std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx = nullptr);
 /// Return the depth of the generated nest(s):
 ///   {generated-depth, is-perfect-nest}
 std::pair<WithReason<int64_t>, bool> GetGeneratedNestDepthWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx = nullptr);
 /// Return the range of the affected nests in the sequence:
 ///   {first, count}.
 /// If the range is "the whole sequence", the return value will be {1, -1}.
 WithReason<std::pair<int64_t, int64_t>> GetAffectedLoopRangeWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx = nullptr);
 /// Return the depth in which all loops must be rectangular.
 WithReason<int64_t> GetRectangularNestDepthWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx = nullptr);
 
 /// Calculate the minimum length of a sequence that contains the specified
@@ -401,7 +402,7 @@ std::optional<int64_t> GetMinimumSequenceCount(
 /// Returns std::nullopt if `x` or code nested in `x` was malformed in a
 /// way that prevented the function from returning an accurate result.
 std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
-    const parser::OpenMPLoopConstruct &x, unsigned version,
+    const parser::OpenMPLoopConstruct &x, llvm::omp::Version version,
     SemanticsContext *semaCtx = nullptr);
 
 /// Returns whether the loop nest associated with `x` is a doacross loop nest,
@@ -411,12 +412,13 @@ std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
 bool IsDoacrossAffected(const parser::OpenMPLoopConstruct &x);
 
 struct LoopSequence {
-  LoopSequence(const parser::ExecutionPartConstruct &root, unsigned version,
-      bool allowAllLoops = false, SemanticsContext *semaCtx = nullptr);
+  LoopSequence(const parser::ExecutionPartConstruct &root,
+      llvm::omp::Version version, bool allowAllLoops = false,
+      SemanticsContext *semaCtx = nullptr);
 
   template <typename R, typename = std::enable_if_t<is_range_v<R>>>
-  LoopSequence(const R &range, unsigned version, bool allowAllLoops = false,
-      SemanticsContext *semaCtx = nullptr)
+  LoopSequence(const R &range, llvm::omp::Version version,
+      bool allowAllLoops = false, SemanticsContext *semaCtx = nullptr)
       : version_(version), allowAllLoops_(allowAllLoops), semaCtx_(semaCtx) {
     entry_ = std::make_unique<Construct>(range, nullptr);
     createChildrenFromRange(entry_->location);
@@ -455,7 +457,7 @@ struct LoopSequence {
 private:
   using Construct = ExecutionPartIterator::Construct;
 
-  LoopSequence(std::unique_ptr<Construct> entry, unsigned version,
+  LoopSequence(std::unique_ptr<Construct> entry, llvm::omp::Version version,
       bool allowAllLoops, SemanticsContext *semaCtx = nullptr);
 
   template <typename R, typename = std::enable_if_t<is_range_v<R>>>
@@ -504,7 +506,7 @@ struct LoopSequence {
   WithReason<int64_t> height_;
 
   // The core structure of the class:
-  unsigned version_; // Needed for GetXyzWithReason
+  llvm::omp::Version version_; // Needed for GetXyzWithReason
   bool allowAllLoops_;
   std::unique_ptr<Construct> entry_;
   std::vector<LoopSequence> children_;
diff --git a/flang/include/flang/Semantics/symbol.h b/flang/include/flang/Semantics/symbol.h
index a51b4542e4f36..e62b52f1f0bd4 100644
--- a/flang/include/flang/Semantics/symbol.h
+++ b/flang/include/flang/Semantics/symbol.h
@@ -99,10 +99,10 @@ class WithOmpDeclarative {
   friend llvm::raw_ostream &operator<<(
       llvm::raw_ostream &, const WithOmpDeclarative &);
 
-  void set_version(unsigned version) { version_ = version; }
+  void set_version(llvm::omp::Version version) { version_ = version; }
 
 private:
-  unsigned version_;
+  llvm::omp::Version version_;
   // The set of clauses from a REQUIRES directive. Only applicable
   // to program unit symbols (i.e. scopes of the REQUIRES directive).
   // The set of requirements for any program unit include requirements
diff --git a/flang/include/flang/Support/LangOptions.h b/flang/include/flang/Support/LangOptions.h
index 42b488c3d18a3..054bafb78354a 100644
--- a/flang/include/flang/Support/LangOptions.h
+++ b/flang/include/flang/Support/LangOptions.h
@@ -18,6 +18,7 @@
 #include <string>
 #include <vector>
 
+#include "llvm/Frontend/OpenMP/OMPVersion.h"
 #include "llvm/TargetParser/Triple.h"
 
 namespace Fortran::common {
@@ -87,6 +88,10 @@ class LangOptions : public LangOptionsBase {
   /// List of triples passed in using -fopenmp-targets.
   std::vector<llvm::Triple> OMPTargetTriples;
 
+  llvm::omp::Version getOpenMP() const {
+    return llvm::omp::Version(OpenMPVersion);
+  }
+
   LangOptions();
 };
 
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index d2f192e656188..c07407c5d4b7b 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1308,8 +1308,10 @@ static bool parseOpenMPArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
   res.getFrontendOpts().features.Enable(
       Fortran::common::LanguageFeature::OpenMP);
   if (auto *arg = args.getLastArg(clang::options::OPT_fopenmp_version_EQ)) {
-    llvm::ArrayRef<llvm::omp::Version> ompVersions =
-        llvm::omp::getOpenMPVersions();
+    llvm::SmallVector<unsigned> ompVersions;
+    for (llvm::omp::Version v : llvm::omp::getOpenMPVersions()) {
+      ompVersions.push_back(static_cast<unsigned>(v));
+    }
     unsigned oldVersions[] = {11, 20, 25, 30};
     unsigned version = 0;
 
@@ -1958,8 +1960,9 @@ void CompilerInvocation::setDefaultPredefinitions() {
   }
   if (frontendOptions.features.IsEnabled(
           Fortran::common::LanguageFeature::OpenMP)) {
-    Fortran::common::setOpenMPMacro(getLangOpts().OpenMPVersion,
-                                    fortranOptions.predefinitions);
+    Fortran::common::setOpenMPMacro(
+        static_cast<unsigned>(getLangOpts().getOpenMP()),
+        fortranOptions.predefinitions);
   }
 
   if (frontendOptions.features.IsEnabled(
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index 8a2d1a6c2e2a2..ce1b0d06a1091 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -275,8 +275,9 @@ bool CodeGenAction::beginSourceFileAction() {
     mlir::omp::setOffloadModuleInterfaceAttributes(
         lb.getModule(),
         makeOffloadModuleOpts(ci.getInvocation().getLangOpts()));
-    mlir::omp::setOpenMPVersionAttribute(
-        lb.getModule(), ci.getInvocation().getLangOpts().OpenMPVersion);
+    llvm::omp::Version version = ci.getInvocation().getLangOpts().getOpenMP();
+    mlir::omp::setOpenMPVersionAttribute(lb.getModule(),
+                                         static_cast<unsigned>(version));
     if (!ci.getInvocation().getLoweringOpts().getIntegerWrapAround())
       mlir::omp::setOpenMPIntegerWrapAround(lb.getModule(), false);
   }
diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp
index 37c856795defe..79f4681c67275 100644
--- a/flang/lib/Lower/OpenMP/Atomic.cpp
+++ b/flang/lib/Lower/OpenMP/Atomic.cpp
@@ -226,7 +226,7 @@ getMemoryOrderFromRequires(const semantics::Scope &scope) {
 
 static std::optional<mlir::omp::ClauseMemoryOrderKind>
 getDefaultAtomicMemOrder(semantics::SemanticsContext &semaCtx) {
-  unsigned version = semaCtx.langOptions().OpenMPVersion;
+  llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
   if (version > 50)
     return mlir::omp::ClauseMemoryOrderKind::Relaxed;
   return std::nullopt;
@@ -250,7 +250,7 @@ getAtomicMemoryOrder(semantics::SemanticsContext &semaCtx,
 
 static std::optional<mlir::omp::ClauseMemoryOrderKind>
 makeValidForAction(std::optional<mlir::omp::ClauseMemoryOrderKind> memOrder,
-                   int action0, int action1, unsigned version) {
+                   int action0, int action1, llvm::omp::Version version) {
   // When the atomic default memory order specified on a REQUIRES directive is
   // disallowed on a given ATOMIC operation, and it's not ACQ_REL, the order
   // reverts to RELAXED. ACQ_REL decays to either ACQUIRE or RELEASE, depending
@@ -355,7 +355,7 @@ genAtomicRead(lower::AbstractConverter &converter,
     if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Release) {
       // Reset it back to the default.
       memOrder = getDefaultAtomicMemOrder(semaCtx);
-    } else if (semaCtx.langOptions().OpenMPVersion <= 50 &&
+    } else if (semaCtx.langOptions().getOpenMP() <= 50 &&
                *memOrder == mlir::omp::ClauseMemoryOrderKind::Acq_rel) {
       // In OpenMP 5.0, acq_rel is not allowed on read; decay to acquire.
       // In OpenMP 5.1+, acq_rel is permitted on read.
@@ -418,7 +418,7 @@ genAtomicWrite(lower::AbstractConverter &converter,
     if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Acquire) {
       // Reset it back to the default.
       memOrder = getDefaultAtomicMemOrder(semaCtx);
-    } else if (semaCtx.langOptions().OpenMPVersion <= 50 &&
+    } else if (semaCtx.langOptions().getOpenMP() <= 50 &&
                *memOrder == mlir::omp::ClauseMemoryOrderKind::Acq_rel) {
       // In OpenMP 5.0, acq_rel is not allowed on write; decay to release.
       // In OpenMP 5.1+, acq_rel is permitted on write.
@@ -574,7 +574,7 @@ void Fortran::lower::omp::lowerAtomic(
   auto [memOrder, canOverride] = getAtomicMemoryOrder(
       semaCtx, clauses, semaCtx.FindScope(construct.source));
 
-  unsigned version = semaCtx.langOptions().OpenMPVersion;
+  llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
   int action0 = analysis.op0.what & analysis.Action;
   int action1 = analysis.op1.what & analysis.Action;
   memOrder = makeValidForAction(memOrder, action0, action1, version);
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 267e1cfd44971..b838791b2dcbe 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1831,7 +1831,7 @@ bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result,
       std::optional<mlir::omp::LinearModifier> linearMod;
       if (explicitLinearMod)
         linearMod = *explicitLinearMod;
-      else if (semaCtx.langOptions().OpenMPVersion >= 52)
+      else if (semaCtx.langOptions().getOpenMP() >= 52)
         linearMod = isDeclareSimd ? getDeclareSimdDefaultMod(*sym)
                                   : mlir::omp::LinearModifier::val;
 
@@ -1979,10 +1979,10 @@ bool ClauseProcessor::processMap(
     // default value
     Map::MapType type;
     if (directive == llvm::omp::Directive::OMPD_target_enter_data &&
-        semaCtx.langOptions().OpenMPVersion >= 52)
+        semaCtx.langOptions().getOpenMP() >= 52)
       type = mapType.value_or(Map::MapType::To);
     else if (directive == llvm::omp::Directive::OMPD_target_exit_data &&
-             semaCtx.langOptions().OpenMPVersion >= 52)
+             semaCtx.langOptions().getOpenMP() >= 52)
       type = mapType.value_or(Map::MapType::From);
     else
       type = mapType.value_or(Map::MapType::Tofrom);
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h
index 10d52bcdf89de..177a5ff33f231 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.h
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h
@@ -236,7 +236,7 @@ void ClauseProcessor::processTODO(mlir::Location currentLocation,
   auto checkUnhandledClause = [&](llvm::omp::Clause id, const auto *x) {
     if (!x)
       return;
-    unsigned version = semaCtx.langOptions().OpenMPVersion;
+    llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
     bool isSimdDirective = llvm::omp::getOpenMPDirectiveName(directive, version)
                                .upper()
                                .find("SIMD") != llvm::StringRef::npos;
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index 1d39c1a8d4b77..a4d09375dfbfc 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -287,7 +287,7 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
         // versions semantics only warns and ignores it, so fall back to a
         // regular lastprivate here to keep lowering consistent and avoid the
         // conditional path for entities it cannot handle (e.g. characters).
-        if (semaCtx.langOptions().OpenMPVersion >= 50) {
+        if (semaCtx.langOptions().getOpenMP() >= 50) {
           collectOmpObjectListSymbol(objects, conditionalLastPrivatizedSymbols);
         } else {
           collectOmpObjectListSymbol(objects, explicitlyPrivatizedSymbols);
@@ -456,14 +456,14 @@ static parser::CharBlock getSource(const semantics::SemanticsContext &semaCtx,
 }
 
 bool DataSharingProcessor::isOpenMPPrivatizingConstruct(
-    const parser::OpenMPConstruct &omp, unsigned version) {
+    const parser::OpenMPConstruct &omp, llvm::omp::Version version) {
   return llvm::omp::isPrivatizingConstruct(
       parser::omp::GetOmpDirectiveName(omp).v, version);
 }
 
 bool DataSharingProcessor::isOpenMPPrivatizingEvaluation(
     const pft::Evaluation &eval) const {
-  unsigned version = semaCtx.langOptions().OpenMPVersion;
+  llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
   return eval.visit([=](auto &&s) {
     using BareS = llvm::remove_cvref_t<decltype(s)>;
     if constexpr (std::is_same_v<BareS, parser::OpenMPConstruct>) {
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.h b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
index 557584c21123f..c11c427d88c18 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.h
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
@@ -38,7 +38,7 @@ class DataSharingProcessor {
   /// order to tell which OMP scope defined vs. references a certain Symbol.
   struct OMPConstructSymbolVisitor {
     OMPConstructSymbolVisitor(semantics::SemanticsContext &ctx)
-        : version(ctx.langOptions().OpenMPVersion) {}
+        : version(ctx.langOptions().getOpenMP()) {}
     template <typename T>
     bool Pre(const T &) {
       return true;
@@ -88,7 +88,7 @@ class DataSharingProcessor {
     llvm::SmallVector<ConstructPtr> constructs;
     llvm::DenseMap<semantics::Symbol *, ConstructPtr> symDefMap;
 
-    unsigned version;
+    llvm::omp::Version version;
   };
 
   mlir::OpBuilder::InsertPoint lastPrivIP;
@@ -153,7 +153,7 @@ class DataSharingProcessor {
   void insertDeallocs();
 
   static bool isOpenMPPrivatizingConstruct(const parser::OpenMPConstruct &omp,
-                                           unsigned version);
+                                           llvm::omp::Version version);
   bool isOpenMPPrivatizingEvaluation(const pft::Evaluation &eval) const;
 
 public:
diff --git a/flang/lib/Lower/OpenMP/Decomposer.cpp b/flang/lib/Lower/OpenMP/Decomposer.cpp
index 1368d6bac0d88..8ebd9eb738069 100644
--- a/flang/lib/Lower/OpenMP/Decomposer.cpp
+++ b/flang/lib/Lower/OpenMP/Decomposer.cpp
@@ -42,9 +42,9 @@ struct ConstructDecomposition {
                          llvm::omp::Directive compound,
                          const List<Clause> &clauses)
       : semaCtx(semaCtx), mod(modOp), eval(ev) {
-    tomp::ConstructDecompositionT decompose(
-        mlir::omp::getOpenMPVersionAttribute(modOp), *this, compound,
-        llvm::ArrayRef(clauses));
+    llvm::omp::Version version(mlir::omp::getOpenMPVersionAttribute(modOp));
+    tomp::ConstructDecompositionT decompose(version, *this, compound,
+                                            llvm::ArrayRef(clauses));
     output = std::move(decompose.output);
   }
 
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index f2ca5c172d11a..8ea5d2f03bd12 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2548,7 +2548,7 @@ genSimdImplicitLinear(lower::AbstractConverter &converter,
           Fortran::semantics::IsAllocatableOrPointer(loopVar->GetUltimate()))) {
       mlir::Type ty = converter.genType(*loopVar);
       typeAttrs.push_back(mlir::TypeAttr::get(ty));
-      if (semaCtx.langOptions().OpenMPVersion >= 52)
+      if (semaCtx.langOptions().getOpenMP() >= 52)
         linearModAttrs.push_back(mlir::omp::LinearModifierAttr::get(
             &converter.getMLIRContext(), mlir::omp::LinearModifier::val));
       else
@@ -7545,7 +7545,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
       const common::LangOptions &options = semaCtx.langOptions();
       if (!options.OpenMPSimd) {
         std::string name =
-            parser::omp::GetUpperName(clause.id, options.OpenMPVersion);
+            parser::omp::GetUpperName(clause.id, options.getOpenMP());
         TODO(clauseLocation, name + " clause is not implemented yet");
       }
     }
@@ -7755,7 +7755,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
         // generating the omp.loop_nest op.
         break;
       default: {
-        unsigned version = semaCtx.langOptions().OpenMPVersion;
+        llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
         TODO(currentLocation,
              "Applying a loop-associated on the loop generated by the " +
                  llvm::omp::getOpenMPDirectiveName(nestedDirective, version) +
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 351ace166c8c0..91f1fedcd6961 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -2629,7 +2629,7 @@ static bool isOmpLoopBody(const Fortran::lower::pft::Evaluation &eval,
   if (!loop)
     return false;
 
-  unsigned version = semaCtx.langOptions().OpenMPVersion;
+  llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
   auto [depth, _] =
       semantics::omp::GetAffectedNestDepthWithReason(loop->BeginDir(), version);
   int64_t n = depth.value.value_or(1);
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 2a9f96618f696..5c9ccf2fed3f1 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -266,7 +266,7 @@ void OmpDirectiveNameParser::initTokens(std::vector<NameWithId> table[]) const {
   for (size_t i{0}, e{llvm::omp::Directive_enumSize}; i != e; ++i) {
     llvm::StringSet spellings;
     auto id{static_cast<llvm::omp::Directive>(i)};
-    for (unsigned version : llvm::omp::getOpenMPVersions()) {
+    for (llvm::omp::Version version : llvm::omp::getOpenMPVersions()) {
       spellings.insert(llvm::omp::getOpenMPDirectiveName(id, version));
     }
     for (auto &[name, _] : spellings) {
@@ -969,7 +969,7 @@ struct OmpMapTypeParser {
   using resultType = OmpMapType::Value;
 
   std::optional<resultType> Parse(ParseState &state) const {
-    unsigned version{state.userState()->langOptions().OpenMPVersion};
+    llvm::omp::Version version{state.userState()->langOptions().getOpenMP()};
     if (version < 60) {
       auto parser{//
           "ALLOC" >> pure(OmpMapType::Value::Alloc) ||
@@ -999,7 +999,7 @@ struct OmpMapTypeModifierParser {
   using resultType = OmpMapTypeModifier::Value;
 
   std::optional<resultType> Parse(ParseState &state) const {
-    unsigned version{state.userState()->langOptions().OpenMPVersion};
+    llvm::omp::Version version{state.userState()->langOptions().getOpenMP()};
     if (version < 60) {
       auto parser{//
           "ALWAYS" >> pure(OmpMapTypeModifier::Value::Always) ||
@@ -1133,7 +1133,7 @@ template <typename MotionClause> struct OmpMotionClauseModifierParser {
   using resultType = typename MotionClause::Modifier;
 
   std::optional<resultType> Parse(ParseState &state) const {
-    unsigned version{state.userState()->langOptions().OpenMPVersion};
+    llvm::omp::Version version{state.userState()->langOptions().getOpenMP()};
     if (version <= 51) {
       auto motion{sourced(construct<resultType>(Parser<OmpMotionModifier>{}))};
       if (auto &&result{attempt(motion).Parse(state)}) {
@@ -1178,7 +1178,7 @@ struct OmpLinearClauseModifierParser {
   using resultType = OmpLinearClause::Modifier;
 
   std::optional<resultType> Parse(ParseState &state) const {
-    unsigned version{state.userState()->langOptions().OpenMPVersion};
+    llvm::omp::Version version{state.userState()->langOptions().getOpenMP()};
     if (version < 52) {
       auto parser{sourced( //
           construct<resultType>(Parser<OmpLinearModifier>{}) ||
diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index 4ea10f8a2e156..94fb8bfddd185 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -117,12 +117,12 @@ const OmpDirectiveSpecification &GetOmpDirectiveSpecification(
       x.u);
 }
 
-std::string GetUpperName(llvm::omp::Clause id, unsigned version) {
+std::string GetUpperName(llvm::omp::Clause id, llvm::omp::Version version) {
   llvm::StringRef name{llvm::omp::getOpenMPClauseName(id, version)};
   return parser::ToUpperCaseLetters(name);
 }
 
-std::string GetUpperName(llvm::omp::Directive id, unsigned version) {
+std::string GetUpperName(llvm::omp::Directive id, llvm::omp::Version version) {
   llvm::StringRef name{llvm::omp::getOpenMPDirectiveName(id, version)};
   return parser::ToUpperCaseLetters(name);
 }
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index 32542d8bc6d5c..a177e90cd6f64 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2172,7 +2172,7 @@ class UnparseVisitor {
     return false;
   }
   void Unparse(const llvm::omp::Directive &x) {
-    unsigned ompVersion{langOpts_.OpenMPVersion};
+    llvm::omp::Version ompVersion{langOpts_.getOpenMP()};
     Word(llvm::omp::getOpenMPDirectiveName(x, ompVersion).str());
   }
   void Unparse(const OmpAbsentClause &x) { Walk("", x.v, ","); }
@@ -2317,7 +2317,7 @@ class UnparseVisitor {
     Put(")");
   }
   void Unparse(const OmpDirectiveNameModifier &x) {
-    unsigned ompVersion{langOpts_.OpenMPVersion};
+    llvm::omp::Version ompVersion{langOpts_.getOpenMP()};
     Word(llvm::omp::getOpenMPDirectiveName(x.v, ompVersion));
   }
   void Unparse(const OmpDirectiveSpecification &x) {
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index 7db4bdefa65b0..afc4638ccc692 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -1571,7 +1571,7 @@ static void checkIncompatibleMemoryOrderClause(SemanticsContext &context,
   llvm::omp::Clause kind{x.GetKind()};
   const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()};
 
-  unsigned version{context.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context.langOptions().getOpenMP()};
   if (version < 50)
     return;
 
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index d1c354a9147b0..0cf497d543121 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -235,7 +235,7 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
 
 void OmpStructureChecker::CheckRectangularNest(
     const parser::OmpDirectiveSpecification &spec, const LoopSequence &nest) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   auto depth{GetRectangularNestDepthWithReason(spec, version)};
   if (!depth || *depth.value == 0) {
     return;
@@ -266,7 +266,7 @@ void OmpStructureChecker::CheckNestedConstruct(
     const parser::OpenMPLoopConstruct &x) {
   const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
   llvm::omp::Directive dir{beginSpec.DirId()};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   parser::CharBlock beginSource{beginSpec.DirName().source};
 
   // End-directive is not allowed in such cases:
@@ -489,7 +489,7 @@ const parser::Name OmpStructureChecker::GetLoopIndex(
 
 void OmpStructureChecker::CheckIterationVariables(
     const parser::OpenMPLoopConstruct &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   auto doLoops{CollectAffectedDoLoops(x, version, &context_)};
   if (!doLoops) {
     return;
@@ -760,7 +760,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   llvm::omp::Directive dir{GetContext().directive};
   parser::CharBlock clauseSource{GetContext().clauseSource};
   const parser::OmpLinearModifier *linearMod{nullptr};
@@ -878,7 +878,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Permutation &c) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   llvm::omp::Clause clause = llvm::omp::Clause::OMPC_permutation;
   if (c.v.size() < 2)
     context_.Say(GetContext().clauseSource,
@@ -924,7 +924,7 @@ void OmpStructureChecker::Enter(const parser::DoConstruct &x) {
 void OmpStructureChecker::Enter(const parser::OmpLoopModifier &x) {
   DirectiveContext &dirCtx = GetContext();
   llvm::omp::Directive dir{dirCtx.directive};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   auto &m{std::get<llvm::omp::LoopModifier>(x.t)};
   if (!llvm::omp::isAllowedLoopModifier(dir, m)) {
     llvm::StringRef name = llvm::omp::getLoopModifierName(m);
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index a25f4c39dcd80..41691ff7113cd 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -164,7 +164,7 @@ void OmpStructureChecker::Enter(const parser::SubroutineStmt &x) {
 }
 
 void OmpStructureChecker::CheckTempDescriptorMappings() {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   for (const auto &[symbol, source] : tempDescriptorEnterMaps_) {
     if (tempDescriptorExitMaps_.find(symbol) == tempDescriptorExitMaps_.end()) {
       if (version >= 61) {
@@ -442,7 +442,7 @@ bool OmpStructureChecker::IsAllowedClause(llvm::omp::Clause clauseId) {
     return true;
   }
   return llvm::omp::isAllowedClauseForDirective(
-      GetContext().directive, clauseId, context_.langOptions().OpenMPVersion);
+      GetContext().directive, clauseId, context_.langOptions().getOpenMP());
 }
 
 bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clauseId,
@@ -457,11 +457,11 @@ bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clauseId,
     return true;
   }
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   if (!llvm::omp::isAllowedClauseForDirective(dirId, clauseId, version)) {
-    unsigned allowedInVersion{[&] {
-      for (unsigned v : llvm::omp::getOpenMPVersions()) {
+    llvm::omp::Version allowedInVersion{[&] {
+      for (llvm::omp::Version v : llvm::omp::getOpenMPVersions()) {
         if (v <= version) {
           continue;
         }
@@ -469,12 +469,12 @@ bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clauseId,
           return v;
         }
       }
-      return 0u;
+      return llvm::omp::Version();
     }()};
 
     // Only report it if there is a later version that allows it.
     // If it's not allowed at all, it will be reported by CheckAllowed.
-    if (allowedInVersion != 0) {
+    if (allowedInVersion) {
       context_.Say(clauseSource,
           "%s clause is not allowed on %s directive in %s, %s"_err_en_US,
           GetUpperName(clauseId, version), GetUpperName(dirId, version),
@@ -588,7 +588,7 @@ void OmpStructureChecker::CheckLabelContext(const parser::CharBlock source,
     return getSource(*lhs).Contains(getSource(*rhs));
   };
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   if (tgtOmp && !isSameOrIncludes(tgtOmp, srcOmp)) {
     parser::OmpDirectiveName name{GetOmpDirectiveName(*tgtOmp)};
     context_
@@ -732,7 +732,7 @@ bool OmpStructureChecker::HasRequires(llvm::omp::Clause req) {
 }
 
 void OmpStructureChecker::CheckArgumentObjectKind(const parser::OmpClause &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   llvm::omp::Directive dirId{GetContext().directive};
   llvm::omp::Clause clauseId{x.Id()};
 
@@ -901,12 +901,12 @@ void OmpStructureChecker::CheckDirectiveSpelling(
     ref = ref.drop_front(3);
   }
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   // For every "future" version v, check if the check if the corresponding
   // spelling of id was introduced later than the current version. If so,
   // and if that spelling matches the source spelling, issue a warning.
-  for (unsigned v : llvm::omp::getOpenMPVersions()) {
+  for (llvm::omp::Version v : llvm::omp::getOpenMPVersions()) {
     if (v <= version) {
       continue;
     }
@@ -917,13 +917,14 @@ void OmpStructureChecker::CheckDirectiveSpelling(
       assert(kind == id && "Directive kind mismatch");
     }
 
-    if (static_cast<int>(version) >= versions.Min) {
+    if (version >= versions.Min) {
       continue;
     }
     if (ref == removeSpaces(name)) {
       context_.Say(spelling,
           "Directive spelling '%s' is introduced in a later OpenMP version, %s"_warn_en_US,
-          parser::ToUpperCaseLetters(ref), TryVersion(versions.Min));
+          parser::ToUpperCaseLetters(ref),
+          TryVersion(llvm::omp::Version(versions.Min)));
       break;
     }
   }
@@ -932,7 +933,7 @@ void OmpStructureChecker::CheckDirectiveSpelling(
 void OmpStructureChecker::CheckDirectiveDeprecation(
     const parser::OpenMPConstruct &x) {
   parser::OmpDirectiveName dirName{GetOmpDirectiveName(x)};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   // We only want to emit the warning when the version being used has the
   // directive deprecated
   if (version >= 52) {
@@ -963,10 +964,10 @@ void OmpStructureChecker::CheckDirectiveInPureProcedure(
   if (!FindPureProcedureContaining(scope)) {
     return;
   }
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   // A directive's "pure" property is version-specific: pureSince is the
   // OpenMP version at which the directive gained that property.
-  unsigned pureSince{llvm::omp::getDirectivePureSince(id)};
+  llvm::omp::Version pureSince{llvm::omp::getDirectivePureSince(id)};
   if (version >= pureSince) {
     return;
   }
@@ -1009,7 +1010,7 @@ OmpStructureChecker::FindMutuallyExclusiveClauses(
 void OmpStructureChecker::CheckClauses(parser::OmpDirectiveName dirName,
     llvm::iterator_range<ClauseIterator> beginClauses,
     llvm::iterator_range<ClauseIterator> endClauses) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   llvm::omp::Directive dirId{dirName.v};
   std::vector<const parser::OmpClause *> allClauses;
 
@@ -1512,7 +1513,7 @@ void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) {
       },
       c.u);
   if (!eligibleTarget) {
-    unsigned version{context_.langOptions().OpenMPVersion};
+    llvm::omp::Version version{context_.langOptions().getOpenMP()};
     context_.Warn(common::UsageWarning::OpenMPUsage, source,
         "If %s directive is nested inside TARGET region, the behaviour is unspecified"_port_en_US,
         parser::omp::GetUpperName(ineligibleTargetDir, version));
@@ -1523,7 +1524,7 @@ void OmpStructureChecker::Enter(const parser::OmpBlockConstruct &x) {
   const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
   const std::optional<parser::OmpEndDirective> &endSpec{x.EndDir()};
   const parser::Block &block{std::get<parser::Block>(x.t)};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   // Missing mandatory end block: this is checked in semantics because that
   // makes it easier to control the error messages.
@@ -1641,7 +1642,7 @@ void OmpStructureChecker::Enter(const parser::OmpBlockConstruct &x) {
 void OmpStructureChecker::CheckSingleConstruct(
     const parser::OmpBlockConstruct &x) {
   const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   SymbolSourceMap copyPrivateSyms;
   parser::CharBlock nowaitSource1, nowaitSource2;
 
@@ -2040,7 +2041,7 @@ void OmpStructureChecker::Leave(const parser::OmpThreadprivateDirective &x) {
 void OmpStructureChecker::Enter(const parser::OmpDeclareSimdDirective &x) {
   const parser::OmpDirectiveName &dirName{x.v.DirName()};
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   const Scope &containingScope = context_.FindScope(dirName.source);
   const Scope &progUnitScope = GetProgramUnitContaining(containingScope);
@@ -2178,7 +2179,7 @@ void OmpStructureChecker::CheckInitOnDepobj(
 
 void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) {
   const auto &dirName{std::get<parser::OmpDirectiveName>(x.v.t)};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   const parser::OmpArgumentList &arguments{x.v.Arguments()};
   const parser::OmpClauseList &clauses{x.v.Clauses()};
@@ -2233,7 +2234,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpRequiresDirective &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   for (const parser::OmpClause &clause : x.v.Clauses().v) {
     llvm::omp::Clause id{clause.Id()};
@@ -2263,11 +2264,12 @@ void OmpStructureChecker::Enter(const parser::OmpRequiresDirective &x) {
             }
           },
           clause.u)};
-      if (version < 60 && hasArgument) {
+      llvm::omp::Version v60(60);
+      if (version < v60 && hasArgument) {
         context_.Say(clause.source,
             "An argument to %s is an %s feature, %s"_warn_en_US,
-            parser::omp::GetUpperName(clause.Id(), version), ThisVersion(60),
-            TryVersion(60));
+            parser::omp::GetUpperName(clause.Id(), version), ThisVersion(v60),
+            TryVersion(v60));
       }
     }
   }
@@ -2345,7 +2347,7 @@ void OmpStructureChecker::CheckIndividualAllocateDirective(
 
   auto maybePredefined{maybeHasPredefinedAllocator(allocator)};
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   std::string condStr{version == 50
           ? "a named common block, has SAVE attribute or is declared in the "
             "scope of a module"
@@ -2466,7 +2468,7 @@ void OmpStructureChecker::Enter(const parser::OmpAllocateDirective &x) {
 
   bool isExecutable{partStack_.back() == PartKind::ExecutionPart};
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   if (isExecutable && allocateDirectiveLevel_ == 1 && version >= 52) {
     context_.Warn(common::UsageWarning::OpenMPUsage, dirName.source,
         "The executable form of the OpenMP ALLOCATE directive has been deprecated, please use ALLOCATORS instead"_warn_en_US);
@@ -2706,7 +2708,7 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareTargetDirective &x) {
       context_.Say(x.source,
           "The INDIRECT clause cannot be used without the ENTER clause with the DECLARE TARGET directive."_err_en_US);
     }
-    unsigned version{context_.langOptions().OpenMPVersion};
+    llvm::omp::Version version{context_.langOptions().getOpenMP()};
     if (toClause && version >= 52) {
       context_.Warn(common::UsageWarning::OpenMPUsage, toClause->source,
           "The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead."_warn_en_US);
@@ -3123,7 +3125,7 @@ struct TaskgraphVisitor {
       }
     }
 
-    unsigned version{context_.langOptions().OpenMPVersion};
+    llvm::omp::Version version{context_.langOptions().getOpenMP()};
     bool allowsNogroup{llvm::omp::isAllowedClauseForDirective(
         leafs[0], llvm::omp::Clause::OMPC_nogroup, version)};
 
@@ -3262,8 +3264,8 @@ void OmpStructureChecker::CheckTaskgraph(const parser::OmpBlockConstruct &x) {
 void OmpStructureChecker::CheckTaskDependenceType(
     const parser::OmpTaskDependenceType::Value &x) {
   // Common checks for task-dependence-type (DEPEND and UPDATE clauses).
-  unsigned version{context_.langOptions().OpenMPVersion};
-  unsigned since{0};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version since;
 
   switch (x) {
   case parser::OmpTaskDependenceType::Value::In:
@@ -3272,10 +3274,10 @@ void OmpStructureChecker::CheckTaskDependenceType(
     break;
   case parser::OmpTaskDependenceType::Value::Mutexinoutset:
   case parser::OmpTaskDependenceType::Value::Depobj:
-    since = 50;
+    since = llvm::omp::Version(50);
     break;
   case parser::OmpTaskDependenceType::Value::Inoutset:
-    since = 52;
+    since = llvm::omp::Version(52);
     break;
   }
 
@@ -3342,7 +3344,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
     }
   }
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   if (version >= 52) {
     auto &flags{std::get<parser::OmpDirectiveSpecification::Flags>(x.v.t)};
     if (flags.test(parser::OmpDirectiveSpecification::Flag::DeprecatedSyntax)) {
@@ -3486,7 +3488,7 @@ void OmpStructureChecker::Enter(
     const parser::OmpClause::CancellationConstructType &x) {
   llvm::omp::Directive dir{GetContext().directive};
   auto &dirName{std::get<parser::OmpDirectiveName>(x.v.t)};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   if (dir != llvm::omp::Directive::OMPD_cancel &&
       dir != llvm::omp::Directive::OMPD_cancellation_point) {
@@ -3532,7 +3534,7 @@ std::optional<llvm::omp::Directive> OmpStructureChecker::GetCancelType(
   }
   // Given clauses from CANCEL or CANCELLATION_POINT, identify the construct
   // to which the cancellation applies.
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   std::optional<llvm::omp::Directive> cancelee;
   std::string cancelName{parser::omp::GetUpperName(cancelDir, version)};
 
@@ -3562,7 +3564,7 @@ std::optional<llvm::omp::Directive> OmpStructureChecker::GetCancelType(
 
 void OmpStructureChecker::CheckCancellationNest(
     const parser::CharBlock &source, llvm::omp::Directive type) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   std::string typeName{parser::omp::GetUpperName(type, version)};
 
   if (CurrentDirectiveIsNested()) {
@@ -3676,7 +3678,7 @@ void OmpStructureChecker::Enter(const parser::OmpClauseList &) {
 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h.
 
 void OmpStructureChecker::Leave(const parser::OmpClauseList &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   // 2.7.1 Loop Construct Restriction
   if (llvm::omp::allDoSet.test(GetContext().directive)) {
@@ -3914,9 +3916,10 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Destroy &x) {
   }
 
   llvm::omp::Directive dir{GetContext().directive};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   if (dir == llvm::omp::Directive::OMPD_depobj) {
-    unsigned argSince{52}, noargDeprecatedIn{52};
+    llvm::omp::Version argSince(52);
+    llvm::omp::Version noargDeprecatedIn(52);
     if (x.v) {
       if (version < argSince) {
         context_.Say(GetContext().clauseSource,
@@ -4009,7 +4012,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::TaskReduction &x) {
 bool OmpStructureChecker::CheckReductionOperator(
     const parser::OmpReductionIdentifier &ident, parser::CharBlock source,
     llvm::omp::Clause clauseId) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   auto visitOperator{[&](const parser::DefinedOperator &dOpr) {
     if (const auto *intrinsicOp{
@@ -4076,7 +4079,7 @@ bool OmpStructureChecker::CheckReductionOperator(
 /// Check restrictions on objects that are common to all reduction clauses.
 void OmpStructureChecker::CheckReductionObjects(
     const parser::OmpObjectList &objects, llvm::omp::Clause clauseId) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   SymbolSourceMap symbols;
   GetSymbolsInObjectList(objects, symbols);
 
@@ -4326,7 +4329,7 @@ void OmpStructureChecker::CheckReductionObjectTypes(
 
 void OmpStructureChecker::CheckReductionModifier(
     const parser::OmpReductionModifier &modifier) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   using ReductionModifier = parser::OmpReductionModifier;
   if (modifier.v == ReductionModifier::Value::Default) {
@@ -4396,7 +4399,7 @@ void OmpStructureChecker::CheckReductionArraySection(
 
 void OmpStructureChecker::CheckSharedBindingInOuterContext(
     const parser::OmpObjectList &redObjectList) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   //  TODO: Verify the assumption here that the immediately enclosing region is
   //  the parallel region to which the worksharing construct having reduction
   //  binds to.
@@ -4455,7 +4458,7 @@ void OmpStructureChecker::CheckTypeParamInquiry(const parser::CharBlock &source,
     if (IsTypeParamInquiry(*symbol)) {
       context_.Say(source,
           "A type parameter inquiry cannot appear on the %s directive"_err_en_US,
-          GetUpperName(dirId, context_.langOptions().OpenMPVersion));
+          GetUpperName(dirId, context_.langOptions().getOpenMP()));
     }
   }
 }
@@ -4466,7 +4469,7 @@ void OmpStructureChecker::CheckTypeParamInquiry(const parser::CharBlock &source,
     if (IsTypeParamInquiry(*symbol)) {
       context_.Say(source,
           "A type parameter inquiry cannot appear on the %s clause"_err_en_US,
-          GetUpperName(clauseId, context_.langOptions().OpenMPVersion));
+          GetUpperName(clauseId, context_.langOptions().getOpenMP()));
     }
   }
 }
@@ -4581,7 +4584,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Aligned &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   using ImplicitBehavior = parser::OmpDefaultmapClause::ImplicitBehavior;
   auto behavior{std::get<ImplicitBehavior>(x.v.t)};
   if (version <= 45) {
@@ -4590,7 +4593,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) {
           "%s is not allowed in %s, %s"_warn_en_US,
           parser::ToUpperCaseLetters(
               parser::OmpDefaultmapClause::EnumToString(behavior)),
-          ThisVersion(version), TryVersion(50));
+          ThisVersion(version), TryVersion(llvm::omp::Version(50)));
     }
   }
   if (!OmpVerifyModifiers(x.v, llvm::omp::OMPC_defaultmap,
@@ -4604,12 +4607,12 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) {
   if (maybeCategory) {
     using VariableCategory = parser::OmpVariableCategory;
     VariableCategory::Value category{maybeCategory->v};
-    unsigned tryVersion{0};
+    llvm::omp::Version tryVersion;
     if (version <= 45 && category != VariableCategory::Value::Scalar) {
-      tryVersion = 50;
+      tryVersion = llvm::omp::Version(50);
     }
     if (version < 52 && category == VariableCategory::Value::All) {
-      tryVersion = 52;
+      tryVersion = llvm::omp::Version(52);
     }
     if (tryVersion) {
       context_.Say(GetContext().clauseSource,
@@ -4621,7 +4624,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::If &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   llvm::omp::Directive dir{GetContext().directive};
 
   auto isConstituent{[](llvm::omp::Directive dir, llvm::omp::Directive part) {
@@ -4696,21 +4699,21 @@ void OmpStructureChecker::Enter(const parser::OmpClause::If &x) {
 
       static auto minVersion{[&](llvm::omp::Directive d) {
         if (valid45.test(d)) {
-          return 45;
+          return llvm::omp::Version(45);
         }
         if (valid50.test(d)) {
-          return 50;
+          return llvm::omp::Version(50);
         }
         if (valid52.test(d)) {
-          return 52;
+          return llvm::omp::Version(52);
         }
         if (valid60.test(d)) {
-          return 60;
+          return llvm::omp::Version(60);
         }
-        return 0;
+        return llvm::omp::Version{};
       }};
-      static auto suggest{[&](unsigned v) -> std::string {
-        if (v != 0) {
+      static auto suggest{[&](llvm::omp::Version v) -> std::string {
+        if (v) {
           return ", " + TryVersion(v);
         } else {
           return "";
@@ -4761,7 +4764,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::If &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Detach &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   CheckTypeParamInquiry(
       GetContext().clauseSource, x.v.v, llvm::omp::Clause::OMPC_detach);
   // OpenMP 5.2: 12.5.2 Detach clause restrictions
@@ -4808,7 +4811,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
   }
 
   auto &modifiers{OmpGetModifiers(x.v)};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   if (auto commas{std::get<bool>(x.v.t)}; !commas && version >= 52) {
     context_.Say(GetContext().clauseSource,
         "The specification of modifiers without comma separators for the "
@@ -4829,7 +4832,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
     mapType = type->v;
 
     static auto isValidForVersion{
-        [](parser::OmpMapType::Value t, unsigned version) {
+        [](parser::OmpMapType::Value t, llvm::omp::Version version) {
           switch (t) {
           case parser::OmpMapType::Value::Delete:
             return version < 60;
@@ -5025,7 +5028,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) {
 void OmpStructureChecker::Enter(const parser::OmpClause::Device &x) {
   const parser::OmpDeviceClause &deviceClause{x.v};
   const auto &device{std::get<parser::ScalarIntExpr>(deviceClause.t)};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   // The predefined identifiers omp_initial_device (-1) and omp_invalid_device
   // (-2) were introduced in OpenMP 5.2. Under earlier versions the device
   // expression must be a non-negative integer.
@@ -5061,7 +5064,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Device &x) {
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) {
   llvm::omp::Directive dir{GetContext().directive};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   auto *doaDep{std::get_if<parser::OmpDoacross>(&x.v.u)};
   auto *taskDep{std::get_if<parser::OmpDependClause::TaskDep>(&x.v.u)};
@@ -5245,7 +5248,7 @@ void OmpStructureChecker::CheckDoacross(
 
 void OmpStructureChecker::CheckCopyingPolymorphicAllocatable(
     SymbolSourceMap &symbols, const llvm::omp::Clause clause) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   if (context_.ShouldWarn(common::UsageWarning::Portability)) {
     for (auto &[symbol, source] : symbols) {
       if (IsPolymorphicAllocatable(*symbol)) {
@@ -5386,7 +5389,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &x) {
 
 void OmpStructureChecker::CheckStructureComponent(
     const parser::OmpObject &object, llvm::omp::Clause clauseId) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   if (auto *desg{GetDesignatorFromObj(object)}) {
     if (auto *symbol{GetLastName(*desg).symbol}) {
       if (!IsTypeParamInquiry(*symbol) &&
@@ -5413,7 +5416,7 @@ void OmpStructureChecker::CheckStructureComponent(
 
 void OmpStructureChecker::Enter(
     const parser::OmpClause::UpdateDependObjects &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   auto *taskType = std::get_if<parser::OmpTaskDependenceType>(&x.v.u);
   if (taskType) {
@@ -5518,7 +5521,7 @@ static constexpr UsesAllocatorsMemSpaceName usesAllocatorsMemSpaceNames[]{
 };
 
 static bool IsUsesAllocatorsMemSpaceName(
-    const parser::Name &name, unsigned version) {
+    const parser::Name &name, llvm::omp::Version version) {
   return llvm::any_of(usesAllocatorsMemSpaceNames, [&](const auto &candidate) {
     return version >= candidate.since && name.ToString() == candidate.name;
   });
@@ -5560,7 +5563,7 @@ static bool IsIntrinsicOmpAlloctrait(
 // of a predefined spelling therefore qualifies, while a rename to some other
 // name does not, even when it denotes the intrinsic entity.
 static bool IsPredefinedHandle(const parser::Name &name,
-    llvm::ArrayRef<std::string_view> names, unsigned version) {
+    llvm::ArrayRef<std::string_view> names, llvm::omp::Version version) {
   if (version >= 60) {
     return llvm::is_contained(names, name.ToString());
   }
@@ -5573,7 +5576,7 @@ static bool IsPredefinedHandle(const parser::Name &name,
 }
 
 static bool ClauseHasTargetEffect(llvm::omp::Directive directive,
-    llvm::omp::Clause clause, unsigned version) {
+    llvm::omp::Clause clause, llvm::omp::Version version) {
   llvm::ArrayRef<llvm::omp::Directive> leafs{
       llvm::omp::getLeafConstructsOrSelf(directive)};
   if (!llvm::is_contained(leafs, llvm::omp::Directive::OMPD_target)) {
@@ -5606,7 +5609,7 @@ static bool ClauseHasTargetEffect(llvm::omp::Directive directive,
 
 void OmpStructureChecker::CheckUsesAllocatorsSpec(
     const parser::OmpUsesAllocatorsClause::AllocatorSpec &spec) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   bool isLegacySyntax{std::get<bool>(spec.t)};
 
   // The traits of the deprecated syntax are stored as a traits-array modifier,
@@ -5753,7 +5756,7 @@ void OmpStructureChecker::CheckUsesAllocatorsTraits(
     return;
   }
   const Symbol &ultimate{symbol->GetUltimate()};
-  if (context_.langOptions().OpenMPVersion < 60 &&
+  if (context_.langOptions().getOpenMP() < 60 &&
       &ultimate.owner() !=
           &GetScopingUnit(context_.FindScope(GetContext().directiveSource))) {
     context_.Say(traitsSource,
@@ -5785,7 +5788,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::UsesAllocators &x) {
     return;
   }
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   const std::list<parser::OmpUsesAllocatorsClause::AllocatorSpec> &specs{x.v.v};
 
   // Classify by the syntax each specification was written in, which the parse
@@ -5901,7 +5904,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::From &x) {
   }
 
   auto &modifiers{OmpGetModifiers(x.v)};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) {
     CheckIteratorModifier(*iter);
@@ -5924,7 +5927,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::To &x) {
   }
 
   auto &modifiers{OmpGetModifiers(x.v)};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   // The "to" clause is only allowed on "declare target" (pre-5.1), and
   // "target update". In the former case it can take an extended list item,
@@ -5953,7 +5956,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::To &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::OmpxBare &x) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   // 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".
@@ -5965,13 +5968,13 @@ void OmpStructureChecker::Enter(const parser::OmpClause::OmpxBare &x) {
 }
 
 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   return llvm::omp::getOpenMPClauseName(clause, version);
 }
 
 llvm::StringRef OmpStructureChecker::getDirectiveName(
     llvm::omp::Directive directive) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   return llvm::omp::getOpenMPDirectiveName(directive, version);
 }
 
@@ -5998,7 +6001,7 @@ void OmpStructureChecker::CheckDependList(const parser::DataRef &d) {
 void OmpStructureChecker::CheckArraySection(
     const parser::ArrayElement &arrayElement, const parser::Name &name,
     const llvm::omp::Clause clause) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   // Sometimes substring operations are incorrectly parsed as array accesses.
   // Detect this by looking for array accesses on character variables which are
   // not arrays.
@@ -6100,7 +6103,7 @@ void OmpStructureChecker::CheckLastPartRefForArraySection(
 
 void OmpStructureChecker::CheckIntentInPointer(
     SymbolSourceMap &symbols, llvm::omp::Clause clauseId) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   for (auto &[symbol, source] : symbols) {
     if (IsPointer(*symbol) && IsIntentIn(*symbol)) {
       context_.Say(source,
@@ -6112,7 +6115,7 @@ void OmpStructureChecker::CheckIntentInPointer(
 
 void OmpStructureChecker::CheckProcedurePointer(
     SymbolSourceMap &symbols, llvm::omp::Clause clause) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   for (const auto &[symbol, source] : symbols) {
     if (IsProcedurePointer(*symbol)) {
       context_.Say(source,
@@ -6162,7 +6165,7 @@ void OmpStructureChecker::GetSymbolsInObjectList(
 
 void OmpStructureChecker::CheckDefinableObjects(
     SymbolSourceMap &symbols, const llvm::omp::Clause clause) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   for (auto &[symbol, source] : symbols) {
     if (!IsVariableListItem(*symbol)) {
       continue;
@@ -6181,7 +6184,7 @@ void OmpStructureChecker::CheckDefinableObjects(
 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt(
     SymbolSourceMap &currSymbols, DirectivesClauseTriple &dirClauseTriple,
     const llvm::omp::Clause currClause) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   SymbolSourceMap enclosingSymbols;
   auto range{dirClauseTriple.equal_range(GetContext().directive)};
   for (auto dirIter{range.first}; dirIter != range.second; ++dirIter) {
@@ -6307,8 +6310,8 @@ void OmpStructureChecker::CheckWorkshareBlockStmts(
 
 void OmpStructureChecker::CheckWorkdistributeBlockStmts(
     const parser::Block &block, parser::CharBlock source) {
-  unsigned version{context_.langOptions().OpenMPVersion};
-  unsigned since{60};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version since(60);
   if (version < since)
     context_.Say(source,
         "WORKDISTRIBUTE construct is not allowed in %s, %s"_err_en_US,
@@ -6385,7 +6388,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::DynamicAllocators &x) {
 void OmpStructureChecker::Enter(const parser::OmpClause::ReverseOffload &x) {
   CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_reverse_offload);
   if (IsAllowedClause(llvm::omp::Clause::OMPC_reverse_offload)) {
-    unsigned version{context_.langOptions().OpenMPVersion};
+    llvm::omp::Version version{context_.langOptions().getOpenMP()};
     context_.Say(GetContext().clauseSource,
         "%s clause is not supported and will be ignored"_warn_en_US,
         GetUpperName(llvm::omp::Clause::OMPC_reverse_offload, version));
@@ -6682,7 +6685,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
 }
 
 void OmpStructureChecker::CheckAllowedRequiresClause(llvm::omp::Clause clause) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   if (clause != llvm::omp::Clause::OMPC_atomic_default_mem_order) {
     // Check that it does not appear after a device construct
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 0f8daf84d25ec..5573afc3ffb84 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -170,7 +170,7 @@ void OmpStructureChecker::CheckDefaultNoneInAssociatedLoop(
   }
 
   SymbolSourceMap explicitDSA;
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   for (const parser::OmpClause &clause : spec.Clauses().v) {
     if (llvm::omp::isDataSharingAttributeClause(clause.Id(), version)) {
       if (const parser::OmpObjectList *objects{
@@ -655,7 +655,7 @@ void OmpStructureChecker::CheckTraitDeviceNum(
 void OmpStructureChecker::CheckTraitRequires(
     const parser::OmpTraitSetSelector &traitSet,
     const parser::OmpTraitSelector &trait) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
   auto &properties{GetTraitPropertyList(trait)};
 
@@ -680,7 +680,7 @@ void OmpStructureChecker::CheckTraitRequires(
 void OmpStructureChecker::CheckTraitSimd(
     const parser::OmpTraitSetSelector &traitSet,
     const parser::OmpTraitSelector &trait) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
   auto &properties{GetTraitPropertyList(trait)};
 
@@ -719,7 +719,7 @@ void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
   if (const parser::OpenMPConstruct *meta{GetCurrentConstruct()}) {
     if (parser::Unwrap<parser::OmpDelimitedMetadirectiveDirective>(meta->u)) {
       checkDefaultNoneInAssociatedLoop = false;
-      unsigned version{context_.langOptions().OpenMPVersion};
+      llvm::omp::Version version{context_.langOptions().getOpenMP()};
       switch (llvm::omp::getDirectiveAssociation(dirId)) {
       case llvm::omp::Association::Block:
       case llvm::omp::Association::LoopNest:
@@ -786,7 +786,7 @@ void OmpStructureChecker::Enter(const parser::ExecutionPartConstruct &x) {
   std::vector<MetadirectiveLoopVariant> variants;
   variants.swap(metadirectiveLoopVariants_);
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   LoopSequence sequence(x, version, /*allowAllLoops=*/true, &context_);
   const parser::DoConstruct &rootLoop{*parser::Unwrap<parser::DoConstruct>(x)};
   const auto &[haveSemantic, havePerfect]{sequence.depth()};
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index e0fabd989bb45..b6c6a36575562 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -382,7 +382,7 @@ static const WithOmpDeclarative *GetOmpDeclarative(const Symbol &symbol) {
 
 static void PutOpenMPRequirements(
     llvm::raw_ostream &os, const Symbol &symbol, SemanticsContext &semaCtx) {
-  unsigned version{semaCtx.langOptions().OpenMPVersion};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
 
   if (const auto *decls{GetOmpDeclarative(symbol)}) {
     if (const llvm::omp::ClauseSet &reqs{decls->ompRequires()}; reqs.count()) {
@@ -397,7 +397,7 @@ static void PutOpenMPRequirements(
 
 static void PutOpenMPDeclarativeDirectives(llvm::raw_ostream &os,
     const SymbolVector &symbols, SemanticsContext &semaCtx) {
-  unsigned version{semaCtx.langOptions().OpenMPVersion};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
 
   for (const Symbol &symbol : symbols) {
     if (const auto *decls{GetOmpDeclarative(symbol)}) {
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index dc3d5a302e841..a19bde31643d8 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -116,14 +116,15 @@ SourcedActionStmt GetActionStmt(const parser::Block &block) {
   return SourcedActionStmt{};
 }
 
-std::string ThisVersion(unsigned version) {
-  std::string tv{
-      std::to_string(version / 10) + "." + std::to_string(version % 10)};
+std::string ThisVersion(llvm::omp::Version version) {
+  auto v{static_cast<unsigned>(version)};
+  std::string tv{std::to_string(v / 10) + "." + std::to_string(v % 10)};
   return "OpenMP v" + tv;
 }
 
-std::string TryVersion(unsigned version) {
-  return "try -fopenmp-version=" + std::to_string(version);
+std::string TryVersion(llvm::omp::Version version) {
+  return "try -fopenmp-version=" +
+      std::to_string(static_cast<unsigned>(version));
 }
 
 static const Symbol *GetFunctionReferenceSymbol(
@@ -686,7 +687,7 @@ MaybeExpr MakeEvaluateExpr(const parser::OmpStylizedInstance &inp) {
 /// For clauses that take argument lists, return the type of the argument
 /// list item. For other clauses return std::nullopt.
 std::optional<ListItemKind> GetArgumentListItemKind(
-    llvm::omp::Clause clause, unsigned version) {
+    llvm::omp::Clause clause, llvm::omp::Version version) {
   switch (clause) {
   case llvm::omp::Clause::OMPC_absent:
     if (version >= 51) {
@@ -1039,7 +1040,7 @@ static SymbolVector SelectUsedSymbols(
 
 WithReason<int64_t> GetArgumentValueWithReason(
     const parser::OmpDirectiveSpecification &spec, llvm::omp::Clause clauseId,
-    unsigned version, SemanticsContext *semaCtx) {
+    llvm::omp::Version version, SemanticsContext *semaCtx) {
   if (auto *clause{parser::omp::FindClause(spec, clauseId)}) {
     if (auto *expr{parser::Unwrap<parser::Expr>(clause->u)}) {
       if (auto value{GetIntValueFromExpr(*expr, semaCtx)}) {
@@ -1071,7 +1072,7 @@ static WithReason<int64_t> GetNumArgumentsWithReasonForType(
 
 WithReason<int64_t> GetNumArgumentsWithReason(
     const parser::OmpDirectiveSpecification &spec, llvm::omp::Clause clauseId,
-    unsigned version, SemanticsContext *semaCtx) {
+    llvm::omp::Version version, SemanticsContext *semaCtx) {
   if (auto *clause{parser::omp::FindClause(spec, clauseId)}) {
     std::string name{GetUpperName(clauseId, version)};
     // Try the types used for list items.
@@ -1092,7 +1093,7 @@ WithReason<int64_t> GetNumArgumentsWithReason(
 }
 
 WithReason<int64_t> GetHeightWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx) {
   bool isFullUnroll{IsFullUnroll(spec)};
 
@@ -1294,7 +1295,7 @@ WithReason<T> operator+(T a, const WithReason<T> &b) {
 /// Return the depth of the affected nest(s):
 ///   {affected-depth, must-be-perfect-nest}.
 std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx) {
   llvm::omp::Directive dir{spec.DirId()};
   bool allowsCollapse{llvm::omp::isAllowedClauseForDirective(
@@ -1411,7 +1412,7 @@ std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
 /// Return the depth of the generated nest(s)
 ///   {generated-depth, is-perfect-nest}
 std::pair<WithReason<int64_t>, bool> GetGeneratedNestDepthWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx) {
   llvm::omp::Directive dir{spec.DirId()};
   if (!IsLoopTransforming(dir)) {
@@ -1451,7 +1452,7 @@ std::pair<WithReason<int64_t>, bool> GetGeneratedNestDepthWithReason(
 /// Return the range of the affected nests in the sequence:
 ///   {first, count}
 WithReason<std::pair<int64_t, int64_t>> GetAffectedLoopRangeWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx) {
   llvm::omp::Directive dir{spec.DirId()};
 
@@ -1490,7 +1491,7 @@ WithReason<std::pair<int64_t, int64_t>> GetAffectedLoopRangeWithReason(
 }
 
 WithReason<int64_t> GetRectangularNestDepthWithReason(
-    const parser::OmpDirectiveSpecification &spec, unsigned version,
+    const parser::OmpDirectiveSpecification &spec, llvm::omp::Version version,
     SemanticsContext *semaCtx) {
   auto [depth, _]{GetAffectedNestDepthWithReason(spec, version, semaCtx)};
   if (!depth) {
@@ -1637,7 +1638,7 @@ bool IsDoacrossAffected(const parser::OpenMPLoopConstruct &x) {
 /// For the top-level DO COLLAPSE(5) construct, the k loop is the only
 /// directly affected loop.
 std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
-    const parser::OpenMPLoopConstruct &x, unsigned version,
+    const parser::OpenMPLoopConstruct &x, llvm::omp::Version version,
     SemanticsContext *semaCtx) {
   std::vector<const parser::DoConstruct *> result;
   const parser::OmpDirectiveSpecification &spec{x.BeginDir()};
@@ -1770,7 +1771,7 @@ static_assert(HasSourceT<parser::ExecutionPartConstruct>::value);
 #endif // EXPENSIVE_CHECKS
 
 LoopSequence::LoopSequence(const parser::ExecutionPartConstruct &root,
-    unsigned version, bool allowAllLoops, SemanticsContext *semaCtx)
+    llvm::omp::Version version, bool allowAllLoops, SemanticsContext *semaCtx)
     : version_(version), allowAllLoops_(allowAllLoops), semaCtx_(semaCtx) {
   entry_ = createConstructEntry(root);
   assert(entry_ && "Expecting loop like code");
@@ -1779,8 +1780,8 @@ LoopSequence::LoopSequence(const parser::ExecutionPartConstruct &root,
   precalculate();
 }
 
-LoopSequence::LoopSequence(std::unique_ptr<Construct> entry, unsigned version,
-    bool allowAllLoops, SemanticsContext *semaCtx)
+LoopSequence::LoopSequence(std::unique_ptr<Construct> entry,
+    llvm::omp::Version version, bool allowAllLoops, SemanticsContext *semaCtx)
     : version_(version), allowAllLoops_(allowAllLoops),
       entry_(std::move(entry)), semaCtx_(semaCtx) {
   createChildrenFromRange(entry_->location);
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index dba3809c110e3..d7f7aa8e84685 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -915,7 +915,7 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
   }
 
   void Post(const parser::OmpMapClause &x) {
-    unsigned version{context_.langOptions().OpenMPVersion};
+    llvm::omp::Version version{context_.langOptions().getOpenMP()};
     std::optional<Symbol::Flag> ompFlag;
 
     auto &mods{OmpGetModifiers(x)};
@@ -2136,7 +2136,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
 
 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
     const parser::Name &iv) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   // Find the parallel, teams or task generating construct enclosing the
   // sequential loop.
   auto targetIt{dirContext_.rbegin()};
@@ -2227,7 +2227,7 @@ bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex(
     const parser::OpenMPLoopConstruct &x) {
   const parser::OmpDirectiveSpecification &spec{x.BeginDir()};
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
 
   auto [depth, _]{
       omp::GetAffectedNestDepthWithReason(spec, version, &context_)};
@@ -2283,7 +2283,7 @@ bool OmpAttributeVisitor::Pre(const parser::OmpGroupprivateDirective &x) {
     device = parser::UnwrapRef<common::OmpDeviceType>(*devClause);
   }
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   llvm::omp::ClauseSet clauses{llvm::omp::Clause::OMPC_device_type};
   for (const parser::OmpArgument &arg : x.v.Arguments().v) {
     if (const parser::OmpObject *object{parser::omp::GetArgumentObject(arg)}) {
@@ -2337,7 +2337,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) {
 bool OmpAttributeVisitor::Pre(const parser::OmpDeclareTargetDirective &x) {
   PushContext(x.source, llvm::omp::Directive::OMPD_declare_target);
 
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   std::map<const Symbol *, WithOmpDeclarative> details;
   std::optional<common::OmpDeviceType> device;
 
@@ -3045,7 +3045,7 @@ static bool SymbolOrEquivalentIsInNamelist(const Symbol &symbol) {
 
 void OmpAttributeVisitor::ResolveOmpDesignator(
     const parser::Designator &designator, Symbol::Flag ompFlag) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   llvm::omp::Directive directive{GetContext().directive};
 
   const auto *name{parser::GetDesignatorNameIfDataRef(designator)};
@@ -3451,7 +3451,7 @@ void OmpAttributeVisitor::CheckObjectIsPrivatizable(
 void OmpAttributeVisitor::AddOmpRequiresToScope(Scope &scope,
     const llvm::omp::ClauseSet &reqs,
     const std::optional<common::OmpMemoryOrderType> &memOrder) {
-  unsigned version{context_.langOptions().OpenMPVersion};
+  llvm::omp::Version version{context_.langOptions().getOpenMP()};
   const Scope &programUnit{omp::GetProgramUnit(scope)};
 
   if (auto *symbol{const_cast<Symbol *>(programUnit.symbol())}) {
diff --git a/flang/unittests/Semantics/OpenMPUtils.cpp b/flang/unittests/Semantics/OpenMPUtils.cpp
index 7b003d8fa4af8..aee4b452ec87f 100644
--- a/flang/unittests/Semantics/OpenMPUtils.cpp
+++ b/flang/unittests/Semantics/OpenMPUtils.cpp
@@ -201,7 +201,8 @@ TEST_F(OpenMPUtilsTest, AffectedNestDepthNoClauses) {
     auto &body = std::get<parser::ExecutionPart>(mainProgram.t).v;
     auto &omp = parser::UnwrapRef<parser::OpenMPLoopConstruct>(body.front());
     auto [depth, mustBePerfect] =
-        semantics::omp::GetAffectedNestDepthWithReason(omp.BeginDir(), 60);
+        semantics::omp::GetAffectedNestDepthWithReason(
+            omp.BeginDir(), llvm::omp::Version(60));
     EXPECT_TRUE(depth.value.has_value());
     if (depth) {
       EXPECT_EQ(*depth.value, 1);
@@ -245,7 +246,8 @@ TEST_F(OpenMPUtilsTest, AffectedNestDepthCollapse) {
     auto &body = std::get<parser::ExecutionPart>(mainProgram.t).v;
     auto &omp = parser::UnwrapRef<parser::OpenMPLoopConstruct>(body.front());
     auto [depth, mustBePerfect] =
-        semantics::omp::GetAffectedNestDepthWithReason(omp.BeginDir(), 60);
+        semantics::omp::GetAffectedNestDepthWithReason(
+            omp.BeginDir(), llvm::omp::Version(60));
     EXPECT_TRUE(depth.value.has_value());
     if (depth) {
       EXPECT_EQ(*depth.value, 2);
@@ -293,7 +295,8 @@ TEST_F(OpenMPUtilsTest, AffectedNestDepthCollapseOrdered) {
     auto &body = std::get<parser::ExecutionPart>(mainProgram.t).v;
     auto &omp = parser::UnwrapRef<parser::OpenMPLoopConstruct>(body.front());
     auto [depth, mustBePerfect] =
-        semantics::omp::GetAffectedNestDepthWithReason(omp.BeginDir(), 60);
+        semantics::omp::GetAffectedNestDepthWithReason(
+            omp.BeginDir(), llvm::omp::Version(60));
     EXPECT_TRUE(depth.value.has_value());
     if (depth) {
       EXPECT_EQ(*depth.value, 3);
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMPVersion.h b/llvm/include/llvm/Frontend/OpenMP/OMPVersion.h
index 3daa1584a3f20..649d5deaa85a3 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMPVersion.h
+++ b/llvm/include/llvm/Frontend/OpenMP/OMPVersion.h
@@ -19,8 +19,8 @@ namespace llvm {
 namespace omp {
 struct Version {
   using value_type = unsigned;
-  constexpr /*TODO explicit*/ Version(value_type Ver = 0) : V(Ver) {}
-  constexpr /*TODO explicit*/ operator value_type() const { return V; }
+  constexpr explicit Version(value_type Ver = 0) : V(Ver) {}
+  constexpr explicit operator value_type() const { return V; }
   constexpr explicit operator bool() const { return V != 0; }
 
   friend constexpr bool operator<(Version A, Version B);
@@ -40,9 +40,6 @@ inline constexpr bool operator>(Version A, Version B) { return !(A <= B); }
 inline constexpr bool operator>=(Version A, Version B) { return !(A < B); }
 
 inline constexpr bool operator==(Version A, int B) { return A == Version(B); }
-inline constexpr bool operator==(Version A, unsigned B) {
-  return A == Version(B);
-}
 inline constexpr bool operator!=(Version A, int B) { return A != Version(B); }
 inline constexpr bool operator<(Version A, int B) { return A < Version(B); }
 inline constexpr bool operator<=(Version A, int B) { return A <= Version(B); }

>From 28e2f8fe506528305e82ddfa2581b79ab9b3ff93 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Tue, 1 Sep 2026 08:53:40 -0500
Subject: [PATCH 2/3] Rename getOpenMP to getOpenMPVersion

---
 .../flang/Semantics/openmp-modifiers.h        |  10 +-
 flang/include/flang/Support/LangOptions.h     |   2 +-
 flang/lib/Frontend/CompilerInvocation.cpp     |   2 +-
 flang/lib/Frontend/FrontendActions.cpp        |   3 +-
 flang/lib/Lower/OpenMP/Atomic.cpp             |   8 +-
 flang/lib/Lower/OpenMP/ClauseProcessor.cpp    |   6 +-
 flang/lib/Lower/OpenMP/ClauseProcessor.h      |   2 +-
 .../lib/Lower/OpenMP/DataSharingProcessor.cpp |   4 +-
 flang/lib/Lower/OpenMP/DataSharingProcessor.h |   2 +-
 flang/lib/Lower/OpenMP/OpenMP.cpp             |   6 +-
 flang/lib/Lower/PFTBuilder.cpp                |   2 +-
 flang/lib/Parser/openmp-parsers.cpp           |  12 +-
 flang/lib/Parser/unparse.cpp                  |   4 +-
 flang/lib/Semantics/check-omp-atomic.cpp      |   2 +-
 flang/lib/Semantics/check-omp-loop.cpp        |  12 +-
 flang/lib/Semantics/check-omp-structure.cpp   | 116 +++++++++---------
 flang/lib/Semantics/check-omp-variant.cpp     |  10 +-
 flang/lib/Semantics/mod-file.cpp              |   4 +-
 flang/lib/Semantics/resolve-directives.cpp    |  14 +--
 19 files changed, 113 insertions(+), 108 deletions(-)

diff --git a/flang/include/flang/Semantics/openmp-modifiers.h b/flang/include/flang/Semantics/openmp-modifiers.h
index ef92cd397dd89..970ebd460685d 100644
--- a/flang/include/flang/Semantics/openmp-modifiers.h
+++ b/flang/include/flang/Semantics/openmp-modifiers.h
@@ -295,7 +295,7 @@ bool verifyVersions(const std::optional<std::list<UnionTy>> &modifiers,
   if (!modifiers) {
     return true;
   }
-  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
   bool result{true};
   for (auto &m : *modifiers) {
     const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor(m)};
@@ -346,7 +346,7 @@ template <typename SpecificTy, typename UnionTy>
 bool verifyIfRequired(const SpecificTy *,
     const std::optional<std::list<UnionTy>> &modifiers,
     parser::CharBlock clauseSource, SemanticsContext &semaCtx) {
-  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
   const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor<SpecificTy>()};
   if (!desc.getProperties(version).test(llvm::omp::Property::Required)) {
     // If the modifier is not required, there is nothing to do.
@@ -400,7 +400,7 @@ bool verifyIfUnique(const SpecificTy *,
   // `specific` is the location of the modifier of type SpecificTy.
   assert(specific != end && "`specific` must be a valid location");
 
-  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
   const llvm::omp::descriptor::Modifier &desc{OmpGetDescriptor<SpecificTy>()};
   // Ultimate implies Unique.
   if (!desc.getProperties(version).test(llvm::omp::Property::Unique) &&
@@ -449,7 +449,7 @@ bool verifyUltimate(const std::optional<std::list<UnionTy>> &modifiers,
   if (!modifiers || modifiers->size() <= 1) {
     return true;
   }
-  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
   bool result{true};
   auto first{modifiers->cbegin()};
   auto last{std::prev(modifiers->cend())};
@@ -498,7 +498,7 @@ bool verifyExclusive(const std::optional<std::list<UnionTy>> &modifiers,
   if (!modifiers || modifiers->size() <= 1) {
     return true;
   }
-  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
   const UnionTy &front{modifiers->front()};
   const llvm::omp::descriptor::Modifier &frontDesc{OmpGetDescriptor(front)};
 
diff --git a/flang/include/flang/Support/LangOptions.h b/flang/include/flang/Support/LangOptions.h
index 054bafb78354a..9043975e30e20 100644
--- a/flang/include/flang/Support/LangOptions.h
+++ b/flang/include/flang/Support/LangOptions.h
@@ -88,7 +88,7 @@ class LangOptions : public LangOptionsBase {
   /// List of triples passed in using -fopenmp-targets.
   std::vector<llvm::Triple> OMPTargetTriples;
 
-  llvm::omp::Version getOpenMP() const {
+  llvm::omp::Version getOpenMPVersion() const {
     return llvm::omp::Version(OpenMPVersion);
   }
 
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index c07407c5d4b7b..ff4c7f6a18624 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1961,7 +1961,7 @@ void CompilerInvocation::setDefaultPredefinitions() {
   if (frontendOptions.features.IsEnabled(
           Fortran::common::LanguageFeature::OpenMP)) {
     Fortran::common::setOpenMPMacro(
-        static_cast<unsigned>(getLangOpts().getOpenMP()),
+        static_cast<unsigned>(getLangOpts().getOpenMPVersion()),
         fortranOptions.predefinitions);
   }
 
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index ce1b0d06a1091..35e0e4004d9a0 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -275,7 +275,8 @@ bool CodeGenAction::beginSourceFileAction() {
     mlir::omp::setOffloadModuleInterfaceAttributes(
         lb.getModule(),
         makeOffloadModuleOpts(ci.getInvocation().getLangOpts()));
-    llvm::omp::Version version = ci.getInvocation().getLangOpts().getOpenMP();
+    llvm::omp::Version version =
+        ci.getInvocation().getLangOpts().getOpenMPVersion();
     mlir::omp::setOpenMPVersionAttribute(lb.getModule(),
                                          static_cast<unsigned>(version));
     if (!ci.getInvocation().getLoweringOpts().getIntegerWrapAround())
diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp
index 79f4681c67275..5a38c97ee77a2 100644
--- a/flang/lib/Lower/OpenMP/Atomic.cpp
+++ b/flang/lib/Lower/OpenMP/Atomic.cpp
@@ -226,7 +226,7 @@ getMemoryOrderFromRequires(const semantics::Scope &scope) {
 
 static std::optional<mlir::omp::ClauseMemoryOrderKind>
 getDefaultAtomicMemOrder(semantics::SemanticsContext &semaCtx) {
-  llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
+  llvm::omp::Version version = semaCtx.langOptions().getOpenMPVersion();
   if (version > 50)
     return mlir::omp::ClauseMemoryOrderKind::Relaxed;
   return std::nullopt;
@@ -355,7 +355,7 @@ genAtomicRead(lower::AbstractConverter &converter,
     if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Release) {
       // Reset it back to the default.
       memOrder = getDefaultAtomicMemOrder(semaCtx);
-    } else if (semaCtx.langOptions().getOpenMP() <= 50 &&
+    } else if (semaCtx.langOptions().getOpenMPVersion() <= 50 &&
                *memOrder == mlir::omp::ClauseMemoryOrderKind::Acq_rel) {
       // In OpenMP 5.0, acq_rel is not allowed on read; decay to acquire.
       // In OpenMP 5.1+, acq_rel is permitted on read.
@@ -418,7 +418,7 @@ genAtomicWrite(lower::AbstractConverter &converter,
     if (*memOrder == mlir::omp::ClauseMemoryOrderKind::Acquire) {
       // Reset it back to the default.
       memOrder = getDefaultAtomicMemOrder(semaCtx);
-    } else if (semaCtx.langOptions().getOpenMP() <= 50 &&
+    } else if (semaCtx.langOptions().getOpenMPVersion() <= 50 &&
                *memOrder == mlir::omp::ClauseMemoryOrderKind::Acq_rel) {
       // In OpenMP 5.0, acq_rel is not allowed on write; decay to release.
       // In OpenMP 5.1+, acq_rel is permitted on write.
@@ -574,7 +574,7 @@ void Fortran::lower::omp::lowerAtomic(
   auto [memOrder, canOverride] = getAtomicMemoryOrder(
       semaCtx, clauses, semaCtx.FindScope(construct.source));
 
-  llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
+  llvm::omp::Version version = semaCtx.langOptions().getOpenMPVersion();
   int action0 = analysis.op0.what & analysis.Action;
   int action1 = analysis.op1.what & analysis.Action;
   memOrder = makeValidForAction(memOrder, action0, action1, version);
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index b838791b2dcbe..22506b14b0fa6 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1831,7 +1831,7 @@ bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result,
       std::optional<mlir::omp::LinearModifier> linearMod;
       if (explicitLinearMod)
         linearMod = *explicitLinearMod;
-      else if (semaCtx.langOptions().getOpenMP() >= 52)
+      else if (semaCtx.langOptions().getOpenMPVersion() >= 52)
         linearMod = isDeclareSimd ? getDeclareSimdDefaultMod(*sym)
                                   : mlir::omp::LinearModifier::val;
 
@@ -1979,10 +1979,10 @@ bool ClauseProcessor::processMap(
     // default value
     Map::MapType type;
     if (directive == llvm::omp::Directive::OMPD_target_enter_data &&
-        semaCtx.langOptions().getOpenMP() >= 52)
+        semaCtx.langOptions().getOpenMPVersion() >= 52)
       type = mapType.value_or(Map::MapType::To);
     else if (directive == llvm::omp::Directive::OMPD_target_exit_data &&
-             semaCtx.langOptions().getOpenMP() >= 52)
+             semaCtx.langOptions().getOpenMPVersion() >= 52)
       type = mapType.value_or(Map::MapType::From);
     else
       type = mapType.value_or(Map::MapType::Tofrom);
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h
index 177a5ff33f231..6bfa939f52c0b 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.h
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h
@@ -236,7 +236,7 @@ void ClauseProcessor::processTODO(mlir::Location currentLocation,
   auto checkUnhandledClause = [&](llvm::omp::Clause id, const auto *x) {
     if (!x)
       return;
-    llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
+    llvm::omp::Version version = semaCtx.langOptions().getOpenMPVersion();
     bool isSimdDirective = llvm::omp::getOpenMPDirectiveName(directive, version)
                                .upper()
                                .find("SIMD") != llvm::StringRef::npos;
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
index a4d09375dfbfc..8d7b41af3602c 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.cpp
@@ -287,7 +287,7 @@ void DataSharingProcessor::collectSymbolsForPrivatization() {
         // versions semantics only warns and ignores it, so fall back to a
         // regular lastprivate here to keep lowering consistent and avoid the
         // conditional path for entities it cannot handle (e.g. characters).
-        if (semaCtx.langOptions().getOpenMP() >= 50) {
+        if (semaCtx.langOptions().getOpenMPVersion() >= 50) {
           collectOmpObjectListSymbol(objects, conditionalLastPrivatizedSymbols);
         } else {
           collectOmpObjectListSymbol(objects, explicitlyPrivatizedSymbols);
@@ -463,7 +463,7 @@ bool DataSharingProcessor::isOpenMPPrivatizingConstruct(
 
 bool DataSharingProcessor::isOpenMPPrivatizingEvaluation(
     const pft::Evaluation &eval) const {
-  llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
+  llvm::omp::Version version = semaCtx.langOptions().getOpenMPVersion();
   return eval.visit([=](auto &&s) {
     using BareS = llvm::remove_cvref_t<decltype(s)>;
     if constexpr (std::is_same_v<BareS, parser::OpenMPConstruct>) {
diff --git a/flang/lib/Lower/OpenMP/DataSharingProcessor.h b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
index c11c427d88c18..9320ba41b0d79 100644
--- a/flang/lib/Lower/OpenMP/DataSharingProcessor.h
+++ b/flang/lib/Lower/OpenMP/DataSharingProcessor.h
@@ -38,7 +38,7 @@ class DataSharingProcessor {
   /// order to tell which OMP scope defined vs. references a certain Symbol.
   struct OMPConstructSymbolVisitor {
     OMPConstructSymbolVisitor(semantics::SemanticsContext &ctx)
-        : version(ctx.langOptions().getOpenMP()) {}
+        : version(ctx.langOptions().getOpenMPVersion()) {}
     template <typename T>
     bool Pre(const T &) {
       return true;
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 8ea5d2f03bd12..bb2c932fca790 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2548,7 +2548,7 @@ genSimdImplicitLinear(lower::AbstractConverter &converter,
           Fortran::semantics::IsAllocatableOrPointer(loopVar->GetUltimate()))) {
       mlir::Type ty = converter.genType(*loopVar);
       typeAttrs.push_back(mlir::TypeAttr::get(ty));
-      if (semaCtx.langOptions().getOpenMP() >= 52)
+      if (semaCtx.langOptions().getOpenMPVersion() >= 52)
         linearModAttrs.push_back(mlir::omp::LinearModifierAttr::get(
             &converter.getMLIRContext(), mlir::omp::LinearModifier::val));
       else
@@ -7545,7 +7545,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
       const common::LangOptions &options = semaCtx.langOptions();
       if (!options.OpenMPSimd) {
         std::string name =
-            parser::omp::GetUpperName(clause.id, options.getOpenMP());
+            parser::omp::GetUpperName(clause.id, options.getOpenMPVersion());
         TODO(clauseLocation, name + " clause is not implemented yet");
       }
     }
@@ -7755,7 +7755,7 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
         // generating the omp.loop_nest op.
         break;
       default: {
-        llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
+        llvm::omp::Version version = semaCtx.langOptions().getOpenMPVersion();
         TODO(currentLocation,
              "Applying a loop-associated on the loop generated by the " +
                  llvm::omp::getOpenMPDirectiveName(nestedDirective, version) +
diff --git a/flang/lib/Lower/PFTBuilder.cpp b/flang/lib/Lower/PFTBuilder.cpp
index 91f1fedcd6961..fc37a47abb802 100644
--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -2629,7 +2629,7 @@ static bool isOmpLoopBody(const Fortran::lower::pft::Evaluation &eval,
   if (!loop)
     return false;
 
-  llvm::omp::Version version = semaCtx.langOptions().getOpenMP();
+  llvm::omp::Version version = semaCtx.langOptions().getOpenMPVersion();
   auto [depth, _] =
       semantics::omp::GetAffectedNestDepthWithReason(loop->BeginDir(), version);
   int64_t n = depth.value.value_or(1);
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 5c9ccf2fed3f1..afe12bef72ab0 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -969,7 +969,8 @@ struct OmpMapTypeParser {
   using resultType = OmpMapType::Value;
 
   std::optional<resultType> Parse(ParseState &state) const {
-    llvm::omp::Version version{state.userState()->langOptions().getOpenMP()};
+    llvm::omp::Version version{
+        state.userState()->langOptions().getOpenMPVersion()};
     if (version < 60) {
       auto parser{//
           "ALLOC" >> pure(OmpMapType::Value::Alloc) ||
@@ -999,7 +1000,8 @@ struct OmpMapTypeModifierParser {
   using resultType = OmpMapTypeModifier::Value;
 
   std::optional<resultType> Parse(ParseState &state) const {
-    llvm::omp::Version version{state.userState()->langOptions().getOpenMP()};
+    llvm::omp::Version version{
+        state.userState()->langOptions().getOpenMPVersion()};
     if (version < 60) {
       auto parser{//
           "ALWAYS" >> pure(OmpMapTypeModifier::Value::Always) ||
@@ -1133,7 +1135,8 @@ template <typename MotionClause> struct OmpMotionClauseModifierParser {
   using resultType = typename MotionClause::Modifier;
 
   std::optional<resultType> Parse(ParseState &state) const {
-    llvm::omp::Version version{state.userState()->langOptions().getOpenMP()};
+    llvm::omp::Version version{
+        state.userState()->langOptions().getOpenMPVersion()};
     if (version <= 51) {
       auto motion{sourced(construct<resultType>(Parser<OmpMotionModifier>{}))};
       if (auto &&result{attempt(motion).Parse(state)}) {
@@ -1178,7 +1181,8 @@ struct OmpLinearClauseModifierParser {
   using resultType = OmpLinearClause::Modifier;
 
   std::optional<resultType> Parse(ParseState &state) const {
-    llvm::omp::Version version{state.userState()->langOptions().getOpenMP()};
+    llvm::omp::Version version{
+        state.userState()->langOptions().getOpenMPVersion()};
     if (version < 52) {
       auto parser{sourced( //
           construct<resultType>(Parser<OmpLinearModifier>{}) ||
diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp
index a177e90cd6f64..d075a77e17b43 100644
--- a/flang/lib/Parser/unparse.cpp
+++ b/flang/lib/Parser/unparse.cpp
@@ -2172,7 +2172,7 @@ class UnparseVisitor {
     return false;
   }
   void Unparse(const llvm::omp::Directive &x) {
-    llvm::omp::Version ompVersion{langOpts_.getOpenMP()};
+    llvm::omp::Version ompVersion{langOpts_.getOpenMPVersion()};
     Word(llvm::omp::getOpenMPDirectiveName(x, ompVersion).str());
   }
   void Unparse(const OmpAbsentClause &x) { Walk("", x.v, ","); }
@@ -2317,7 +2317,7 @@ class UnparseVisitor {
     Put(")");
   }
   void Unparse(const OmpDirectiveNameModifier &x) {
-    llvm::omp::Version ompVersion{langOpts_.getOpenMP()};
+    llvm::omp::Version ompVersion{langOpts_.getOpenMPVersion()};
     Word(llvm::omp::getOpenMPDirectiveName(x.v, ompVersion));
   }
   void Unparse(const OmpDirectiveSpecification &x) {
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index afc4638ccc692..bf0520e1e00a8 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -1571,7 +1571,7 @@ static void checkIncompatibleMemoryOrderClause(SemanticsContext &context,
   llvm::omp::Clause kind{x.GetKind()};
   const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()};
 
-  llvm::omp::Version version{context.langOptions().getOpenMP()};
+  llvm::omp::Version version{context.langOptions().getOpenMPVersion()};
   if (version < 50)
     return;
 
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 0cf497d543121..393d67ac5f150 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -235,7 +235,7 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
 
 void OmpStructureChecker::CheckRectangularNest(
     const parser::OmpDirectiveSpecification &spec, const LoopSequence &nest) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   auto depth{GetRectangularNestDepthWithReason(spec, version)};
   if (!depth || *depth.value == 0) {
     return;
@@ -266,7 +266,7 @@ void OmpStructureChecker::CheckNestedConstruct(
     const parser::OpenMPLoopConstruct &x) {
   const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
   llvm::omp::Directive dir{beginSpec.DirId()};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   parser::CharBlock beginSource{beginSpec.DirName().source};
 
   // End-directive is not allowed in such cases:
@@ -489,7 +489,7 @@ const parser::Name OmpStructureChecker::GetLoopIndex(
 
 void OmpStructureChecker::CheckIterationVariables(
     const parser::OpenMPLoopConstruct &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   auto doLoops{CollectAffectedDoLoops(x, version, &context_)};
   if (!doLoops) {
     return;
@@ -760,7 +760,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Ordered &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Linear &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::Directive dir{GetContext().directive};
   parser::CharBlock clauseSource{GetContext().clauseSource};
   const parser::OmpLinearModifier *linearMod{nullptr};
@@ -878,7 +878,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Permutation &c) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::Clause clause = llvm::omp::Clause::OMPC_permutation;
   if (c.v.size() < 2)
     context_.Say(GetContext().clauseSource,
@@ -924,7 +924,7 @@ void OmpStructureChecker::Enter(const parser::DoConstruct &x) {
 void OmpStructureChecker::Enter(const parser::OmpLoopModifier &x) {
   DirectiveContext &dirCtx = GetContext();
   llvm::omp::Directive dir{dirCtx.directive};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   auto &m{std::get<llvm::omp::LoopModifier>(x.t)};
   if (!llvm::omp::isAllowedLoopModifier(dir, m)) {
     llvm::StringRef name = llvm::omp::getLoopModifierName(m);
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 41691ff7113cd..6f9caeea9de2b 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -164,7 +164,7 @@ void OmpStructureChecker::Enter(const parser::SubroutineStmt &x) {
 }
 
 void OmpStructureChecker::CheckTempDescriptorMappings() {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   for (const auto &[symbol, source] : tempDescriptorEnterMaps_) {
     if (tempDescriptorExitMaps_.find(symbol) == tempDescriptorExitMaps_.end()) {
       if (version >= 61) {
@@ -441,8 +441,8 @@ bool OmpStructureChecker::IsAllowedClause(llvm::omp::Clause clauseId) {
   if (GetDirectiveNest(ContextSelectorNest) > 0) {
     return true;
   }
-  return llvm::omp::isAllowedClauseForDirective(
-      GetContext().directive, clauseId, context_.langOptions().getOpenMP());
+  return llvm::omp::isAllowedClauseForDirective(GetContext().directive,
+      clauseId, context_.langOptions().getOpenMPVersion());
 }
 
 bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clauseId,
@@ -457,7 +457,7 @@ bool OmpStructureChecker::CheckAllowedClause(llvm::omp::Clause clauseId,
     return true;
   }
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   if (!llvm::omp::isAllowedClauseForDirective(dirId, clauseId, version)) {
     llvm::omp::Version allowedInVersion{[&] {
@@ -588,7 +588,7 @@ void OmpStructureChecker::CheckLabelContext(const parser::CharBlock source,
     return getSource(*lhs).Contains(getSource(*rhs));
   };
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   if (tgtOmp && !isSameOrIncludes(tgtOmp, srcOmp)) {
     parser::OmpDirectiveName name{GetOmpDirectiveName(*tgtOmp)};
     context_
@@ -732,7 +732,7 @@ bool OmpStructureChecker::HasRequires(llvm::omp::Clause req) {
 }
 
 void OmpStructureChecker::CheckArgumentObjectKind(const parser::OmpClause &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::Directive dirId{GetContext().directive};
   llvm::omp::Clause clauseId{x.Id()};
 
@@ -901,7 +901,7 @@ void OmpStructureChecker::CheckDirectiveSpelling(
     ref = ref.drop_front(3);
   }
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   // For every "future" version v, check if the check if the corresponding
   // spelling of id was introduced later than the current version. If so,
@@ -933,7 +933,7 @@ void OmpStructureChecker::CheckDirectiveSpelling(
 void OmpStructureChecker::CheckDirectiveDeprecation(
     const parser::OpenMPConstruct &x) {
   parser::OmpDirectiveName dirName{GetOmpDirectiveName(x)};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   // We only want to emit the warning when the version being used has the
   // directive deprecated
   if (version >= 52) {
@@ -964,7 +964,7 @@ void OmpStructureChecker::CheckDirectiveInPureProcedure(
   if (!FindPureProcedureContaining(scope)) {
     return;
   }
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   // A directive's "pure" property is version-specific: pureSince is the
   // OpenMP version at which the directive gained that property.
   llvm::omp::Version pureSince{llvm::omp::getDirectivePureSince(id)};
@@ -1010,7 +1010,7 @@ OmpStructureChecker::FindMutuallyExclusiveClauses(
 void OmpStructureChecker::CheckClauses(parser::OmpDirectiveName dirName,
     llvm::iterator_range<ClauseIterator> beginClauses,
     llvm::iterator_range<ClauseIterator> endClauses) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::Directive dirId{dirName.v};
   std::vector<const parser::OmpClause *> allClauses;
 
@@ -1513,7 +1513,7 @@ void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) {
       },
       c.u);
   if (!eligibleTarget) {
-    llvm::omp::Version version{context_.langOptions().getOpenMP()};
+    llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
     context_.Warn(common::UsageWarning::OpenMPUsage, source,
         "If %s directive is nested inside TARGET region, the behaviour is unspecified"_port_en_US,
         parser::omp::GetUpperName(ineligibleTargetDir, version));
@@ -1524,7 +1524,7 @@ void OmpStructureChecker::Enter(const parser::OmpBlockConstruct &x) {
   const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
   const std::optional<parser::OmpEndDirective> &endSpec{x.EndDir()};
   const parser::Block &block{std::get<parser::Block>(x.t)};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   // Missing mandatory end block: this is checked in semantics because that
   // makes it easier to control the error messages.
@@ -1642,7 +1642,7 @@ void OmpStructureChecker::Enter(const parser::OmpBlockConstruct &x) {
 void OmpStructureChecker::CheckSingleConstruct(
     const parser::OmpBlockConstruct &x) {
   const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   SymbolSourceMap copyPrivateSyms;
   parser::CharBlock nowaitSource1, nowaitSource2;
 
@@ -2041,7 +2041,7 @@ void OmpStructureChecker::Leave(const parser::OmpThreadprivateDirective &x) {
 void OmpStructureChecker::Enter(const parser::OmpDeclareSimdDirective &x) {
   const parser::OmpDirectiveName &dirName{x.v.DirName()};
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   const Scope &containingScope = context_.FindScope(dirName.source);
   const Scope &progUnitScope = GetProgramUnitContaining(containingScope);
@@ -2179,7 +2179,7 @@ void OmpStructureChecker::CheckInitOnDepobj(
 
 void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) {
   const auto &dirName{std::get<parser::OmpDirectiveName>(x.v.t)};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   const parser::OmpArgumentList &arguments{x.v.Arguments()};
   const parser::OmpClauseList &clauses{x.v.Clauses()};
@@ -2234,7 +2234,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPDepobjConstruct &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpRequiresDirective &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   for (const parser::OmpClause &clause : x.v.Clauses().v) {
     llvm::omp::Clause id{clause.Id()};
@@ -2347,7 +2347,7 @@ void OmpStructureChecker::CheckIndividualAllocateDirective(
 
   auto maybePredefined{maybeHasPredefinedAllocator(allocator)};
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   std::string condStr{version == 50
           ? "a named common block, has SAVE attribute or is declared in the "
             "scope of a module"
@@ -2468,7 +2468,7 @@ void OmpStructureChecker::Enter(const parser::OmpAllocateDirective &x) {
 
   bool isExecutable{partStack_.back() == PartKind::ExecutionPart};
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   if (isExecutable && allocateDirectiveLevel_ == 1 && version >= 52) {
     context_.Warn(common::UsageWarning::OpenMPUsage, dirName.source,
         "The executable form of the OpenMP ALLOCATE directive has been deprecated, please use ALLOCATORS instead"_warn_en_US);
@@ -2708,7 +2708,7 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareTargetDirective &x) {
       context_.Say(x.source,
           "The INDIRECT clause cannot be used without the ENTER clause with the DECLARE TARGET directive."_err_en_US);
     }
-    llvm::omp::Version version{context_.langOptions().getOpenMP()};
+    llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
     if (toClause && version >= 52) {
       context_.Warn(common::UsageWarning::OpenMPUsage, toClause->source,
           "The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead."_warn_en_US);
@@ -3125,7 +3125,7 @@ struct TaskgraphVisitor {
       }
     }
 
-    llvm::omp::Version version{context_.langOptions().getOpenMP()};
+    llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
     bool allowsNogroup{llvm::omp::isAllowedClauseForDirective(
         leafs[0], llvm::omp::Clause::OMPC_nogroup, version)};
 
@@ -3264,7 +3264,7 @@ void OmpStructureChecker::CheckTaskgraph(const parser::OmpBlockConstruct &x) {
 void OmpStructureChecker::CheckTaskDependenceType(
     const parser::OmpTaskDependenceType::Value &x) {
   // Common checks for task-dependence-type (DEPEND and UPDATE clauses).
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::Version since;
 
   switch (x) {
@@ -3344,7 +3344,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
     }
   }
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  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)) {
@@ -3488,7 +3488,7 @@ void OmpStructureChecker::Enter(
     const parser::OmpClause::CancellationConstructType &x) {
   llvm::omp::Directive dir{GetContext().directive};
   auto &dirName{std::get<parser::OmpDirectiveName>(x.v.t)};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   if (dir != llvm::omp::Directive::OMPD_cancel &&
       dir != llvm::omp::Directive::OMPD_cancellation_point) {
@@ -3534,7 +3534,7 @@ std::optional<llvm::omp::Directive> OmpStructureChecker::GetCancelType(
   }
   // Given clauses from CANCEL or CANCELLATION_POINT, identify the construct
   // to which the cancellation applies.
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   std::optional<llvm::omp::Directive> cancelee;
   std::string cancelName{parser::omp::GetUpperName(cancelDir, version)};
 
@@ -3564,7 +3564,7 @@ std::optional<llvm::omp::Directive> OmpStructureChecker::GetCancelType(
 
 void OmpStructureChecker::CheckCancellationNest(
     const parser::CharBlock &source, llvm::omp::Directive type) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   std::string typeName{parser::omp::GetUpperName(type, version)};
 
   if (CurrentDirectiveIsNested()) {
@@ -3678,7 +3678,7 @@ void OmpStructureChecker::Enter(const parser::OmpClauseList &) {
 // 3. Checks on clauses which are not in 'struct OmpClause' from parse-tree.h.
 
 void OmpStructureChecker::Leave(const parser::OmpClauseList &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   // 2.7.1 Loop Construct Restriction
   if (llvm::omp::allDoSet.test(GetContext().directive)) {
@@ -3916,7 +3916,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Destroy &x) {
   }
 
   llvm::omp::Directive dir{GetContext().directive};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   if (dir == llvm::omp::Directive::OMPD_depobj) {
     llvm::omp::Version argSince(52);
     llvm::omp::Version noargDeprecatedIn(52);
@@ -4012,7 +4012,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::TaskReduction &x) {
 bool OmpStructureChecker::CheckReductionOperator(
     const parser::OmpReductionIdentifier &ident, parser::CharBlock source,
     llvm::omp::Clause clauseId) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   auto visitOperator{[&](const parser::DefinedOperator &dOpr) {
     if (const auto *intrinsicOp{
@@ -4079,7 +4079,7 @@ bool OmpStructureChecker::CheckReductionOperator(
 /// Check restrictions on objects that are common to all reduction clauses.
 void OmpStructureChecker::CheckReductionObjects(
     const parser::OmpObjectList &objects, llvm::omp::Clause clauseId) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   SymbolSourceMap symbols;
   GetSymbolsInObjectList(objects, symbols);
 
@@ -4329,7 +4329,7 @@ void OmpStructureChecker::CheckReductionObjectTypes(
 
 void OmpStructureChecker::CheckReductionModifier(
     const parser::OmpReductionModifier &modifier) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   using ReductionModifier = parser::OmpReductionModifier;
   if (modifier.v == ReductionModifier::Value::Default) {
@@ -4399,7 +4399,7 @@ void OmpStructureChecker::CheckReductionArraySection(
 
 void OmpStructureChecker::CheckSharedBindingInOuterContext(
     const parser::OmpObjectList &redObjectList) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   //  TODO: Verify the assumption here that the immediately enclosing region is
   //  the parallel region to which the worksharing construct having reduction
   //  binds to.
@@ -4458,7 +4458,7 @@ void OmpStructureChecker::CheckTypeParamInquiry(const parser::CharBlock &source,
     if (IsTypeParamInquiry(*symbol)) {
       context_.Say(source,
           "A type parameter inquiry cannot appear on the %s directive"_err_en_US,
-          GetUpperName(dirId, context_.langOptions().getOpenMP()));
+          GetUpperName(dirId, context_.langOptions().getOpenMPVersion()));
     }
   }
 }
@@ -4469,7 +4469,7 @@ void OmpStructureChecker::CheckTypeParamInquiry(const parser::CharBlock &source,
     if (IsTypeParamInquiry(*symbol)) {
       context_.Say(source,
           "A type parameter inquiry cannot appear on the %s clause"_err_en_US,
-          GetUpperName(clauseId, context_.langOptions().getOpenMP()));
+          GetUpperName(clauseId, context_.langOptions().getOpenMPVersion()));
     }
   }
 }
@@ -4584,7 +4584,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Aligned &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   using ImplicitBehavior = parser::OmpDefaultmapClause::ImplicitBehavior;
   auto behavior{std::get<ImplicitBehavior>(x.v.t)};
   if (version <= 45) {
@@ -4624,7 +4624,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Defaultmap &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::If &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::Directive dir{GetContext().directive};
 
   auto isConstituent{[](llvm::omp::Directive dir, llvm::omp::Directive part) {
@@ -4764,7 +4764,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::If &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Detach &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   CheckTypeParamInquiry(
       GetContext().clauseSource, x.v.v, llvm::omp::Clause::OMPC_detach);
   // OpenMP 5.2: 12.5.2 Detach clause restrictions
@@ -4811,7 +4811,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Map &x) {
   }
 
   auto &modifiers{OmpGetModifiers(x.v)};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   if (auto commas{std::get<bool>(x.v.t)}; !commas && version >= 52) {
     context_.Say(GetContext().clauseSource,
         "The specification of modifiers without comma separators for the "
@@ -5028,7 +5028,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Schedule &x) {
 void OmpStructureChecker::Enter(const parser::OmpClause::Device &x) {
   const parser::OmpDeviceClause &deviceClause{x.v};
   const auto &device{std::get<parser::ScalarIntExpr>(deviceClause.t)};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   // The predefined identifiers omp_initial_device (-1) and omp_invalid_device
   // (-2) were introduced in OpenMP 5.2. Under earlier versions the device
   // expression must be a non-negative integer.
@@ -5064,7 +5064,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Device &x) {
 
 void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) {
   llvm::omp::Directive dir{GetContext().directive};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   auto *doaDep{std::get_if<parser::OmpDoacross>(&x.v.u)};
   auto *taskDep{std::get_if<parser::OmpDependClause::TaskDep>(&x.v.u)};
@@ -5248,7 +5248,7 @@ void OmpStructureChecker::CheckDoacross(
 
 void OmpStructureChecker::CheckCopyingPolymorphicAllocatable(
     SymbolSourceMap &symbols, const llvm::omp::Clause clause) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   if (context_.ShouldWarn(common::UsageWarning::Portability)) {
     for (auto &[symbol, source] : symbols) {
       if (IsPolymorphicAllocatable(*symbol)) {
@@ -5389,7 +5389,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Copyin &x) {
 
 void OmpStructureChecker::CheckStructureComponent(
     const parser::OmpObject &object, llvm::omp::Clause clauseId) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   if (auto *desg{GetDesignatorFromObj(object)}) {
     if (auto *symbol{GetLastName(*desg).symbol}) {
       if (!IsTypeParamInquiry(*symbol) &&
@@ -5416,7 +5416,7 @@ void OmpStructureChecker::CheckStructureComponent(
 
 void OmpStructureChecker::Enter(
     const parser::OmpClause::UpdateDependObjects &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   auto *taskType = std::get_if<parser::OmpTaskDependenceType>(&x.v.u);
   if (taskType) {
@@ -5609,7 +5609,7 @@ static bool ClauseHasTargetEffect(llvm::omp::Directive directive,
 
 void OmpStructureChecker::CheckUsesAllocatorsSpec(
     const parser::OmpUsesAllocatorsClause::AllocatorSpec &spec) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   bool isLegacySyntax{std::get<bool>(spec.t)};
 
   // The traits of the deprecated syntax are stored as a traits-array modifier,
@@ -5756,7 +5756,7 @@ void OmpStructureChecker::CheckUsesAllocatorsTraits(
     return;
   }
   const Symbol &ultimate{symbol->GetUltimate()};
-  if (context_.langOptions().getOpenMP() < 60 &&
+  if (context_.langOptions().getOpenMPVersion() < 60 &&
       &ultimate.owner() !=
           &GetScopingUnit(context_.FindScope(GetContext().directiveSource))) {
     context_.Say(traitsSource,
@@ -5788,7 +5788,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::UsesAllocators &x) {
     return;
   }
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   const std::list<parser::OmpUsesAllocatorsClause::AllocatorSpec> &specs{x.v.v};
 
   // Classify by the syntax each specification was written in, which the parse
@@ -5904,7 +5904,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::From &x) {
   }
 
   auto &modifiers{OmpGetModifiers(x.v)};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   if (auto *iter{OmpGetUniqueModifier<parser::OmpIterator>(modifiers)}) {
     CheckIteratorModifier(*iter);
@@ -5927,7 +5927,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::To &x) {
   }
 
   auto &modifiers{OmpGetModifiers(x.v)};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   // The "to" clause is only allowed on "declare target" (pre-5.1), and
   // "target update". In the former case it can take an extended list item,
@@ -5956,7 +5956,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::To &x) {
 }
 
 void OmpStructureChecker::Enter(const parser::OmpClause::OmpxBare &x) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  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".
@@ -5968,13 +5968,13 @@ void OmpStructureChecker::Enter(const parser::OmpClause::OmpxBare &x) {
 }
 
 llvm::StringRef OmpStructureChecker::getClauseName(llvm::omp::Clause clause) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   return llvm::omp::getOpenMPClauseName(clause, version);
 }
 
 llvm::StringRef OmpStructureChecker::getDirectiveName(
     llvm::omp::Directive directive) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   return llvm::omp::getOpenMPDirectiveName(directive, version);
 }
 
@@ -6001,7 +6001,7 @@ void OmpStructureChecker::CheckDependList(const parser::DataRef &d) {
 void OmpStructureChecker::CheckArraySection(
     const parser::ArrayElement &arrayElement, const parser::Name &name,
     const llvm::omp::Clause clause) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   // Sometimes substring operations are incorrectly parsed as array accesses.
   // Detect this by looking for array accesses on character variables which are
   // not arrays.
@@ -6103,7 +6103,7 @@ void OmpStructureChecker::CheckLastPartRefForArraySection(
 
 void OmpStructureChecker::CheckIntentInPointer(
     SymbolSourceMap &symbols, llvm::omp::Clause clauseId) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   for (auto &[symbol, source] : symbols) {
     if (IsPointer(*symbol) && IsIntentIn(*symbol)) {
       context_.Say(source,
@@ -6115,7 +6115,7 @@ void OmpStructureChecker::CheckIntentInPointer(
 
 void OmpStructureChecker::CheckProcedurePointer(
     SymbolSourceMap &symbols, llvm::omp::Clause clause) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   for (const auto &[symbol, source] : symbols) {
     if (IsProcedurePointer(*symbol)) {
       context_.Say(source,
@@ -6165,7 +6165,7 @@ void OmpStructureChecker::GetSymbolsInObjectList(
 
 void OmpStructureChecker::CheckDefinableObjects(
     SymbolSourceMap &symbols, const llvm::omp::Clause clause) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   for (auto &[symbol, source] : symbols) {
     if (!IsVariableListItem(*symbol)) {
       continue;
@@ -6184,7 +6184,7 @@ void OmpStructureChecker::CheckDefinableObjects(
 void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt(
     SymbolSourceMap &currSymbols, DirectivesClauseTriple &dirClauseTriple,
     const llvm::omp::Clause currClause) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   SymbolSourceMap enclosingSymbols;
   auto range{dirClauseTriple.equal_range(GetContext().directive)};
   for (auto dirIter{range.first}; dirIter != range.second; ++dirIter) {
@@ -6310,7 +6310,7 @@ void OmpStructureChecker::CheckWorkshareBlockStmts(
 
 void OmpStructureChecker::CheckWorkdistributeBlockStmts(
     const parser::Block &block, parser::CharBlock source) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::Version since(60);
   if (version < since)
     context_.Say(source,
@@ -6388,7 +6388,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::DynamicAllocators &x) {
 void OmpStructureChecker::Enter(const parser::OmpClause::ReverseOffload &x) {
   CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_reverse_offload);
   if (IsAllowedClause(llvm::omp::Clause::OMPC_reverse_offload)) {
-    llvm::omp::Version version{context_.langOptions().getOpenMP()};
+    llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
     context_.Say(GetContext().clauseSource,
         "%s clause is not supported and will be ignored"_warn_en_US,
         GetUpperName(llvm::omp::Clause::OMPC_reverse_offload, version));
@@ -6685,7 +6685,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
 }
 
 void OmpStructureChecker::CheckAllowedRequiresClause(llvm::omp::Clause clause) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   if (clause != llvm::omp::Clause::OMPC_atomic_default_mem_order) {
     // Check that it does not appear after a device construct
diff --git a/flang/lib/Semantics/check-omp-variant.cpp b/flang/lib/Semantics/check-omp-variant.cpp
index 5573afc3ffb84..2e747408cec87 100644
--- a/flang/lib/Semantics/check-omp-variant.cpp
+++ b/flang/lib/Semantics/check-omp-variant.cpp
@@ -170,7 +170,7 @@ void OmpStructureChecker::CheckDefaultNoneInAssociatedLoop(
   }
 
   SymbolSourceMap explicitDSA;
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   for (const parser::OmpClause &clause : spec.Clauses().v) {
     if (llvm::omp::isDataSharingAttributeClause(clause.Id(), version)) {
       if (const parser::OmpObjectList *objects{
@@ -655,7 +655,7 @@ void OmpStructureChecker::CheckTraitDeviceNum(
 void OmpStructureChecker::CheckTraitRequires(
     const parser::OmpTraitSetSelector &traitSet,
     const parser::OmpTraitSelector &trait) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
   auto &properties{GetTraitPropertyList(trait)};
 
@@ -680,7 +680,7 @@ void OmpStructureChecker::CheckTraitRequires(
 void OmpStructureChecker::CheckTraitSimd(
     const parser::OmpTraitSetSelector &traitSet,
     const parser::OmpTraitSelector &trait) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   auto &traitName{std::get<parser::OmpTraitSelectorName>(trait.t)};
   auto &properties{GetTraitPropertyList(trait)};
 
@@ -719,7 +719,7 @@ void OmpStructureChecker::Enter(const parser::OmpDirectiveSpecification &x) {
   if (const parser::OpenMPConstruct *meta{GetCurrentConstruct()}) {
     if (parser::Unwrap<parser::OmpDelimitedMetadirectiveDirective>(meta->u)) {
       checkDefaultNoneInAssociatedLoop = false;
-      llvm::omp::Version version{context_.langOptions().getOpenMP()};
+      llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
       switch (llvm::omp::getDirectiveAssociation(dirId)) {
       case llvm::omp::Association::Block:
       case llvm::omp::Association::LoopNest:
@@ -786,7 +786,7 @@ void OmpStructureChecker::Enter(const parser::ExecutionPartConstruct &x) {
   std::vector<MetadirectiveLoopVariant> variants;
   variants.swap(metadirectiveLoopVariants_);
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   LoopSequence sequence(x, version, /*allowAllLoops=*/true, &context_);
   const parser::DoConstruct &rootLoop{*parser::Unwrap<parser::DoConstruct>(x)};
   const auto &[haveSemantic, havePerfect]{sequence.depth()};
diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp
index b6c6a36575562..87f7973f7e183 100644
--- a/flang/lib/Semantics/mod-file.cpp
+++ b/flang/lib/Semantics/mod-file.cpp
@@ -382,7 +382,7 @@ static const WithOmpDeclarative *GetOmpDeclarative(const Symbol &symbol) {
 
 static void PutOpenMPRequirements(
     llvm::raw_ostream &os, const Symbol &symbol, SemanticsContext &semaCtx) {
-  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
 
   if (const auto *decls{GetOmpDeclarative(symbol)}) {
     if (const llvm::omp::ClauseSet &reqs{decls->ompRequires()}; reqs.count()) {
@@ -397,7 +397,7 @@ static void PutOpenMPRequirements(
 
 static void PutOpenMPDeclarativeDirectives(llvm::raw_ostream &os,
     const SymbolVector &symbols, SemanticsContext &semaCtx) {
-  llvm::omp::Version version{semaCtx.langOptions().getOpenMP()};
+  llvm::omp::Version version{semaCtx.langOptions().getOpenMPVersion()};
 
   for (const Symbol &symbol : symbols) {
     if (const auto *decls{GetOmpDeclarative(symbol)}) {
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index d7f7aa8e84685..4e3f14e6aa58f 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -915,7 +915,7 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
   }
 
   void Post(const parser::OmpMapClause &x) {
-    llvm::omp::Version version{context_.langOptions().getOpenMP()};
+    llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
     std::optional<Symbol::Flag> ompFlag;
 
     auto &mods{OmpGetModifiers(x)};
@@ -2136,7 +2136,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
 
 void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct(
     const parser::Name &iv) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   // Find the parallel, teams or task generating construct enclosing the
   // sequential loop.
   auto targetIt{dirContext_.rbegin()};
@@ -2227,7 +2227,7 @@ bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
 void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex(
     const parser::OpenMPLoopConstruct &x) {
   const parser::OmpDirectiveSpecification &spec{x.BeginDir()};
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
 
   auto [depth, _]{
       omp::GetAffectedNestDepthWithReason(spec, version, &context_)};
@@ -2283,7 +2283,7 @@ bool OmpAttributeVisitor::Pre(const parser::OmpGroupprivateDirective &x) {
     device = parser::UnwrapRef<common::OmpDeviceType>(*devClause);
   }
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::ClauseSet clauses{llvm::omp::Clause::OMPC_device_type};
   for (const parser::OmpArgument &arg : x.v.Arguments().v) {
     if (const parser::OmpObject *object{parser::omp::GetArgumentObject(arg)}) {
@@ -2337,7 +2337,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) {
 bool OmpAttributeVisitor::Pre(const parser::OmpDeclareTargetDirective &x) {
   PushContext(x.source, llvm::omp::Directive::OMPD_declare_target);
 
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   std::map<const Symbol *, WithOmpDeclarative> details;
   std::optional<common::OmpDeviceType> device;
 
@@ -3045,7 +3045,7 @@ static bool SymbolOrEquivalentIsInNamelist(const Symbol &symbol) {
 
 void OmpAttributeVisitor::ResolveOmpDesignator(
     const parser::Designator &designator, Symbol::Flag ompFlag) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   llvm::omp::Directive directive{GetContext().directive};
 
   const auto *name{parser::GetDesignatorNameIfDataRef(designator)};
@@ -3451,7 +3451,7 @@ void OmpAttributeVisitor::CheckObjectIsPrivatizable(
 void OmpAttributeVisitor::AddOmpRequiresToScope(Scope &scope,
     const llvm::omp::ClauseSet &reqs,
     const std::optional<common::OmpMemoryOrderType> &memOrder) {
-  llvm::omp::Version version{context_.langOptions().getOpenMP()};
+  llvm::omp::Version version{context_.langOptions().getOpenMPVersion()};
   const Scope &programUnit{omp::GetProgramUnit(scope)};
 
   if (auto *symbol{const_cast<Symbol *>(programUnit.symbol())}) {

>From b2ad33483ddd7dbf980e434053c9833db475bb76 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Tue, 1 Sep 2026 09:09:02 -0500
Subject: [PATCH 3/3] Better handling of missing version attribute

---
 flang/lib/Lower/OpenMP/Decomposer.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/flang/lib/Lower/OpenMP/Decomposer.cpp b/flang/lib/Lower/OpenMP/Decomposer.cpp
index 8ebd9eb738069..14c99364cf3a6 100644
--- a/flang/lib/Lower/OpenMP/Decomposer.cpp
+++ b/flang/lib/Lower/OpenMP/Decomposer.cpp
@@ -42,7 +42,9 @@ struct ConstructDecomposition {
                          llvm::omp::Directive compound,
                          const List<Clause> &clauses)
       : semaCtx(semaCtx), mod(modOp), eval(ev) {
-    llvm::omp::Version version(mlir::omp::getOpenMPVersionAttribute(modOp));
+    int64_t verAttr = mlir::omp::getOpenMPVersionAttribute(modOp);
+    // verAttr will be -1 if the module attribute is missing.
+    llvm::omp::Version version(std::max<int64_t>(verAttr, 0));
     tomp::ConstructDecompositionT decompose(version, *this, compound,
                                             llvm::ArrayRef(clauses));
     output = std::move(decompose.output);



More information about the llvm-branch-commits mailing list