[llvm-branch-commits] [flang] [llvm] [Flang] Introduce KindsEnum for strong typing (PR #217828)

Michael Kruse via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Aug 21 00:01:48 PDT 2026


https://github.com/Meinersbur created https://github.com/llvm/llvm-project/pull/217828

Proposed here: https://github.com/llvm/llvm-project/pull/206907#discussion_r3783309935

>From 3aa2a68d9289f4d6f48891f334e60b136edb92d0 Mon Sep 17 00:00:00 2001
From: Michael Kruse <llvm-project at meinersbur.de>
Date: Wed, 19 Aug 2026 21:34:23 +0200
Subject: [PATCH] Conversion to KindsEnum

---
 flang-rt/lib/runtime/descriptor.cpp           |   3 +-
 flang-rt/lib/runtime/iso_fortran_env_impl.cpp |  20 +-
 flang/include/flang/Common/type-kinds.h       | 114 ++++++--
 flang/include/flang/Evaluate/call.h           |  16 +-
 .../include/flang/Evaluate/character-value.h  |  36 +--
 flang/include/flang/Evaluate/common.h         |  11 +
 flang/include/flang/Evaluate/complex-value.h  |  19 +-
 flang/include/flang/Evaluate/constant.h       |  50 ++--
 flang/include/flang/Evaluate/expression.h     |  97 +++----
 .../include/flang/Evaluate/fold-designator.h  |   9 +-
 flang/include/flang/Evaluate/initial-image.h  |   6 +-
 flang/include/flang/Evaluate/integer-value.h  |  44 ++--
 flang/include/flang/Evaluate/logical-value.h  |  27 +-
 flang/include/flang/Evaluate/real-value.h     |  57 ++--
 flang/include/flang/Evaluate/rewrite.h        |   2 +-
 flang/include/flang/Evaluate/shape.h          |   2 +-
 flang/include/flang/Evaluate/target.h         |  29 +-
 flang/include/flang/Evaluate/tools.h          |  64 ++---
 flang/include/flang/Evaluate/type.h           | 102 ++++----
 .../include/flang/Evaluate/typekind-traits.h  |  64 +++--
 flang/include/flang/Evaluate/variable.h       |  30 +--
 flang/include/flang/Frontend/TargetOptions.h  |   5 +-
 flang/include/flang/Lower/Mangler.h           |   6 +-
 flang/include/flang/Lower/Support/Utils.h     |   4 +-
 flang/include/flang/Semantics/expression.h    |  14 +-
 flang/include/flang/Semantics/semantics.h     |  13 +-
 flang/include/flang/Semantics/type.h          |   3 +-
 flang/include/flang/Support/default-kinds.h   |  50 ++--
 flang/include/flang/Tools/TargetSetup.h       |  30 ++-
 flang/lib/Evaluate/character-value-impl.cpp   |  26 +-
 flang/lib/Evaluate/character-value-impl.h     |  35 +--
 flang/lib/Evaluate/character-value.cpp        |  22 +-
 flang/lib/Evaluate/character.h                |  25 +-
 flang/lib/Evaluate/characteristics.cpp        |   4 +-
 flang/lib/Evaluate/check-expression.cpp       |  19 +-
 flang/lib/Evaluate/complex-value.cpp          |   8 +-
 flang/lib/Evaluate/constant.cpp               |  23 +-
 flang/lib/Evaluate/expression.cpp             |  26 +-
 flang/lib/Evaluate/fold-character.cpp         |  19 +-
 flang/lib/Evaluate/fold-complex.cpp           |   9 +-
 flang/lib/Evaluate/fold-designator.cpp        |   9 +-
 flang/lib/Evaluate/fold-implementation.h      | 183 ++++++-------
 flang/lib/Evaluate/fold-integer.cpp           | 122 +++++----
 flang/lib/Evaluate/fold-logical.cpp           |  64 +++--
 flang/lib/Evaluate/fold-matmul.h              |   2 +-
 flang/lib/Evaluate/fold-real.cpp              |  52 ++--
 flang/lib/Evaluate/fold-reduction.h           |  20 +-
 flang/lib/Evaluate/formatting.cpp             |  11 +-
 flang/lib/Evaluate/host.h                     |  14 +-
 flang/lib/Evaluate/initial-image.cpp          |   7 +-
 flang/lib/Evaluate/int-power.h                |   6 +-
 flang/lib/Evaluate/integer-value-impl.cpp     |   6 +-
 flang/lib/Evaluate/integer-value-impl.h       |   1 +
 flang/lib/Evaluate/integer-value.cpp          |  49 ++--
 flang/lib/Evaluate/intrinsics-library.cpp     |  25 +-
 flang/lib/Evaluate/intrinsics.cpp             |  60 +++--
 flang/lib/Evaluate/real-value-impl.cpp        |  31 ++-
 flang/lib/Evaluate/real-value-impl.h          |  14 +-
 flang/lib/Evaluate/real-value.cpp             |  79 +++---
 flang/lib/Evaluate/shape.cpp                  |   4 +-
 flang/lib/Evaluate/target.cpp                 |  85 +++---
 flang/lib/Evaluate/tools.cpp                  | 116 ++++----
 flang/lib/Evaluate/type.cpp                   |  70 +++--
 flang/lib/Evaluate/variable.cpp               |   7 +-
 flang/lib/Frontend/CompilerInvocation.cpp     |  50 ++--
 flang/lib/Lower/Bridge.cpp                    |   3 +-
 flang/lib/Lower/CallInterface.cpp             |  28 +-
 flang/lib/Lower/ConvertArrayConstructor.cpp   |   4 +-
 flang/lib/Lower/ConvertConstant.cpp           |  18 +-
 flang/lib/Lower/ConvertExprToHLFIR.cpp        |  25 +-
 flang/lib/Lower/ConvertType.cpp               |  23 +-
 flang/lib/Lower/IO.cpp                        |   7 +-
 flang/lib/Lower/OpenMP/Atomic.cpp             |   1 -
 flang/lib/Lower/Support/Utils.cpp             |  28 +-
 flang/lib/Semantics/check-allocate.cpp        |   4 +-
 flang/lib/Semantics/check-call.cpp            |  22 +-
 flang/lib/Semantics/check-case.cpp            |  22 +-
 flang/lib/Semantics/check-data.cpp            |   2 +-
 flang/lib/Semantics/check-declarations.cpp    |  14 +-
 flang/lib/Semantics/check-io.cpp              |  16 +-
 flang/lib/Semantics/check-omp-atomic.cpp      |   6 +-
 flang/lib/Semantics/check-omp-structure.cpp   |   2 +-
 flang/lib/Semantics/compute-offsets.cpp       |   3 +-
 flang/lib/Semantics/data-to-inits.cpp         |   8 +-
 flang/lib/Semantics/expression.cpp            | 162 +++++++-----
 flang/lib/Semantics/resolve-names-utils.cpp   |  14 +-
 flang/lib/Semantics/resolve-names.cpp         |  30 ++-
 flang/lib/Semantics/runtime-type-info.cpp     |  80 +++---
 flang/lib/Semantics/semantics.cpp             |  10 +-
 flang/lib/Semantics/type.cpp                  |  15 +-
 flang/lib/Support/default-kinds.cpp           |  21 +-
 flang/tools/f18-parse-demo/f18-parse-demo.cpp |   6 +-
 .../unittests/Evaluate/CharacterValueTest.cpp | 112 ++++----
 flang/unittests/Evaluate/ComplexValueTest.cpp | 114 ++++----
 flang/unittests/Evaluate/IntegerValueTest.cpp | 247 +++++++++---------
 flang/unittests/Evaluate/LogicalValueTest.cpp |  64 ++---
 flang/unittests/Evaluate/RealValueTest.cpp    | 238 +++++++++--------
 flang/unittests/Evaluate/expression.cpp       |   5 +-
 flang/unittests/Evaluate/folding.cpp          |  10 +-
 flang/unittests/Evaluate/intrinsics.cpp       | 135 +++++-----
 flang/unittests/Evaluate/logical.cpp          |  13 +-
 101 files changed, 2056 insertions(+), 1686 deletions(-)

diff --git a/flang-rt/lib/runtime/descriptor.cpp b/flang-rt/lib/runtime/descriptor.cpp
index 4aacbf6ae7949..8e142e2c3e443 100644
--- a/flang-rt/lib/runtime/descriptor.cpp
+++ b/flang-rt/lib/runtime/descriptor.cpp
@@ -66,7 +66,8 @@ RT_API_ATTRS void Descriptor::Establish(TypeCode t, std::size_t elementBytes,
 
 RT_API_ATTRS std::size_t Descriptor::BytesFor(TypeCategory category, int kind) {
   Terminator terminator{__FILE__, __LINE__};
-  int bytes{common::TypeSizeInBytes(category, kind)};
+  int bytes{static_cast<int>(
+      common::TypeSizeInBytes(category, static_cast<common::KindsEnum>(kind)))};
   RUNTIME_CHECK(terminator, bytes > 0);
   return bytes;
 }
diff --git a/flang-rt/lib/runtime/iso_fortran_env_impl.cpp b/flang-rt/lib/runtime/iso_fortran_env_impl.cpp
index 70230d0f16c7a..2572b94a11896 100644
--- a/flang-rt/lib/runtime/iso_fortran_env_impl.cpp
+++ b/flang-rt/lib/runtime/iso_fortran_env_impl.cpp
@@ -230,11 +230,23 @@ extern const std::int32_t FORTRAN_NAMED_CONST(real80){real80};
 extern const std::int32_t FORTRAN_NAMED_CONST(real64x2){real64x2};
 extern const std::int32_t FORTRAN_NAMED_CONST(real128){real128};
 
-extern const std::int32_t FORTRAN_NAMED_CONST(
-    __builtin_integer_kinds)[] = FORTRAN_INTEGER_KINDS;
+// FORTRAN_INTEGER_KINDS/FORTRAN_LOGICAL_KINDS expand to lists of
+// Fortran::common::KindsEnum values; cast each to std::int32_t for this
+// externally-visible ABI array.
+extern const std::int32_t FORTRAN_NAMED_CONST(__builtin_integer_kinds)[]{
+    static_cast<std::int32_t>(Fortran::common::KindsEnum::Kind1),
+    static_cast<std::int32_t>(Fortran::common::KindsEnum::Kind2),
+    static_cast<std::int32_t>(Fortran::common::KindsEnum::Kind4),
+    static_cast<std::int32_t>(Fortran::common::KindsEnum::Kind8),
+    static_cast<std::int32_t>(Fortran::common::KindsEnum::Kind16),
+};
 
-extern const std::int32_t FORTRAN_NAMED_CONST(
-    __builtin_logical_kinds)[] = FORTRAN_LOGICAL_KINDS;
+extern const std::int32_t FORTRAN_NAMED_CONST(__builtin_logical_kinds)[]{
+    static_cast<std::int32_t>(Fortran::common::KindsEnum::Kind1),
+    static_cast<std::int32_t>(Fortran::common::KindsEnum::Kind2),
+    static_cast<std::int32_t>(Fortran::common::KindsEnum::Kind4),
+    static_cast<std::int32_t>(Fortran::common::KindsEnum::Kind8),
+};
 
 // Target-filtered subset of FORTRAN_REAL_KINDS from type-kinds.h.
 extern const std::int32_t FORTRAN_NAMED_CONST(__builtin_real_kinds)[]{
diff --git a/flang/include/flang/Common/type-kinds.h b/flang/include/flang/Common/type-kinds.h
index 926cce6f25909..fdee0279046dd 100644
--- a/flang/include/flang/Common/type-kinds.h
+++ b/flang/include/flang/Common/type-kinds.h
@@ -14,53 +14,126 @@
 #include <cinttypes>
 
 // Canonical lists of supported Fortran kinds for each intrinsic type.
-#define FORTRAN_INTEGER_KINDS {1, 2, 4, 8, 16}
+#define FORTRAN_INTEGER_KINDS \
+  {Fortran::common::KindsEnum::Kind1, Fortran::common::KindsEnum::Kind2, \
+      Fortran::common::KindsEnum::Kind4, Fortran::common::KindsEnum::Kind8, \
+      Fortran::common::KindsEnum::Kind16}
 #define FORTRAN_UNSIGNED_KINDS FORTRAN_INTEGER_KINDS
-#define FORTRAN_REAL_KINDS {2, 3, 4, 8, 10, 16}
-#define FORTRAN_LOGICAL_KINDS {1, 2, 4, 8}
-#define FORTRAN_CHARACTER_KINDS {1, 2, 4}
+#define FORTRAN_REAL_KINDS \
+  {Fortran::common::KindsEnum::Kind2, Fortran::common::KindsEnum::Kind3, \
+      Fortran::common::KindsEnum::Kind4, Fortran::common::KindsEnum::Kind8, \
+      Fortran::common::KindsEnum::Kind10, Fortran::common::KindsEnum::Kind16}
+#define FORTRAN_LOGICAL_KINDS \
+  {Fortran::common::KindsEnum::Kind1, Fortran::common::KindsEnum::Kind2, \
+      Fortran::common::KindsEnum::Kind4, Fortran::common::KindsEnum::Kind8}
+#define FORTRAN_CHARACTER_KINDS \
+  {Fortran::common::KindsEnum::Kind1, Fortran::common::KindsEnum::Kind2, \
+      Fortran::common::KindsEnum::Kind4}
 
 namespace Fortran::common {
 
-static constexpr int IntegerKinds[] FORTRAN_INTEGER_KINDS;
-static constexpr int UnsignedKinds[] FORTRAN_UNSIGNED_KINDS;
-static constexpr int RealKinds[] FORTRAN_REAL_KINDS;
-static constexpr int LogicalKinds[] FORTRAN_LOGICAL_KINDS;
-static constexpr int CharacterKinds[] FORTRAN_CHARACTER_KINDS;
+/// All possible kinds. Used to strongly type Fortran KIND values.
+///
+/// Cannot use ENUM_CLASS here because it does not support assigned values.
+enum class KindsEnum : int {
+  InvalidKind = -1,
+  NoKind = 0,
+
+  Kind1 = 1,
+  Kind2 = 2,
+  Kind3 = 3,
+  Kind4 = 4,
+  Kind8 = 8,
+  Kind10 = 10,
+  Kind16 = 16,
+
+  // SpecialKinds
+  AssumedTypeKind = -3,
+  ClassKind = -2,
+  TypelessKind = -1,
+};
+
+inline constexpr bool operator<(KindsEnum a, KindsEnum b) {
+  return static_cast<int>(a) < static_cast<int>(b);
+}
+inline constexpr bool operator<=(KindsEnum a, KindsEnum b) {
+  return static_cast<int>(a) <= static_cast<int>(b);
+}
+inline constexpr bool operator>(KindsEnum a, KindsEnum b) {
+  return static_cast<int>(a) > static_cast<int>(b);
+}
+inline constexpr bool operator>=(KindsEnum a, KindsEnum b) {
+  return static_cast<int>(a) >= static_cast<int>(b);
+}
+
+inline constexpr KindsEnum IntegerKinds[] FORTRAN_INTEGER_KINDS;
+inline constexpr KindsEnum UnsignedKinds[] FORTRAN_UNSIGNED_KINDS;
+inline constexpr KindsEnum RealKinds[] FORTRAN_REAL_KINDS;
+inline constexpr KindsEnum LogicalKinds[] FORTRAN_LOGICAL_KINDS;
+inline constexpr KindsEnum CharacterKinds[] FORTRAN_CHARACTER_KINDS;
+
+/// Iterable lists of valid kinds for each TypeCategory for use by SearchTypes.
+template <TypeCategory CAT> struct KindsByType;
+template <> struct KindsByType<TypeCategory::Integer> {
+  static inline constexpr KindsEnum kinds[] FORTRAN_INTEGER_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Unsigned> {
+  static inline constexpr KindsEnum kinds[] FORTRAN_UNSIGNED_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Real> {
+  static inline constexpr KindsEnum kinds[] FORTRAN_REAL_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Complex> {
+  static inline constexpr KindsEnum kinds[] FORTRAN_REAL_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Logical> {
+  static inline constexpr KindsEnum kinds[] FORTRAN_LOGICAL_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Character> {
+  static inline constexpr KindsEnum kinds[] FORTRAN_CHARACTER_KINDS;
+};
+template <> struct KindsByType<TypeCategory::Derived> {
+  static inline constexpr KindsEnum kinds[]{KindsEnum::NoKind};
+};
 
 static constexpr int maxKind{16};
 
 template <typename T, std::size_t N>
-static constexpr bool IsKindInList(const T (&kinds)[N], std::int64_t kind) {
+static constexpr bool IsKindInList(const T (&kinds)[N], KindsEnum kind) {
   for (std::size_t i{0}; i < N; ++i) {
-    if (kinds[i] == kind)
+    if (kinds[i] == static_cast<T>(kind))
       return true;
   }
   return false;
 }
 
+inline constexpr bool IsValidKind(KindsEnum kind) {
+  int val{static_cast<int>(kind)};
+  return 1 <= val && val <= maxKind;
+}
+
 // A predicate that is true when a kind value is a kind that could possibly
 // be supported for an intrinsic type category on some target instruction
 // set architecture.
 static constexpr bool IsValidKindOfIntrinsicType(
-    TypeCategory category, std::int64_t kind) {
+    TypeCategory category, KindsEnum kind) {
   switch (category) {
   case TypeCategory::Integer:
   case TypeCategory::Unsigned: {
-    constexpr int kinds[] = FORTRAN_INTEGER_KINDS;
+    constexpr auto &kinds = KindsByType<TypeCategory::Integer>::kinds;
     return IsKindInList(kinds, kind);
   }
   case TypeCategory::Real:
   case TypeCategory::Complex: {
-    constexpr int kinds[] = FORTRAN_REAL_KINDS;
+    constexpr auto &kinds = KindsByType<TypeCategory::Real>::kinds;
     return IsKindInList(kinds, kind);
   }
   case TypeCategory::Character: {
-    constexpr int kinds[] = FORTRAN_CHARACTER_KINDS;
+    constexpr auto &kinds = KindsByType<TypeCategory::Character>::kinds;
     return IsKindInList(kinds, kind);
   }
   case TypeCategory::Logical: {
-    constexpr int kinds[] = FORTRAN_LOGICAL_KINDS;
+    constexpr auto &kinds = KindsByType<TypeCategory::Logical>::kinds;
     return IsKindInList(kinds, kind);
   }
   default:
@@ -68,10 +141,11 @@ static constexpr bool IsValidKindOfIntrinsicType(
   }
 }
 
-static constexpr int TypeSizeInBytes(TypeCategory category, std::int64_t kind) {
+static constexpr KindsEnum TypeSizeInBytes(
+    TypeCategory category, KindsEnum kind) {
   if (IsValidKindOfIntrinsicType(category, kind)) {
     if (category == TypeCategory::Real || category == TypeCategory::Complex) {
-      int precision{PrecisionOfRealKind(kind)};
+      int precision{PrecisionOfRealKind(static_cast<int>(kind))};
       int bits{BitsForBinaryPrecision(precision)};
       if (bits == 80) { // x87 is stored in 16-byte containers
         bits = 128;
@@ -79,12 +153,12 @@ static constexpr int TypeSizeInBytes(TypeCategory category, std::int64_t kind) {
       if (category == TypeCategory::Complex) {
         bits *= 2;
       }
-      return bits >> 3;
+      return static_cast<KindsEnum>(bits >> 3);
     } else {
       return kind;
     }
   } else {
-    return -1;
+    return KindsEnum::InvalidKind;
   }
 }
 
diff --git a/flang/include/flang/Evaluate/call.h b/flang/include/flang/Evaluate/call.h
index 5001fbc85920e..99663379c03f7 100644
--- a/flang/include/flang/Evaluate/call.h
+++ b/flang/include/flang/Evaluate/call.h
@@ -296,9 +296,9 @@ struct SpecificIntrinsic {
 };
 
 struct ProcedureDesignator {
-  static int kind() {
+  static KindsEnum kind() {
     llvm_unreachable("This class has no kind");
-    return 0;
+    return NoKind;
   }
 
   EVALUATE_UNION_CLASS_BOILERPLATE(ProcedureDesignator)
@@ -338,9 +338,9 @@ using Chevrons = std::vector<Expr<SomeType>>;
 
 class ProcedureRef {
 public:
-  static int kind() {
+  static KindsEnum kind() {
     llvm_unreachable("This class has no kind");
-    return 0;
+    return NoKind;
   }
 
   CLASS_BOILERPLATE(ProcedureRef)
@@ -403,14 +403,14 @@ class ProcedureRef {
 
 template <typename A> class FunctionRef : public ProcedureRef {
 public:
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
   using Result = A;
   CLASS_BOILERPLATE(FunctionRef)
 
-  explicit FunctionRef(int kind, ProcedureRef &&pr)
+  explicit FunctionRef(KindsEnum kind, ProcedureRef &&pr)
       : ProcedureRef{std::move(pr)}, kind_{kind} {}
-  FunctionRef(int kind, ProcedureDesignator &&p, ActualArguments &&a)
+  FunctionRef(KindsEnum kind, ProcedureDesignator &&p, ActualArguments &&a)
       : ProcedureRef{std::move(p), std::move(a)}, kind_{kind} {}
 
   std::optional<DynamicType> GetType() const {
@@ -429,7 +429,7 @@ template <typename A> class FunctionRef : public ProcedureRef {
   }
 
 private:
-  int kind_;
+  KindsEnum kind_;
 };
 } // namespace Fortran::evaluate
 #endif // FORTRAN_EVALUATE_CALL_H_
diff --git a/flang/include/flang/Evaluate/character-value.h b/flang/include/flang/Evaluate/character-value.h
index 84fa95818c966..9d08e9fa71d00 100644
--- a/flang/include/flang/Evaluate/character-value.h
+++ b/flang/include/flang/Evaluate/character-value.h
@@ -9,6 +9,7 @@
 #ifndef FORTRAN_EVALUATE_CHARACTER_VALUE_H_
 #define FORTRAN_EVALUATE_CHARACTER_VALUE_H_
 
+#include "flang/Common/type-kinds.h"
 #include "flang/Evaluate/common.h"
 #include "flang/Evaluate/object-sizes.h"
 #include "flang/Evaluate/type.h"
@@ -21,6 +22,7 @@
 
 namespace Fortran::evaluate::value {
 class CharacterValueImpl;
+using common::KindsEnum;
 
 /// A character string with dynamic character representation with
 /// std::basic_string-like API.
@@ -47,18 +49,18 @@ class CharacterValue {
   /// operations are supported in this state.
   CharacterValue();
 
-  explicit CharacterValue(int kind, std::string s);
-  explicit CharacterValue(int kind, std::u16string s);
-  explicit CharacterValue(int kind, std::u32string s);
+  explicit CharacterValue(KindsEnum kind, std::string s);
+  explicit CharacterValue(KindsEnum kind, std::u16string s);
+  explicit CharacterValue(KindsEnum kind, std::u32string s);
 
   /// Fill constructor: create a string of n copies of the given character.
-  CharacterValue(int kind, std::size_t n, char32_t c);
+  CharacterValue(KindsEnum kind, std::size_t n, char32_t c);
 
   // Named ctors
-  static CharacterValue Zero(int kind);
+  static CharacterValue Zero(KindsEnum kind);
 
   static CharacterValue FromRawBytes(
-      int kind, const void *raw, size_t byteSize);
+      KindsEnum kind, const void *raw, size_t byteSize);
 
   void print(llvm::raw_ostream &os) const;
 
@@ -71,14 +73,14 @@ class CharacterValue {
   bool IsMonostate() const;
 
   /// The kind of the value currently stored.
-  int kind() const;
+  KindsEnum kind() const;
 
   bool empty() const;
   std::size_t size() const;
   std::size_t length() const { return size(); }
 
   /// Byte size of one character unit (1, 2, or 4).
-  std::size_t charSize() const { return kind(); }
+  std::size_t charSize() const { return static_cast<std::size_t>(kind()); }
 
   /// Number of bytes accessed by FromRawBytes/StoreRawBytes
   size_t bytesStored() const { return length() * charSize(); }
@@ -119,11 +121,11 @@ class CharacterValue {
   bool operator>=(const CharacterValue &y) const { return !(*this < y); }
   bool operator>(const CharacterValue &y) const { return y < *this; }
 
-  CharacterValue ToAscii(int kind) const;
+  CharacterValue ToAscii(KindsEnum kind) const;
 
   /// Assign n copies of the given character, fixing the kind from the char
   /// type.
-  void assign(int kind, std::size_t n, char32_t c);
+  void assign(KindsEnum kind, std::size_t n, char32_t c);
 
   /// Assign from a raw character pointer and length.
   void assign(const char *p, std::size_t n);
@@ -194,14 +196,14 @@ class CharacterValue {
   void StoreRawBytes(void *dst, size_t size, bool *changed = nullptr) const;
 
   template <typename F>
-  static auto withCharProto(int kind, F &&f)
+  static auto withCharProto(KindsEnum kind, F &&f)
       -> decltype(std::declval<F>()(std::declval<char>())) {
     switch (kind) {
-    case 1:
+    case KindsEnum::Kind1:
       return f(char{});
-    case 2:
+    case KindsEnum::Kind2:
       return f(char16_t{});
-    case 4:
+    case KindsEnum::Kind4:
       return f(char32_t{});
     default:
       llvm_unreachable("unsupported character kind/monostate");
@@ -210,11 +212,11 @@ class CharacterValue {
 
   template <typename F> decltype(auto) withStdString(F &&f) const {
     switch (kind()) {
-    case 1:
+    case KindsEnum::Kind1:
       return f(*AsStdString());
-    case 2:
+    case KindsEnum::Kind2:
       return f(*AsU16String());
-    case 4:
+    case KindsEnum::Kind4:
       return f(*AsU32String());
     default:
       llvm_unreachable("unsupported kind/monostate");
diff --git a/flang/include/flang/Evaluate/common.h b/flang/include/flang/Evaluate/common.h
index 7368a424b46b1..50e302414a586 100644
--- a/flang/include/flang/Evaluate/common.h
+++ b/flang/include/flang/Evaluate/common.h
@@ -15,6 +15,7 @@
 #include "flang/Common/indirection.h"
 #include "flang/Common/restorer.h"
 #include "flang/Common/target-rounding.h"
+#include "flang/Common/type-kinds.h"
 #include "flang/Parser/char-block.h"
 #include "flang/Parser/message.h"
 #include "flang/Support/FPMaxminBehavior.h"
@@ -26,6 +27,16 @@
 #include <set>
 #include <string>
 
+namespace llvm {
+class raw_ostream;
+}
+
+namespace Fortran::common {
+/// Declared here (rather than in flang/Common/type-kinds.h) because
+/// Fortran::common must not depend on LLVM; defined in lib/Evaluate/type.cpp.
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, KindsEnum);
+} // namespace Fortran::common
+
 namespace Fortran::semantics {
 class DerivedTypeSpec;
 }
diff --git a/flang/include/flang/Evaluate/complex-value.h b/flang/include/flang/Evaluate/complex-value.h
index eed00cc503962..7b854627c25fa 100644
--- a/flang/include/flang/Evaluate/complex-value.h
+++ b/flang/include/flang/Evaluate/complex-value.h
@@ -17,6 +17,7 @@ class raw_ostream;
 }
 
 namespace Fortran::evaluate::value {
+using common::KindsEnum;
 
 /// A complex floating-point value with dynamic precision.
 ///
@@ -40,22 +41,22 @@ class ComplexValue {
   explicit ComplexValue(const RealValue &r)
       : ComplexValue{r, RealValue::Zero(r.kind())} {}
 
-  ComplexValue(int kind, const RealValue &r) : ComplexValue{r} {
+  ComplexValue(KindsEnum kind, const RealValue &r) : ComplexValue{r} {
     CHECK(kind == r.kind());
   }
 
-  ComplexValue(int kind, const ComplexValue &v) : ComplexValue{v} {
+  ComplexValue(KindsEnum kind, const ComplexValue &v) : ComplexValue{v} {
     CHECK(kind == v.kind());
   }
 
-  ComplexValue(int kind, ComplexValue &&v) : ComplexValue{std::move(v)} {
+  ComplexValue(KindsEnum kind, ComplexValue &&v) : ComplexValue{std::move(v)} {
     CHECK(kind == v.kind());
   }
 
   /// Creates a complex value (+0.0 + +0.0i) of a given kind. This is
   /// different from the default-ctor which creates a "monostate" that
   /// represents zero of unknown kind.
-  static ComplexValue Zero(int kind) {
+  static ComplexValue Zero(KindsEnum kind) {
     RealValue zero{RealValue::Zero(kind)};
     return ComplexValue{zero, zero};
   }
@@ -74,7 +75,7 @@ class ComplexValue {
   }
 
   /// The kind of the value currently stored.
-  int kind() const {
+  KindsEnum kind() const {
     CHECK(re_.kind() == im_.kind());
     return re_.kind();
   }
@@ -83,7 +84,7 @@ class ComplexValue {
   std::size_t bytesStored() const {
     return re_.bytesStored() + im_.bytesStored();
   }
-  static constexpr std::size_t bytesStored(int kind) {
+  static constexpr std::size_t bytesStored(KindsEnum kind) {
     return 2 * RealValue::bytesStored(kind);
   }
 
@@ -118,7 +119,7 @@ class ComplexValue {
     return re_.IsSignalingNaN() || im_.IsSignalingNaN();
   }
 
-  static ValueWithRealFlags<ComplexValue> FromInteger(int kind,
+  static ValueWithRealFlags<ComplexValue> FromInteger(KindsEnum kind,
       const IntegerValue &n, bool isUnsigned = false,
       Rounding rounding = TargetCharacteristics::defaultRounding);
 
@@ -146,7 +147,7 @@ class ComplexValue {
     return ComplexValue{re_.FlushSubnormalToZero(), im_.FlushSubnormalToZero()};
   }
 
-  static ComplexValue NotANumber(int kind) {
+  static ComplexValue NotANumber(KindsEnum kind) {
     return {RealValue::NotANumber(kind), RealValue::NotANumber(kind)};
   }
 
@@ -157,7 +158,7 @@ class ComplexValue {
   void StoreRawBytes(void *dst, size_t size, bool *changed = nullptr) const;
 
   static ComplexValue FromRawBytes(
-      int kind, const void *raw, std::size_t expectedSize);
+      KindsEnum kind, const void *raw, std::size_t expectedSize);
 
   // TODO: unit testing
 
diff --git a/flang/include/flang/Evaluate/constant.h b/flang/include/flang/Evaluate/constant.h
index 530328e1802bb..b8976125e8b64 100644
--- a/flang/include/flang/Evaluate/constant.h
+++ b/flang/include/flang/Evaluate/constant.h
@@ -12,6 +12,7 @@
 #include "formatting.h"
 #include "type.h"
 #include "flang/Common/reference.h"
+#include "flang/Common/type-kinds.h"
 #include "flang/Support/default-kinds.h"
 #include <map>
 #include <vector>
@@ -110,47 +111,49 @@ class ConstantBase : public ConstantBounds {
   using Result = RESULT;
   using Element = ELEMENT;
 
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
   // Constructor for creating ConstantBase from an actual value (i.e.
   // literals, etc.)
   template <typename A>
-  ConstantBase(int kind, const A &x, Result res)
+  ConstantBase(KindsEnum kind, const A &x, Result res)
       : kind_{kind}, result_{res}, values_{A{kind, x}} {
     CHECK_KIND(kind, RESULT);
   }
-  ConstantBase(int kind, ELEMENT &&x)
+  ConstantBase(KindsEnum kind, ELEMENT &&x)
       : kind_{kind}, result_{Result{kind}}, values_{std::move(x)} {
     CHECK_KIND(kind, RESULT);
   }
 
   template <TypeCategory CAT>
-  ConstantBase(int kind, const SomeKind<CAT> &x)
+  ConstantBase(KindsEnum kind, const SomeKind<CAT> &x)
       : kind_{kind}, result_{Result{kind}}, values_{x} {
     CHECK_KIND(kind, RESULT);
   }
   template <TypeCategory CAT>
-  ConstantBase(int kind, SomeKind<CAT> &&x)
+  ConstantBase(KindsEnum kind, SomeKind<CAT> &&x)
       : kind_{kind}, result_{Result{kind}}, values_{std::move(x)} {
     CHECK_KIND(kind, RESULT);
   }
 
   template <typename A>
-  ConstantBase(int kind, const A &x)
+  ConstantBase(KindsEnum kind, const A &x)
       : kind_{kind}, result_{Result{kind}}, values_{A{kind, x}} {
     CHECK_KIND(kind, RESULT);
   }
-  ConstantBase(int kind, ELEMENT &&x, Result res)
+  ConstantBase(KindsEnum kind, ELEMENT &&x, Result res)
       : kind_{kind}, result_{res}, values_{std::move(x)} {
     CHECK_KIND(kind, RESULT);
   }
 
-  ConstantBase(int kind, std::vector<Element> &&x, ConstantSubscripts &&sh)
+  ConstantBase(
+      KindsEnum kind, std::vector<Element> &&x, ConstantSubscripts &&sh)
       : ConstantBase{kind, std::move(x), std::move(sh), Result{kind}} {}
   ConstantBase(
-      int kind, std::vector<Element> &&, ConstantSubscripts &&, Result);
+      KindsEnum kind, std::vector<Element> &&, ConstantSubscripts &&, Result);
   template <typename A, typename B, typename C>
-  ConstantBase(int kind, const std::map<A, B, C> &x, Result res = Result{})
+  ConstantBase(
+      KindsEnum kind, const std::map<A, B, C> &x, Result res = Result{})
       : kind_{kind}, result_{res}, values_{x} {}
 
   DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ConstantBase)
@@ -172,7 +175,7 @@ class ConstantBase : public ConstantBounds {
   std::size_t CopyFrom(const ConstantBase &source, std::size_t count,
       ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
 
-  int kind_;
+  KindsEnum kind_;
   Result result_; // usually empty except for Real & Complex
   std::vector<Element> values_;
 };
@@ -209,12 +212,12 @@ class Constant<Type<TypeCategory::Character>> : public ConstantBounds {
   using Result = Type<TypeCategory::Character>;
   using Element = Scalar<Result>;
 
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
   CLASS_BOILERPLATE(Constant)
-  explicit Constant(int kind, const Scalar<Result> &);
-  explicit Constant(int kind, Scalar<Result> &&);
-  Constant(int kind, ConstantSubscript length, std::vector<Element> &&,
+  explicit Constant(KindsEnum kind, const Scalar<Result> &);
+  explicit Constant(KindsEnum kind, Scalar<Result> &&);
+  Constant(KindsEnum kind, ConstantSubscript length, std::vector<Element> &&,
       ConstantSubscripts &&);
   ~Constant();
 
@@ -252,7 +255,7 @@ class Constant<Type<TypeCategory::Character>> : public ConstantBounds {
       ConstantSubscripts &resultSubscripts, const std::vector<int> *dimOrder);
 
 private:
-  int kind_;
+  KindsEnum kind_;
   Scalar<Result> values_; // one contiguous string
   ConstantSubscript length_;
   bool wasHollerith_{false};
@@ -275,11 +278,11 @@ class Constant<SomeDerived>
 
   Constant(const StructureConstructor &);
   Constant(StructureConstructor &&);
-  Constant(int kind, const StructureConstructor &v) : Constant(v) {
-    CHECK(kind == 0);
+  Constant(KindsEnum kind, const StructureConstructor &v) : Constant(v) {
+    CHECK(kind == NoKind);
   }
-  Constant(int kind, StructureConstructor &&v) : Constant(std::move(v)) {
-    CHECK(kind == 0);
+  Constant(KindsEnum kind, StructureConstructor &&v) : Constant(std::move(v)) {
+    CHECK(kind == NoKind);
   }
   Constant(const semantics::DerivedTypeSpec &,
       std::vector<StructureConstructorValues> &&, ConstantSubscripts &&);
@@ -313,20 +316,21 @@ inline Constant<LogicalResult> MakeLogicalResultConstant(bool v) {
 template <typename T, typename CharT,
     typename =
         std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Character>>>>
-inline Constant<T> MakeConstant(int kind, const std::basic_string<CharT> &v) {
+inline Constant<T> MakeConstant(
+    KindsEnum kind, const std::basic_string<CharT> &v) {
   return Constant<T>{kind, value::CharacterValue{kind, v}};
 }
 
 template <typename T, typename CharT,
     typename =
         std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Character>>>>
-inline Constant<T> MakeConstant(int kind, std::basic_string<CharT> &&v) {
+inline Constant<T> MakeConstant(KindsEnum kind, std::basic_string<CharT> &&v) {
   return Constant<T>{kind, value::CharacterValue{kind, std::move(v)}};
 }
 
 template <typename T,
     typename = std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Integer>>>>
-inline Constant<T> MakeConstant(int kind, int64_t v) {
+inline Constant<T> MakeConstant(KindsEnum kind, int64_t v) {
   return Constant<T>{kind, value::IntegerValue{kind, v}};
 }
 
diff --git a/flang/include/flang/Evaluate/expression.h b/flang/include/flang/Evaluate/expression.h
index be7fa33738257..e4bf9dd8b5e2a 100644
--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -60,14 +60,14 @@ using common::RelationalOperator;
 // maps to some instantiation of Type<CATEGORY, KIND>, SomeKind<CATEGORY>,
 // or SomeType.  (Exception: BOZ literal constants in generic Expr<SomeType>.)
 template <typename A> using ResultType = typename std::decay_t<A>::Result;
-template <typename A> constexpr int ResultKind = std::decay_t<A>::kind();
+template <typename A> constexpr KindsEnum ResultKind = std::decay_t<A>::kind();
 
 // Common Expr<> behaviors: every Expr<T> derives from ExpressionBase<T>.
 template <typename RESULT> class ExpressionBase {
 public:
   using Result = RESULT;
 
-  int kind() const;
+  KindsEnum kind() const;
 
 private:
   using Derived = Expr<Result>;
@@ -128,7 +128,7 @@ class Operation {
       (operands == 1 && std::is_same_v<Result, SomeDerived>));
   template <int J> using Operand = std::tuple_element_t<J, OperandTypes>;
 
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
   // Unary operations wrap a single Expr with a CopyableIndirection.
   // Binary operations wrap a tuple of CopyableIndirections to Exprs.
@@ -139,11 +139,11 @@ class Operation {
 
 public:
   CLASS_BOILERPLATE(Operation)
-  explicit Operation(int kind, const Expr<OPERANDS> &...x)
+  explicit Operation(KindsEnum kind, const Expr<OPERANDS> &...x)
       : kind_{kind}, operand_{x...} {
     CHECK_KIND(kind, RESULT);
   }
-  explicit Operation(int kind, Expr<OPERANDS> &&...x)
+  explicit Operation(KindsEnum kind, Expr<OPERANDS> &&...x)
       : kind_{kind}, operand_{std::move(x)...} {
     CHECK_KIND(kind, RESULT);
   }
@@ -208,7 +208,7 @@ class Operation {
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
 private:
-  int kind_;
+  KindsEnum kind_;
   Container operand_;
 };
 
@@ -415,7 +415,7 @@ template <typename T> class ConditionalExpr {
 public:
   using Result = T;
 
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
   CLASS_BOILERPLATE(ConditionalExpr)
   ConditionalExpr(Expr<LogicalResult> &&cond, Expr<Result> &&thenVal,
@@ -451,7 +451,7 @@ template <typename T> class ConditionalExpr {
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
 private:
-  int kind_;
+  KindsEnum kind_;
   common::CopyableIndirection<Expr<LogicalResult>> condition_;
   common::CopyableIndirection<Expr<Result>> thenValue_;
   common::CopyableIndirection<Expr<Result>> elseValue_;
@@ -464,7 +464,7 @@ struct ImpliedDoIndex {
   using Result = SubscriptInteger;
   bool operator==(const ImpliedDoIndex &) const;
 
-  static constexpr int kind() { return SubscriptIntegerKind; }
+  static constexpr KindsEnum kind() { return SubscriptIntegerKind; }
 
   static constexpr DynamicType GetType() {
     return {TypeCategory::Integer, kind()};
@@ -541,23 +541,23 @@ class ArrayConstructor : public ArrayConstructorValues<RESULT> {
   using Result = RESULT;
   using Base = ArrayConstructorValues<Result>;
 
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
   DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ArrayConstructor)
-  explicit ArrayConstructor(int kind, Base &&values)
+  explicit ArrayConstructor(KindsEnum kind, Base &&values)
       : Base{std::move(values)}, kind_{kind} {
     CHECK_KIND(kind, RESULT);
   }
   template <typename T>
-  explicit ArrayConstructor(int kind, const Expr<T> &) : kind_{kind} {
+  explicit ArrayConstructor(KindsEnum kind, const Expr<T> &) : kind_{kind} {
     CHECK_KIND(kind, RESULT);
   }
-  static constexpr Result result(int kind) { return Result{kind}; }
+  static constexpr Result result(KindsEnum kind) { return Result{kind}; }
   DynamicType GetType() const { return {Result::category, kind_}; }
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
 
 private:
-  int kind_;
+  KindsEnum kind_;
 };
 
 template <>
@@ -567,20 +567,20 @@ class ArrayConstructor<Type<TypeCategory::Character>>
   using Result = Type<TypeCategory::Character>;
   using Base = ArrayConstructorValues<Result>;
 
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
   DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(ArrayConstructor)
-  explicit ArrayConstructor(int kind, Base &&values)
+  explicit ArrayConstructor(KindsEnum kind, Base &&values)
       : Base{std::move(values)}, kind_{kind} {
-    CHECK(kind != 0);
+    CHECK(kind != NoKind);
   }
   template <typename T>
-  explicit ArrayConstructor(int kind, const Expr<T> &) : kind_{kind} {
-    CHECK(kind != 0);
+  explicit ArrayConstructor(KindsEnum kind, const Expr<T> &) : kind_{kind} {
+    CHECK(kind != NoKind);
   }
   ArrayConstructor &set_LEN(Expr<SubscriptInteger> &&);
   bool operator==(const ArrayConstructor &) const;
-  static constexpr Result result(int kind) { return Result{kind}; }
+  static constexpr Result result(KindsEnum kind) { return Result{kind}; }
   DynamicType GetType() const { return {Result::category, kind()}; }
   llvm::raw_ostream &AsFortran(llvm::raw_ostream &) const;
   const Expr<SubscriptInteger> *LEN() const {
@@ -588,7 +588,7 @@ class ArrayConstructor<Type<TypeCategory::Character>>
   }
 
 private:
-  int kind_;
+  KindsEnum kind_;
   std::optional<common::CopyableIndirection<Expr<SubscriptInteger>>> length_;
 };
 
@@ -599,7 +599,7 @@ class ArrayConstructor<SomeDerived>
   using Result = SomeDerived;
   using Base = ArrayConstructorValues<Result>;
 
-  constexpr int kind() const { return 0; }
+  constexpr KindsEnum kind() const { return NoKind; }
 
   CLASS_BOILERPLATE(ArrayConstructor)
 
@@ -609,9 +609,9 @@ class ArrayConstructor<SomeDerived>
   explicit ArrayConstructor(const A &prototype)
       : result_{prototype.GetType().value().GetDerivedTypeSpec()} {}
   template <typename A>
-  explicit ArrayConstructor(int kind, const A &prototype)
+  explicit ArrayConstructor(KindsEnum kind, const A &prototype)
       : result_{prototype.GetType().value().GetDerivedTypeSpec()} {
-    CHECK(kind == 0);
+    CHECK(kind == NoKind);
   }
 
   bool operator==(const ArrayConstructor &) const;
@@ -683,7 +683,7 @@ class Expr<Type<TypeCategory::Real>>
   using Result = Type<TypeCategory::Real>;
 
   EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
-  explicit Expr(int kind, const Scalar<Result> &x)
+  explicit Expr(KindsEnum kind, const Scalar<Result> &x)
       : u{Constant<Result>{kind, x}} {}
 
 private:
@@ -709,7 +709,7 @@ class Expr<Type<TypeCategory::Complex>>
 public:
   using Result = Type<TypeCategory::Complex>;
   EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
-  explicit Expr(int kind, const Scalar<Result> &x)
+  explicit Expr(KindsEnum kind, const Scalar<Result> &x)
       : u{Constant<Result>{kind, x}} {}
   using Operations = std::variant<Parentheses<Result>, Negate<Result>,
       Convert<Result, TypeCategory::Complex>, Add<Result>, Subtract<Result>,
@@ -733,9 +733,9 @@ class Expr<Type<TypeCategory::Character>>
 public:
   using Result = Type<TypeCategory::Character>;
   EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
-  explicit Expr(int kind, const Scalar<Result> &x)
+  explicit Expr(KindsEnum kind, const Scalar<Result> &x)
       : u{Constant<Result>{kind, x}} {}
-  explicit Expr(int kind, Scalar<Result> &&x)
+  explicit Expr(KindsEnum kind, Scalar<Result> &&x)
       : u{Constant<Result>{kind, std::move(x)}} {}
 
   std::optional<Expr<SubscriptInteger>> LEN() const;
@@ -791,7 +791,7 @@ template <> class Relational<SomeType> {
 public:
   using Result = LogicalResult;
   EVALUATE_UNION_CLASS_BOILERPLATE(Relational)
-  int kind() const {
+  KindsEnum kind() const {
     return common::visit([](const auto &x) { return x.kind(); }, u);
   }
   static constexpr DynamicType GetType() {
@@ -823,9 +823,9 @@ class Expr<Type<TypeCategory::Logical>>
   EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
 
   explicit Expr(const Scalar<Result> &x) : u{Constant<Result>{x.kind(), x}} {}
-  explicit Expr(int kind, const Scalar<Result> &x)
+  explicit Expr(KindsEnum kind, const Scalar<Result> &x)
       : u{Constant<Result>{kind, x}} {}
-  explicit Expr(int kind, bool x)
+  explicit Expr(KindsEnum kind, bool x)
       : u{Constant<Result>{kind, Scalar<Result>{kind, x}}} {}
 
 private:
@@ -860,7 +860,7 @@ class StructureConstructor {
 public:
   using Result = SomeDerived;
 
-  static int kind() { return 0; }
+  static KindsEnum kind() { return NoKind; }
 
   explicit StructureConstructor(const semantics::DerivedTypeSpec &spec)
       : result_{spec} {}
@@ -923,8 +923,8 @@ class Expr<SomeKind<CAT>> : public ExpressionBase<SomeKind<CAT>> {
   using Result = SomeKind<CAT>;
   EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
 
-  int kind() const {
-    return common::visit([](auto v) -> int { return v.kind(); }, u);
+  KindsEnum kind() const {
+    return common::visit([](auto v) { return v.kind(); }, u);
   }
 
   common::MapTemplate<evaluate::Expr, CategoryTypes<CAT>> u;
@@ -947,11 +947,11 @@ using CategoryExpression = common::MapTemplate<Expr, SomeCategory>;
 // distinguishable from other integer constants, since they are permitted
 // to be used in only a few situations.
 using BOZLiteralConstant = typename LargestReal::Scalar::Word;
-static constexpr int BOZLiteralConstantKind = LargestRealKind;
+static constexpr KindsEnum BOZLiteralConstantKind = LargestRealKind;
 
 // Null pointers without MOLD= arguments are typed by context.
 struct NullPointer {
-  static constexpr int kind() { return 0; }
+  static constexpr KindsEnum kind() { return NoKind; }
 
   constexpr bool operator==(const NullPointer &) const { return true; }
   static constexpr int Rank() { return 0; }
@@ -970,7 +970,7 @@ template <> class Expr<SomeType> : public ExpressionBase<SomeType> {
 public:
   using Result = SomeType;
 
-  static int kind() { return 0; }
+  static KindsEnum kind() { return NoKind; }
 
   EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
 
@@ -1061,24 +1061,25 @@ FOR_EACH_INTRINSIC_KIND(extern template class ArrayConstructor, )
   FOR_EACH_INTRINSIC_KIND(template class ConditionalExpr, )
 
 template <typename T>
-inline Expr<T> MakeConstantExpr(int kind, const Scalar<T> &v) {
+inline Expr<T> MakeConstantExpr(KindsEnum kind, const Scalar<T> &v) {
   CHECK(kind == v.kind());
   return Expr<T>{Constant<T>{kind, v}};
 }
 
-template <typename T> inline Expr<T> MakeConstantExpr(int kind, Scalar<T> &&v) {
+template <typename T>
+inline Expr<T> MakeConstantExpr(KindsEnum kind, Scalar<T> &&v) {
   CHECK(kind == v.kind());
   return Expr<T>{Constant<T>{kind, std::move(v)}};
 }
 
 template <typename T>
-inline Expr<T> MakeConstantExpr(int kind, const Constant<T> &c) {
+inline Expr<T> MakeConstantExpr(KindsEnum kind, const Constant<T> &c) {
   CHECK(kind == c.kind());
   return Expr<T>{c};
 }
 
 template <typename T>
-inline Expr<T> MakeConstantExpr(int kind, Constant<T> &&c) {
+inline Expr<T> MakeConstantExpr(KindsEnum kind, Constant<T> &&c) {
   CHECK(kind == c.kind());
   return Expr<T>{std::move(c)};
 }
@@ -1086,30 +1087,30 @@ inline Expr<T> MakeConstantExpr(int kind, Constant<T> &&c) {
 template <typename T,
     typename =
         std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Character>>>>
-inline Expr<T> MakeConstantExpr(int kind, const std::string &v) {
+inline Expr<T> MakeConstantExpr(KindsEnum kind, const std::string &v) {
   return Expr<T>{MakeConstant<T>(kind, v)};
 }
 
 template <typename T,
     typename = std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Integer>>>>
-inline Expr<T> MakeConstantExpr(int kind, int64_t v) {
+inline Expr<T> MakeConstantExpr(KindsEnum kind, int64_t v) {
   return Expr<T>{Constant<T>{kind, Scalar<T>{kind, v}}};
 }
 
 template <typename T,
     typename =
         std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Unsigned>>>>
-inline Expr<T> MakeConstantExpr(int kind, uint64_t v) {
+inline Expr<T> MakeConstantExpr(KindsEnum kind, uint64_t v) {
   return Expr<T>{Constant<T>{kind, Scalar<T>{kind, v}}};
 }
 
 template <typename T,
     typename = std::enable_if_t<std::is_same_v<T, Type<TypeCategory::Logical>>>>
-inline Expr<T> MakeConstantExpr(int kind, bool v) {
+inline Expr<T> MakeConstantExpr(KindsEnum kind, bool v) {
   return Expr<T>{Constant<T>{kind, Scalar<T>{kind, v}}};
 }
 
-template <typename T> inline Expr<T> MakeZeroExpr(int kind) {
+template <typename T> inline Expr<T> MakeZeroExpr(KindsEnum kind) {
   return MakeConstantExpr<T>(kind, Scalar<T>::Zero(kind));
 }
 
@@ -1129,6 +1130,10 @@ inline Expr<SubscriptInteger> MakeSubscriptIntExpr(
       Constant<SubscriptInteger>{SubscriptIntegerKind, std::move(v)}};
 }
 
+inline Expr<SubscriptInteger> MakeKindExpr(KindsEnum v) {
+  return MakeSubscriptIntExpr(static_cast<int>(v));
+}
+
 inline Expr<CInteger> MakeCIntegerExpr(int32_t v) {
   return Expr<CInteger>{MakeCIntegerConstant(v)};
 }
diff --git a/flang/include/flang/Evaluate/fold-designator.h b/flang/include/flang/Evaluate/fold-designator.h
index 8c1495b41511a..193d629525734 100644
--- a/flang/include/flang/Evaluate/fold-designator.h
+++ b/flang/include/flang/Evaluate/fold-designator.h
@@ -116,7 +116,7 @@ class DesignatorFolder {
   std::optional<OffsetSymbol> FoldDesignator(
       const Designator<Type<TypeCategory::Character>> &designator,
       ConstantSubscript which) {
-    const int kind{designator.kind()};
+    const KindsEnum kind{designator.kind()};
     return common::visit(
         common::visitors{
             [&, kind](const Substring &ss) {
@@ -134,9 +134,10 @@ class DesignatorFolder {
                       if (*start < 1) {
                         isOutOfRange_ = true;
                       }
-                      result->Augment(kind * (*start - 1));
-                      result->set_size(
-                          *end >= *start ? kind * (*end - *start + 1) : 0);
+                      result->Augment(static_cast<int>(kind) * (*start - 1));
+                      result->set_size(*end >= *start
+                              ? static_cast<int>(kind) * (*end - *start + 1)
+                              : 0);
                       if (len) {
                         if (auto lenVal{ToInt64(*len)}) {
                           if (*end > *lenVal) {
diff --git a/flang/include/flang/Evaluate/initial-image.h b/flang/include/flang/Evaluate/initial-image.h
index c8afa80e71977..f7c656394d1cc 100644
--- a/flang/include/flang/Evaluate/initial-image.h
+++ b/flang/include/flang/Evaluate/initial-image.h
@@ -29,7 +29,7 @@ inline void StoreSerialValues(char *dst, llvm::ArrayRef<SCALAR> values,
 }
 
 template <typename SCALAR>
-inline void LoadSerialValues(int kind, const char *src,
+inline void LoadSerialValues(KindsEnum kind, const char *src,
     llvm::MutableArrayRef<SCALAR> values, size_t stride) {
   for (auto it : llvm::enumerate(values)) {
     it.value() = SCALAR::FromRawBytes(
@@ -82,7 +82,7 @@ class InitialImage {
   }
   Result Add(ConstantSubscript offset, std::size_t bytes,
       const Constant<Type<TypeCategory::Character>> &x, FoldingContext &) {
-    const int kind{x.kind()};
+    const KindsEnum kind{x.kind()};
     if (offset < 0 || offset + bytes > data_.size()) {
       return OutOfRange;
     } else {
@@ -100,7 +100,7 @@ class InitialImage {
         Result result{OkNoChange};
         for (auto at{x.lbounds()}; elements-- > 0; x.IncrementSubscripts(at)) {
           value::CharacterValue scalar{x.At(at)};
-          auto scalarBytes{scalar.size() * kind};
+          auto scalarBytes{scalar.size() * static_cast<size_t>(kind)};
           if (scalarBytes != elementBytes) {
             result = LengthMismatch;
           }
diff --git a/flang/include/flang/Evaluate/integer-value.h b/flang/include/flang/Evaluate/integer-value.h
index d8ddeb39f039d..5e08587e3d88c 100644
--- a/flang/include/flang/Evaluate/integer-value.h
+++ b/flang/include/flang/Evaluate/integer-value.h
@@ -9,6 +9,7 @@
 #ifndef FORTRAN_EVALUATE_INTEGER_VALUE_H_
 #define FORTRAN_EVALUATE_INTEGER_VALUE_H_
 
+#include "flang/Common/type-kinds.h"
 #include "flang/Common/uint128.h"
 #include "flang/Evaluate/common.h"
 #include "flang/Evaluate/object-sizes.h"
@@ -23,6 +24,7 @@
 
 namespace Fortran::evaluate::value {
 class IntegerValueImpl;
+using common::KindsEnum;
 
 /// A two's-complement integer with dynamic bitwidth.
 ///
@@ -48,10 +50,10 @@ class IntegerValue {
   IntegerValue &operator=(const IntegerValue &);
   IntegerValue &operator=(IntegerValue &&);
 
-  IntegerValue(int kind, const IntegerValue &x) : IntegerValue(x) {
+  IntegerValue(KindsEnum kind, const IntegerValue &x) : IntegerValue(x) {
     CHECK(x.kind() == kind);
   }
-  IntegerValue(int kind, IntegerValue &&x) : IntegerValue(std::move(x)) {
+  IntegerValue(KindsEnum kind, IntegerValue &&x) : IntegerValue(std::move(x)) {
     CHECK(x.kind() == kind);
   }
 
@@ -64,7 +66,7 @@ class IntegerValue {
   // is a class type that never does.
   template <typename INT,
       typename = std::enable_if_t<std::numeric_limits<INT>::is_integer>>
-  IntegerValue(int kind, INT v) {
+  IntegerValue(KindsEnum kind, INT v) {
     if constexpr (sizeof(INT) > 8) {
       static_assert(sizeof(INT) == 16);
       ConstructFromIntegral(kind, static_cast<Fortran::common::uint128_t>(v));
@@ -79,7 +81,7 @@ class IntegerValue {
   /// Creates an integer with value 0 of a given kind. This is different from
   /// the default-ctor which creates a "monostate" that represents 0 of unknown
   /// kind.
-  static IntegerValue Zero(int kind);
+  static IntegerValue Zero(KindsEnum kind);
 
   void print(llvm::raw_ostream &os) const;
 
@@ -92,21 +94,21 @@ class IntegerValue {
   bool IsMonostate() const;
 
   /// The kind of the value currently stored.
-  int kind() const;
+  KindsEnum kind() const;
 
   int bits() const { return bits(kind()); }
-  static constexpr int bits(int kind) { return bytesStored(kind) * 8; }
+  static constexpr int bits(KindsEnum kind) { return bytesStored(kind) * 8; }
 
   /// Number of bytes accessed by FromRawBytes/StoreRawBytes
   std::size_t bytesStored() const { return bytesStored(kind()); }
-  static constexpr std::size_t bytesStored(int kind) {
+  static constexpr std::size_t bytesStored(KindsEnum kind) {
     switch (kind) {
-    case 3:
+    case KindsEnum::Kind3:
       return 2;
-    case 10:
+    case KindsEnum::Kind10:
       return 16;
     default:
-      return kind;
+      return static_cast<std::size_t>(kind);
     }
   }
 
@@ -120,13 +122,13 @@ class IntegerValue {
   bool operator>(const IntegerValue &y) const { return y < *this; }
 
   /// Left-justified mask (e.g., MASKL(1) has only its sign bit set)
-  static IntegerValue MASKL(int kind, int places);
+  static IntegerValue MASKL(KindsEnum kind, int places);
 
   /// Right-justified mask (e.g., MASKR(1) == 1, MASKR(2) == 3, &c.)
-  static IntegerValue MASKR(int kind, int places);
+  static IntegerValue MASKR(KindsEnum kind, int places);
 
   static ValueWithOverflow Read(
-      int kind, const char *&pp, int base, bool isSigned);
+      KindsEnum kind, const char *&pp, int base, bool isSigned);
 
   /// ZExt or Trunc
   static ValueWithOverflow ConvertUnsigned(
@@ -142,18 +144,18 @@ class IntegerValue {
   /// Omits a leading "0x".
   std::string Hexadecimal() const;
 
-  static constexpr int DIGITS(int kind) {
+  static constexpr int DIGITS(KindsEnum kind) {
     // don't count the sign bit
     return bits(kind) - 1;
   }
 
-  static IntegerValue HUGE(int kind);
+  static IntegerValue HUGE(KindsEnum kind);
 
-  static IntegerValue Least(int kind);
+  static IntegerValue Least(KindsEnum kind);
 
-  static int RANGE(int kind);
+  static int RANGE(KindsEnum kind);
 
-  static int UnsignedRANGE(int kind);
+  static int UnsignedRANGE(KindsEnum kind);
 
   bool IsZero() const;
 
@@ -312,12 +314,12 @@ class IntegerValue {
   PowerWithErrors Power(const IntegerValue &e) const;
 
   static IntegerValue FromRawBytes(
-      int kind, const void *raw, std::size_t expectedSize);
+      KindsEnum kind, const void *raw, std::size_t expectedSize);
   void StoreRawBytes(void *dst, size_t size, bool *changed = nullptr) const;
 
 private:
-  void ConstructFromIntegral(int kind, std::uint64_t n, bool isSigned);
-  void ConstructFromIntegral(int kind, Fortran::common::uint128_t n);
+  void ConstructFromIntegral(KindsEnum kind, std::uint64_t n, bool isSigned);
+  void ConstructFromIntegral(KindsEnum kind, Fortran::common::uint128_t n);
 
   static IntegerValue FromImpl(const IntegerValueImpl &x);
   static IntegerValue FromImpl(IntegerValueImpl &&x);
diff --git a/flang/include/flang/Evaluate/logical-value.h b/flang/include/flang/Evaluate/logical-value.h
index f313e88cff5a0..e794856d260d8 100644
--- a/flang/include/flang/Evaluate/logical-value.h
+++ b/flang/include/flang/Evaluate/logical-value.h
@@ -14,6 +14,7 @@
 #include <utility>
 
 namespace Fortran::evaluate::value {
+using common::KindsEnum;
 
 /// A Fortran LOGICAL value.
 ///
@@ -32,22 +33,22 @@ class LogicalValue {
   LogicalValue &operator=(const LogicalValue &) = default;
   LogicalValue &operator=(LogicalValue &&) = default;
 
-  LogicalValue(int kind, const LogicalValue &v) : LogicalValue{v} {
+  LogicalValue(KindsEnum kind, const LogicalValue &v) : LogicalValue{v} {
     CHECK(kind == v.kind());
   }
 
-  LogicalValue(int kind, LogicalValue &&v) : LogicalValue{std::move(v)} {
+  LogicalValue(KindsEnum kind, LogicalValue &&v) : LogicalValue{std::move(v)} {
     CHECK(kind == v.kind());
   }
 
-  LogicalValue(int kind, bool truth) : word_(Represent(kind, truth)) {}
+  LogicalValue(KindsEnum kind, bool truth) : word_(Represent(kind, truth)) {}
 
-  LogicalValue(int kind, const Word &w) : word_(kind, w) {}
+  LogicalValue(KindsEnum kind, const Word &w) : word_(kind, w) {}
 
   /// Creates a logical with value 'false' of a given kind. This is in contrast
   /// to the default-ctor which creates a "monostate" that represents 'false' of
   /// a not-yet-known kind.
-  static LogicalValue Zero(int kind) { return LogicalValue{kind, false}; }
+  static LogicalValue Zero(KindsEnum kind) { return LogicalValue{kind, false}; }
 
   void print(llvm::raw_ostream &os) const;
 
@@ -60,21 +61,21 @@ class LogicalValue {
   bool IsMonostate() const { return word_.IsMonostate(); }
 
   /// The kind of the value currently stored.
-  int kind() const { return word_.kind(); }
+  KindsEnum kind() const { return word_.kind(); }
 
   int bits() const { return bits(kind()); }
-  static constexpr int bits(int kind) { return Word::bits(kind); }
+  static constexpr int bits(KindsEnum kind) { return Word::bits(kind); }
 
   /// Number of bytes accessed by FromRawBytes/StoreRawBytes
   std::size_t bytesStored() const { return bytesStored(kind()); }
-  static constexpr std::size_t bytesStored(int kind) {
+  static constexpr std::size_t bytesStored(KindsEnum kind) {
     return Word::bytesStored(kind);
   }
 
   Word word() const { return word_; }
 
   bool IsCanonical() const {
-    const int kind{this->kind()};
+    const KindsEnum kind{this->kind()};
     return word_ == canonicalFalse(kind) || word_ == canonicalTrue(kind);
   }
 
@@ -119,7 +120,7 @@ class LogicalValue {
   }
 
   static LogicalValue FromRawBytes(
-      int kind, const void *raw, std::size_t expectedSize) {
+      KindsEnum kind, const void *raw, std::size_t expectedSize) {
     Word w{Word::FromRawBytes(kind, raw, expectedSize)};
     return LogicalValue{w.kind(), w};
   }
@@ -129,11 +130,11 @@ class LogicalValue {
   }
 
 private:
-  static Word canonicalTrue(int kind) { return Word{kind, 1}; }
+  static Word canonicalTrue(KindsEnum kind) { return Word{kind, 1}; }
 
-  static Word canonicalFalse(int kind) { return Word{kind, 0}; }
+  static Word canonicalFalse(KindsEnum kind) { return Word{kind, 0}; }
 
-  static Word Represent(int kind, bool x) {
+  static Word Represent(KindsEnum kind, bool x) {
     return x ? canonicalTrue(kind) : canonicalFalse(kind);
   }
 
diff --git a/flang/include/flang/Evaluate/real-value.h b/flang/include/flang/Evaluate/real-value.h
index f30c65d41478d..1fec1e5f96f76 100644
--- a/flang/include/flang/Evaluate/real-value.h
+++ b/flang/include/flang/Evaluate/real-value.h
@@ -9,6 +9,7 @@
 #ifndef FORTRAN_EVALUATE_REAL_VALUE_H_
 #define FORTRAN_EVALUATE_REAL_VALUE_H_
 
+#include "flang/Common/type-kinds.h"
 #include "flang/Evaluate/integer-value.h"
 #include "flang/Evaluate/object-sizes.h"
 #include "flang/Evaluate/target.h"
@@ -20,6 +21,7 @@
 
 namespace Fortran::evaluate::value {
 class RealValueImpl;
+using common::KindsEnum;
 
 /// A floating-point value with dynamic precision.
 ///
@@ -39,33 +41,33 @@ class RealValue {
   RealValue &operator=(const RealValue &);
   RealValue &operator=(RealValue &&);
 
-  RealValue(int kind, const RealValue &v) : RealValue(v) {
+  RealValue(KindsEnum kind, const RealValue &v) : RealValue(v) {
     CHECK(kind == v.kind());
   }
-  RealValue(int kind, RealValue &&v) : RealValue(std::move(v)) {
+  RealValue(KindsEnum kind, RealValue &&v) : RealValue(std::move(v)) {
     CHECK(kind == v.kind());
   }
 
   /// Interpret w as the raw bit pattern for the given runtime kind.
-  RealValue(int kind, const Word &w);
+  RealValue(KindsEnum kind, const Word &w);
 
   /// Creates a floating-point value of a given kind from a host double,
   /// rounded to the target kind's precision (per the default rounding mode).
   /// Portable: does not assume that the host "double" shares any bit layout
   /// with the target kind, only that <cmath>'s frexp()/ldexp() are available.
-  RealValue(int kind, double x);
+  RealValue(KindsEnum kind, double x);
 
   /// Creates a floating-point with value +0.0 of a given kind. In contrast, the
   /// default ctor creates a "monostate" that represents +0.0 of unknown kind.
-  static RealValue Zero(int kind);
+  static RealValue Zero(KindsEnum kind);
 
   /// Creates a floating-point with value -0.0 of a given kind.
-  static RealValue NegativeZero(int kind);
+  static RealValue NegativeZero(KindsEnum kind);
 
-  static RealValue Infinity(int kind, bool negative = false);
+  static RealValue Infinity(KindsEnum kind, bool negative = false);
 
   /// A signaling NaN, as opposed to the quiet NaN returned by NotANumber().
-  static RealValue SignalingNaN(int kind);
+  static RealValue SignalingNaN(KindsEnum kind);
 
   void print(llvm::raw_ostream &os) const;
 
@@ -78,21 +80,21 @@ class RealValue {
   bool IsMonostate() const;
 
   /// The kind of the value currently stored.
-  int kind() const;
+  KindsEnum kind() const;
 
   int bits() const { return bits(kind()); }
-  static constexpr int bits(int kind) { return bytesStored(kind) * 8; }
+  static constexpr int bits(KindsEnum kind) { return bytesStored(kind) * 8; }
 
   /// Number of bytes accessed by FromRawBytes/StoreRawBytes
   std::size_t bytesStored() const { return bytesStored(kind()); }
-  static constexpr std::size_t bytesStored(int kind) {
+  static constexpr std::size_t bytesStored(KindsEnum kind) {
     switch (kind) {
-    case 3:
+    case KindsEnum::Kind3:
       return 2;
-    case 10:
+    case KindsEnum::Kind10:
       return 16;
     default:
-      return kind;
+      return static_cast<std::size_t>(kind);
     }
   }
 
@@ -164,21 +166,21 @@ class RealValue {
 
   IntegerValue EXPONENT() const;
 
-  static RealValue EPSILON(int kind);
+  static RealValue EPSILON(KindsEnum kind);
 
-  static RealValue HUGE(int kind);
+  static RealValue HUGE(KindsEnum kind);
 
-  static RealValue TINY(int kind);
+  static RealValue TINY(KindsEnum kind);
 
-  static int DIGITS(int kind);
+  static int DIGITS(KindsEnum kind);
 
-  static int PRECISION(int kind);
+  static int PRECISION(KindsEnum kind);
 
-  static int RANGE(int kind);
+  static int RANGE(KindsEnum kind);
 
-  static int MAXEXPONENT(int kind);
+  static int MAXEXPONENT(KindsEnum kind);
 
-  static int MINEXPONENT(int kind);
+  static int MINEXPONENT(KindsEnum kind);
 
   RealValue RRSPACING() const;
 
@@ -195,9 +197,9 @@ class RealValue {
   RealValue FlushSubnormalToZero() const;
 
   // TODO: Configurable NotANumber representations
-  static RealValue NotANumber(int kind);
+  static RealValue NotANumber(KindsEnum kind);
 
-  static ValueWithRealFlags<RealValue> FromInteger(int kind,
+  static ValueWithRealFlags<RealValue> FromInteger(KindsEnum kind,
       const IntegerValue &n, bool isUnsigned = false,
       Rounding rounding = TargetCharacteristics::defaultRounding);
 
@@ -210,7 +212,8 @@ class RealValue {
       common::RoundingMode mode = common::RoundingMode::ToZero,
       int toBits = 0) const;
 
-  static ValueWithRealFlags<RealValue> Convert(int kind, const RealValue &from,
+  static ValueWithRealFlags<RealValue> Convert(KindsEnum kind,
+      const RealValue &from,
       Rounding rounding = TargetCharacteristics::defaultRounding);
 
   Word RawBits() const;
@@ -218,7 +221,7 @@ class RealValue {
   /// Extracts "raw" biased exponent field.
   int Exponent() const;
 
-  static ValueWithRealFlags<RealValue> Read(int kind, const char *&pp,
+  static ValueWithRealFlags<RealValue> Read(KindsEnum kind, const char *&pp,
       Rounding rounding = TargetCharacteristics::defaultRounding);
 
   std::string DumpHexadecimal() const;
@@ -229,7 +232,7 @@ class RealValue {
       llvm::raw_ostream &o, int kind, bool minimal = false) const;
 
   static RealValue FromRawBytes(
-      int kind, const void *raw, std::size_t expectedSize);
+      KindsEnum kind, const void *raw, std::size_t expectedSize);
 
   void StoreRawBytes(void *dst, size_t size, bool *changed = nullptr) const;
 
diff --git a/flang/include/flang/Evaluate/rewrite.h b/flang/include/flang/Evaluate/rewrite.h
index 92c058b29d792..d69f9e3d6bd35 100644
--- a/flang/include/flang/Evaluate/rewrite.h
+++ b/flang/include/flang/Evaluate/rewrite.h
@@ -126,7 +126,7 @@ template <typename Rewriter> struct Mutator {
 
   template <typename D, size_t... Is>
   D MutateOp(D &&op, std::index_sequence<Is...>) const {
-    const int kind{op.kind()};
+    const KindsEnum kind{op.kind()};
     return D(kind, Mutate(std::move(op.template operand<Is>()))...);
   }
 
diff --git a/flang/include/flang/Evaluate/shape.h b/flang/include/flang/Evaluate/shape.h
index 5bca399c50ef4..cdb7793e1dc10 100644
--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -31,7 +31,7 @@ using ExtentExpr = Expr<ExtentType>;
 using MaybeExtentExpr = std::optional<ExtentExpr>;
 using Shape = std::vector<MaybeExtentExpr>;
 
-inline constexpr int ExtentIntKind = Fortran::evaluate::SubscriptIntegerKind;
+inline constexpr KindsEnum ExtentIntKind = SubscriptIntegerKind;
 
 inline Constant<ExtentType> MakeExtentConstant(int64_t v) {
   return Constant<ExtentType>{
diff --git a/flang/include/flang/Evaluate/target.h b/flang/include/flang/Evaluate/target.h
index 7b38db2db1956..90df18cdb22f9 100644
--- a/flang/include/flang/Evaluate/target.h
+++ b/flang/include/flang/Evaluate/target.h
@@ -22,6 +22,7 @@
 
 namespace Fortran::evaluate {
 
+using common::KindsEnum;
 using common::Rounding;
 
 ENUM_CLASS(IeeeFeature, Denormal, Divide, Flags, Halting, Inf, Io, NaN,
@@ -50,17 +51,17 @@ class TargetCharacteristics {
   void set_areSubnormalsFlushedToZero(bool yes = true);
 
   // Check if a given real kind has flushing control.
-  bool hasSubnormalFlushingControl(int kind) const;
+  bool hasSubnormalFlushingControl(KindsEnum kind) const;
   // Check if any or all real kinds have flushing control.
   bool hasSubnormalFlushingControl(bool any = false) const;
-  void set_hasSubnormalFlushingControl(int kind, bool yes = true);
+  void set_hasSubnormalFlushingControl(KindsEnum kind, bool yes = true);
 
   // Check if a given real kind has support for raising a nonstandard
   // ieee_denorm exception.
-  bool hasSubnormalExceptionSupport(int kind) const;
+  bool hasSubnormalExceptionSupport(KindsEnum kind) const;
   // Check if all real kinds have support for the ieee_denorm exception.
   bool hasSubnormalExceptionSupport() const;
-  void set_hasSubnormalExceptionSupport(int kind, bool yes = true);
+  void set_hasSubnormalExceptionSupport(KindsEnum kind, bool yes = true);
 
   Rounding roundingMode() const { return roundingMode_; }
   void set_roundingMode(Rounding);
@@ -83,16 +84,14 @@ class TargetCharacteristics {
   std::size_t maxByteSize() const { return maxByteSize_; }
   std::size_t maxAlignment() const { return maxAlignment_; }
 
-  static bool CanSupportType(common::TypeCategory, std::int64_t kind);
-  bool EnableType(common::TypeCategory category, std::int64_t kind,
+  static bool CanSupportType(common::TypeCategory, KindsEnum kind);
+  bool EnableType(common::TypeCategory category, KindsEnum kind,
       std::size_t byteSize, std::size_t align);
-  void DisableType(common::TypeCategory category, std::int64_t kind);
+  void DisableType(common::TypeCategory category, KindsEnum kind);
 
-  std::size_t GetByteSize(
-      common::TypeCategory category, std::int64_t kind) const;
-  std::size_t GetAlignment(
-      common::TypeCategory category, std::int64_t kind) const;
-  bool IsTypeEnabled(common::TypeCategory category, std::int64_t kind) const;
+  std::size_t GetByteSize(common::TypeCategory category, KindsEnum kind) const;
+  std::size_t GetAlignment(common::TypeCategory category, KindsEnum kind) const;
+  bool IsTypeEnabled(common::TypeCategory category, KindsEnum kind) const;
 
   int SelectedIntKind(std::int64_t precision = 0) const;
   int SelectedLogicalKind(std::int64_t bits = 1) const;
@@ -131,8 +130,8 @@ class TargetCharacteristics {
   IeeeFeatures &ieeeFeatures() { return ieeeFeatures_; }
   const IeeeFeatures &ieeeFeatures() const { return ieeeFeatures_; }
 
-  std::size_t integerKindForPointer() { return integerKindForPointer_; }
-  void set_integerKindForPointer(std::size_t newKind) {
+  KindsEnum integerKindForPointer() { return integerKindForPointer_; }
+  void set_integerKindForPointer(KindsEnum newKind) {
     integerKindForPointer_ = newKind;
   }
 
@@ -161,7 +160,7 @@ class TargetCharacteristics {
       IeeeFeature::Io, IeeeFeature::NaN, IeeeFeature::Rounding,
       IeeeFeature::Sqrt, IeeeFeature::Standard, IeeeFeature::Subnormal,
       IeeeFeature::UnderflowControl};
-  std::size_t integerKindForPointer_{8}; /* For 64 bit pointer */
+  KindsEnum integerKindForPointer_{KindsEnum::Kind8}; /* For 64 bit pointer */
 };
 
 } // namespace Fortran::evaluate
diff --git a/flang/include/flang/Evaluate/tools.h b/flang/include/flang/Evaluate/tools.h
index f50ddb53249fc..b4ee31d5139de 100644
--- a/flang/include/flang/Evaluate/tools.h
+++ b/flang/include/flang/Evaluate/tools.h
@@ -196,7 +196,7 @@ auto UnwrapExpr(B &x) -> common::Constify<A, B> * {
   return nullptr;
 }
 template <typename A, typename B>
-auto UnwrapExpr(int kind, B &x) -> common::Constify<A, B> * {
+auto UnwrapExpr(KindsEnum kind, B &x) -> common::Constify<A, B> * {
   using Ty = std::decay_t<B>;
   if (x.kind() != kind)
     return nullptr;
@@ -226,7 +226,7 @@ const A *UnwrapExpr(const std::optional<B> &x) {
   }
 }
 template <typename A, typename B>
-const A *UnwrapExpr(int kind, const std::optional<B> &x) {
+const A *UnwrapExpr(KindsEnum kind, const std::optional<B> &x) {
   if (x) {
     return UnwrapExpr<A>(kind, *x);
   } else {
@@ -241,7 +241,8 @@ template <typename A, typename B> A *UnwrapExpr(std::optional<B> &x) {
     return nullptr;
   }
 }
-template <typename A, typename B> A *UnwrapExpr(int kind, std::optional<B> &x) {
+template <typename A, typename B>
+A *UnwrapExpr(KindsEnum kind, std::optional<B> &x) {
   if (x) {
     return UnwrapExpr<A>(kind, *x);
   } else {
@@ -257,7 +258,8 @@ template <typename A, typename B> const A *UnwrapExpr(const B *x) {
   }
 }
 
-template <typename A, typename B> const A *UnwrapExpr(int kind, const B *x) {
+template <typename A, typename B>
+const A *UnwrapExpr(KindsEnum kind, const B *x) {
   if (x) {
     return UnwrapExpr<A>(kind, *x);
   } else {
@@ -273,7 +275,7 @@ template <typename A, typename B> A *UnwrapExpr(B *x) {
   }
 }
 
-template <typename A, typename B> A *UnwrapExpr(int kind, B *x) {
+template <typename A, typename B> A *UnwrapExpr(KindsEnum kind, B *x) {
   if (x) {
     return UnwrapExpr<A>(kind, *x);
   } else {
@@ -597,7 +599,7 @@ const Symbol *GetLastPointerSymbol(const evaluate::DataRef &);
 // one arbitrary expression to the type of another with ConvertTo(to, from).
 
 template <typename TO, TypeCategory FROMCAT>
-Expr<TO> ConvertToType(int toKind, Expr<SomeKind<FROMCAT>> &&x) {
+Expr<TO> ConvertToType(KindsEnum toKind, Expr<SomeKind<FROMCAT>> &&x) {
   static_assert(IsSpecificIntrinsicType<TO>);
   if constexpr (FROMCAT == TO::category) {
     auto *already{std::get_if<Expr<TO>>(&x.u)};
@@ -627,13 +629,13 @@ Expr<TO> ConvertToType(int toKind, Expr<SomeKind<FROMCAT>> &&x) {
 }
 
 template <typename TO, TypeCategory FROMCAT>
-Expr<TO> ConvertToType(int toKind, Expr<Type<FROMCAT>> &&x) {
+Expr<TO> ConvertToType(KindsEnum toKind, Expr<Type<FROMCAT>> &&x) {
   return ConvertToType<TO, FROMCAT>(
       toKind, Expr<SomeKind<FROMCAT>>{std::move(x)});
 }
 
 template <typename TO>
-Expr<TO> ConvertToType(int toKind, BOZLiteralConstant &&x) {
+Expr<TO> ConvertToType(KindsEnum toKind, BOZLiteralConstant &&x) {
   static_assert(IsSpecificIntrinsicType<TO>);
   if constexpr (TO::category == TypeCategory::Integer ||
       TO::category == TypeCategory::Unsigned) {
@@ -666,7 +668,7 @@ std::optional<Expr<SomeType>> ConvertToType(
 // Conversions to the type of another expression
 template <TypeCategory TC, typename FROM>
 common::IfNoLvalue<Expr<Type<TC>>, FROM> ConvertTo(
-    int toKind, const Expr<Type<TC>> &to, FROM &&x) {
+    KindsEnum toKind, const Expr<Type<TC>> &to, FROM &&x) {
   return ConvertToType<Type<TC>>(toKind, std::move(x));
 }
 
@@ -676,7 +678,7 @@ common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo(
   return common::visit(
       [&](const auto &toKindExpr) {
         using KindExpr = std::decay_t<decltype(toKindExpr)>;
-        const int toKind{toKindExpr.kind()};
+        const KindsEnum toKind{toKindExpr.kind()};
         return AsCategoryExpr(
             ConvertToType<ResultType<KindExpr>>(toKind, std::move(from)));
       },
@@ -687,7 +689,7 @@ common::IfNoLvalue<Expr<SomeKind<TC>>, FROM> ConvertTo(
 // kind of some category (usually but not necessarily distinct).
 template <TypeCategory TOCAT, typename VALUE>
 common::IfNoLvalue<Expr<SomeKind<TOCAT>>, VALUE> ConvertToKind(
-    int kind, VALUE &&x) {
+    KindsEnum kind, VALUE &&x) {
   return AsCategoryExpr(ConvertToType<Type<TOCAT>>(kind, std::move(x)));
 }
 
@@ -710,9 +712,9 @@ template <TypeCategory CAT>
 SameKindExprs<CAT, 2> AsSameKindExprs(
     Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
   Expr<Type<CAT>> kx{std::get<Expr<Type<CAT>>>(std::move(x.u))};
-  int xKind{kx.kind()};
+  KindsEnum xKind{kx.kind()};
   Expr<Type<CAT>> ky{std::get<Expr<Type<CAT>>>(std::move(y.u))};
-  int yKind{ky.kind()};
+  KindsEnum yKind{ky.kind()};
   if (xKind == yKind) {
     return {SameExprs<Type<CAT>>{std::move(kx), std::move(ky)}};
   } else if (xKind < yKind) {
@@ -736,21 +738,21 @@ SameKindExprs<CAT, 2> AsSameKindExprs(
 using ConvertRealOperandsResult =
     std::optional<SameKindExprs<TypeCategory::Real, 2>>;
 ConvertRealOperandsResult ConvertRealOperands(parser::ContextualMessages &,
-    Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
+    Expr<SomeType> &&, Expr<SomeType> &&, KindsEnum defaultRealKind);
 
 // Per F'2018 R718, if both components are INTEGER, they are both converted
 // to default REAL and the result is default COMPLEX.  Otherwise, the
 // kind of the result is the kind of most precise REAL component, and the other
 // component is converted if necessary to its type.
 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
-    Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
+    Expr<SomeType> &&, Expr<SomeType> &&, KindsEnum defaultRealKind);
 std::optional<Expr<SomeComplex>> ConstructComplex(parser::ContextualMessages &,
     std::optional<Expr<SomeType>> &&, std::optional<Expr<SomeType>> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 
 template <typename A> Expr<TypeOf<A>> ScalarConstantToExpr(const A &x) {
   using Ty = TypeOf<A>;
-  const int kind{x.kind()};
+  const KindsEnum kind{x.kind()};
   static_assert(
       std::is_same_v<Scalar<Ty>, std::decay_t<A>>, "TypeOf<> is broken");
   return MakeConstantExpr<Ty>(kind, x);
@@ -788,23 +790,23 @@ Expr<SomeKind<CAT>> PromoteAndCombine(
 // powers.
 template <template <typename> class OPR>
 std::optional<Expr<SomeType>> NumericOperation(parser::ContextualMessages &,
-    Expr<SomeType> &&, Expr<SomeType> &&, int defaultRealKind);
+    Expr<SomeType> &&, Expr<SomeType> &&, KindsEnum defaultRealKind);
 
 extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 extern template std::optional<Expr<SomeType>> NumericOperation<Add>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 extern template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 
 std::optional<Expr<SomeType>> Negation(
     parser::ContextualMessages &, Expr<SomeType> &&);
@@ -826,7 +828,7 @@ Expr<LogicalResult> PackageRelation(
 
 inline Expr<Type<TypeCategory::Logical>> LogicalNegation(
     Expr<Type<TypeCategory::Logical>> &&x) {
-  const int kind{x.kind()};
+  const KindsEnum kind{static_cast<KindsEnum>(x.kind())};
   return AsExpr(Not{kind, std::move(x)});
 }
 
@@ -907,17 +909,17 @@ struct TypeKindVisitor {
   using Result = std::optional<Expr<SomeType>>;
   using Types = CategoryTypes<CAT>;
 
-  TypeKindVisitor(int k, VALUE &&x) : kind{k}, value{std::move(x)} {}
-  TypeKindVisitor(int k, const VALUE &x) : kind{k}, value{x} {}
+  TypeKindVisitor(KindsEnum k, VALUE &&x) : kind{k}, value{std::move(x)} {}
+  TypeKindVisitor(KindsEnum k, const VALUE &x) : kind{k}, value{x} {}
 
-  template <typename T> Result Test(int k) {
+  template <typename T> Result Test(KindsEnum k) {
     if (kind == k) {
       return AsGenericExpr(TEMPLATE<T>{k, std::move(value)});
     }
     return std::nullopt;
   }
 
-  int kind;
+  KindsEnum kind;
   VALUE value;
 };
 
@@ -927,7 +929,7 @@ struct TypeKindVisitor {
 template <TypeCategory CATEGORY, template <typename> typename WRAPPER,
     typename WRAPPED>
 common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> WrapperHelper(
-    int kind, WRAPPED &&x) {
+    KindsEnum kind, WRAPPED &&x) {
   return SearchTypes(
       TypeKindVisitor<CATEGORY, WRAPPER, WRAPPED>{kind, std::move(x)});
 }
@@ -957,7 +959,7 @@ common::IfNoLvalue<std::optional<Expr<SomeType>>, WRAPPED> TypedWrapper(
         dyType.kind(), std::move(x));
   case TypeCategory::Derived:
     return AsGenericExpr(
-        Expr<SomeDerived>{WRAPPER<SomeDerived>{0, std::move(x)}});
+        Expr<SomeDerived>{WRAPPER<SomeDerived>{NoKind, std::move(x)}});
   }
 }
 
@@ -1282,7 +1284,7 @@ class ScalarConstantExpander {
 // If the type is Character or a derived type, take the length or type
 // (resp.) from a another Constant.
 template <typename T>
-Constant<T> PackageConstant(int kind, std::vector<Scalar<T>> &&elements,
+Constant<T> PackageConstant(KindsEnum kind, std::vector<Scalar<T>> &&elements,
     const Constant<T> &reference, const ConstantSubscripts &shape) {
   if constexpr (T::category == TypeCategory::Character) {
     return Constant<T>{
diff --git a/flang/include/flang/Evaluate/type.h b/flang/include/flang/Evaluate/type.h
index 0974b87b01650..c3d3f3e2a2bc4 100644
--- a/flang/include/flang/Evaluate/type.h
+++ b/flang/include/flang/Evaluate/type.h
@@ -49,9 +49,20 @@ bool IsPassedViaDescriptor(const Symbol &);
 
 namespace Fortran::evaluate {
 
+using common::KindsEnum;
 using common::TypeCategory;
 class TargetCharacteristics;
 
+inline constexpr KindsEnum InvalidKind{KindsEnum::InvalidKind};
+inline constexpr KindsEnum NoKind{KindsEnum::NoKind};
+inline constexpr KindsEnum Kind1{KindsEnum::Kind1};
+inline constexpr KindsEnum Kind2{KindsEnum::Kind2};
+inline constexpr KindsEnum Kind3{KindsEnum::Kind3};
+inline constexpr KindsEnum Kind4{KindsEnum::Kind4};
+inline constexpr KindsEnum Kind8{KindsEnum::Kind8};
+inline constexpr KindsEnum Kind10{KindsEnum::Kind10};
+inline constexpr KindsEnum Kind16{KindsEnum::Kind16};
+
 // Specific intrinsic types are represented by specializations of
 // this class template Type<CATEGORY>.
 // This used to be Type<CATEGORY,KIND>, but now KIND is passed at runtime.
@@ -60,22 +71,22 @@ class TargetCharacteristics;
 template <TypeCategory CATEGORY> class Type;
 
 using SubscriptInteger = Type<TypeCategory::Integer>;
-inline constexpr int SubscriptIntegerKind{8};
+inline constexpr KindsEnum SubscriptIntegerKind{Kind8};
 
 using CInteger = Type<TypeCategory::Integer>;
-inline constexpr int CIntegerKind{4};
+inline constexpr KindsEnum CIntegerKind{Kind4};
 
 using LargestInt = Type<TypeCategory::Integer>;
-inline constexpr int LargestIntKind{16};
+inline constexpr KindsEnum LargestIntKind{Kind16};
 
 using LogicalResult = Type<TypeCategory::Logical>;
-inline constexpr int LogicalResultKind{4};
+inline constexpr KindsEnum LogicalResultKind{Kind4};
 
 using LargestReal = Type<TypeCategory::Real>;
-inline constexpr int LargestRealKind{16};
+inline constexpr KindsEnum LargestRealKind{Kind16};
 
 using Ascii = Type<TypeCategory::Character>;
-inline constexpr int AsciiKind{1};
+inline constexpr KindsEnum AsciiKind{Kind1};
 
 // DynamicType is meant to be suitable for use as the result type for
 // GetType() functions and member functions; consequently, it must be
@@ -87,15 +98,16 @@ inline constexpr int AsciiKind{1};
 // if one is supplied, or a known integer value.
 class DynamicType {
 public:
-  constexpr DynamicType(TypeCategory cat, int k) : category_{cat}, kind_{k} {
+  constexpr DynamicType(TypeCategory cat, KindsEnum k)
+      : category_{cat}, kind_{k} {
     CHECK(common::IsValidKindOfIntrinsicType(category_, kind_));
   }
-  DynamicType(int charKind, const semantics::ParamValue &len);
+  DynamicType(KindsEnum charKind, const semantics::ParamValue &len);
   // When a known length is presented, resolve it to its effective
   // length of zero if it is negative.
-  constexpr DynamicType(int k, std::int64_t len)
-      : category_{TypeCategory::Character}, kind_{k}, knownLength_{
-                                                          len >= 0 ? len : 0} {
+  constexpr DynamicType(KindsEnum k, std::int64_t len)
+      : category_{TypeCategory::Character}, kind_{k},
+        knownLength_{len >= 0 ? len : 0} {
     CHECK(common::IsValidKindOfIntrinsicType(category_, kind_));
   }
   explicit constexpr DynamicType(
@@ -139,8 +151,8 @@ class DynamicType {
   bool operator!=(const DynamicType &that) const { return !(*this == that); }
 
   constexpr TypeCategory category() const { return category_; }
-  constexpr int kind() const {
-    CHECK(kind_ > 0);
+  constexpr KindsEnum kind() const {
+    CHECK(kind_ > NoKind);
     return kind_;
   }
   constexpr const semantics::ParamValue *charLengthParamValue() const {
@@ -239,17 +251,23 @@ class DynamicType {
 
 private:
   // Special kind codes are used to distinguish the following Fortran types.
-  enum SpecialKind {
-    TypelessKind = -1, // BOZ actual argument to intrinsic function or pointer
-                       // argument to ASSOCIATED
-    ClassKind = -2, // CLASS(T) or CLASS(*)
-    AssumedTypeKind = -3, // TYPE(*)
-  };
+  using SpecialKind = KindsEnum;
+
+  // BOZ actual argument to intrinsic function or pointer
+  // argument to ASSOCIATED
+  static inline constexpr SpecialKind TypelessKind{KindsEnum::TypelessKind};
+
+  // CLASS(T) or CLASS(*)
+  static inline constexpr SpecialKind ClassKind{KindsEnum::ClassKind};
+
+  // TYPE(*)
+  static inline constexpr SpecialKind AssumedTypeKind{
+      KindsEnum::AssumedTypeKind};
 
   constexpr DynamicType() {}
 
   TypeCategory category_{TypeCategory::Derived}; // overridable default
-  int kind_{0};
+  KindsEnum kind_{NoKind};
   const semantics::ParamValue *charLengthParamValue_{nullptr};
 #if defined(_GLIBCXX_RELEASE) && _GLIBCXX_RELEASE == 7
   // GCC 7's optional<> lacks a constexpr operator=
@@ -268,10 +286,10 @@ const semantics::DerivedTypeSpec *GetParentTypeSpec(
     const semantics::DerivedTypeSpec &);
 
 template <TypeCategory CATEGORY> struct TypeBase {
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
   static constexpr TypeCategory category{CATEGORY};
-  explicit constexpr TypeBase(int kind) : kind_{kind} {}
+  explicit constexpr TypeBase(KindsEnum kind) : kind_{kind} {}
   constexpr bool operator==(const TypeBase &that) const {
     return kind_ == that.kind_;
   }
@@ -279,7 +297,7 @@ template <TypeCategory CATEGORY> struct TypeBase {
   std::string AsFortran() const { return GetType().AsFortran(); }
 
 private:
-  int kind_;
+  KindsEnum kind_;
 };
 
 template <>
@@ -417,8 +435,10 @@ template <> class SomeKind<TypeCategory::Derived> {
   using Scalar = StructureConstructor;
 
   // Argument provided for having the same signature as Types. Derived types
-  // don't have a kind, it is expected to be zero.
-  constexpr explicit SomeKind(int kind = 0) { CHECK(kind == 0); }
+  // don't have a kind, it is expected to be NoKind.
+  constexpr explicit SomeKind(KindsEnum kind = NoKind) {
+    CHECK(kind == NoKind);
+  }
   constexpr explicit SomeKind(const semantics::DerivedTypeSpec &dts)
       : derivedTypeSpec_{&dts} {}
   constexpr explicit SomeKind(const DynamicType &dt)
@@ -477,7 +497,7 @@ template <typename CONST> struct TypeOfHelper {
 
 template <typename CONST> using TypeOf = typename TypeOfHelper<CONST>::type;
 
-int SelectedCharKind(const std::string &, int defaultKind);
+int SelectedCharKind(const std::string &, KindsEnum defaultKind);
 // SelectedIntKind and SelectedRealKind are now member functions of
 // TargetCharactertics.
 
@@ -514,14 +534,14 @@ bool AreSameDerivedTypeIgnoringSequence(
     if constexpr (std::is_same_v<TY, SomeDerived> || \
         std::is_same_v<TY, SomeKind<TypeCategory::Derived>> || \
         std::is_same_v<TY, SomeType>) { \
-      CHECK((KIND) == 0 && "Type does not have a kind"); \
+      CHECK((KIND) == NoKind && "Type does not have a kind"); \
     } else if constexpr (std::is_same_v<TY, Type<TypeCategory::Integer>> || \
         std::is_same_v<TY, Type<TypeCategory::Unsigned>> || \
         std::is_same_v<TY, Type<TypeCategory::Real>> || \
         std::is_same_v<TY, Type<TypeCategory::Complex>> || \
         std::is_same_v<TY, Type<TypeCategory::Logical>> || \
         std::is_same_v<TY, Type<TypeCategory::Character>>) { \
-      CHECK((KIND) != 0 && "Type must come with a kind"); \
+      CHECK((KIND) != NoKind && "Type must come with a kind"); \
     } else { \
       static_assert(false, "Don't know whether TY should have a kind"); \
     } \
@@ -568,30 +588,6 @@ bool AreSameDerivedTypeIgnoringSequence(
   FOR_EACH_INTRINSIC_KIND(PREFIX, SUFFIX) \
   FOR_EACH_CATEGORY_TYPE(PREFIX, SUFFIX)
 
-/// Iterable lists of valid kinds for each TypeCategory for use by SearchTypes.
-template <TypeCategory CAT> struct KindsByType;
-template <> struct KindsByType<TypeCategory::Integer> {
-  static constexpr int kinds[] = FORTRAN_INTEGER_KINDS;
-};
-template <> struct KindsByType<TypeCategory::Unsigned> {
-  static constexpr int kinds[] = FORTRAN_UNSIGNED_KINDS;
-};
-template <> struct KindsByType<TypeCategory::Real> {
-  static constexpr int kinds[] = FORTRAN_REAL_KINDS;
-};
-template <> struct KindsByType<TypeCategory::Complex> {
-  static constexpr int kinds[] = FORTRAN_REAL_KINDS;
-};
-template <> struct KindsByType<TypeCategory::Logical> {
-  static constexpr int kinds[] = FORTRAN_LOGICAL_KINDS;
-};
-template <> struct KindsByType<TypeCategory::Character> {
-  static constexpr int kinds[] = FORTRAN_CHARACTER_KINDS;
-};
-template <> struct KindsByType<TypeCategory::Derived> {
-  static constexpr int kinds[] = {0};
-};
-
 // Given a VISITOR class of the general form
 //   struct VISITOR {
 //     using Result = ...;
@@ -609,7 +605,7 @@ common::IfNoLvalue<typename VISITOR::Result, VISITOR> SearchTypesHelper(
   using Tuple = typename VISITOR::Types;
   if constexpr (J < std::tuple_size_v<Tuple>) {
     using TYPE = std::tuple_element_t<J, Tuple>;
-    for (int kind : evaluate::KindsByType<TYPE::category>::kinds) {
+    for (KindsEnum kind : common::KindsByType<TYPE::category>::kinds) {
       if (auto result{visitor.template Test<TYPE>(kind)}) {
         return result;
       }
diff --git a/flang/include/flang/Evaluate/typekind-traits.h b/flang/include/flang/Evaluate/typekind-traits.h
index 38aef267d8b61..8d2a2dddabb36 100644
--- a/flang/include/flang/Evaluate/typekind-traits.h
+++ b/flang/include/flang/Evaluate/typekind-traits.h
@@ -46,14 +46,17 @@ template <common::TypeCategory CAT, int KIND> struct TypeKind;
 template <int KIND> struct TypeKind<common::TypeCategory::Integer, KIND> {
   static constexpr common::TypeCategory category{common::TypeCategory::Integer};
   static constexpr int kind{KIND};
-  static constexpr int bits{value::IntegerValue::bits(KIND)};
-  static constexpr int bytesStored{value::IntegerValue::bytesStored(kind)};
+  static constexpr KindsEnum kindsEnum{static_cast<KindsEnum>(KIND)};
+  static constexpr int bits{value::IntegerValue::bits(kindsEnum)};
+  static constexpr int bytesStored{value::IntegerValue::bytesStored(kindsEnum)};
   using UnsignedT = common::HostUnsignedIntType<bits>;
   using SignedT = common::HostSignedIntType<bits>;
   using HostT = SignedT;
   using Scalar = value::IntegerValue;
   using FortranType = Fortran::evaluate::Type<common::TypeCategory::Integer>;
-  static constexpr DynamicType GetType() { return DynamicType{category, kind}; }
+  static constexpr DynamicType GetType() {
+    return DynamicType{category, kindsEnum};
+  }
 };
 
 using IntegerKindTypes = std::tuple<TypeKind<TypeCategory::Integer, 1>,
@@ -64,14 +67,17 @@ template <int KIND> struct TypeKind<common::TypeCategory::Unsigned, KIND> {
   static constexpr common::TypeCategory category{
       common::TypeCategory::Unsigned};
   static constexpr int kind{KIND};
-  static constexpr int bits{value::IntegerValue::bits(KIND)};
-  static constexpr int bytesStored{value::IntegerValue::bytesStored(kind)};
+  static constexpr KindsEnum kindsEnum{static_cast<KindsEnum>(KIND)};
+  static constexpr int bits{value::IntegerValue::bits(kindsEnum)};
+  static constexpr int bytesStored{value::IntegerValue::bytesStored(kindsEnum)};
   using UnsignedT = common::HostUnsignedIntType<bits>;
   using SignedT = common::HostSignedIntType<bits>;
   using HostT = UnsignedT;
   using Scalar = value::IntegerValue;
   using FortranType = Fortran::evaluate::Type<common::TypeCategory::Unsigned>;
-  static constexpr DynamicType GetType() { return DynamicType{category, kind}; }
+  static constexpr DynamicType GetType() {
+    return DynamicType{category, kindsEnum};
+  }
 };
 
 using UnsignedKindTypes = std::tuple<TypeKind<TypeCategory::Unsigned, 1>,
@@ -81,14 +87,17 @@ using UnsignedKindTypes = std::tuple<TypeKind<TypeCategory::Unsigned, 1>,
 template <int KIND> struct TypeKind<common::TypeCategory::Logical, KIND> {
   static constexpr common::TypeCategory category{common::TypeCategory::Logical};
   static constexpr int kind{KIND};
-  static constexpr int bits{value::LogicalValue::bits(KIND)};
-  static constexpr int bytesStored{value::IntegerValue::bytesStored(kind)};
+  static constexpr KindsEnum kindsEnum{static_cast<KindsEnum>(KIND)};
+  static constexpr int bits{value::LogicalValue::bits(kindsEnum)};
+  static constexpr int bytesStored{value::IntegerValue::bytesStored(kindsEnum)};
   using UnsignedT = common::HostUnsignedIntType<bits>;
   using SignedT = common::HostSignedIntType<bits>;
   using HostT = UnsignedT;
   using Scalar = value::LogicalValue;
   using FortranType = Fortran::evaluate::Type<common::TypeCategory::Logical>;
-  static constexpr DynamicType GetType() { return DynamicType{category, kind}; }
+  static constexpr DynamicType GetType() {
+    return DynamicType{category, kindsEnum};
+  }
 };
 
 using LogicalKindTypes = std::tuple<TypeKind<TypeCategory::Logical, 1>,
@@ -111,14 +120,17 @@ template <> struct RealHostType<64> {
 template <int KIND> struct TypeKind<common::TypeCategory::Real, KIND> {
   static constexpr common::TypeCategory category{common::TypeCategory::Real};
   static constexpr int kind{KIND};
-  static constexpr int bits{value::RealValue::bits(KIND)};
-  static constexpr int bytesStored{value::IntegerValue::bytesStored(kind)};
+  static constexpr KindsEnum kindsEnum{static_cast<KindsEnum>(KIND)};
+  static constexpr int bits{value::RealValue::bits(kindsEnum)};
+  static constexpr int bytesStored{value::IntegerValue::bytesStored(kindsEnum)};
   using UnsignedT = common::HostUnsignedIntType<bits>;
   using SignedT = common::HostSignedIntType<bits>;
   using HostT = typename detail::RealHostType<bits>::type;
   using Scalar = value::RealValue;
   using FortranType = Fortran::evaluate::Type<common::TypeCategory::Real>;
-  static constexpr DynamicType GetType() { return DynamicType{category, kind}; }
+  static constexpr DynamicType GetType() {
+    return DynamicType{category, kindsEnum};
+  }
 };
 
 using RealKindTypes =
@@ -129,10 +141,13 @@ using RealKindTypes =
 template <int KIND> struct TypeKind<common::TypeCategory::Complex, KIND> {
   static constexpr common::TypeCategory category{common::TypeCategory::Complex};
   static constexpr int kind{KIND};
-  static constexpr int bytesStored{value::IntegerValue::bytesStored(kind)};
+  static constexpr KindsEnum kindsEnum{static_cast<KindsEnum>(KIND)};
+  static constexpr int bytesStored{value::IntegerValue::bytesStored(kindsEnum)};
   using FortranType = Fortran::evaluate::Type<common::TypeCategory::Complex>;
   using Scalar = value::ComplexValue;
-  static constexpr DynamicType GetType() { return DynamicType{category, kind}; }
+  static constexpr DynamicType GetType() {
+    return DynamicType{category, kindsEnum};
+  }
   using Part = TypeKind<common::TypeCategory::Real, KIND>;
 };
 
@@ -145,39 +160,48 @@ template <> struct TypeKind<common::TypeCategory::Character, 1> {
   static constexpr common::TypeCategory category{
       common::TypeCategory::Character};
   static constexpr int kind{1};
-  static constexpr int bytesStored{value::IntegerValue::bytesStored(kind)};
+  static constexpr KindsEnum kindsEnum{Kind1};
+  static constexpr int bytesStored{value::IntegerValue::bytesStored(kindsEnum)};
   using CharT = char;
   using StringT = std::basic_string<CharT>;
   using HostT = void;
   using Scalar = value::CharacterValue;
   using FortranType = Fortran::evaluate::Type<common::TypeCategory::Character>;
-  static constexpr DynamicType GetType() { return DynamicType{category, kind}; }
+  static constexpr DynamicType GetType() {
+    return DynamicType{category, kindsEnum};
+  }
 };
 
 template <> struct TypeKind<common::TypeCategory::Character, 2> {
   static constexpr common::TypeCategory category{
       common::TypeCategory::Character};
   static constexpr int kind{2};
-  static constexpr int bytesStored{value::IntegerValue::bytesStored(kind)};
+  static constexpr KindsEnum kindsEnum{Kind2};
+  static constexpr int bytesStored{value::IntegerValue::bytesStored(kindsEnum)};
   using CharT = char16_t;
   using StringT = std::basic_string<CharT>;
   using HostT = void;
   using Scalar = value::CharacterValue;
   using FortranType = Fortran::evaluate::Type<common::TypeCategory::Character>;
-  static constexpr DynamicType GetType() { return DynamicType{category, kind}; }
+  static constexpr DynamicType GetType() {
+    return DynamicType{category, kindsEnum};
+  }
 };
 
 template <> struct TypeKind<common::TypeCategory::Character, 4> {
   static constexpr common::TypeCategory category{
       common::TypeCategory::Character};
   static constexpr int kind{4};
-  static constexpr int bytesStored{value::IntegerValue::bytesStored(kind)};
+  static constexpr KindsEnum kindsEnum{Kind4};
+  static constexpr int bytesStored{value::IntegerValue::bytesStored(kindsEnum)};
   using CharT = char32_t;
   using StringT = std::basic_string<CharT>;
   using HostT = void;
   using Scalar = value::CharacterValue;
   using FortranType = Fortran::evaluate::Type<common::TypeCategory::Character>;
-  static constexpr DynamicType GetType() { return DynamicType{category, kind}; }
+  static constexpr DynamicType GetType() {
+    return DynamicType{category, kindsEnum};
+  }
 };
 
 using CharacterKindTypes = std::tuple<TypeKind<TypeCategory::Character, 1>,
diff --git a/flang/include/flang/Evaluate/variable.h b/flang/include/flang/Evaluate/variable.h
index d4b861006ec36..bf3ef0c88e67c 100644
--- a/flang/include/flang/Evaluate/variable.h
+++ b/flang/include/flang/Evaluate/variable.h
@@ -136,8 +136,8 @@ class NamedEntity {
 class TypeParamInquiry {
 public:
   using Result = SubscriptInteger;
-  static constexpr int ResultKind{SubscriptIntegerKind};
-  constexpr int kind() const { return SubscriptIntegerKind; }
+  static constexpr KindsEnum ResultKind{SubscriptIntegerKind};
+  constexpr KindsEnum kind() const { return ResultKind; }
   static constexpr DynamicType GetType() {
     return DynamicType{TypeCategory::Integer, SubscriptIntegerKind};
   }
@@ -398,38 +398,38 @@ template <typename T> class Designator {
   static_assert(
       IsSpecificIntrinsicType<Result> || std::is_same_v<Result, SomeDerived>);
 
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
   CLASS_BOILERPLATE(Designator)
   template <typename _A>
-  explicit Designator(int kind, const _A &x) : u{x}, kind_{kind} {
+  explicit Designator(KindsEnum kind, const _A &x) : u{x}, kind_{kind} {
     CHECK_KIND(kind, T);
   }
   template <typename _A, typename = common::NoLvalue<_A>>
-  explicit Designator(int kind, _A &&x) : u(std::move(x)), kind_{kind} {
+  explicit Designator(KindsEnum kind, _A &&x) : u(std::move(x)), kind_{kind} {
     CHECK_KIND(kind, T);
   }
   template <typename _A, typename U = T,
       typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
-  explicit Designator(const _A &x) : Designator(0, x) {}
+  explicit Designator(const _A &x) : Designator(NoKind, x) {}
   template <typename _A, typename U = T, typename = common::NoLvalue<_A>,
       typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
-  explicit Designator(_A &&x) : Designator(0, std::move(x)) {}
+  explicit Designator(_A &&x) : Designator(NoKind, std::move(x)) {}
   bool operator==(const Designator &) const;
-  Designator(int kind, const DataRef &that)
+  Designator(KindsEnum kind, const DataRef &that)
       : u{common::CopyVariant<Variant>(that.u)}, kind_{kind} {
     CHECK_KIND(kind, T);
   }
-  Designator(int kind, DataRef &&that)
+  Designator(KindsEnum kind, DataRef &&that)
       : u{common::MoveVariant<Variant>(std::move(that.u))}, kind_{kind} {
     CHECK_KIND(kind, T);
   }
   template <typename U = T,
       typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
-  Designator(const DataRef &that) : Designator(0, that) {}
+  Designator(const DataRef &that) : Designator(NoKind, that) {}
   template <typename U = T,
       typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
-  Designator(DataRef &&that) : Designator(0, std::move(that)) {}
+  Designator(DataRef &&that) : Designator(NoKind, std::move(that)) {}
 
   std::optional<DynamicType> GetType() const;
   int Rank() const;
@@ -442,7 +442,7 @@ template <typename T> class Designator {
   Variant u;
 
 private:
-  int kind_;
+  KindsEnum kind_;
 };
 
 FOR_EACH_CHARACTER_KIND(extern template class Designator, )
@@ -450,7 +450,7 @@ FOR_EACH_CHARACTER_KIND(extern template class Designator, )
 class DescriptorInquiry {
 public:
   using Result = SubscriptInteger;
-  static constexpr int kind() { return SubscriptIntegerKind; }
+  static constexpr KindsEnum kind() { return SubscriptIntegerKind; }
   static constexpr DynamicType GetType() {
     return DynamicType{TypeCategory::Integer, SubscriptIntegerKind};
   }
@@ -483,7 +483,7 @@ class DescriptorInquiry {
 class RankOneBoundElement {
 public:
   using Result = SubscriptInteger;
-  static constexpr int ResultKind{SubscriptIntegerKind};
+  static constexpr KindsEnum ResultKind{SubscriptIntegerKind};
   CLASS_BOILERPLATE(RankOneBoundElement)
   RankOneBoundElement(
       common::CopyableIndirection<Expr<SubscriptInteger>> &&e, int dim)
@@ -495,7 +495,7 @@ class RankOneBoundElement {
   Expr<SubscriptInteger> &base() { return base_.value(); }
   int dimension() const { return dimension_; }
 
-  static constexpr int kind() { return ResultKind; }
+  static constexpr KindsEnum kind() { return ResultKind; }
 
   static constexpr int Rank() { return 0; } // always scalar
   static constexpr int Corank() { return 0; }
diff --git a/flang/include/flang/Frontend/TargetOptions.h b/flang/include/flang/Frontend/TargetOptions.h
index f6e5634d5a995..3f079aad688d6 100644
--- a/flang/include/flang/Frontend/TargetOptions.h
+++ b/flang/include/flang/Frontend/TargetOptions.h
@@ -18,6 +18,7 @@
 #ifndef FORTRAN_FRONTEND_TARGETOPTIONS_H
 #define FORTRAN_FRONTEND_TARGETOPTIONS_H
 
+#include "flang/Common/type-kinds.h"
 #include <string>
 #include <vector>
 
@@ -43,10 +44,10 @@ class TargetOptions {
   std::vector<std::string> featuresAsWritten;
 
   /// The real KINDs disabled for this target
-  std::vector<int> disabledRealKinds;
+  std::vector<common::KindsEnum> disabledRealKinds;
 
   /// The integer KINDs disabled for this target
-  std::vector<int> disabledIntegerKinds;
+  std::vector<common::KindsEnum> disabledIntegerKinds;
 
   /// Extended Altivec ABI on AIX
   bool EnableAIXExtendedAltivecABI;
diff --git a/flang/include/flang/Lower/Mangler.h b/flang/include/flang/Lower/Mangler.h
index a243b7824dfcd..e0f2d6a5c1d9f 100644
--- a/flang/include/flang/Lower/Mangler.h
+++ b/flang/include/flang/Lower/Mangler.h
@@ -69,19 +69,17 @@ template <Fortran::common::TypeCategory TC>
 std::string mangleArrayLiteral(
     mlir::Type,
     const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC>> &x) {
-  const int kind{x.kind()};
   return mangleArrayLiteral(x.values().size() * sizeof(x.values()[0]),
-                            x.shape(), TC, kind);
+                            x.shape(), TC, static_cast<int>(x.kind()));
 }
 
 inline std::string mangleArrayLiteral(
     mlir::Type,
     const Fortran::evaluate::Constant<
         Fortran::evaluate::Type<Fortran::common::TypeCategory::Character>> &x) {
-  const int kind{x.kind()};
   return mangleArrayLiteral(x.values().size() * sizeof(x.values()[0]),
                             x.shape(), Fortran::common::TypeCategory::Character,
-                            kind, x.LEN());
+                            static_cast<int>(x.kind()), x.LEN());
 }
 
 inline std::string mangleArrayLiteral(
diff --git a/flang/include/flang/Lower/Support/Utils.h b/flang/include/flang/Lower/Support/Utils.h
index 555cdec21774e..f9efdc10738e0 100644
--- a/flang/include/flang/Lower/Support/Utils.h
+++ b/flang/include/flang/Lower/Support/Utils.h
@@ -66,7 +66,7 @@ static Fortran::lower::SomeExpr
 ignoreEvConvert(const Fortran::evaluate::Convert<
                 Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>,
                 FROM> &x) {
-  CHECK(x.kind() == 8);
+  CHECK(x.kind() == Fortran::common::KindsEnum::Kind8);
   return toEvExpr(x.left());
 }
 template <typename A>
@@ -80,7 +80,7 @@ static Fortran::lower::SomeExpr ignoreEvConvert(const A &x) {
 inline Fortran::lower::SomeExpr ignoreEvConvert(
     const Fortran::evaluate::Expr<
         Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>> &x) {
-  CHECK(x.kind() == 8);
+  CHECK(x.kind() == Fortran::common::KindsEnum::Kind8);
   return Fortran::common::visit(
       [](const auto &v) { return ignoreEvConvert(v); }, x.u);
 }
diff --git a/flang/include/flang/Semantics/expression.h b/flang/include/flang/Semantics/expression.h
index 59cd424c4dae9..11a7a3fa84e82 100644
--- a/flang/include/flang/Semantics/expression.h
+++ b/flang/include/flang/Semantics/expression.h
@@ -137,15 +137,15 @@ class ExpressionAnalyzer {
     return Say(parser::FindSourceLocation(parsed), std::forward<A>(args)...);
   }
 
-  int GetDefaultKind(common::TypeCategory);
+  KindsEnum GetDefaultKind(common::TypeCategory);
   DynamicType GetDefaultKindOfType(common::TypeCategory);
 
   // Return false and emit error if these checks fail:
-  bool CheckIntrinsicKind(TypeCategory, std::int64_t kind);
+  bool CheckIntrinsicKind(TypeCategory, KindsEnum kind);
   bool CheckIntrinsicSize(TypeCategory, std::int64_t size);
 
   // Manage a set of active implied DO loops.
-  bool AddImpliedDo(parser::CharBlock, int kind);
+  bool AddImpliedDo(parser::CharBlock, KindsEnum kind);
   void RemoveImpliedDo(parser::CharBlock);
 
   // When the argument is the name of an active implied DO index, returns
@@ -272,7 +272,7 @@ class ExpressionAnalyzer {
   }
 
 protected:
-  int IntegerTypeSpecKind(const parser::IntegerTypeSpec &);
+  KindsEnum IntegerTypeSpecKind(const parser::IntegerTypeSpec &);
 
 private:
   // Allows an Expr to be a null pointer.
@@ -329,13 +329,13 @@ class ExpressionAnalyzer {
   }
 
   // Analysis subroutines
-  int AnalyzeKindParam(
-      const std::optional<parser::KindParam> &, int defaultKind);
+  KindsEnum AnalyzeKindParam(
+      const std::optional<parser::KindParam> &, KindsEnum defaultKind);
   template <typename PARSED>
   MaybeExpr ExprOrVariable(const PARSED &, parser::CharBlock source);
   template <typename TYPES, TypeCategory CAT, typename PARSED>
   MaybeExpr IntLiteralConstant(const PARSED &, bool isNegated = false);
-  MaybeExpr AnalyzeString(std::string &&, int kind);
+  MaybeExpr AnalyzeString(std::string &&, KindsEnum kind);
   std::optional<Expr<SubscriptInteger>> AsSubscript(MaybeExpr &&);
   std::optional<Expr<SubscriptInteger>> TripletPart(
       const std::optional<parser::Subscript> &);
diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h
index 28e5ccd9ad409..fb39a08d695be 100644
--- a/flang/include/flang/Semantics/semantics.h
+++ b/flang/include/flang/Semantics/semantics.h
@@ -82,11 +82,13 @@ class SemanticsContext {
   const std::string &openAccDefaultNoneScalarsStrictDisableOption() const {
     return openAccDefaultNoneScalarsStrictDisableOption_;
   }
-  int GetDefaultKind(TypeCategory) const;
-  int doublePrecisionKind() const {
+  KindsEnum GetDefaultKind(TypeCategory) const;
+  KindsEnum doublePrecisionKind() const {
     return defaultKinds_.doublePrecisionKind();
   }
-  int quadPrecisionKind() const { return defaultKinds_.quadPrecisionKind(); }
+  KindsEnum quadPrecisionKind() const {
+    return defaultKinds_.quadPrecisionKind();
+  }
   bool IsEnabled(common::LanguageFeature feature) const {
     return languageFeatures_.IsEnabled(feature);
   }
@@ -193,8 +195,9 @@ class SemanticsContext {
     return *this;
   }
 
-  const DeclTypeSpec &MakeNumericType(TypeCategory, int kind = 0);
-  const DeclTypeSpec &MakeLogicalType(int kind = 0);
+  const DeclTypeSpec &MakeNumericType(
+      TypeCategory, KindsEnum kind = KindsEnum::NoKind);
+  const DeclTypeSpec &MakeLogicalType(KindsEnum kind = KindsEnum::NoKind);
 
   std::size_t maxErrors() const { return maxErrors_; }
 
diff --git a/flang/include/flang/Semantics/type.h b/flang/include/flang/Semantics/type.h
index 2f216e3bc0cd6..873f61631cf70 100644
--- a/flang/include/flang/Semantics/type.h
+++ b/flang/include/flang/Semantics/type.h
@@ -45,6 +45,7 @@ class Symbol;
 /// i.e. a range of lower-case characters with provenance.
 using SourceName = parser::CharBlock;
 using TypeCategory = common::TypeCategory;
+using KindsEnum = common::KindsEnum;
 using SomeExpr = evaluate::Expr<evaluate::SomeType>;
 using MaybeExpr = std::optional<SomeExpr>;
 using SomeIntExpr = evaluate::Expr<evaluate::SomeInteger>;
@@ -53,7 +54,7 @@ using SubscriptIntExpr = evaluate::Expr<evaluate::SubscriptInteger>;
 using MaybeSubscriptIntExpr = std::optional<SubscriptIntExpr>;
 using KindExpr = SubscriptIntExpr;
 
-KindExpr MakeKindExpr(int v);
+using evaluate::MakeKindExpr;
 
 // An array spec bound: an explicit integer expression, assumed size
 // or implied shape(*), or assumed or deferred shape(:).  In the absence
diff --git a/flang/include/flang/Support/default-kinds.h b/flang/include/flang/Support/default-kinds.h
index ab1422d63af30..ecd7b4132fc0b 100644
--- a/flang/include/flang/Support/default-kinds.h
+++ b/flang/include/flang/Support/default-kinds.h
@@ -10,6 +10,7 @@
 #define FORTRAN_SUPPORT_DEFAULT_KINDS_H_
 
 #include "Fortran.h"
+#include "flang/Common/type-kinds.h"
 #include <cstdint>
 
 namespace Fortran::common {
@@ -26,21 +27,21 @@ using ConstantSubscript = std::int64_t;
 class IntrinsicTypeDefaultKinds {
 public:
   IntrinsicTypeDefaultKinds();
-  int subscriptIntegerKind() const { return subscriptIntegerKind_; }
-  int sizeIntegerKind() const { return sizeIntegerKind_; }
-  int doublePrecisionKind() const { return doublePrecisionKind_; }
-  int quadPrecisionKind() const { return quadPrecisionKind_; }
-
-  IntrinsicTypeDefaultKinds &set_defaultIntegerKind(int);
-  IntrinsicTypeDefaultKinds &set_subscriptIntegerKind(int);
-  IntrinsicTypeDefaultKinds &set_sizeIntegerKind(int);
-  IntrinsicTypeDefaultKinds &set_defaultRealKind(int);
-  IntrinsicTypeDefaultKinds &set_doublePrecisionKind(int);
-  IntrinsicTypeDefaultKinds &set_quadPrecisionKind(int);
-  IntrinsicTypeDefaultKinds &set_defaultCharacterKind(int);
-  IntrinsicTypeDefaultKinds &set_defaultLogicalKind(int);
-
-  int GetDefaultKind(TypeCategory) const;
+  KindsEnum subscriptIntegerKind() const { return subscriptIntegerKind_; }
+  KindsEnum sizeIntegerKind() const { return sizeIntegerKind_; }
+  KindsEnum doublePrecisionKind() const { return doublePrecisionKind_; }
+  KindsEnum quadPrecisionKind() const { return quadPrecisionKind_; }
+
+  IntrinsicTypeDefaultKinds &set_defaultIntegerKind(KindsEnum);
+  IntrinsicTypeDefaultKinds &set_subscriptIntegerKind(KindsEnum);
+  IntrinsicTypeDefaultKinds &set_sizeIntegerKind(KindsEnum);
+  IntrinsicTypeDefaultKinds &set_defaultRealKind(KindsEnum);
+  IntrinsicTypeDefaultKinds &set_doublePrecisionKind(KindsEnum);
+  IntrinsicTypeDefaultKinds &set_quadPrecisionKind(KindsEnum);
+  IntrinsicTypeDefaultKinds &set_defaultCharacterKind(KindsEnum);
+  IntrinsicTypeDefaultKinds &set_defaultLogicalKind(KindsEnum);
+
+  KindsEnum GetDefaultKind(TypeCategory) const;
 
 private:
   // Default REAL just simply has to be IEEE-754 single precision today.
@@ -48,14 +49,17 @@ class IntrinsicTypeDefaultKinds {
   // and default LOGICAL intrinsic types also have to occupy one numeric
   // storage unit, so their kinds are also forced.  Default COMPLEX must always
   // comprise two default REAL components.
-  int defaultIntegerKind_{4};
-  int subscriptIntegerKind_{8};
-  int sizeIntegerKind_{4}; // SIZE(), UBOUND(), &c. default KIND=
-  int defaultRealKind_{defaultIntegerKind_};
-  int doublePrecisionKind_{2 * defaultRealKind_};
-  int quadPrecisionKind_{2 * doublePrecisionKind_};
-  int defaultCharacterKind_{1};
-  int defaultLogicalKind_{defaultIntegerKind_};
+  KindsEnum defaultIntegerKind_{KindsEnum::Kind4};
+  KindsEnum subscriptIntegerKind_{KindsEnum::Kind8};
+  KindsEnum sizeIntegerKind_{
+      KindsEnum::Kind4}; // SIZE(), UBOUND(), &c. default KIND=
+  KindsEnum defaultRealKind_{defaultIntegerKind_};
+  KindsEnum doublePrecisionKind_{
+      static_cast<KindsEnum>(2 * static_cast<int>(defaultRealKind_))};
+  KindsEnum quadPrecisionKind_{
+      static_cast<KindsEnum>(2 * static_cast<int>(doublePrecisionKind_))};
+  KindsEnum defaultCharacterKind_{KindsEnum::Kind1};
+  KindsEnum defaultLogicalKind_{defaultIntegerKind_};
 };
 } // namespace Fortran::common
 #endif // FORTRAN_SUPPORT_DEFAULT_KINDS_H_
diff --git a/flang/include/flang/Tools/TargetSetup.h b/flang/include/flang/Tools/TargetSetup.h
index 47f886141b002..74a4f16421ab0 100644
--- a/flang/include/flang/Tools/TargetSetup.h
+++ b/flang/include/flang/Tools/TargetSetup.h
@@ -15,6 +15,7 @@
 #include "llvm/Target/TargetMachine.h"
 
 namespace Fortran::tools {
+using common::KindsEnum;
 
 [[maybe_unused]] inline static void setUpTargetCharacteristics(
     Fortran::evaluate::TargetCharacteristics &targetCharacteristics,
@@ -25,13 +26,13 @@ namespace Fortran::tools {
   const llvm::Triple &targetTriple{targetMachine.getTargetTriple()};
 
   if (targetTriple.getArch() == llvm::Triple::ArchType::x86_64) {
-    targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/3);
-    targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/4);
-    targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/8);
+    targetCharacteristics.set_hasSubnormalFlushingControl(KindsEnum::Kind3);
+    targetCharacteristics.set_hasSubnormalFlushingControl(KindsEnum::Kind4);
+    targetCharacteristics.set_hasSubnormalFlushingControl(KindsEnum::Kind8);
     // ieee_denorm exception support is nonstandard.
-    targetCharacteristics.set_hasSubnormalExceptionSupport(/*kind=*/3);
-    targetCharacteristics.set_hasSubnormalExceptionSupport(/*kind=*/4);
-    targetCharacteristics.set_hasSubnormalExceptionSupport(/*kind=*/8);
+    targetCharacteristics.set_hasSubnormalExceptionSupport(KindsEnum::Kind3);
+    targetCharacteristics.set_hasSubnormalExceptionSupport(KindsEnum::Kind4);
+    targetCharacteristics.set_hasSubnormalExceptionSupport(KindsEnum::Kind8);
   }
 
   if (targetTriple.isARM() || targetTriple.isAArch64()) {
@@ -40,9 +41,9 @@ namespace Fortran::tools {
         evaluate::IeeeFeature::Halting, false);
     targetCharacteristics.set_ieeeFeature(
         evaluate::IeeeFeature::Standard, false);
-    targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/3);
-    targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/4);
-    targetCharacteristics.set_hasSubnormalFlushingControl(/*kind=*/8);
+    targetCharacteristics.set_hasSubnormalFlushingControl(KindsEnum::Kind3);
+    targetCharacteristics.set_hasSubnormalFlushingControl(KindsEnum::Kind4);
+    targetCharacteristics.set_hasSubnormalFlushingControl(KindsEnum::Kind8);
   }
 
   switch (targetTriple.getArch()) {
@@ -51,9 +52,9 @@ namespace Fortran::tools {
     break;
   default:
     targetCharacteristics.DisableType(
-        Fortran::common::TypeCategory::Real, /*kind=*/10);
+        Fortran::common::TypeCategory::Real, KindsEnum::Kind10);
     targetCharacteristics.DisableType(
-        Fortran::common::TypeCategory::Complex, /*kind=*/10);
+        Fortran::common::TypeCategory::Complex, KindsEnum::Kind10);
     break;
   }
 
@@ -68,9 +69,10 @@ namespace Fortran::tools {
 #endif
 
   if constexpr (!f128Support) {
-    targetCharacteristics.DisableType(Fortran::common::TypeCategory::Real, 16);
     targetCharacteristics.DisableType(
-        Fortran::common::TypeCategory::Complex, 16);
+        Fortran::common::TypeCategory::Real, KindsEnum::Kind16);
+    targetCharacteristics.DisableType(
+        Fortran::common::TypeCategory::Complex, KindsEnum::Kind16);
   }
 
   for (auto realKind : targetOptions.disabledRealKinds) {
@@ -95,7 +97,7 @@ namespace Fortran::tools {
 
   // Currently the integer kind happens to be the same as the byte size
   targetCharacteristics.set_integerKindForPointer(
-      targetTriple.getArchPointerBitWidth() / 8);
+      static_cast<KindsEnum>(targetTriple.getArchPointerBitWidth() / 8));
 
   // TODO: use target machine data layout to set-up the target characteristics
   // type size and alignment info.
diff --git a/flang/lib/Evaluate/character-value-impl.cpp b/flang/lib/Evaluate/character-value-impl.cpp
index 44f5b1c46d0b5..bd031f9ea0ab1 100644
--- a/flang/lib/Evaluate/character-value-impl.cpp
+++ b/flang/lib/Evaluate/character-value-impl.cpp
@@ -15,14 +15,15 @@
 
 namespace Fortran::evaluate::value {
 
-CharacterValueImpl::CharacterValueImpl(int kind, std::size_t n, char32_t c) {
+CharacterValueImpl::CharacterValueImpl(
+    KindsEnum kind, std::size_t n, char32_t c) {
   withCharProto(kind, [this, n, c](auto ct) {
     using CharT = std::decay_t<decltype(ct)>;
     storage_ = std::basic_string<CharT>(n, static_cast<CharT>(c));
   });
 }
 
-CharacterValueImpl CharacterValueImpl::Zero(int kind) {
+CharacterValueImpl CharacterValueImpl::Zero(KindsEnum kind) {
   return withCharProto(kind, [kind](auto c) {
     using Char = std::decay_t<decltype(c)>;
     return CharacterValueImpl{kind, std::basic_string<Char>{}};
@@ -30,7 +31,7 @@ CharacterValueImpl CharacterValueImpl::Zero(int kind) {
 }
 
 CharacterValueImpl CharacterValueImpl::FromRawBytes(
-    int kind, const void *raw, size_t size) {
+    KindsEnum kind, const void *raw, size_t size) {
   return withCharProto(kind, [kind, raw, size](auto charProto) {
     using CharT = decltype(charProto);
     CHECK(size % sizeof(CharT) == 0);
@@ -43,7 +44,7 @@ CharacterValueImpl CharacterValueImpl::FromRawBytes(
 }
 
 void CharacterValueImpl::print(llvm::raw_ostream &os) const {
-  os << kind() << '_';
+  os << static_cast<int>(kind()) << '_';
   withStdString(
       [&](const auto &s) { os << parser::QuoteCharacterLiteral(s, true); });
 }
@@ -178,7 +179,7 @@ bool CharacterValueImpl::operator==(const CharacterValueImpl &y) const {
       this->storage_, y.storage_);
 }
 
-void CharacterValueImpl::assign(int kind, std::size_t n, char32_t c) {
+void CharacterValueImpl::assign(KindsEnum kind, std::size_t n, char32_t c) {
   return withCharProto(kind, [this, n, c](auto ct) {
     using CharT = decltype(ct);
     storage_ = std::basic_string<CharT>(n, static_cast<CharT>(c));
@@ -239,7 +240,8 @@ CharacterValueImpl CharacterValueImpl::substr(std::size_t pos) const {
           llvm_unreachable("operation not supported on uninitialized value");
         } else {
           return CharacterValueImpl{
-              sizeof(typename StringT::value_type), s.substr(pos)};
+              static_cast<KindsEnum>(sizeof(typename StringT::value_type)),
+              s.substr(pos)};
         }
       },
       storage_);
@@ -255,7 +257,8 @@ CharacterValueImpl CharacterValueImpl::substr(
           llvm_unreachable("operation not supported on uninitialized value");
         } else {
           return CharacterValueImpl{
-              sizeof(typename StringT::value_type), s.substr(pos, len)};
+              static_cast<KindsEnum>(sizeof(typename StringT::value_type)),
+              s.substr(pos, len)};
         }
       },
       storage_);
@@ -327,7 +330,7 @@ std::string CharacterValueImpl::ToStdString() const {
       storage_);
 }
 
-CharacterValueImpl CharacterValueImpl::ToAscii(int kind) const {
+CharacterValueImpl CharacterValueImpl::ToAscii(KindsEnum kind) const {
   if (IsMonostate()) {
     return Zero(kind);
   }
@@ -341,11 +344,11 @@ CharacterValueImpl CharacterValueImpl::ToAscii(int kind) const {
       StringT str;
       for (auto iter{s.cbegin()}; iter != s.cend(); ++iter) {
         if (static_cast<std::uint64_t>(*iter) > 127) {
-          return Zero(sizeof(ct));
+          return Zero(static_cast<KindsEnum>(sizeof(ct)));
         }
         str.push_back(static_cast<CharT>(*iter));
       }
-      return CharacterValueImpl{sizeof(CharT), str};
+      return CharacterValueImpl{static_cast<KindsEnum>(sizeof(CharT)), str};
     });
   });
 }
@@ -384,7 +387,8 @@ CharacterValueImpl CharacterValueImpl::operator+(
             !std::is_same_v<std::decay_t<decltype(a)>, std::monostate>) {
           using StringT = std::decay_t<decltype(a)>;
           return CharacterValueImpl{
-              sizeof(typename StringT::value_type), a + b};
+              static_cast<KindsEnum>(sizeof(typename StringT::value_type)),
+              a + b};
         } else {
           llvm_unreachable("operation not supported on uninitialized value or "
                            "values of different kinds");
diff --git a/flang/lib/Evaluate/character-value-impl.h b/flang/lib/Evaluate/character-value-impl.h
index 9e51587e657c0..3acf977e2ba6c 100644
--- a/flang/lib/Evaluate/character-value-impl.h
+++ b/flang/lib/Evaluate/character-value-impl.h
@@ -9,6 +9,7 @@
 #ifndef FORTRAN_EVALUATE_CHARACTER_VALUE_IMPL_H_
 #define FORTRAN_EVALUATE_CHARACTER_VALUE_IMPL_H_
 
+#include "flang/Common/type-kinds.h"
 #include "flang/Evaluate/common.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <cstddef>
@@ -18,6 +19,7 @@
 #include <variant>
 
 namespace Fortran::evaluate::value {
+using common::KindsEnum;
 
 class CharacterValueImpl {
   using Storage =
@@ -32,7 +34,7 @@ class CharacterValueImpl {
   CharacterValueImpl &operator=(CharacterValueImpl &&) = default;
 
   CharacterValueImpl() = default;
-  explicit CharacterValueImpl(int kind, std::string s) {
+  explicit CharacterValueImpl(KindsEnum kind, std::string s) {
     withCharProto(kind, [&](auto c) {
       using CharT = std::decay_t<decltype(c)>;
       using StringT = std::basic_string<CharT>;
@@ -51,25 +53,25 @@ class CharacterValueImpl {
     CHECK(this->kind() == kind);
   }
 
-  explicit CharacterValueImpl(int kind, std::u16string s)
+  explicit CharacterValueImpl(KindsEnum kind, std::u16string s)
       : storage_{std::move(s)} {
-    CHECK(kind == 2);
+    CHECK(kind == KindsEnum::Kind2);
     CHECK(this->kind() == kind);
   }
 
-  explicit CharacterValueImpl(int kind, std::u32string s)
+  explicit CharacterValueImpl(KindsEnum kind, std::u32string s)
       : storage_{std::move(s)} {
-    CHECK(kind == 4);
+    CHECK(kind == KindsEnum::Kind4);
     CHECK(this->kind() == kind);
   }
 
   /// Fill constructors: create a string of n copies of the given character.
-  CharacterValueImpl(int kind, std::size_t n, char32_t c);
+  CharacterValueImpl(KindsEnum kind, std::size_t n, char32_t c);
 
-  static CharacterValueImpl Zero(int kind);
+  static CharacterValueImpl Zero(KindsEnum kind);
 
   static CharacterValueImpl FromRawBytes(
-      int kind, const void *raw, size_t byteSize);
+      KindsEnum kind, const void *raw, size_t byteSize);
 
   void print(llvm::raw_ostream &os) const;
 
@@ -87,8 +89,9 @@ class CharacterValueImpl {
   std::string ToStdString() const;
 
   bool IsMonostate() const { return storage_.index() == 0; }
-  int kind() const {
-    return withCharProto([](auto ct) { return sizeof(ct); });
+  KindsEnum kind() const {
+    return withCharProto(
+        [](auto ct) { return static_cast<KindsEnum>(sizeof(ct)); });
   }
 
   /// Byte size of one character unit (1, 2, or 4).
@@ -119,7 +122,7 @@ class CharacterValueImpl {
   bool operator>(const CharacterValueImpl &y) const { return y < *this; }
 
   /// Assign n copies of the given character.
-  void assign(int kind, std::size_t n, char32_t c);
+  void assign(KindsEnum kind, std::size_t n, char32_t c);
 
   /// Assign from a raw character pointer and length.
   void assign(const char *p, std::size_t n) { storage_ = std::string(p, n); }
@@ -146,7 +149,7 @@ class CharacterValueImpl {
   /// Return a substring of len characters starting at pos.
   CharacterValueImpl substr(std::size_t pos, std::size_t len) const;
 
-  CharacterValueImpl ToAscii(int kind) const;
+  CharacterValueImpl ToAscii(KindsEnum kind) const;
 
   /// Reserve storage for at least n characters.
   void reserve(std::size_t n);
@@ -209,14 +212,14 @@ class CharacterValueImpl {
   }
 
   template <typename F>
-  static auto withCharProto(int kind, F &&f)
+  static auto withCharProto(KindsEnum kind, F &&f)
       -> decltype(std::declval<F>()(std::declval<char>())) {
     switch (kind) {
-    case 1:
+    case KindsEnum::Kind1:
       return f(char{});
-    case 2:
+    case KindsEnum::Kind2:
       return f(char16_t{});
-    case 4:
+    case KindsEnum::Kind4:
       return f(char32_t{});
     default:
       llvm_unreachable("unsupported character kind/monostate");
diff --git a/flang/lib/Evaluate/character-value.cpp b/flang/lib/Evaluate/character-value.cpp
index 303fa80b175e6..6e54bbcceea5c 100644
--- a/flang/lib/Evaluate/character-value.cpp
+++ b/flang/lib/Evaluate/character-value.cpp
@@ -41,30 +41,30 @@ CharacterValue &CharacterValue::operator=(CharacterValue &&x) {
   return *this;
 }
 
-CharacterValue::CharacterValue(int kind, std::string s) {
+CharacterValue::CharacterValue(KindsEnum kind, std::string s) {
   new (this) CharacterValueImpl(kind, std::move(s));
 }
 
-CharacterValue::CharacterValue(int kind, std::u16string s) {
-  CHECK(kind == 2);
+CharacterValue::CharacterValue(KindsEnum kind, std::u16string s) {
+  CHECK(kind == KindsEnum::Kind2);
   new (this) CharacterValueImpl(kind, std::move(s));
 }
 
-CharacterValue::CharacterValue(int kind, std::u32string s) {
-  CHECK(kind == 4);
+CharacterValue::CharacterValue(KindsEnum kind, std::u32string s) {
+  CHECK(kind == KindsEnum::Kind4);
   new (this) CharacterValueImpl(kind, std::move(s));
 }
 
-CharacterValue::CharacterValue(int kind, std::size_t n, char32_t c) {
+CharacterValue::CharacterValue(KindsEnum kind, std::size_t n, char32_t c) {
   new (this) CharacterValueImpl(kind, n, c);
 }
 
-CharacterValue CharacterValue::Zero(int kind) {
+CharacterValue CharacterValue::Zero(KindsEnum kind) {
   return FromImpl(CharacterValueImpl::Zero(kind));
 }
 
 CharacterValue CharacterValue::FromRawBytes(
-    int kind, const void *raw, size_t byteSize) {
+    KindsEnum kind, const void *raw, size_t byteSize) {
   return FromImpl(CharacterValueImpl::FromRawBytes(kind, raw, byteSize));
 }
 
@@ -80,7 +80,7 @@ bool CharacterValue::empty() const { return impl().empty(); }
 
 std::size_t CharacterValue::size() const { return impl().size(); }
 
-int CharacterValue::kind() const { return impl().kind(); }
+KindsEnum CharacterValue::kind() const { return impl().kind(); }
 
 std::optional<llvm::StringRef> CharacterValue::AsStringRef() const {
   return impl().AsStringRef();
@@ -108,11 +108,11 @@ bool CharacterValue::operator==(const CharacterValue &y) const {
   return impl() == y.impl();
 }
 
-CharacterValue CharacterValue::ToAscii(int kind) const {
+CharacterValue CharacterValue::ToAscii(KindsEnum kind) const {
   return FromImpl(impl().ToAscii(kind));
 }
 
-void CharacterValue::assign(int kind, std::size_t n, char32_t c) {
+void CharacterValue::assign(KindsEnum kind, std::size_t n, char32_t c) {
   impl().assign(kind, n, c);
 }
 
diff --git a/flang/lib/Evaluate/character.h b/flang/lib/Evaluate/character.h
index c1a34257e5682..8d95795aeafe4 100644
--- a/flang/lib/Evaluate/character.h
+++ b/flang/lib/Evaluate/character.h
@@ -19,6 +19,8 @@
 
 namespace Fortran::evaluate {
 
+using common::KindsEnum;
+
 class CharacterUtils {
   using Character = Scalar<Type<TypeCategory::Character>>;
   using CharT = char32_t;
@@ -27,7 +29,7 @@ class CharacterUtils {
   // CHAR also implements ACHAR under assumption that character encodings
   // contain ASCII
   static Character CHAR(int kind, std::uint64_t code) {
-    return Character{kind, 1, static_cast<CharT>(code)};
+    return Character{static_cast<KindsEnum>(kind), 1, static_cast<CharT>(code)};
   }
 
   // ICHAR also implements IACHAR under assumption that character encodings
@@ -37,20 +39,23 @@ class CharacterUtils {
     // Mask to the character kind width to avoid sign extension
     auto ch{static_cast<std::uint64_t>(c[0])};
     switch (c.kind()) {
-    case 1:
+    case Kind1:
       return static_cast<std::int64_t>(ch & 0xffu);
-    case 2:
+    case Kind2:
       return static_cast<std::int64_t>(ch & 0xffffu);
-    case 4:
+    case Kind4:
       return static_cast<std::int64_t>(ch & 0xffffffffu);
+    default:
+      llvm_unreachable("unsupported character kind");
     }
-    llvm_unreachable("unsupported character kind");
   }
 
-  static Character NEW_LINE(int kind) { return Character{kind, 1, NewLine()}; }
+  static Character NEW_LINE(int kind) {
+    return Character{static_cast<KindsEnum>(kind), 1, NewLine()};
+  }
 
   static Character ADJUSTL(const Character &str) {
-    const int kind{str.kind()};
+    const KindsEnum kind{str.kind()};
     auto pos{str.find_first_not_of(Space())};
     if (pos != Character::npos && pos != 0) {
       return Character{str.substr(pos) + Character{kind, pos, Space()}};
@@ -60,7 +65,7 @@ class CharacterUtils {
   }
 
   static Character ADJUSTR(const Character &str) {
-    const int kind{str.kind()};
+    const KindsEnum kind{str.kind()};
     auto pos{str.find_last_not_of(Space())};
     if (pos != Character::npos && pos != str.length() - 1) {
       auto delta{str.length() - 1 - pos};
@@ -92,7 +97,7 @@ class CharacterUtils {
   // Resize adds spaces on the right if the new size is bigger than the
   // original, or by trimming the rightmost characters otherwise.
   static Character Resize(const Character &str, std::size_t newLength) {
-    const int kind{str.kind()};
+    const KindsEnum kind{str.kind()};
     auto oldLength{str.length()};
     if (newLength > oldLength) {
       return str + Character{kind, newLength - oldLength, Space()};
@@ -112,7 +117,7 @@ class CharacterUtils {
   }
 
   static Character REPEAT(const Character &str, ConstantSubscript ncopies) {
-    const int kind{str.kind()};
+    const KindsEnum kind{str.kind()};
     Character result{Character::Zero(kind)};
     if (!str.empty() && ncopies > 0) {
       result.reserve(ncopies * str.size());
diff --git a/flang/lib/Evaluate/characteristics.cpp b/flang/lib/Evaluate/characteristics.cpp
index 4f1c6831e3aeb..ccc87fc61a525 100644
--- a/flang/lib/Evaluate/characteristics.cpp
+++ b/flang/lib/Evaluate/characteristics.cpp
@@ -76,7 +76,7 @@ TypeAndShape &TypeAndShape::Rewrite(FoldingContext &context) {
   LEN_ = Fold(context, std::move(LEN_));
   if (LEN_) {
     if (auto n{ToInt64(*LEN_)}) {
-      type_ = DynamicType{type_.kind(), *n};
+      type_ = DynamicType{static_cast<KindsEnum>(type_.kind()), *n};
     }
   }
   shape_ = Fold(context, std::move(shape_));
@@ -940,7 +940,7 @@ std::optional<DummyArgument> DummyArgument::FromActual(std::string &&name,
                     type->type().GetDerivedTypeSpec(), /*poly=*/false});
               }
               if (type->type().category() == TypeCategory::Character &&
-                  type->type().kind() == 1) {
+                  type->type().kind() == Kind1) {
                 type->set_isPossibleSequenceAssociation(true);
               } else if (const Symbol * array{IsArrayElement(expr)}) {
                 type->set_isPossibleSequenceAssociation(
diff --git a/flang/lib/Evaluate/check-expression.cpp b/flang/lib/Evaluate/check-expression.cpp
index d3722c46632f3..925cc173940fe 100644
--- a/flang/lib/Evaluate/check-expression.cpp
+++ b/flang/lib/Evaluate/check-expression.cpp
@@ -476,26 +476,24 @@ class SuspiciousRealLiteralFinder
     : public AnyTraverse<SuspiciousRealLiteralFinder> {
 public:
   using Base = AnyTraverse<SuspiciousRealLiteralFinder>;
-  SuspiciousRealLiteralFinder(int kind, FoldingContext &c)
+  SuspiciousRealLiteralFinder(KindsEnum kind, FoldingContext &c)
       : Base{*this}, kind_{kind}, context_{c} {}
   using Base::operator();
   bool operator()(const Constant<Type<TypeCategory::Real>> &x) const {
-    const int kind{x.kind()};
-    if (kind_ > kind && x.result().isFromInexactLiteralConversion()) {
+    if (kind_ > x.kind() && x.result().isFromInexactLiteralConversion()) {
       context_.Warn(common::UsageWarning::RealConstantWidening,
           "Default real literal in REAL(%d) context might need a kind suffix, as its rounded value %s is inexact"_warn_en_US,
-          kind_, x.AsFortran());
+          static_cast<int>(kind_), x.AsFortran());
       return true;
     } else {
       return false;
     }
   }
   bool operator()(const Constant<Type<TypeCategory::Complex>> &x) const {
-    const int kind{x.kind()};
-    if (kind_ > kind && x.result().isFromInexactLiteralConversion()) {
+    if (kind_ > x.kind() && x.result().isFromInexactLiteralConversion()) {
       context_.Warn(common::UsageWarning::RealConstantWidening,
           "Default real literal in COMPLEX(%d) context might need a kind suffix, as its rounded value %s is inexact"_warn_en_US,
-          kind_, x.AsFortran());
+          static_cast<int>(kind_), x.AsFortran());
       return true;
     } else {
       return false;
@@ -503,7 +501,7 @@ class SuspiciousRealLiteralFinder
   }
   template <TypeCategory TOCAT, TypeCategory FROMCAT>
   bool operator()(const Convert<Type<TOCAT>, FROMCAT> &x) const {
-    const int toKind{x.kind()};
+    const KindsEnum toKind{x.kind()};
     if constexpr ((TOCAT == TypeCategory::Real ||
                       TOCAT == TypeCategory::Complex) &&
         (FROMCAT == TypeCategory::Real || FROMCAT == TypeCategory::Complex)) {
@@ -516,7 +514,7 @@ class SuspiciousRealLiteralFinder
   }
 
 private:
-  int kind_;
+  KindsEnum kind_;
   FoldingContext &context_;
 };
 
@@ -528,7 +526,8 @@ void CheckRealWidening(const Expr<SomeType> &expr, const DynamicType &toType,
       if ((fromType->category() == TypeCategory::Real ||
               fromType->category() == TypeCategory::Complex) &&
           toType.kind() > fromType->kind()) {
-        SuspiciousRealLiteralFinder{toType.kind(), context}(expr);
+        SuspiciousRealLiteralFinder{
+            static_cast<KindsEnum>(toType.kind()), context}(expr);
       }
     }
   }
diff --git a/flang/lib/Evaluate/complex-value.cpp b/flang/lib/Evaluate/complex-value.cpp
index 40ba3aeaefa2f..495def8c08052 100644
--- a/flang/lib/Evaluate/complex-value.cpp
+++ b/flang/lib/Evaluate/complex-value.cpp
@@ -13,7 +13,9 @@
 
 namespace Fortran::evaluate::value {
 
-void ComplexValue::print(llvm::raw_ostream &os) const { AsFortran(os, kind()); }
+void ComplexValue::print(llvm::raw_ostream &os) const {
+  AsFortran(os, static_cast<int>(kind()));
+}
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
 LLVM_DUMP_METHOD void ComplexValue::dump() const {
@@ -23,7 +25,7 @@ LLVM_DUMP_METHOD void ComplexValue::dump() const {
 #endif
 
 ValueWithRealFlags<ComplexValue> ComplexValue::FromInteger(
-    int kind, const IntegerValue &n, bool isUnsigned, Rounding rounding) {
+    KindsEnum kind, const IntegerValue &n, bool isUnsigned, Rounding rounding) {
   CHECK(!n.IsMonostate());
 
   ValueWithRealFlags<ComplexValue> result;
@@ -171,7 +173,7 @@ void ComplexValue::StoreRawBytes(
 }
 
 ComplexValue ComplexValue::FromRawBytes(
-    int kind, const void *raw, std::size_t expectedSize) {
+    KindsEnum kind, const void *raw, std::size_t expectedSize) {
   CHECK(expectedSize == static_cast<size_t>(-1) ||
       expectedSize == bytesStored(kind));
   std::size_t partBytes{RealValue::bytesStored(kind)};
diff --git a/flang/lib/Evaluate/constant.cpp b/flang/lib/Evaluate/constant.cpp
index 7a78c1ade352a..5e5963ff02557 100644
--- a/flang/lib/Evaluate/constant.cpp
+++ b/flang/lib/Evaluate/constant.cpp
@@ -143,8 +143,8 @@ bool HasNegativeExtent(const ConstantSubscripts &shape) {
 }
 
 template <typename RESULT, typename ELEMENT>
-ConstantBase<RESULT, ELEMENT>::ConstantBase(
-    int kind, std::vector<Element> &&x, ConstantSubscripts &&sh, Result res)
+ConstantBase<RESULT, ELEMENT>::ConstantBase(KindsEnum kind,
+    std::vector<Element> &&x, ConstantSubscripts &&sh, Result res)
     : ConstantBounds(std::move(sh)), kind_{kind}, result_{res},
       values_(std::move(x)) {
   CHECK_KIND(kind, RESULT);
@@ -201,7 +201,7 @@ auto Constant<T>::At(const ConstantSubscripts &index) const -> Element {
 
 template <typename T>
 auto Constant<T>::Reshape(ConstantSubscripts &&dims) const -> Constant {
-  const int kind{Base::kind()};
+  const KindsEnum kind{Base::kind_};
   return {kind, Base::Reshape(dims), std::move(dims)};
 }
 
@@ -213,20 +213,20 @@ std::size_t Constant<T>::CopyFrom(const Constant<T> &source, std::size_t count,
 
 // Constant<Type<TypeCategory::Character>> specialization
 Constant<Type<TypeCategory::Character>>::Constant(
-    int kind, const Scalar<Result> &str)
+    KindsEnum kind, const Scalar<Result> &str)
     : kind_{kind}, values_{str},
       length_{static_cast<ConstantSubscript>(values_.size())} {
   CHECK(str.kind() == kind);
 }
 
 Constant<Type<TypeCategory::Character>>::Constant(
-    int kind, Scalar<Result> &&str)
+    KindsEnum kind, Scalar<Result> &&str)
     : kind_{kind}, values_{std::move(str)},
       length_{static_cast<ConstantSubscript>(values_.size())} {
   CHECK(str.kind() == kind);
 }
 
-Constant<Type<TypeCategory::Character>>::Constant(int kind,
+Constant<Type<TypeCategory::Character>>::Constant(KindsEnum kind,
     ConstantSubscript len, std::vector<Scalar<Result>> &&strings,
     ConstantSubscripts &&sh)
     : ConstantBounds(std::move(sh)), kind_{kind}, length_{len} {
@@ -319,7 +319,8 @@ std::size_t Constant<Type<TypeCategory::Character>>::CopyFrom(
     return count;
   } else {
     std::size_t copied{0};
-    std::size_t elementBytes{static_cast<std::size_t>(length_) * kind()};
+    std::size_t elementBytes{
+        static_cast<std::size_t>(length_) * static_cast<std::size_t>(kind())};
     ConstantSubscripts sourceSubscripts{source.lbounds()};
     while (copied < count) {
       auto *dest{values_.at(SubscriptsToOffset(resultSubscripts) * length_)};
@@ -336,14 +337,14 @@ std::size_t Constant<Type<TypeCategory::Character>>::CopyFrom(
 
 // Constant<SomeDerived> specialization
 Constant<SomeDerived>::Constant(const StructureConstructor &x)
-    : Base{/*kind=*/0, x.values(), Result{x.derivedTypeSpec()}} {}
+    : Base{NoKind, x.values(), Result{x.derivedTypeSpec()}} {}
 
 Constant<SomeDerived>::Constant(StructureConstructor &&x)
-    : Base{/*kind=*/0, std::move(x.values()), Result{x.derivedTypeSpec()}} {}
+    : Base{NoKind, std::move(x.values()), Result{x.derivedTypeSpec()}} {}
 
 Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec,
     std::vector<StructureConstructorValues> &&x, ConstantSubscripts &&s)
-    : Base{/*kind=*/0, std::move(x), std::move(s), Result{spec}} {}
+    : Base{NoKind, std::move(x), std::move(s), Result{spec}} {}
 
 static std::vector<StructureConstructorValues> AcquireValues(
     std::vector<StructureConstructor> &&x) {
@@ -356,7 +357,7 @@ static std::vector<StructureConstructorValues> AcquireValues(
 
 Constant<SomeDerived>::Constant(const semantics::DerivedTypeSpec &spec,
     std::vector<StructureConstructor> &&x, ConstantSubscripts &&shape)
-    : Base{/*kind=*/0, AcquireValues(std::move(x)), std::move(shape),
+    : Base{NoKind, AcquireValues(std::move(x)), std::move(shape),
           Result{spec}} {}
 
 std::optional<StructureConstructor>
diff --git a/flang/lib/Evaluate/expression.cpp b/flang/lib/Evaluate/expression.cpp
index e8279bb288588..6593cc6454c9d 100644
--- a/flang/lib/Evaluate/expression.cpp
+++ b/flang/lib/Evaluate/expression.cpp
@@ -91,10 +91,10 @@ const typename ExpressionBase<A>::Derived &ExpressionBase<A>::derived() const {
 }
 #endif
 
-template <typename A> int ExpressionBase<A>::kind() const {
+template <typename A> KindsEnum ExpressionBase<A>::kind() const {
   // Storing/deriving the kind handled by the subclasses
   return common::visit(
-      [&](const auto &x) -> int { return x.kind(); }, derived().u);
+      [&](const auto &x) -> KindsEnum { return x.kind(); }, derived().u);
 }
 
 template <typename A>
@@ -367,45 +367,47 @@ std::optional<Expr<SubscriptInteger>> Expr<SomeCharacter>::LEN() const {
 }
 
 Parentheses<SomeDerived>::Parentheses(const Expr<SomeDerived> &x)
-    : Base{x.kind(), x} {}
+    : Base{static_cast<KindsEnum>(x.kind()), x} {}
 Parentheses<SomeDerived>::Parentheses(Expr<SomeDerived> &&x)
-    : Base{x.kind(), std::move(x)} {}
+    : Base{static_cast<KindsEnum>(x.kind()), std::move(x)} {}
 
 ComplexComponent::ComplexComponent(bool isImaginary, const Expr<Operand> &x)
-    : Base{x.kind(), x}, isImaginaryPart{isImaginary} {}
+    : Base{static_cast<KindsEnum>(x.kind()), x}, isImaginaryPart{isImaginary} {}
 ComplexComponent::ComplexComponent(bool isImaginary, Expr<Operand> &&x)
-    : Base{x.kind(), std::move(x)}, isImaginaryPart{isImaginary} {}
+    : Base{static_cast<KindsEnum>(x.kind()), std::move(x)},
+      isImaginaryPart{isImaginary} {}
 
 ComplexConstructor::ComplexConstructor(const Expr<Type<TypeCategory::Real>> &re,
     const Expr<Type<TypeCategory::Real>> &im)
-    : Base{re.kind(), re, im} {
+    : Base{static_cast<KindsEnum>(re.kind()), re, im} {
   CHECK(re.kind() == im.kind());
 }
 ComplexConstructor::ComplexConstructor(
     Expr<Type<TypeCategory::Real>> &&re, Expr<Type<TypeCategory::Real>> &&im)
-    : Base{re.kind(), std::move(re), std::move(im)} {
+    : Base{static_cast<KindsEnum>(re.kind()), std::move(re), std::move(im)} {
   CHECK(left().kind() == right().kind());
 }
 
 LogicalOperation::LogicalOperation(
     LogicalOperator opr, const Expr<Operand> &x, const Expr<Operand> &y)
-    : Base{x.kind(), x, y}, logicalOperator{opr} {
+    : Base{static_cast<KindsEnum>(x.kind()), x, y}, logicalOperator{opr} {
   CHECK(x.kind() == y.kind());
 }
 LogicalOperation::LogicalOperation(
     LogicalOperator opr, Expr<Operand> &&x, Expr<Operand> &&y)
-    : Base{x.kind(), std::move(x), std::move(y)}, logicalOperator{opr} {
+    : Base{static_cast<KindsEnum>(x.kind()), std::move(x), std::move(y)},
+      logicalOperator{opr} {
   CHECK(x.kind() == y.kind());
 }
 
 Concat::Concat(const Expr<Type<TypeCategory::Character>> &x,
     const Expr<Type<TypeCategory::Character>> &y)
-    : Base{x.kind(), x, y} {
+    : Base{static_cast<KindsEnum>(x.kind()), x, y} {
   CHECK(x.kind() == y.kind());
 }
 Concat::Concat(Expr<Type<TypeCategory::Character>> &&x,
     Expr<Type<TypeCategory::Character>> &&y)
-    : Base{x.kind(), std::move(x), std::move(y)} {
+    : Base{static_cast<KindsEnum>(x.kind()), std::move(x), std::move(y)} {
   CHECK(left().kind() == right().kind());
 }
 
diff --git a/flang/lib/Evaluate/fold-character.cpp b/flang/lib/Evaluate/fold-character.cpp
index 0a23c91df0654..f6dceed2042eb 100644
--- a/flang/lib/Evaluate/fold-character.cpp
+++ b/flang/lib/Evaluate/fold-character.cpp
@@ -47,7 +47,7 @@ Expr<Type<TypeCategory::Character>> FoldIntrinsicFunction(
     FunctionRef<Type<TypeCategory::Character>> &&funcRef) {
   using T = Type<TypeCategory::Character>;
   using StringType = Scalar<T>; // CharacterValue
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
   CHECK(intrinsic);
   std::string name{intrinsic->name};
@@ -57,13 +57,15 @@ Expr<Type<TypeCategory::Character>> FoldIntrinsicFunction(
         context, std::move(funcRef),
         ScalarFunc<T, IntT>([&](const Scalar<IntT> &i) {
           if (i.IsNegative() ||
-              i.BGE(Scalar<IntT>{SubscriptIntegerKind, 0}.IBSET(8 * kind))) {
+              i.BGE(Scalar<IntT>{SubscriptIntegerKind, 0}.IBSET(
+                  8 * static_cast<int>(kind)))) {
             context.Warn(common::UsageWarning::FoldingValueChecks,
                 "%s(I=%jd) is out of range for CHARACTER(KIND=%d)"_warn_en_US,
                 parser::ToUpperCaseLetters(name),
-                static_cast<std::intmax_t>(i.ToInt64()), kind);
+                static_cast<std::intmax_t>(i.ToInt64()),
+                static_cast<int>(kind));
           }
-          return CharacterUtils::CHAR(kind, i.ToUInt64());
+          return CharacterUtils::CHAR(static_cast<int>(kind), i.ToUInt64());
         }));
   } else if (name == "adjustl") {
     return FoldElementalIntrinsic<T, T>(
@@ -84,14 +86,15 @@ Expr<Type<TypeCategory::Character>> FoldIntrinsicFunction(
     return FoldMINorMAX(context, std::move(funcRef), Ordering::Less);
   } else if (name == "minval") {
     // Collating sequences correspond to positive integers (3.31)
-    StringType most{kind, 1, 0xffffffff >> (8 * (4 - kind))};
+    StringType most{kind, 1, 0xffffffff >> (8 * (4 - static_cast<int>(kind)))};
     if (auto identity{
             Identity<T>(most, GetConstantLength(context, funcRef, 0))}) {
       return FoldMaxvalMinval<T>(
           kind, context, std::move(funcRef), RelationalOperator::LT, *identity);
     }
   } else if (name == "new_line") {
-    return MakeConstantExpr<T>(kind, CharacterUtils::NEW_LINE(kind));
+    return MakeConstantExpr<T>(
+        kind, CharacterUtils::NEW_LINE(static_cast<int>(kind)));
   } else if (name == "repeat") { // not elemental
     if (auto scalars{GetScalarConstantArguments<T, SubscriptInteger>(
             {kind, SubscriptIntegerKind}, context, funcRef.arguments(),
@@ -129,7 +132,7 @@ Expr<Type<TypeCategory::Character>> FoldIntrinsicFunction(
 
 Expr<Type<TypeCategory::Character>> FoldOperation(
     FoldingContext &context, Concat &&x) {
-  const int kind{x.kind()};
+  const KindsEnum kind{static_cast<KindsEnum>(x.kind())};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
@@ -142,7 +145,7 @@ Expr<Type<TypeCategory::Character>> FoldOperation(
 
 Expr<Type<TypeCategory::Character>> FoldOperation(
     FoldingContext &context, SetLength &&x) {
-  const int kind{x.kind()};
+  const KindsEnum kind{static_cast<KindsEnum>(x.kind())};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
diff --git a/flang/lib/Evaluate/fold-complex.cpp b/flang/lib/Evaluate/fold-complex.cpp
index fda86906d7fb4..59c67b4eefaa9 100644
--- a/flang/lib/Evaluate/fold-complex.cpp
+++ b/flang/lib/Evaluate/fold-complex.cpp
@@ -14,7 +14,7 @@ namespace Fortran::evaluate {
 
 Expr<Type<TypeCategory::Complex>> FoldIntrinsicFunction(FoldingContext &context,
     FunctionRef<Type<TypeCategory::Complex>> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   using T = Type<TypeCategory::Complex>;
   using Part = typename T::Part;
   ActualArguments &args{funcRef.arguments()};
@@ -31,7 +31,7 @@ Expr<Type<TypeCategory::Complex>> FoldIntrinsicFunction(FoldingContext &context,
     } else {
       context.Warn(common::UsageWarning::FoldingFailure,
           "%s(complex(kind=%d)) cannot be folded on host"_warn_en_US, name,
-          kind);
+          static_cast<int>(kind));
     }
   } else if (name == "conjg") {
     return FoldElementalIntrinsic<T, T>(
@@ -67,7 +67,8 @@ Expr<Type<TypeCategory::Complex>> FoldIntrinsicFunction(FoldingContext &context,
   } else if (name == "matmul") {
     return FoldMatmul(context, std::move(funcRef));
   } else if (name == "product") {
-    auto one{Scalar<Part>::FromInteger(kind, value::IntegerValue{1, 1}).value};
+    auto one{
+        Scalar<Part>::FromInteger(kind, value::IntegerValue{Kind1, 1}).value};
     return FoldProduct<T>(context, std::move(funcRef), Scalar<T>{one});
   } else if (name == "sum") {
     return FoldSum<T>(context, std::move(funcRef));
@@ -77,7 +78,7 @@ Expr<Type<TypeCategory::Complex>> FoldIntrinsicFunction(FoldingContext &context,
 
 Expr<Type<TypeCategory::Complex>> FoldOperation(
     FoldingContext &context, ComplexConstructor &&x) {
-  const int kind{x.kind()};
+  const KindsEnum kind{x.kind()};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
diff --git a/flang/lib/Evaluate/fold-designator.cpp b/flang/lib/Evaluate/fold-designator.cpp
index 4e0cc1b841194..7f65bafda5121 100644
--- a/flang/lib/Evaluate/fold-designator.cpp
+++ b/flang/lib/Evaluate/fold-designator.cpp
@@ -340,7 +340,7 @@ std::optional<Expr<SomeType>> OffsetToDesignator(FoldingContext &context,
               return common::visit(
                   [&](const auto &z) -> std::optional<Expr<SomeType>> {
                     using PartType = typename ResultType<decltype(z)>::Part;
-                    const int kind{z.kind()};
+                    const KindsEnum kind{z.kind()};
                     return AsGenericExpr(Designator<PartType>{kind,
                         ComplexPart{
                             ExtractDataRef(std::move(*zExpr)).value(), part}});
@@ -354,12 +354,13 @@ std::optional<Expr<SomeType>> OffsetToDesignator(FoldingContext &context,
               return common::visit(
                   [&](const auto &x) -> std::optional<Expr<SomeType>> {
                     using T = typename std::decay_t<decltype(x)>::Result;
-                    const int kind{x.kind()};
+                    const KindsEnum kind{x.kind()};
+                    const int kindBytes{static_cast<int>(kind)};
                     return AsGenericExpr(Designator<T>{kind,
                         Substring{ExtractDataRef(std::move(*cExpr)).value(),
-                            MakeSubscriptIntExpr(1 + (offset / kind)),
+                            MakeSubscriptIntExpr(1 + (offset / kindBytes)),
                             MakeSubscriptIntExpr(
-                                1 + ((offset + size - 1) / kind))}});
+                                1 + ((offset + size - 1) / kindBytes))}});
                   },
                   cExpr->u);
             }
diff --git a/flang/lib/Evaluate/fold-implementation.h b/flang/lib/Evaluate/fold-implementation.h
index 24e68fb3e74cd..6a0aab5b4b2e3 100644
--- a/flang/lib/Evaluate/fold-implementation.h
+++ b/flang/lib/Evaluate/fold-implementation.h
@@ -53,12 +53,13 @@ static constexpr bool useKahanSummation{false};
 // Utilities
 template <typename T> class Folder {
 public:
-  explicit Folder(int kind, FoldingContext &c, bool forOptionalArgument = false)
+  explicit Folder(
+      KindsEnum kind, FoldingContext &c, bool forOptionalArgument = false)
       : kind_{kind}, context_{c}, forOptionalArgument_{forOptionalArgument} {}
   template <typename U = T,
       typename = std::enable_if_t<std::is_same_v<U, SomeDerived>>>
   explicit Folder(FoldingContext &c, bool forOptionalArgument = false)
-      : Folder(0, c, forOptionalArgument) {}
+      : Folder(NoKind, c, forOptionalArgument) {}
 
   std::optional<Constant<T>> GetNamedConstant(const Symbol &);
   std::optional<Constant<T>> ApplySubscripts(const Constant<T> &array,
@@ -85,7 +86,7 @@ template <typename T> class Folder {
   Expr<T> TRANSFER(FunctionRef<T> &&);
 
 private:
-  int kind_;
+  KindsEnum kind_;
   FoldingContext &context_;
   bool forOptionalArgument_{false};
 };
@@ -95,8 +96,8 @@ std::optional<Constant<SubscriptInteger>> GetConstantSubscript(
 
 template <typename TR, typename... TA, std::size_t... I>
 static std::optional<std::function<Scalar<TR>(FoldingContext &, Scalar<TA>...)>>
-GetHostRuntimeWrapperHelper(int resultKind,
-    std::array<int, sizeof...(TA)> argKinds, const std::string &name,
+GetHostRuntimeWrapperHelper(KindsEnum resultKind,
+    std::array<KindsEnum, sizeof...(TA)> argKinds, const std::string &name,
     std::index_sequence<I...>) {
   std::vector<DynamicType> argTypes{DynamicType{TA::category, argKinds[I]}...};
   if (auto hostWrapper{GetHostRuntimeWrapper(
@@ -116,8 +117,8 @@ GetHostRuntimeWrapperHelper(int resultKind,
 // Helper to use host runtime on scalars for folding.
 template <typename TR, typename... TA>
 static std::optional<std::function<Scalar<TR>(FoldingContext &, Scalar<TA>...)>>
-GetHostRuntimeWrapper(int resultKind, std::array<int, sizeof...(TA)> argKinds,
-    const std::string &name) {
+GetHostRuntimeWrapper(KindsEnum resultKind,
+    std::array<KindsEnum, sizeof...(TA)> argKinds, const std::string &name) {
   return GetHostRuntimeWrapperHelper<TR, TA...>(
       resultKind, argKinds, name, std::index_sequence_for<TA...>{});
 }
@@ -154,8 +155,7 @@ template <typename T>
 Expr<T> FoldOperation(FoldingContext &, FunctionRef<T> &&);
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Designator<T> &&designator) {
-  const int kind{designator.kind()};
-  return Folder<T>{kind, context}.Folding(std::move(designator));
+  return Folder<T>{designator.kind(), context}.Folding(std::move(designator));
 }
 Expr<TypeParamInquiry::Result> FoldOperation(
     FoldingContext &, TypeParamInquiry &&);
@@ -226,7 +226,7 @@ std::optional<Constant<T>> Folder<T>::Folding(DataRef &ref) {
 template <typename T>
 std::optional<Constant<T>> Folder<T>::ApplySubscripts(const Constant<T> &array,
     const std::vector<Constant<SubscriptInteger>> &subscripts) {
-  const int kind{array.kind()};
+  const KindsEnum kind{array.kind()};
   const auto &shape{array.shape()};
   const auto &lbounds{array.lbounds()};
   int rank{GetRank(shape)};
@@ -279,7 +279,7 @@ std::optional<Constant<T>> Folder<T>::ApplySubscripts(const Constant<T> &array,
     return Constant<T>{
         kind, array.LEN(), std::move(values), std::move(resultShape)};
   } else if constexpr (std::is_same_v<T, SomeDerived>) {
-    CHECK(kind == 0);
+    CHECK(kind == NoKind);
     return Constant<T>{array.result().derivedTypeSpec(), std::move(values),
         std::move(resultShape)};
   } else {
@@ -375,7 +375,7 @@ std::optional<Constant<T>> Folder<T>::GetConstantComponent(Component &component,
 }
 
 template <typename T> Expr<T> Folder<T>::Folding(Designator<T> &&designator) {
-  const int kind{designator.kind()};
+  const KindsEnum kind{designator.kind()};
   if constexpr (T::category == TypeCategory::Character) {
     if (auto *substring{common::Unwrap<Substring>(designator.u)}) {
       if (std::optional<Expr<SomeCharacter>> folded{
@@ -461,7 +461,7 @@ Constant<T> *Folder<T>::Folding(std::optional<ActualArgument> &arg) {
 
 template <typename... A, std::size_t... I>
 std::optional<std::tuple<const Constant<A> *...>> GetConstantArgumentsHelper(
-    const std::array<int, sizeof...(A)> &kinds, FoldingContext &context,
+    const std::array<KindsEnum, sizeof...(A)> &kinds, FoldingContext &context,
     ActualArguments &arguments, bool hasOptionalArgument,
     std::index_sequence<I...>) {
   static_assert(sizeof...(A) > 0);
@@ -477,7 +477,7 @@ std::optional<std::tuple<const Constant<A> *...>> GetConstantArgumentsHelper(
 
 template <typename... A>
 std::optional<std::tuple<const Constant<A> *...>> GetConstantArguments(
-    const std::array<int, sizeof...(A)> &kinds, FoldingContext &context,
+    const std::array<KindsEnum, sizeof...(A)> &kinds, FoldingContext &context,
     ActualArguments &args, bool hasOptionalArgument) {
   return GetConstantArgumentsHelper<A...>(kinds, context, args,
       hasOptionalArgument, std::index_sequence_for<A...>{});
@@ -485,7 +485,7 @@ std::optional<std::tuple<const Constant<A> *...>> GetConstantArguments(
 
 template <typename... A, std::size_t... I>
 std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArgumentsHelper(
-    const std::array<int, sizeof...(A)> &kinds, FoldingContext &context,
+    const std::array<KindsEnum, sizeof...(A)> &kinds, FoldingContext &context,
     ActualArguments &args, bool hasOptionalArgument,
     std::index_sequence<I...>) {
   if (auto constArgs{GetConstantArguments<A...>(
@@ -499,7 +499,7 @@ std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArgumentsHelper(
 
 template <typename... A>
 std::optional<std::tuple<Scalar<A>...>> GetScalarConstantArguments(
-    const std::array<int, sizeof...(A)> &kinds, FoldingContext &context,
+    const std::array<KindsEnum, sizeof...(A)> &kinds, FoldingContext &context,
     ActualArguments &args, bool hasOptionalArgument) {
   return GetScalarConstantArgumentsHelper<A...>(kinds, context, args,
       hasOptionalArgument, std::index_sequence_for<A...>{});
@@ -517,12 +517,13 @@ using ScalarFuncWithContext =
 
 template <template <typename, typename...> typename WrapperType, typename TR,
     typename... TA, std::size_t... I>
-Expr<TR> FoldElementalIntrinsicHelper(int resultKind,
-    const std::array<int, sizeof...(TA)> &argKinds, FoldingContext &context,
-    FunctionRef<TR> &&funcRef, WrapperType<TR, TA...> func,
-    bool hasOptionalArgument, std::index_sequence<I...>) {
+Expr<TR> FoldElementalIntrinsicHelper(KindsEnum resultKind,
+    const std::array<KindsEnum, sizeof...(TA)> &argKinds,
+    FoldingContext &context, FunctionRef<TR> &&funcRef,
+    WrapperType<TR, TA...> func, bool hasOptionalArgument,
+    std::index_sequence<I...>) {
   CHECK(funcRef.kind() == resultKind);
-  std::array<int, sizeof...(TA)> kinds{argKinds[I]...};
+  std::array<KindsEnum, sizeof...(TA)> kinds{argKinds[I]...};
   if (std::optional<std::tuple<const Constant<TA> *...>> args{
           GetConstantArguments<TA...>(
               kinds, context, funcRef.arguments(), hasOptionalArgument)}) {
@@ -596,19 +597,19 @@ Expr<TR> FoldElementalIntrinsicHelper(int resultKind,
 }
 
 template <typename TR, typename... TA>
-Expr<TR> FoldElementalIntrinsic(int resultKind,
-    const std::array<int, sizeof...(TA)> &argKinds, FoldingContext &context,
-    FunctionRef<TR> &&funcRef, ScalarFunc<TR, TA...> func,
-    bool hasOptionalArgument = false) {
+Expr<TR> FoldElementalIntrinsic(KindsEnum resultKind,
+    const std::array<KindsEnum, sizeof...(TA)> &argKinds,
+    FoldingContext &context, FunctionRef<TR> &&funcRef,
+    ScalarFunc<TR, TA...> func, bool hasOptionalArgument = false) {
   return FoldElementalIntrinsicHelper<ScalarFunc, TR, TA...>(resultKind,
       argKinds, context, std::move(funcRef), func, hasOptionalArgument,
       std::index_sequence_for<TA...>{});
 }
 template <typename TR, typename... TA>
-Expr<TR> FoldElementalIntrinsic(int resultKind,
-    const std::array<int, sizeof...(TA)> &argKinds, FoldingContext &context,
-    FunctionRef<TR> &&funcRef, ScalarFuncWithContext<TR, TA...> func,
-    bool hasOptionalArgument = false) {
+Expr<TR> FoldElementalIntrinsic(KindsEnum resultKind,
+    const std::array<KindsEnum, sizeof...(TA)> &argKinds,
+    FoldingContext &context, FunctionRef<TR> &&funcRef,
+    ScalarFuncWithContext<TR, TA...> func, bool hasOptionalArgument = false) {
   return FoldElementalIntrinsicHelper<ScalarFuncWithContext, TR, TA...>(
       resultKind, argKinds, context, std::move(funcRef), func,
       hasOptionalArgument, std::index_sequence_for<TA...>{});
@@ -645,7 +646,7 @@ std::optional<std::vector<A>> GetIntegerVector(const B &x) {
 // This to prevent generating warnings over and over if the expression
 // gets re-folded.
 template <typename T> Expr<T> MakeInvalidIntrinsic(FunctionRef<T> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   SpecificIntrinsic invalid{std::get<SpecificIntrinsic>(funcRef.proc().u)};
   invalid.name = IntrinsicProcTable::InvalidName;
   return Expr<T>{FunctionRef<T>{kind, ProcedureDesignator{std::move(invalid)},
@@ -653,7 +654,7 @@ template <typename T> Expr<T> MakeInvalidIntrinsic(FunctionRef<T> &&funcRef) {
 }
 
 template <typename T> Expr<T> Folder<T>::CSHIFT(FunctionRef<T> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   auto args{funcRef.arguments()};
   CHECK(args.size() == 3);
   const auto *array{UnwrapConstantValue<T>(args[0])};
@@ -733,7 +734,7 @@ template <typename T> Expr<T> Folder<T>::CSHIFT(FunctionRef<T> &&funcRef) {
 }
 
 template <typename T> Expr<T> Folder<T>::EOSHIFT(FunctionRef<T> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   auto args{funcRef.arguments()};
   CHECK(args.size() == 4);
   const auto *array{UnwrapConstantValue<T>(args[0])};
@@ -867,7 +868,7 @@ template <typename T> Expr<T> Folder<T>::EOSHIFT(FunctionRef<T> &&funcRef) {
 }
 
 template <typename T> Expr<T> Folder<T>::MERGE(FunctionRef<T> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   return FoldElementalIntrinsic<T, T, T, LogicalResult>(kind,
       {kind, kind, LogicalResultKind}, context_, std::move(funcRef),
       ScalarFunc<T, T, T, LogicalResult>(
@@ -878,7 +879,7 @@ template <typename T> Expr<T> Folder<T>::MERGE(FunctionRef<T> &&funcRef) {
 }
 
 template <typename T> Expr<T> Folder<T>::PACK(FunctionRef<T> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   auto args{funcRef.arguments()};
   CHECK(args.size() == 3);
   const auto *array{UnwrapConstantValue<T>(args[0])};
@@ -1067,7 +1068,7 @@ template <typename T> Expr<T> Folder<T>::SPREAD(FunctionRef<T> &&funcRef) {
 }
 
 template <typename T> Expr<T> Folder<T>::TRANSPOSE(FunctionRef<T> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   auto args{funcRef.arguments()};
   CHECK(args.size() == 1);
   const auto *matrix{UnwrapConstantValue<T>(args[0])};
@@ -1091,7 +1092,7 @@ template <typename T> Expr<T> Folder<T>::TRANSPOSE(FunctionRef<T> &&funcRef) {
 }
 
 template <typename T> Expr<T> Folder<T>::UNPACK(FunctionRef<T> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   auto args{funcRef.arguments()};
   CHECK(args.size() == 3);
   const auto *vector{UnwrapConstantValue<T>(args[0])};
@@ -1157,7 +1158,7 @@ template <typename T> Expr<T> Folder<T>::TRANSFER(FunctionRef<T> &&funcRef) {
 template <typename T>
 Expr<T> FoldMINorMAX(
     FoldingContext &context, FunctionRef<T> &&funcRef, Ordering order) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   static_assert(T::category == TypeCategory::Integer ||
       T::category == TypeCategory::Unsigned ||
       T::category == TypeCategory::Real ||
@@ -1273,7 +1274,7 @@ Expr<T> RewriteSpecificMINorMAX(
   intrinsic.characteristics.value().functionResult.value().SetType(*resultType);
   auto insertConversion{[&](const auto &x) -> Expr<T> {
     using TR = ResultType<decltype(x)>;
-    const int kind{x.kind()};
+    const KindsEnum kind{x.kind()};
     FunctionRef<TR> maxRef{
         kind, ProcedureDesignator{funcRef.proc()}, ActualArguments{args}};
     return Fold(
@@ -1302,7 +1303,7 @@ Expr<Type<TypeCategory::Logical>> FoldIntrinsicFunction(
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, FunctionRef<T> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   ActualArguments &args{funcRef.arguments()};
   const auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
   if (!intrinsic || intrinsic->name != "kind") {
@@ -1388,11 +1389,11 @@ Expr<T> FoldOperation(FoldingContext &context, FunctionRef<T> &&funcRef) {
 // Array constructor folding
 template <typename T> class ArrayConstructorFolder {
 public:
-  explicit ArrayConstructorFolder(int kind, FoldingContext &c)
+  explicit ArrayConstructorFolder(KindsEnum kind, FoldingContext &c)
       : context_{c}, resultInfo_{kind} {}
 
   Expr<T> FoldArray(ArrayConstructor<T> &&array) {
-    const int kind{array.kind()};
+    const KindsEnum kind{array.kind()};
     if constexpr (T::category == TypeCategory::Character) {
       if (const auto *len{array.LEN()}) {
         charLength_ = ToInt64(Fold(context_, common::Clone(*len)));
@@ -1403,7 +1404,7 @@ template <typename T> class ArrayConstructorFolder {
     if (FoldArray(array)) {
       auto n{static_cast<ConstantSubscript>(elements_.size())};
       if constexpr (std::is_same_v<T, SomeDerived>) {
-        CHECK(kind == 0);
+        CHECK(kind == NoKind);
         return Expr<T>{Constant<T>{array.GetType().GetDerivedTypeSpec(),
             std::move(elements_), ConstantSubscripts{n}}};
       } else if constexpr (T::category == TypeCategory::Character) {
@@ -1412,7 +1413,7 @@ template <typename T> class ArrayConstructorFolder {
               kind, *charLength_, std::move(elements_), ConstantSubscripts{n}}};
         }
       } else {
-        // resultInfo_ is default-constructed (kind 0) and only ever has its
+        // resultInfo_ is default-constructed (NoKind) and only ever has its
         // isFromInexactLiteralConversion flag set above; give it the actual
         // runtime kind here so that the Constant's result_ carries a valid
         // kind (kind_ is a public field of Type<CAT>, not a category with
@@ -1502,7 +1503,7 @@ template <typename T> class ArrayConstructorFolder {
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, ArrayConstructor<T> &&array) {
-  const int kind{array.kind()};
+  const KindsEnum kind{array.kind()};
   return ArrayConstructorFolder<T>{kind, context}.FoldArray(std::move(array));
 }
 
@@ -1526,7 +1527,7 @@ bool ArrayConstructorIsFlat(const ArrayConstructorValues<T> &values) {
 
 template <typename T>
 std::optional<Expr<T>> AsFlatArrayConstructor(const Expr<T> &expr) {
-  const int kind{expr.kind()};
+  const KindsEnum kind{static_cast<KindsEnum>(expr.kind())};
   if (const auto *c{UnwrapConstantValue<T>(expr)}) {
     ArrayConstructor<T> result{kind, expr};
     if (!c->empty()) {
@@ -1600,8 +1601,9 @@ std::optional<Expr<T>> FromArrayConstructor(
 
 // Unary case
 template <typename RESULT, typename OPERAND>
-std::optional<Expr<RESULT>> MapOperation(int kind, FoldingContext &context,
-    std::function<Expr<RESULT>(Expr<OPERAND> &&)> &&f, const Shape &shape,
+std::optional<Expr<RESULT>> MapOperation(KindsEnum kind,
+    FoldingContext &context, std::function<Expr<RESULT>(Expr<OPERAND> &&)> &&f,
+    const Shape &shape,
     [[maybe_unused]] std::optional<Expr<SubscriptInteger>> &&length,
     Expr<OPERAND> &&values) {
   ArrayConstructor<RESULT> result{kind, values};
@@ -1632,8 +1634,8 @@ std::optional<Expr<RESULT>> MapOperation(int kind, FoldingContext &context,
 }
 
 template <typename RESULT, typename A>
-ArrayConstructor<RESULT> ArrayConstructorFromMold(int kind, const A &prototype,
-    std::optional<Expr<SubscriptInteger>> &&length) {
+ArrayConstructor<RESULT> ArrayConstructorFromMold(KindsEnum kind,
+    const A &prototype, std::optional<Expr<SubscriptInteger>> &&length) {
   ArrayConstructor<RESULT> result{kind, prototype};
   if constexpr (RESULT::category == TypeCategory::Character) {
     if (length) {
@@ -1667,7 +1669,7 @@ bool ShapesMatch(FoldingContext &context,
 
 // array * array case
 template <typename RESULT, typename LEFT, typename RIGHT>
-auto MapOperation(int kind, FoldingContext &context,
+auto MapOperation(KindsEnum kind, FoldingContext &context,
     std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)> &&f,
     const Shape &shape, std::optional<Expr<SubscriptInteger>> &&length,
     Expr<LEFT> &&leftValues, Expr<RIGHT> &&rightValues)
@@ -1719,7 +1721,7 @@ auto MapOperation(int kind, FoldingContext &context,
 
 // array * scalar case
 template <typename RESULT, typename LEFT, typename RIGHT>
-auto MapOperation(int kind, FoldingContext &context,
+auto MapOperation(KindsEnum kind, FoldingContext &context,
     std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)> &&f,
     const Shape &shape, std::optional<Expr<SubscriptInteger>> &&length,
     Expr<LEFT> &&leftValues, const Expr<RIGHT> &rightScalar)
@@ -1737,7 +1739,7 @@ auto MapOperation(int kind, FoldingContext &context,
 
 // scalar * array case
 template <typename RESULT, typename LEFT, typename RIGHT>
-auto MapOperation(int kind, FoldingContext &context,
+auto MapOperation(KindsEnum kind, FoldingContext &context,
     std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)> &&f,
     const Shape &shape, std::optional<Expr<SubscriptInteger>> &&length,
     const Expr<LEFT> &leftScalar, Expr<RIGHT> &&rightValues)
@@ -1786,7 +1788,7 @@ auto ApplyElementwise(FoldingContext &context,
     Operation<DERIVED, RESULT, OPERAND> &operation,
     std::function<Expr<RESULT>(Expr<OPERAND> &&)> &&f)
     -> std::optional<Expr<RESULT>> {
-  const int kind{operation.kind()};
+  const KindsEnum kind{operation.kind()};
   auto &expr{operation.left()};
   expr = Fold(context, std::move(expr));
   if (expr.Rank() > 0) {
@@ -1804,7 +1806,7 @@ template <typename DERIVED, typename RESULT, typename OPERAND>
 auto ApplyElementwise(
     FoldingContext &context, Operation<DERIVED, RESULT, OPERAND> &operation)
     -> std::optional<Expr<RESULT>> {
-  const int kind{operation.kind()};
+  const KindsEnum kind{operation.kind()};
   return ApplyElementwise(context, operation,
       std::function<Expr<RESULT>(Expr<OPERAND> &&)>{
           [kind](Expr<OPERAND> &&operand) {
@@ -1817,7 +1819,7 @@ auto ApplyElementwise(FoldingContext &context,
     Operation<DERIVED, RESULT, LEFT, RIGHT> &operation,
     std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)> &&f)
     -> std::optional<Expr<RESULT>> {
-  const int kind{operation.kind()};
+  const KindsEnum kind{operation.kind()};
   auto resultLength{ComputeResultLength(operation)};
   auto &leftExpr{operation.left()};
   auto &rightExpr{operation.right()};
@@ -1869,7 +1871,7 @@ template <typename DERIVED, typename RESULT, typename LEFT, typename RIGHT>
 auto ApplyElementwise(
     FoldingContext &context, Operation<DERIVED, RESULT, LEFT, RIGHT> &operation)
     -> std::optional<Expr<RESULT>> {
-  const int kind{operation.kind()};
+  const KindsEnum kind{operation.kind()};
   return ApplyElementwise(context, operation,
       std::function<Expr<RESULT>(Expr<LEFT> &&, Expr<RIGHT> &&)>{
           [kind](Expr<LEFT> &&left, Expr<RIGHT> &&right) {
@@ -1882,9 +1884,8 @@ auto ApplyElementwise(
 
 template <typename TO, typename FROM>
 common::IfNoLvalue<std::optional<TO>, FROM> ConvertString(
-    int toKind, FROM &&s) {
-  const int fromKind{s.kind()};
-  if (std::is_same_v<TO, FROM> && toKind == fromKind) {
+    KindsEnum toKind, FROM &&s) {
+  if (std::is_same_v<TO, FROM> && toKind == s.kind()) {
     return std::make_optional<TO>(std::move(s));
   } else if (auto result{s.ToAscii(toKind)}; !result.IsMonostate()) {
     return result;
@@ -1896,19 +1897,19 @@ common::IfNoLvalue<std::optional<TO>, FROM> ConvertString(
 template <typename TO, TypeCategory FROMCAT>
 Expr<TO> FoldOperation(
     FoldingContext &context, Convert<TO, FROMCAT> &&convert) {
-  const int toKind{convert.kind()};
+  const KindsEnum toKind{convert.kind()};
   if (auto array{ApplyElementwise(context, convert)}) {
     return *array;
   }
   struct {
     FoldingContext &context;
     Convert<TO, FROMCAT> &convert;
-    int toKind;
+    KindsEnum toKind;
   } msvcWorkaround{context, convert, toKind};
   return common::visit(
       [&msvcWorkaround](auto &kindExpr) -> Expr<TO> {
         using Operand = ResultType<decltype(kindExpr)>;
-        const int toKind{msvcWorkaround.toKind};
+        const KindsEnum toKind{msvcWorkaround.toKind};
         // This variable is a workaround for msvc which emits an error when
         // using the FROMCAT template parameter below.
         TypeCategory constexpr FromCat{FROMCAT};
@@ -1916,7 +1917,7 @@ Expr<TO> FoldOperation(
         auto &convert{msvcWorkaround.convert};
         if (auto value{GetScalarConstantValue<Operand>(kindExpr)}) {
           FoldingContext &ctx{msvcWorkaround.context};
-          const int fromKind{value->kind()};
+          const KindsEnum fromKind{value->kind()};
           if constexpr (TO::category == TypeCategory::Integer) {
             if constexpr (FromCat == TypeCategory::Integer) {
               auto converted{
@@ -1924,8 +1925,8 @@ Expr<TO> FoldOperation(
               if (converted.overflow) {
                 ctx.Warn(common::UsageWarning::FoldingException,
                     "conversion of %s_%d to INTEGER(%d) overflowed; result is %s"_warn_en_US,
-                    value->SignedDecimal(), fromKind, toKind,
-                    converted.value.SignedDecimal());
+                    value->SignedDecimal(), static_cast<int>(fromKind),
+                    static_cast<int>(toKind), converted.value.SignedDecimal());
               }
               return MakeConstantExpr<TO>(toKind, std::move(converted.value));
             } else if constexpr (FromCat == TypeCategory::Unsigned) {
@@ -1934,8 +1935,8 @@ Expr<TO> FoldOperation(
               if ((converted.overflow || converted.value.IsNegative())) {
                 ctx.Warn(common::UsageWarning::FoldingException,
                     "conversion of %s_U%d to INTEGER(%d) overflowed; result is %s"_warn_en_US,
-                    value->UnsignedDecimal(), fromKind, toKind,
-                    converted.value.SignedDecimal());
+                    value->UnsignedDecimal(), static_cast<int>(fromKind),
+                    static_cast<int>(toKind), converted.value.SignedDecimal());
               }
               return MakeConstantExpr<TO>(toKind, std::move(converted.value));
             } else if constexpr (FromCat == TypeCategory::Real) {
@@ -1944,11 +1945,11 @@ Expr<TO> FoldOperation(
               if (converted.flags.test(RealFlag::InvalidArgument)) {
                 ctx.Warn(common::UsageWarning::FoldingException,
                     "REAL(%d) to INTEGER(%d) conversion: invalid argument"_warn_en_US,
-                    fromKind, toKind);
+                    static_cast<int>(fromKind), static_cast<int>(toKind));
               } else if (converted.flags.test(RealFlag::Overflow)) {
                 ctx.Warn(common::UsageWarning::FoldingException,
                     "REAL(%d) to INTEGER(%d) conversion overflowed"_warn_en_US,
-                    fromKind, toKind);
+                    static_cast<int>(fromKind), static_cast<int>(toKind));
               }
               return MakeConstantExpr<TO>(toKind, std::move(converted.value));
             }
@@ -1973,7 +1974,8 @@ Expr<TO> FoldOperation(
               if (!converted.flags.empty()) {
                 char buffer[64];
                 std::snprintf(buffer, sizeof buffer,
-                    "INTEGER(%d) to REAL(%d) conversion", fromKind, toKind);
+                    "INTEGER(%d) to REAL(%d) conversion",
+                    static_cast<int>(fromKind), static_cast<int>(toKind));
                 ctx.RealFlagWarnings(converted.flags, buffer);
               }
               return MakeConstantExpr<TO>(toKind, std::move(converted.value));
@@ -1982,7 +1984,8 @@ Expr<TO> FoldOperation(
               char buffer[64];
               if (!converted.flags.empty()) {
                 std::snprintf(buffer, sizeof buffer,
-                    "REAL(%d) to REAL(%d) conversion", fromKind, toKind);
+                    "REAL(%d) to REAL(%d) conversion",
+                    static_cast<int>(fromKind), static_cast<int>(toKind));
                 ctx.RealFlagWarnings(converted.flags, buffer);
               }
               if (ctx.targetCharacteristics().areSubnormalsFlushedToZero()) {
@@ -2035,9 +2038,11 @@ Expr<TO> FoldOperation(
                 // match the outer target for the round trip to be a no-op.
                 auto intermediateTy{kindExpr.GetType()};
                 auto xTy{x->GetType()};
-                int intermediateKind{
-                    intermediateTy ? intermediateTy->kind() : 0};
-                int xKind{xTy ? xTy->kind() : 0};
+                KindsEnum intermediateKind{intermediateTy
+                        ? static_cast<KindsEnum>(intermediateTy->kind())
+                        : NoKind};
+                KindsEnum xKind{
+                    xTy ? static_cast<KindsEnum>(xTy->kind()) : NoKind};
                 if constexpr (TO::category == TypeCategory::Logical) {
                   return std::move(*x); // no-op Logical conversion pair
                 } else { // Integer
@@ -2063,7 +2068,7 @@ Expr<TO> FoldOperation(
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Parentheses<T> &&x) {
-  const int kind{x.kind()};
+  const KindsEnum kind{x.kind()};
   auto &operand{x.left()};
   operand = Fold(context, std::move(operand));
   if (auto value{GetScalarConstantValue<T>(operand)}) {
@@ -2079,7 +2084,7 @@ Expr<T> FoldOperation(FoldingContext &context, Parentheses<T> &&x) {
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Negate<T> &&x) {
-  const int kind{x.kind()};
+  const auto kind{static_cast<KindsEnum>(x.kind())};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
@@ -2130,7 +2135,7 @@ std::optional<std::pair<Scalar<LEFT>, Scalar<RIGHT>>> OperandsAreConstants(
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Add<T> &&x) {
-  const int kind{x.kind()};
+  const auto kind{static_cast<KindsEnum>(x.kind())};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
@@ -2179,7 +2184,7 @@ Expr<T> FoldOperation(FoldingContext &context, Add<T> &&x) {
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Subtract<T> &&x) {
-  const int kind{x.kind()};
+  const auto kind{static_cast<KindsEnum>(x.kind())};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
@@ -2221,7 +2226,7 @@ Expr<T> FoldOperation(FoldingContext &context, Subtract<T> &&x) {
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Multiply<T> &&x) {
-  const int kind{x.kind()};
+  const auto kind{static_cast<KindsEnum>(x.kind())};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
@@ -2272,7 +2277,7 @@ Expr<T> FoldOperation(FoldingContext &context, Multiply<T> &&x) {
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Divide<T> &&x) {
-  const int kind{x.kind()};
+  const auto kind{static_cast<KindsEnum>(x.kind())};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
@@ -2309,8 +2314,8 @@ Expr<T> FoldOperation(FoldingContext &context, Divide<T> &&x) {
           using IntType = typename T::Scalar::Word;
           auto intNumerator{folded->first.ToInteger()};
           isCanonicalNaNOrInf = intNumerator.flags == RealFlags{} &&
-              intNumerator.value >= IntType{16, -1} &&
-              intNumerator.value <= IntType{16, 1};
+              intNumerator.value >= IntType{Kind16, -1} &&
+              intNumerator.value <= IntType{Kind16, 1};
         }
       }
       if (!isCanonicalNaNOrInf) {
@@ -2327,7 +2332,7 @@ Expr<T> FoldOperation(FoldingContext &context, Divide<T> &&x) {
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Power<T> &&x) {
-  const int kind{x.kind()};
+  const KindsEnum kind{x.kind()};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
@@ -2352,8 +2357,8 @@ Expr<T> FoldOperation(FoldingContext &context, Power<T> &&x) {
           context.Warn(common::UsageWarning::FoldingException,
               "REAL/COMPLEX 0**0 is not defined"_warn_en_US);
         } else {
-          return Expr<T>(
-              Constant<T>{kind, folded->first}); // 0. ** nonzero -> 0.
+          return Expr<T>{
+              Constant<T>{kind, folded->first}}; // 0. ** nonzero -> 0.
         }
       } else if (auto callable{GetHostRuntimeWrapper<T, T, T>(
                      kind, {kind, kind}, "pow")}) {
@@ -2371,7 +2376,7 @@ Expr<T> FoldOperation(FoldingContext &context, Power<T> &&x) {
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, RealToIntPower<T> &&x) {
-  const int kind{x.kind()};
+  const auto kind{static_cast<KindsEnum>(x.kind())};
   if (auto array{ApplyElementwise(context, x)}) {
     return *array;
   }
@@ -2404,7 +2409,7 @@ Expr<T> FoldOperation(FoldingContext &context, ConditionalExpr<T> &&x) {
 
 template <typename T>
 Expr<T> FoldOperation(FoldingContext &context, Extremum<T> &&x) {
-  const int kind{x.kind()};
+  const auto kind{static_cast<KindsEnum>(x.kind())};
   if (auto array{ApplyElementwise(context, x,
           std::function<Expr<T>(Expr<T> &&, Expr<T> &&)>{[=](Expr<T> &&l,
                                                              Expr<T> &&r) {
@@ -2445,7 +2450,7 @@ Expr<T> FoldOperation(FoldingContext &context, Extremum<T> &&x) {
 }
 
 inline Expr<Type<TypeCategory::Real>> ToReal(
-    int kind, FoldingContext &context, Expr<SomeType> &&expr) {
+    KindsEnum kind, FoldingContext &context, Expr<SomeType> &&expr) {
   using Result = Type<TypeCategory::Real>;
   std::optional<Expr<Result>> result;
   common::visit(
@@ -2477,7 +2482,7 @@ inline Expr<Type<TypeCategory::Real>> ToReal(
 // REAL(z) and AIMAG(z)
 inline Expr<Type<TypeCategory::Real>> FoldOperation(
     FoldingContext &context, ComplexComponent &&x) {
-  const int kind{x.kind()};
+  const auto kind{x.kind()};
   using Operand = Type<TypeCategory::Complex>;
   using Result = Type<TypeCategory::Real>;
   if (auto array{ApplyElementwise(context, x,
diff --git a/flang/lib/Evaluate/fold-integer.cpp b/flang/lib/Evaluate/fold-integer.cpp
index 2545f4b4210b5..6b079260cb82c 100644
--- a/flang/lib/Evaluate/fold-integer.cpp
+++ b/flang/lib/Evaluate/fold-integer.cpp
@@ -18,7 +18,7 @@ namespace Fortran::evaluate {
 // Return scalar value if asScalar == true and shape-dim array otherwise.
 template <typename T>
 Expr<T> PackageConstantBounds(
-    int kind, const ConstantSubscripts &&bounds, bool asScalar = false) {
+    KindsEnum kind, const ConstantSubscripts &&bounds, bool asScalar = false) {
   if (asScalar) {
     return MakeConstantExpr<T>(kind, bounds.at(0));
   } else {
@@ -101,7 +101,7 @@ class GetConstantArrayBoundHelper {
 public:
   template <typename T>
   static Expr<T> GetLbound(
-      int kind, const Expr<SomeType> &array, std::optional<int> dim) {
+      KindsEnum kind, const Expr<SomeType> &array, std::optional<int> dim) {
     return PackageConstantBounds<T>(kind,
         GetConstantArrayBoundHelper(dim, /*getLbound=*/true).Get(array),
         dim.has_value());
@@ -109,7 +109,7 @@ class GetConstantArrayBoundHelper {
 
   template <typename T>
   static Expr<T> GetUbound(
-      int kind, const Expr<SomeType> &array, std::optional<int> dim) {
+      KindsEnum kind, const Expr<SomeType> &array, std::optional<int> dim) {
     return PackageConstantBounds<T>(kind,
         GetConstantArrayBoundHelper(dim, /*getLbound=*/false).Get(array),
         dim.has_value());
@@ -178,7 +178,7 @@ class GetConstantArrayBoundHelper {
 Expr<Type<TypeCategory::Integer>> LBOUND(FoldingContext &context,
     FunctionRef<Type<TypeCategory::Integer>> &&funcRef) {
   using T = Type<TypeCategory::Integer>;
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   ActualArguments &args{funcRef.arguments()};
   if (const auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
     std::optional<int> dim;
@@ -229,7 +229,7 @@ Expr<Type<TypeCategory::Integer>> LBOUND(FoldingContext &context,
 Expr<Type<TypeCategory::Integer>> UBOUND(FoldingContext &context,
     FunctionRef<Type<TypeCategory::Integer>> &&funcRef) {
   using T = Type<TypeCategory::Integer>;
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   ActualArguments &args{funcRef.arguments()};
   if (auto *array{UnwrapExpr<Expr<SomeType>>(args[0])}) {
     std::optional<int> dim;
@@ -294,7 +294,7 @@ Expr<Type<TypeCategory::Integer>> UBOUND(FoldingContext &context,
 Expr<Type<TypeCategory::Integer>> COBOUND(FoldingContext &context,
     FunctionRef<Type<TypeCategory::Integer>> &&funcRef, bool isUCOBOUND) {
   using T = Type<TypeCategory::Integer>;
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   ActualArguments &args{funcRef.arguments()};
   if (const Symbol * coarray{UnwrapWholeSymbolOrComponentDataRef(args[0])}) {
     std::optional<int> dim;
@@ -327,9 +327,9 @@ template <typename T> class CountAccumulator {
   using MaskT = Type<TypeCategory::Logical>;
 
 public:
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
-  CountAccumulator(int kind, const Constant<MaskT> &mask)
+  CountAccumulator(KindsEnum kind, const Constant<MaskT> &mask)
       : kind_{kind}, mask_{mask} {}
   void operator()(
       Scalar<T> &element, const ConstantSubscripts &at, bool /*first*/) {
@@ -344,7 +344,7 @@ template <typename T> class CountAccumulator {
   void Done(Scalar<T> &) const {}
 
 private:
-  int kind_;
+  KindsEnum kind_;
   const Constant<MaskT> &mask_;
   bool overflow_{false};
 };
@@ -353,7 +353,7 @@ template <typename T>
 static Expr<T> FoldCount(FoldingContext &context, FunctionRef<T> &&ref) {
   using KindLogical = Type<TypeCategory::Logical>;
   static_assert(T::category == TypeCategory::Integer);
-  const int kind{ref.kind()};
+  const auto kind{ref.kind()};
   std::optional<int> dim;
   if (std::optional<ArrayAndMask<KindLogical>> arrayAndMask{
           ProcessReductionArgs<KindLogical>(LogicalResultKind, context,
@@ -381,7 +381,7 @@ template <WhichLocation WHICH> class LocationHelper {
   using Types = std::conditional_t<WHICH == WhichLocation::Findloc,
       AllIntrinsicTypes, RelationalTypes>;
 
-  template <typename T> Result Test(int kind) const {
+  template <typename T> Result Test(KindsEnum kind) const {
     if (T::category != type_.category() || kind != type_.kind()) {
       return std::nullopt;
     }
@@ -408,8 +408,8 @@ template <WhichLocation WHICH> class LocationHelper {
     }
     bool back{false};
     if (arg_[backArg]) {
-      const auto *backConst{Folder<LogicalResult>{
-          LogicalResultKind, context_, /*forOptionalArgument=*/true}
+      const auto *backConst{Folder<LogicalResult>{LogicalResultKind, context_,
+          /*forOptionalArgument=*/true}
               .Folding(arg_[backArg])};
       if (backConst) {
         back = backConst->GetScalarValue().value().IsTrue();
@@ -510,7 +510,7 @@ template <WhichLocation WHICH> class LocationHelper {
       std::optional<Constant<T>> &value,
       [[maybe_unused]] RelationalOperator relation,
       [[maybe_unused]] bool back) const {
-    const int kind{element.kind()};
+    const KindsEnum kind{element.kind()};
     std::optional<Expr<LogicalResult>> cmp;
     bool result{true};
     if (value) {
@@ -584,7 +584,7 @@ static std::optional<Constant<SubscriptInteger>> FoldLocationCall(
 template <WhichLocation which, typename T>
 static Expr<T> FoldLocation(FoldingContext &context, FunctionRef<T> &&ref) {
   static_assert(T::category == TypeCategory::Integer);
-  const int kind{ref.kind()};
+  const KindsEnum kind{ref.kind()};
   if (std::optional<Constant<SubscriptInteger>> found{
           FoldLocationCall<which>(ref.arguments(), context)}) {
     return Expr<T>{Fold(context,
@@ -599,7 +599,7 @@ template <typename T>
 static Expr<T> FoldBitReduction(FoldingContext &context, FunctionRef<T> &&ref,
     Scalar<T> (Scalar<T>::*operation)(const Scalar<T> &) const,
     Scalar<T> identity) {
-  const int kind{ref.kind()};
+  const auto kind{ref.kind()};
   static_assert(T::category == TypeCategory::Integer ||
       T::category == TypeCategory::Unsigned);
   std::optional<int> dim;
@@ -617,7 +617,7 @@ static Expr<T> FoldBitReduction(FoldingContext &context, FunctionRef<T> &&ref,
 template <typename T>
 std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
     FoldingContext &context, FunctionRef<T> &funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   ActualArguments &args{funcRef.arguments()};
   auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
   CHECK(intrinsic);
@@ -665,9 +665,8 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
     // than BIT_SIZE. It can be converted to Int4 to simplify.
     if (const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
         argCon && argCon->empty()) {
-    } else if (const auto *shiftCon{Folder<Int4>(
-                   /*kind=*/4, context)
-                       .Folding(args[2])}) {
+    } else if (const auto *shiftCon{
+                   Folder<Int4>(Kind4, context).Folding(args[2])}) {
       for (const auto &scalar : shiftCon->values()) {
         std::int64_t shiftVal{scalar.ToInt64()};
         if (shiftVal < 0) {
@@ -682,8 +681,8 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
         }
       }
     }
-    return FoldElementalIntrinsic<T, T, T, Int4>(kind, {kind, kind, 4}, context,
-        std::move(funcRef),
+    return FoldElementalIntrinsic<T, T, T, Int4>(kind, {kind, kind, Kind4},
+        context, std::move(funcRef),
         ScalarFunc<T, T, T, Int4>(
             [&fptr](const Scalar<T> &i, const Scalar<T> &j,
                 const Scalar<Int4> &shift) -> Scalar<T> {
@@ -719,7 +718,8 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
     }
     if (const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
         argCon && argCon->empty()) {
-    } else if (const auto *posCon{Folder<Int4>(4, context).Folding(args[1])}) {
+    } else if (const auto *posCon{
+                   Folder<Int4>(Kind4, context).Folding(args[1])}) {
       for (const auto &scalar : posCon->values()) {
         std::int64_t posVal{scalar.ToInt64()};
         if (posVal < 0) {
@@ -735,15 +735,15 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
         }
       }
     }
-    return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, 4}, context,
+    return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, Kind4}, context,
         std::move(funcRef),
         ScalarFunc<T, T, Int4>(
             [&](const Scalar<T> &i, const Scalar<Int4> &pos) -> Scalar<T> {
               return std::invoke(fptr, i, static_cast<int>(pos.ToInt64()));
             }));
   } else if (name == "ibits") {
-    const auto *posCon{Folder<Int4>(4, context).Folding(args[1])};
-    const auto *lenCon{Folder<Int4>(4, context).Folding(args[2])};
+    const auto *posCon{Folder<Int4>(Kind4, context).Folding(args[1])};
+    const auto *lenCon{Folder<Int4>(Kind4, context).Folding(args[2])};
     if (const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
         argCon && argCon->empty()) {
     } else {
@@ -775,8 +775,8 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
         }
       }
     }
-    return FoldElementalIntrinsic<T, T, Int4, Int4>(kind, {kind, 4, 4}, context,
-        std::move(funcRef),
+    return FoldElementalIntrinsic<T, T, Int4, Int4>(kind, {kind, Kind4, Kind4},
+        context, std::move(funcRef),
         ScalarFunc<T, T, Int4, Int4>(
             [&](const Scalar<T> &i, const Scalar<Int4> &pos,
                 const Scalar<Int4> &len) -> Scalar<T> {
@@ -818,12 +818,12 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
         context, std::move(funcRef), &Scalar<T>::IEOR, Scalar<T>{kind, 0});
   } else if (name == "ishft" || name == "ishftc") {
     const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
-    const auto *shiftCon{Folder<Int4>(4, context).Folding(args[1])};
+    const auto *shiftCon{Folder<Int4>(Kind4, context).Folding(args[1])};
     const auto *shiftVals{shiftCon ? &shiftCon->values() : nullptr};
-    const auto *sizeCon{args.size() == 3
-            ? Folder<Int4>{4, context, /*forOptionalArgument=*/true}.Folding(
-                  args[2])
-            : nullptr};
+    const auto *sizeCon{args.size() == 3 ? Folder<Int4>{Kind4, context,
+                                               /*forOptionalArgument=*/true}
+                                               .Folding(args[2])
+                                         : nullptr};
     const auto *sizeVals{sizeCon ? &sizeCon->values() : nullptr};
     if ((argCon && argCon->empty()) || !shiftVals || shiftVals->empty() ||
         (sizeVals && sizeVals->empty())) {
@@ -877,22 +877,22 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
       }
     }
     if (name == "ishft") {
-      return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, 4}, context,
+      return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, Kind4}, context,
           std::move(funcRef),
           ScalarFunc<T, T, Int4>(
               [&](const Scalar<T> &i, const Scalar<Int4> &shift) -> Scalar<T> {
                 return i.ISHFT(static_cast<int>(shift.ToInt64()));
               }));
     } else if (!args.at(2)) { // ISHFTC(no SIZE=)
-      return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, 4}, context,
+      return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, Kind4}, context,
           std::move(funcRef),
           ScalarFunc<T, T, Int4>(
               [&](const Scalar<T> &i, const Scalar<Int4> &shift) -> Scalar<T> {
                 return i.ISHFTC(static_cast<int>(shift.ToInt64()));
               }));
     } else { // ISHFTC(with SIZE=)
-      return FoldElementalIntrinsic<T, T, Int4, Int4>(kind, {kind, 4, 4},
-          context, std::move(funcRef),
+      return FoldElementalIntrinsic<T, T, Int4, Int4>(kind,
+          {kind, Kind4, Kind4}, context, std::move(funcRef),
           ScalarFunc<T, T, Int4, Int4>(
               [&](const Scalar<T> &i, const Scalar<Int4> &shift,
                   const Scalar<Int4> &size) -> Scalar<T> {
@@ -920,7 +920,7 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
     // It can be safely converted to Int4 to simplify.
     const auto fptr{name == "maskl" || name == "umaskl" ? &Scalar<T>::MASKL
                                                         : &Scalar<T>::MASKR};
-    return FoldElementalIntrinsic<T, Int4>(kind, {4}, context,
+    return FoldElementalIntrinsic<T, Int4>(kind, {Kind4}, context,
         std::move(funcRef),
         ScalarFunc<T, Int4>(
             [&fptr, kind](const Scalar<Int4> &places) -> Scalar<T> {
@@ -967,7 +967,7 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
     if (const auto *argCon{Folder<T>(kind, context).Folding(args[0])};
         argCon && argCon->empty()) {
     } else if (const auto *shiftCon{
-                   Folder<Int4>(4, context).Folding(args[1])}) {
+                   Folder<Int4>(Kind4, context).Folding(args[1])}) {
       for (const auto &scalar : shiftCon->values()) {
         std::int64_t shiftVal{scalar.ToInt64()};
         if (shiftVal < 0) {
@@ -982,7 +982,7 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
         }
       }
     }
-    return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, 4}, context,
+    return FoldElementalIntrinsic<T, T, Int4>(kind, {kind, Kind4}, context,
         std::move(funcRef),
         ScalarFunc<T, T, Int4>(
             [&](const Scalar<T> &i, const Scalar<Int4> &shift) -> Scalar<T> {
@@ -997,7 +997,7 @@ std::optional<Expr<T>> FoldIntrinsicFunctionCommon(
 Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
     FunctionRef<Type<TypeCategory::Integer>> &&funcRef) {
   using T = Type<TypeCategory::Integer>;
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   if (auto foldedCommon{FoldIntrinsicFunctionCommon(context, funcRef)}) {
     return std::move(*foldedCommon);
   }
@@ -1024,7 +1024,8 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
           typename Scalar<T>::ValueWithOverflow j{i.ABS()};
           if (j.overflow) {
             context.Warn(common::UsageWarning::FoldingException,
-                "abs(integer(kind=%d)) folding overflowed"_warn_en_US, kind);
+                "abs(integer(kind=%d)) folding overflowed"_warn_en_US,
+                static_cast<int>(kind));
           }
           return j.value;
         }));
@@ -1068,8 +1069,9 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
       return common::visit(
           [&funcRef, &context, kind](const auto &x) -> Expr<T> {
             using TR = typename std::decay_t<decltype(x)>::Result;
-            return FoldElementalIntrinsic<T, TR>(kind, {x.kind()}, context,
-                std::move(funcRef), &Scalar<TR>::EXPONENT);
+            return FoldElementalIntrinsic<T, TR>(kind,
+                {static_cast<KindsEnum>(x.kind())}, context, std::move(funcRef),
+                &Scalar<TR>::EXPONENT);
           },
           sx->u);
     } else {
@@ -1097,8 +1099,9 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
         return common::visit(
             [&funcRef, &context, &FromInt64, kind](const auto &str) -> Expr<T> {
               using Char = typename std::decay_t<decltype(str)>::Result;
-              return FoldElementalIntrinsic<T, Char>(kind, {str.kind()},
-                  context, std::move(funcRef),
+              return FoldElementalIntrinsic<T, Char>(kind,
+                  {static_cast<KindsEnum>(str.kind())}, context,
+                  std::move(funcRef),
                   ScalarFunc<T, Char>(
 #ifndef _MSC_VER
                       [&FromInt64](const Scalar<Char> &c) {
@@ -1127,7 +1130,7 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
       return common::visit(
           [&](const auto &kch) -> Expr<T> {
             using TC = typename std::decay_t<decltype(kch)>::Result;
-            const int kchKind{kch.kind()};
+            const KindsEnum kchKind{static_cast<KindsEnum>(kch.kind())};
             if (UnwrapExpr<Expr<SomeLogical>>(args[2])) { // BACK=
               return FoldElementalIntrinsic<T, TC, TC, LogicalResult>(kind,
                   {kchKind, kchKind, LogicalResultKind}, context,
@@ -1179,7 +1182,7 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
           }
         }
       } else if (auto dyType{expr->GetType()}) {
-        return MakeConstantExpr<T>(kind, dyType->kind());
+        return MakeConstantExpr<T>(kind, static_cast<int>(dyType->kind()));
       }
     }
   } else if (name == "lbound") {
@@ -1192,8 +1195,9 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
       return common::visit(
           [&funcRef, &context, &name, kind](const auto &n) -> Expr<T> {
             using TI = typename std::decay_t<decltype(n)>::Result;
+            const KindsEnum nKind{static_cast<KindsEnum>(n.kind())};
             if (name == "poppar") {
-              return FoldElementalIntrinsic<T, TI>(kind, {n.kind()}, context,
+              return FoldElementalIntrinsic<T, TI>(kind, {nKind}, context,
                   std::move(funcRef),
                   ScalarFunc<T, TI>([kind](const Scalar<TI> &i) -> Scalar<T> {
                     return Scalar<T>{kind, i.POPPAR() ? 1 : 0};
@@ -1209,7 +1213,7 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
               common::die(
                   "missing case to fold intrinsic function %s", name.c_str());
             }
-            return FoldElementalIntrinsic<T, TI>(kind, {n.kind()}, context,
+            return FoldElementalIntrinsic<T, TI>(kind, {nKind}, context,
                 std::move(funcRef),
                 // `i` should be declared as `const Scalar<TI>&`.
                 // We declare it as `auto` to workaround an msvc bug:
@@ -1245,7 +1249,8 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
       return common::visit(
           [&](const auto &kch) -> Expr<T> {
             using TC = typename std::decay_t<decltype(kch)>::Result;
-            return FoldElementalIntrinsic<T, TC>(kind, {kch.kind()}, context,
+            return FoldElementalIntrinsic<T, TC>(kind,
+                {static_cast<KindsEnum>(kch.kind())}, context,
                 std::move(funcRef),
                 ScalarFunc<T, TC>{[&FromInt64](const Scalar<TC> &str) {
                   return FromInt64(CharacterUtils::LEN_TRIM(str));
@@ -1262,7 +1267,8 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
       return common::visit(
           [&](const auto &x) {
             using TR = typename std::decay_t<decltype(x)>::Result;
-            return MakeConstantExpr<T>(kind, Scalar<TR>::MAXEXPONENT(x.kind()));
+            return MakeConstantExpr<T>(kind,
+                Scalar<TR>::MAXEXPONENT(static_cast<KindsEnum>(x.kind())));
           },
           sx->u);
     }
@@ -1275,7 +1281,8 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
       return common::visit(
           [&](const auto &x) {
             using TR = typename std::decay_t<decltype(x)>::Result;
-            return MakeConstantExpr<T>(kind, Scalar<TR>::MINEXPONENT(x.kind()));
+            return MakeConstantExpr<T>(kind,
+                Scalar<TR>::MINEXPONENT(static_cast<KindsEnum>(x.kind())));
           },
           sx->u);
     }
@@ -1402,7 +1409,7 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
             UnwrapExpr<Constant<Type<TypeCategory::Character>>>(args[0])}) {
       if (std::optional<value::CharacterValue> charVal{
               chCon->GetScalarValue()}) {
-        int defaultKind{
+        KindsEnum defaultKind{
             context.defaults().GetDefaultKind(TypeCategory::Character)};
         return MakeConstantExpr<T>(
             kind, SelectedCharKind(*charVal->AsStdString(), defaultKind));
@@ -1442,7 +1449,8 @@ Expr<Type<TypeCategory::Integer>> FoldIntrinsicFunction(FoldingContext &context,
           typename Scalar<T>::ValueWithOverflow result{j.SIGN(k)};
           if (result.overflow) {
             context.Warn(common::UsageWarning::FoldingException,
-                "sign(integer(kind=%d)) folding overflowed"_warn_en_US, kind);
+                "sign(integer(kind=%d)) folding overflowed"_warn_en_US,
+                static_cast<int>(kind));
           }
           return result.value;
         }));
@@ -1518,7 +1526,7 @@ Expr<Type<TypeCategory::Unsigned>> FoldIntrinsicFunction(
     FoldingContext &context,
     FunctionRef<Type<TypeCategory::Unsigned>> &&funcRef) {
   using T = Type<TypeCategory::Unsigned>;
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   if (auto foldedCommon{FoldIntrinsicFunctionCommon(context, funcRef)}) {
     return std::move(*foldedCommon);
   }
@@ -1628,7 +1636,7 @@ Expr<RankOneBoundElement::Result> FoldOperation(
     ConstantSubscripts at{c->lbounds()};
     at[0] = c->lbounds()[0] + x.dimension();
     return MakeConstantExpr<ResultType>(
-        RankOneBoundElement::ResultKind, c->At(at));
+        static_cast<KindsEnum>(RankOneBoundElement::ResultKind), c->At(at));
   }
   return Expr<ResultType>{
       RankOneBoundElement{std::move(folded), x.dimension()}};
diff --git a/flang/lib/Evaluate/fold-logical.cpp b/flang/lib/Evaluate/fold-logical.cpp
index f8fe642f38536..5e9a307b695a9 100644
--- a/flang/lib/Evaluate/fold-logical.cpp
+++ b/flang/lib/Evaluate/fold-logical.cpp
@@ -28,7 +28,7 @@ static std::optional<Expr<SomeType>> ZeroExtend(const Constant<T> &c) {
 
 // for ALL, ANY & PARITY
 template <typename T>
-static Expr<T> FoldAllAnyParity(int kind, FoldingContext &context,
+static Expr<T> FoldAllAnyParity(KindsEnum kind, FoldingContext &context,
     FunctionRef<T> &&ref,
     Scalar<T> (Scalar<T>::*operation)(const Scalar<T> &) const,
     Scalar<T> identity) {
@@ -50,7 +50,7 @@ static Expr<T> FoldAllAnyParity(int kind, FoldingContext &context,
 // TODO: unsigned
 
 static Expr<SomeReal> RealToIntBound(
-    int xRKind, int moldIKind, bool round, bool negate) {
+    KindsEnum xRKind, KindsEnum moldIKind, bool round, bool negate) {
   using RealType = Scalar<Type<TypeCategory::Real>>;
   using IntType = Scalar<Type<TypeCategory::Integer>>;
   RealType result{RealType::Zero(xRKind)}; // 0.
@@ -96,9 +96,9 @@ class RealToIntLimitHelper {
   RealToIntLimitHelper(
       FoldingContext &context, Expr<SomeReal> &&hi, Expr<SomeReal> &lo)
       : context_{context}, hi_{std::move(hi)}, lo_{lo} {}
-  template <typename T> Result Test(int kind) {
+  template <typename T> Result Test(KindsEnum kind) {
     if (UnwrapExpr<Expr<T>>(kind, hi_)) {
-      bool promote{kind < 16};
+      bool promote{kind < Kind16};
       Result constResult;
       if (auto hiV{GetScalarConstantValue<T>(hi_)}) {
         auto loV{GetScalarConstantValue<T>(lo_)};
@@ -110,7 +110,9 @@ class RealToIntLimitHelper {
         constResult = AsCategoryExpr(Constant<T>{kind, std::move(diff.value)});
       }
       if (promote) {
-        int nextKind{kind < 4 ? 4 : kind == 4 ? 8 : 16};
+        KindsEnum nextKind{kind < Kind4 ? Kind4
+                : kind == Kind4         ? Kind8
+                                        : Kind16};
         using T2 = Type<TypeCategory::Real>;
         hi_ = Expr<SomeReal>{
             Fold(context_, ConvertToType<T2>(nextKind, std::move(hi_)))};
@@ -145,7 +147,7 @@ static std::optional<Expr<SomeReal>> RealToIntLimit(
 // RealToRealBounds() returns a pair (HUGE(x),REAL(HUGE(mold),KIND(x)))
 // when REAL(HUGE(x),KIND(mold)) overflows, and std::nullopt otherwise.
 static std::optional<std::pair<Expr<SomeReal>, Expr<SomeReal>>>
-RealToRealBounds(int xRKind, int moldRKind) {
+RealToRealBounds(KindsEnum xRKind, KindsEnum moldRKind) {
   using RType = Type<TypeCategory::Real>;
   using RealType = Scalar<Type<TypeCategory::Real>>;
   using MoldRealType = Scalar<Type<TypeCategory::Real>>;
@@ -161,7 +163,7 @@ RealToRealBounds(int xRKind, int moldRKind) {
 }
 
 static std::optional<Expr<SomeInteger>> IntToRealBoundHelper(
-    int xIKind, int moldRKind, bool negate) {
+    KindsEnum xIKind, KindsEnum moldRKind, bool negate) {
   using IType = Type<TypeCategory::Integer>;
   using IntType = Scalar<Type<TypeCategory::Integer>>;
   using RealType = Scalar<Type<TypeCategory::Real>>;
@@ -200,12 +202,12 @@ static std::optional<Expr<SomeInteger>> IntToRealBoundHelper(
 }
 
 static std::optional<Expr<SomeInteger>> IntToRealBound(
-    int xIKind, int moldRKind, bool negate) {
+    KindsEnum xIKind, KindsEnum moldRKind, bool negate) {
   return IntToRealBoundHelper(xIKind, moldRKind, negate);
 }
 
 static std::optional<Expr<SomeInteger>> IntToIntBoundHelper(
-    int xIKind, int moldIKind) {
+    KindsEnum xIKind, KindsEnum moldIKind) {
   if (xIKind <= moldIKind) {
     return std::nullopt;
   } else {
@@ -221,7 +223,7 @@ static std::optional<Expr<SomeInteger>> IntToIntBoundHelper(
 }
 
 static std::optional<Expr<SomeInteger>> IntToIntBound(
-    int xIKind, int moldIKind) {
+    KindsEnum xIKind, KindsEnum moldIKind) {
   return IntToIntBoundHelper(xIKind, moldIKind);
 }
 
@@ -237,10 +239,10 @@ class IntrinsicCallHelper {
   }
   using Result = std::optional<Expr<SomeType>>;
   using Types = LengthlessIntrinsicTypes;
-  template <typename T> Result Test(int kind) {
+  template <typename T> Result Test(KindsEnum kind) {
     if (T::category == typeAndShape_->type().category() &&
         kind == typeAndShape_->type().kind()) {
-      return AsGenericExpr(FunctionRef<T>{typeAndShape_->type().kind(),
+      return AsGenericExpr(FunctionRef<T>{kind,
           ProcedureDesignator{std::move(call_.specificIntrinsic)},
           std::move(call_.arguments)});
     } else {
@@ -277,8 +279,8 @@ static Expr<LogicalResult> CompareUnsigned(FoldingContext &context,
 static Expr<SomeType> IntTransferMold(
     const TargetCharacteristics &target, DynamicType realType, bool asVector) {
   CHECK(realType.category() == TypeCategory::Real);
-  int rKind{realType.kind()};
-  int iKind{std::max<int>(target.GetAlignment(TypeCategory::Real, rKind),
+  KindsEnum rKind{realType.kind()};
+  KindsEnum iKind{std::max<int>(target.GetAlignment(TypeCategory::Real, rKind),
       target.GetByteSize(TypeCategory::Real, rKind))};
   CHECK(target.CanSupportType(TypeCategory::Integer, iKind));
   DynamicType iType{TypeCategory::Integer, iKind};
@@ -309,7 +311,7 @@ static Expr<Type<TypeCategory::Logical>> RewriteOutOfRange(
     FoldingContext &context,
     FunctionRef<Type<TypeCategory::Logical>> &&funcRef) {
   using ResultType = Type<TypeCategory::Logical>;
-  const int resultKind{funcRef.kind()};
+  const KindsEnum resultKind{funcRef.kind()};
   ActualArguments &args{funcRef.arguments()};
   // Fold x= and round= unconditionally
   if (auto *x{UnwrapExpr<Expr<SomeType>>(args[0])}) {
@@ -328,10 +330,11 @@ static Expr<Type<TypeCategory::Logical>> RewriteOutOfRange(
       std::optional<Expr<LogicalResult>> result;
       bool alwaysFalse{false};
       if (auto *iXExpr{UnwrapExpr<Expr<SomeInteger>>(*x)}) {
-        int iXKind{iXExpr->GetType().value().kind()};
+        auto iXKind{static_cast<KindsEnum>(iXExpr->GetType().value().kind())};
         if (auto *iMoldExpr{UnwrapExpr<Expr<SomeInteger>>(*mold)}) {
           // INTEGER -> INTEGER
-          int iMoldKind{iMoldExpr->GetType().value().kind()};
+          auto iMoldKind{
+              static_cast<KindsEnum>(iMoldExpr->GetType().value().kind())};
           if (auto hi{IntToIntBound(iXKind, iMoldKind)}) {
             // 'hi' is INT(HUGE(mold), KIND(x))
             // OUT_OF_RANGE(x,mold) = (x + (hi + 1)) .UGT. (2*hi + 1)
@@ -349,7 +352,8 @@ static Expr<Type<TypeCategory::Logical>> RewriteOutOfRange(
           }
         } else if (auto *rMoldExpr{UnwrapExpr<Expr<SomeReal>>(*mold)}) {
           // INTEGER -> REAL
-          int rMoldKind{rMoldExpr->GetType().value().kind()};
+          auto rMoldKind{
+              static_cast<KindsEnum>(rMoldExpr->GetType().value().kind())};
           if (auto hi{IntToRealBound(iXKind, rMoldKind, /*negate=*/false)}) {
             // OUT_OF_RANGE(x,mold) = (x - lo) .UGT. (hi - lo)
             auto lo{IntToRealBound(iXKind, rMoldKind, /*negate=*/true)};
@@ -363,10 +367,11 @@ static Expr<Type<TypeCategory::Logical>> RewriteOutOfRange(
           }
         }
       } else if (auto *rXExpr{UnwrapExpr<Expr<SomeReal>>(*x)}) {
-        int rXKind{rXExpr->GetType().value().kind()};
+        auto rXKind{static_cast<KindsEnum>(rXExpr->GetType().value().kind())};
         if (auto *iMoldExpr{UnwrapExpr<Expr<SomeInteger>>(*mold)}) {
           // REAL -> INTEGER
-          int iMoldKind{iMoldExpr->GetType().value().kind()};
+          auto iMoldKind{
+              static_cast<KindsEnum>(iMoldExpr->GetType().value().kind())};
           auto hi{RealToIntBound(rXKind, iMoldKind, false, false)};
           auto lo{RealToIntBound(rXKind, iMoldKind, false, true)};
           if (args.size() >= 3) {
@@ -416,7 +421,8 @@ static Expr<Type<TypeCategory::Logical>> RewriteOutOfRange(
           //   TRANSFER(HUGE(mold), int)
           // Note that OUT_OF_RANGE(+/-Inf or NaN,mold) =
           //   TRANSFER(+Inf or Nan, int) - 1 .ULT. TRANSFER(HUGE(mold), int)
-          int rMoldKind{rMoldExpr->GetType().value().kind()};
+          auto rMoldKind{
+              static_cast<KindsEnum>(rMoldExpr->GetType().value().kind())};
           if (auto bounds{RealToRealBounds(rXKind, rMoldKind)}) {
             auto &[moldHuge, xHuge]{*bounds};
             Expr<SomeType> abs{ApplyIntrinsic(context, "abs",
@@ -481,7 +487,7 @@ static std::optional<common::RoundingMode> GetRoundingMode(
 Expr<Type<TypeCategory::Logical>> FoldIntrinsicFunction(FoldingContext &context,
     FunctionRef<Type<TypeCategory::Logical>> &&funcRef) {
   using T = Type<TypeCategory::Logical>;
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   ActualArguments &args{funcRef.arguments()};
   auto *intrinsic{std::get_if<SpecificIntrinsic>(&funcRef.proc().u)};
   CHECK(intrinsic);
@@ -613,7 +619,7 @@ Expr<Type<TypeCategory::Logical>> FoldIntrinsicFunction(FoldingContext &context,
         IsActuallyConstant(*args[0]->UnwrapExpr())) {
       auto restorer{context.messages().DiscardMessages()};
       using DefaultReal = Type<TypeCategory::Real>;
-      constexpr int DefaultRealKind{4};
+      constexpr KindsEnum DefaultRealKind{Kind4};
       return FoldElementalIntrinsic<T, DefaultReal>(kind, {DefaultRealKind},
           context, std::move(funcRef),
           ScalarFunc<T, DefaultReal>([kind](const Scalar<DefaultReal> &x) {
@@ -623,7 +629,7 @@ Expr<Type<TypeCategory::Logical>> FoldIntrinsicFunction(FoldingContext &context,
   } else if (name == "__builtin_ieee_is_negative") {
     auto restorer{context.messages().DiscardMessages()};
     using DefaultReal = Type<TypeCategory::Real>;
-    constexpr int DefaultRealKind{4};
+    constexpr KindsEnum DefaultRealKind{Kind4};
     if (args[0] && args[0]->UnwrapExpr() &&
         IsActuallyConstant(*args[0]->UnwrapExpr())) {
       return FoldElementalIntrinsic<T, DefaultReal>(kind, {DefaultRealKind},
@@ -635,7 +641,7 @@ Expr<Type<TypeCategory::Logical>> FoldIntrinsicFunction(FoldingContext &context,
   } else if (name == "__builtin_ieee_is_normal") {
     auto restorer{context.messages().DiscardMessages()};
     using DefaultReal = Type<TypeCategory::Real>;
-    constexpr int DefaultRealKind = 4;
+    constexpr KindsEnum DefaultRealKind{Kind4};
     if (args[0] && args[0]->UnwrapExpr() &&
         IsActuallyConstant(*args[0]->UnwrapExpr())) {
       return FoldElementalIntrinsic<T, DefaultReal>(kind, {DefaultRealKind},
@@ -669,7 +675,7 @@ Expr<Type<TypeCategory::Logical>> FoldIntrinsicFunction(FoldingContext &context,
       // Int64 used to be Type<Integer,8>; force the argument to that kind as
       // before.
       using Int64 = Type<TypeCategory::Integer>;
-      constexpr int Int64Kind{8};
+      constexpr KindsEnum Int64Kind{Kind8};
       return FoldElementalIntrinsic<T, Int64>(kind, {Int64Kind}, context,
           std::move(funcRef),
           ScalarFunc<T, Int64>([kind](const Scalar<Int64> &x) {
@@ -680,7 +686,7 @@ Expr<Type<TypeCategory::Logical>> FoldIntrinsicFunction(FoldingContext &context,
     if (args[0] && args[0]->UnwrapExpr() &&
         IsActuallyConstant(*args[0]->UnwrapExpr())) {
       using Int64 = Type<TypeCategory::Integer>;
-      constexpr int Int64Kind{8};
+      constexpr KindsEnum Int64Kind{Kind8};
       return FoldElementalIntrinsic<T, Int64>(kind, {Int64Kind}, context,
           std::move(funcRef),
           ScalarFunc<T, Int64>([kind](const Scalar<Int64> &x) {
@@ -872,7 +878,7 @@ Expr<Type<TypeCategory::Logical>> FoldOperation(
     return *array;
   }
   using Ty = Type<TypeCategory::Logical>;
-  const int kind{x.kind()};
+  const KindsEnum kind{x.kind()};
   auto &operand{x.left()};
   if (auto value{GetScalarConstantValue<Ty>(operand)}) {
     return MakeConstantExpr<Ty>(kind, !value->IsTrue());
@@ -883,7 +889,7 @@ Expr<Type<TypeCategory::Logical>> FoldOperation(
 Expr<Type<TypeCategory::Logical>> FoldOperation(
     FoldingContext &context, LogicalOperation &&operation) {
   using LOGICAL = Type<TypeCategory::Logical>;
-  const int kind{operation.kind()};
+  const KindsEnum kind{operation.kind()};
   if (auto array{ApplyElementwise(context, operation,
           std::function<Expr<LOGICAL>(Expr<LOGICAL> &&, Expr<LOGICAL> &&)>{
               [=](Expr<LOGICAL> &&x, Expr<LOGICAL> &&y) {
diff --git a/flang/lib/Evaluate/fold-matmul.h b/flang/lib/Evaluate/fold-matmul.h
index e0c58acdbb4bc..2fc655cb3e1a9 100644
--- a/flang/lib/Evaluate/fold-matmul.h
+++ b/flang/lib/Evaluate/fold-matmul.h
@@ -15,7 +15,7 @@ namespace Fortran::evaluate {
 
 template <typename T>
 static Expr<T> FoldMatmul(FoldingContext &context, FunctionRef<T> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const auto kind{static_cast<KindsEnum>(funcRef.kind())};
   using Element = typename Constant<T>::Element;
   auto args{funcRef.arguments()};
   CHECK(args.size() == 2);
diff --git a/flang/lib/Evaluate/fold-real.cpp b/flang/lib/Evaluate/fold-real.cpp
index 57b935ac05799..d16282917746e 100644
--- a/flang/lib/Evaluate/fold-real.cpp
+++ b/flang/lib/Evaluate/fold-real.cpp
@@ -15,17 +15,17 @@ namespace Fortran::evaluate {
 template <typename T>
 static Expr<T> FoldTransformationalBessel(
     FunctionRef<T> &&funcRef, FoldingContext &context) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   CHECK(funcRef.arguments().size() == 3);
   /// Bessel runtime functions use `int` integer arguments. Convert integer
   /// arguments to Int4, any overflow error will be reported during the
   /// conversion folding.
   using Int4 = Type<TypeCategory::Integer>;
-  if (auto args{GetConstantArguments<Int4, Int4, T>({4, 4, kind}, context,
-          funcRef.arguments(), /*hasOptionalArgument=*/false)}) {
+  if (auto args{GetConstantArguments<Int4, Int4, T>({Kind4, Kind4, kind},
+          context, funcRef.arguments(), /*hasOptionalArgument=*/false)}) {
     const std::string &name{std::get<SpecificIntrinsic>(funcRef.proc().u).name};
     if (auto elementalBessel{
-            GetHostRuntimeWrapper<T, Int4, T>(kind, {4, kind}, name)}) {
+            GetHostRuntimeWrapper<T, Int4, T>(kind, {Kind4, kind}, name)}) {
       std::vector<Scalar<T>> results;
       int n1{static_cast<int>(
           std::get<0>(*args)->GetScalarValue().value().ToInt64())};
@@ -34,14 +34,14 @@ static Expr<T> FoldTransformationalBessel(
       Scalar<T> x{std::get<2>(*args)->GetScalarValue().value()};
       for (int i{n1}; i <= n2; ++i) {
         results.emplace_back(
-            (*elementalBessel)(context, value::IntegerValue{4, i}, x));
+            (*elementalBessel)(context, value::IntegerValue{Kind4, i}, x));
       }
       return Expr<T>{Constant<T>{kind, std::move(results),
           ConstantSubscripts{std::max(n2 - n1 + 1, 0)}}};
     } else {
       context.Warn(common::UsageWarning::FoldingFailure,
           "%s(integer(kind=4), real(kind=%d)) cannot be folded on host"_warn_en_US,
-          name, kind);
+          name, static_cast<int>(kind));
     }
   }
   return Expr<T>{std::move(funcRef)};
@@ -111,7 +111,7 @@ class Norm2Accumulator {
   ConstantSubscripts maxAbsAt_{maxAbs_.lbounds()};
 };
 
-static Expr<Type<TypeCategory::Real>> FoldNorm2(int kind,
+static Expr<Type<TypeCategory::Real>> FoldNorm2(KindsEnum kind,
     FoldingContext &context, FunctionRef<Type<TypeCategory::Real>> &&funcRef) {
   using T = Type<TypeCategory::Real>;
   using Element = typename Constant<T>::Element;
@@ -130,7 +130,8 @@ static Expr<Type<TypeCategory::Real>> FoldNorm2(int kind,
         arrayAndMask->mask, dim, identity, norm2Accumulator)};
     if (norm2Accumulator.overflow()) {
       context.Warn(common::UsageWarning::FoldingException,
-          "NORM2() of REAL(%d) data overflowed"_warn_en_US, kind);
+          "NORM2() of REAL(%d) data overflowed"_warn_en_US,
+          static_cast<int>(kind));
     }
     return Expr<T>{std::move(result)};
   }
@@ -139,7 +140,7 @@ static Expr<Type<TypeCategory::Real>> FoldNorm2(int kind,
 
 Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
     FoldingContext &context, FunctionRef<Type<TypeCategory::Real>> &&funcRef) {
-  const int kind{funcRef.kind()};
+  const KindsEnum kind{funcRef.kind()};
   using T = Type<TypeCategory::Real>;
   using ComplexT = Type<TypeCategory::Complex>;
   using Int4 = Type<TypeCategory::Integer>;
@@ -161,7 +162,8 @@ Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
           kind, {kind}, context, std::move(funcRef), *callable);
     } else {
       context.Warn(common::UsageWarning::FoldingFailure,
-          "%s(real(kind=%d)) cannot be folded on host"_warn_en_US, name, kind);
+          "%s(real(kind=%d)) cannot be folded on host"_warn_en_US, name,
+          static_cast<int>(kind));
     }
   } else if (name == "amax0" || name == "amin0" || name == "amin1" ||
       name == "amax1" || name == "dmin1" || name == "dmax1") {
@@ -176,19 +178,19 @@ Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
     } else {
       context.Warn(common::UsageWarning::FoldingFailure,
           "%s(real(kind=%d), real(kind%d)) cannot be folded on host"_warn_en_US,
-          name, kind, kind);
+          name, static_cast<int>(kind), static_cast<int>(kind));
     }
   } else if (name == "bessel_jn" || name == "bessel_yn") {
     if (args.size() == 2) { // elemental
       // runtime functions use int arg
       if (auto callable{
-              GetHostRuntimeWrapper<T, Int4, T>(kind, {4, kind}, name)}) {
+              GetHostRuntimeWrapper<T, Int4, T>(kind, {Kind4, kind}, name)}) {
         return FoldElementalIntrinsic<T, Int4, T>(
-            kind, {4, kind}, context, std::move(funcRef), *callable);
+            kind, {Kind4, kind}, context, std::move(funcRef), *callable);
       } else {
         context.Warn(common::UsageWarning::FoldingFailure,
             "%s(integer(kind=4), real(kind=%d)) cannot be folded on host"_warn_en_US,
-            name, kind);
+            name, static_cast<int>(kind));
       }
     } else {
       return FoldTransformationalBessel<T>(std::move(funcRef), context);
@@ -352,8 +354,9 @@ Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
                   sConst->IsZero() ? "zero" : "NaN");
               badSConst = true;
             }
-            return FoldElementalIntrinsic<T, T, TS>(kind, {kind, sVal.kind()},
-                context, std::move(funcRef),
+            return FoldElementalIntrinsic<T, T, TS>(kind,
+                {kind, static_cast<KindsEnum>(sVal.kind())}, context,
+                std::move(funcRef),
                 ScalarFunc<T, T, TS>([&](const Scalar<T> &x,
                                          const Scalar<TS> &s) -> Scalar<T> {
                   if (!badSConst && (s.IsZero() || s.IsNotANumber())) {
@@ -374,7 +377,7 @@ Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
   } else if (name == "norm2") {
     return FoldNorm2(kind, context, std::move(funcRef));
   } else if (name == "product") {
-    auto one{Scalar<T>::FromInteger(kind, value::IntegerValue{1, 1}).value};
+    auto one{Scalar<T>::FromInteger(kind, value::IntegerValue{Kind1, 1}).value};
     return FoldProduct<T>(context, std::move(funcRef), one);
   } else if (name == "real" || name == "dble") {
     if (auto *expr{args[0].value().UnwrapExpr()}) {
@@ -390,8 +393,9 @@ Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
       return common::visit(
           [&](const auto &byVal) {
             using TBY = ResultType<decltype(byVal)>;
-            return FoldElementalIntrinsic<T, T, TBY>(kind, {kind, byVal.kind()},
-                context, std::move(funcRef),
+            return FoldElementalIntrinsic<T, T, TBY>(kind,
+                {kind, static_cast<KindsEnum>(byVal.kind())}, context,
+                std::move(funcRef),
                 ScalarFunc<T, T, TBY>(
                     [&](const Scalar<T> &x, const Scalar<TBY> &y) -> Scalar<T> {
                       ValueWithRealFlags<Scalar<T>> result{x.SCALE(y)};
@@ -409,8 +413,9 @@ Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
       return common::visit(
           [&](const auto &iVal) {
             using TY = ResultType<decltype(iVal)>;
-            return FoldElementalIntrinsic<T, T, TY>(kind, {kind, iVal.kind()},
-                context, std::move(funcRef),
+            return FoldElementalIntrinsic<T, T, TY>(kind,
+                {kind, static_cast<KindsEnum>(iVal.kind())}, context,
+                std::move(funcRef),
                 ScalarFunc<T, T, TY>(
                     [&](const Scalar<T> &x, const Scalar<TY> &i) -> Scalar<T> {
                       return x.SET_EXPONENT(i.ToInt64());
@@ -448,8 +453,9 @@ Expr<Type<TypeCategory::Real>> FoldIntrinsicFunction(
       return common::visit(
           [&](const auto &yVal) {
             using TY = ResultType<decltype(yVal)>;
-            return FoldElementalIntrinsic<T, T, TY>(kind, {kind, yVal.kind()},
-                context, std::move(funcRef),
+            return FoldElementalIntrinsic<T, T, TY>(kind,
+                {kind, static_cast<KindsEnum>(yVal.kind())}, context,
+                std::move(funcRef),
                 ScalarFunc<T, T, TY>([&](const Scalar<T> &x,
                                          const Scalar<TY> &y) -> Scalar<T> {
                   auto xBig{
diff --git a/flang/lib/Evaluate/fold-reduction.h b/flang/lib/Evaluate/fold-reduction.h
index 0acc731fd58b1..26dd9cd6a3a00 100644
--- a/flang/lib/Evaluate/fold-reduction.h
+++ b/flang/lib/Evaluate/fold-reduction.h
@@ -18,7 +18,7 @@ template <typename T>
 static Expr<T> FoldDotProduct(
     FoldingContext &context, FunctionRef<T> &&funcRef) {
   using Element = typename Constant<T>::Element;
-  const int kind{funcRef.kind()};
+  const auto kind{static_cast<KindsEnum>(funcRef.kind())};
   auto args{funcRef.arguments()};
   CHECK(args.size() == 2);
   Folder<T> folder{kind, context};
@@ -133,7 +133,7 @@ template <typename T> struct ArrayAndMask {
   Constant<LogicalResult> mask;
 };
 template <typename T>
-static std::optional<ArrayAndMask<T>> ProcessReductionArgs(int kind,
+static std::optional<ArrayAndMask<T>> ProcessReductionArgs(KindsEnum kind,
     FoldingContext &context, ActualArguments &arg, std::optional<int> &dim,
     int arrayIndex, std::optional<int> dimIndex = std::nullopt,
     std::optional<int> maskIndex = std::nullopt) {
@@ -176,7 +176,7 @@ static std::optional<ArrayAndMask<T>> ProcessReductionArgs(int kind,
 // operator()(Scalar<T> &, const ConstantSubscripts &, bool first)
 // and Done(Scalar<T> &).
 template <typename T, typename ACCUMULATOR, typename ARRAY>
-static Constant<T> DoReduction(int kind, const Constant<ARRAY> &array,
+static Constant<T> DoReduction(KindsEnum kind, const Constant<ARRAY> &array,
     const Constant<LogicalResult> &mask, std::optional<int> &dim,
     const Scalar<T> &identity, ACCUMULATOR &accumulator) {
   ConstantSubscripts at{array.lbounds()};
@@ -232,9 +232,9 @@ static Constant<T> DoReduction(int kind, const Constant<ARRAY> &array,
 // MAXVAL & MINVAL
 template <typename T, bool ABS = false> class MaxvalMinvalAccumulator {
 public:
-  constexpr int kind() const { return kind_; }
+  constexpr KindsEnum kind() const { return kind_; }
 
-  MaxvalMinvalAccumulator(int kind, RelationalOperator opr,
+  MaxvalMinvalAccumulator(KindsEnum kind, RelationalOperator opr,
       FoldingContext &context, const Constant<T> &array)
       : kind_{kind}, opr_{opr}, context_{context}, array_{array} {};
   void operator()(Scalar<T> &element, const ConstantSubscripts &at,
@@ -264,14 +264,14 @@ template <typename T, bool ABS = false> class MaxvalMinvalAccumulator {
   void Done(Scalar<T> &) const {}
 
 private:
-  int kind_;
+  KindsEnum kind_;
   RelationalOperator opr_;
   FoldingContext &context_;
   const Constant<T> &array_;
 };
 
 template <typename T>
-static Expr<T> FoldMaxvalMinval(int kind, FoldingContext &context,
+static Expr<T> FoldMaxvalMinval(KindsEnum kind, FoldingContext &context,
     FunctionRef<T> &&ref, RelationalOperator opr, const Scalar<T> &identity) {
   static_assert(T::category == TypeCategory::Integer ||
       T::category == TypeCategory::Unsigned ||
@@ -322,7 +322,7 @@ static Expr<T> FoldProduct(
       T::category == TypeCategory::Unsigned ||
       T::category == TypeCategory::Real ||
       T::category == TypeCategory::Complex);
-  const int kind{ref.kind()};
+  const auto kind{static_cast<KindsEnum>(ref.kind())};
   std::optional<int> dim;
   if (std::optional<ArrayAndMask<T>> arrayAndMask{
           ProcessReductionArgs<T>(kind, context, ref.arguments(), dim,
@@ -373,7 +373,7 @@ template <typename T> class SumAccumulator {
   }
 
 private:
-  int kind_;
+  KindsEnum kind_;
   const Constant<T> &array_;
   Rounding rounding_;
   bool overflow_{false};
@@ -387,7 +387,7 @@ static Expr<T> FoldSum(FoldingContext &context, FunctionRef<T> &&ref) {
       T::category == TypeCategory::Real ||
       T::category == TypeCategory::Complex);
   using Element = typename Constant<T>::Element;
-  const int kind{ref.kind()};
+  const auto kind{static_cast<KindsEnum>(ref.kind())};
   std::optional<int> dim;
   Element identity{Element::Zero(kind)};
   if (std::optional<ArrayAndMask<T>> arrayAndMask{
diff --git a/flang/lib/Evaluate/formatting.cpp b/flang/lib/Evaluate/formatting.cpp
index 7ee14e37baee6..18a1e4069e11a 100644
--- a/flang/lib/Evaluate/formatting.cpp
+++ b/flang/lib/Evaluate/formatting.cpp
@@ -78,7 +78,7 @@ llvm::raw_ostream &ConstantBase<RESULT, VALUE>::AsFortran(
       o << value.UnsignedDecimal() << "U_" << kind_;
     } else if constexpr (Result::category == TypeCategory::Real ||
         Result::category == TypeCategory::Complex) {
-      value.AsFortran(o, kind_);
+      value.AsFortran(o, static_cast<int>(kind_));
     } else if constexpr (Result::category == TypeCategory::Character) {
       o << value.kind() << '_';
       value.withStdString(
@@ -126,7 +126,7 @@ llvm::raw_ostream &Constant<Type<TypeCategory::Character>>::AsFortran(
     if (j > 0) {
       o << ',';
     }
-    if (kind_ != 1) {
+    if (kind_ != Kind1) {
       o << kind_ << '_';
     }
     value.withStdString(
@@ -718,7 +718,8 @@ std::string DynamicType::AsFortran() const {
     }
     return result;
   } else if (charLengthParamValue_ || knownLength()) {
-    std::string result{"CHARACTER(KIND="s + std::to_string(kind_) + ",LEN="};
+    std::string result{
+        "CHARACTER(KIND="s + std::to_string(static_cast<int>(kind_)) + ",LEN="};
     if (knownLength()) {
       result += std::to_string(*knownLength()) + "_8";
     } else if (charLengthParamValue_->isAssumed()) {
@@ -737,13 +738,13 @@ std::string DynamicType::AsFortran() const {
     return "(typeless intrinsic function argument)";
   } else {
     return parser::ToUpperCaseLetters(EnumToString(category_)) + '(' +
-        std::to_string(kind_) + ')';
+        std::to_string(static_cast<int>(kind_)) + ')';
   }
 }
 
 std::string DynamicType::AsFortran(std::string &&charLenExpr) const {
   if (!charLenExpr.empty() && category_ == TypeCategory::Character) {
-    return "CHARACTER(KIND=" + std::to_string(kind_) +
+    return "CHARACTER(KIND=" + std::to_string(static_cast<int>(kind_)) +
         ",LEN=" + std::move(charLenExpr) + ')';
   } else {
     return AsFortran();
diff --git a/flang/lib/Evaluate/host.h b/flang/lib/Evaluate/host.h
index 6d3f61a5536b2..a084623c56686 100644
--- a/flang/lib/Evaluate/host.h
+++ b/flang/lib/Evaluate/host.h
@@ -73,16 +73,16 @@ template <typename... T> constexpr inline bool HostTypeExists() {
 template <typename FTN_T>
 inline constexpr Scalar<FTN_T> CastHostToFortran(const HostType<FTN_T> &x) {
   static_assert(HostTypeExists<FTN_T>());
+  constexpr KindsEnum kind{static_cast<KindsEnum>(FTN_T::kind)};
   if constexpr (FTN_T::category == TypeCategory::Complex &&
-      Scalar<FTN_T>::bytesStored(FTN_T::kind) != sizeof(HostType<FTN_T>)) {
+      Scalar<FTN_T>::bytesStored(kind) != sizeof(HostType<FTN_T>)) {
     // X87 is usually padded to 12 or 16bytes. Need to cast piecewise for
     // complex
     return Scalar<FTN_T>{CastHostToFortran<typename FTN_T::Part>(std::real(x)),
         CastHostToFortran<typename FTN_T::Part>(std::imag(x))};
   } else {
-    static_assert(
-        Scalar<FTN_T>::bytesStored(FTN_T::kind) == sizeof(HostType<FTN_T>));
-    return Scalar<FTN_T>::FromRawBytes(FTN_T::kind, &x, sizeof(x));
+    static_assert(Scalar<FTN_T>::bytesStored(kind) == sizeof(HostType<FTN_T>));
+    return Scalar<FTN_T>::FromRawBytes(kind, &x, sizeof(x));
   }
 }
 
@@ -90,14 +90,14 @@ inline constexpr Scalar<FTN_T> CastHostToFortran(const HostType<FTN_T> &x) {
 template <typename FTN_T>
 inline constexpr HostType<FTN_T> CastFortranToHost(const Scalar<FTN_T> &x) {
   static_assert(HostTypeExists<FTN_T>());
+  constexpr KindsEnum kind{static_cast<KindsEnum>(FTN_T::kind)};
   if constexpr (FTN_T::category == TypeCategory::Complex &&
-      Scalar<FTN_T>::bytesStored(FTN_T::kind) != sizeof(HostType<FTN_T>)) {
+      Scalar<FTN_T>::bytesStored(kind) != sizeof(HostType<FTN_T>)) {
     using FortranPartType = typename FTN_T::Part;
     return HostType<FTN_T>{CastFortranToHost<FortranPartType>(x.REAL()),
         CastFortranToHost<FortranPartType>(x.AIMAG())};
   } else {
-    static_assert(
-        Scalar<FTN_T>::bytesStored(FTN_T::kind) == sizeof(HostType<FTN_T>));
+    static_assert(Scalar<FTN_T>::bytesStored(kind) == sizeof(HostType<FTN_T>));
     HostType<FTN_T> result;
     x.StoreRawBytes(&result, sizeof(result));
     return result;
diff --git a/flang/lib/Evaluate/initial-image.cpp b/flang/lib/Evaluate/initial-image.cpp
index 6c82726d63774..776fb165b59df 100644
--- a/flang/lib/Evaluate/initial-image.cpp
+++ b/flang/lib/Evaluate/initial-image.cpp
@@ -96,7 +96,7 @@ class AsConstantHelper {
         extents_{extents}, padWithZero_{padWithZero}, offset_{offset} {
     CHECK(!type.IsPolymorphic());
   }
-  template <typename T> Result Test(int kind) {
+  template <typename T> Result Test(KindsEnum kind) {
     if (T::category != type_.category()) {
       return std::nullopt;
     }
@@ -160,12 +160,13 @@ class AsConstantHelper {
       return AsGenericExpr(
           Const{derived, std::move(typedValue), std::move(extents_)});
     } else if constexpr (T::category == TypeCategory::Character) {
-      auto length{static_cast<ConstantSubscript>(stride) / kind};
+      auto length{
+          static_cast<ConstantSubscript>(stride) / static_cast<int>(kind)};
       llvm::SmallVector<char, 256> buffer;
       const char *data{GetTailPaddedData(offset_, elements * stride, buffer)};
       for (std::size_t j{0}; j < elements; ++j) {
         typedValue[j] = value::CharacterValue::FromRawBytes(
-            kind, data + j * stride, length * kind);
+            kind, data + j * stride, length * static_cast<int>(kind));
       }
       return AsGenericExpr(
           Const{kind, length, std::move(typedValue), std::move(extents_)});
diff --git a/flang/lib/Evaluate/int-power.h b/flang/lib/Evaluate/int-power.h
index 3acdec06286f7..2df35fcadc333 100644
--- a/flang/lib/Evaluate/int-power.h
+++ b/flang/lib/Evaluate/int-power.h
@@ -19,7 +19,7 @@ template <typename REAL, typename INT>
 ValueWithRealFlags<REAL> TimesIntPowerOf(const REAL &factor, const REAL &base,
     const INT &power,
     Rounding rounding = TargetCharacteristics::defaultRounding) {
-  const int realKind{base.kind()};
+  const KindsEnum realKind{base.kind()};
   CHECK(factor.kind() == base.kind());
   ValueWithRealFlags<REAL> result{factor};
   if (base.IsNotANumber()) {
@@ -56,8 +56,8 @@ ValueWithRealFlags<REAL> TimesIntPowerOf(const REAL &factor, const REAL &base,
 template <typename REAL, typename INT>
 ValueWithRealFlags<REAL> IntPower(const REAL &base, const INT &power,
     Rounding rounding = TargetCharacteristics::defaultRounding) {
-  const int realKind{base.kind()};
-  const int intKind{power.kind()};
+  const KindsEnum realKind{base.kind()};
+  const KindsEnum intKind{power.kind()};
   REAL one{REAL::FromInteger(realKind, INT{intKind, 1}).value};
   return TimesIntPowerOf(one, base, power, rounding);
 }
diff --git a/flang/lib/Evaluate/integer-value-impl.cpp b/flang/lib/Evaluate/integer-value-impl.cpp
index bc1a1d1c8cf73..0b41e1cea30b3 100644
--- a/flang/lib/Evaluate/integer-value-impl.cpp
+++ b/flang/lib/Evaluate/integer-value-impl.cpp
@@ -21,10 +21,12 @@ IntegerValueImpl IntegerValueImpl::Zero(int kind) {
 
 IntegerValueImpl IntegerValueImpl::FromRawBytes(
     int kind, const void *raw, std::size_t expectedSize) {
-  CHECK(expectedSize == IntegerValue::bytesStored(kind));
+  CHECK(
+      expectedSize == IntegerValue::bytesStored(static_cast<KindsEnum>(kind)));
 
   return withWordProto(kind, [&](auto proto) {
-    assert(IntegerValue::bytesStored(kind) == sizeof(proto));
+    assert(IntegerValue::bytesStored(static_cast<KindsEnum>(kind)) ==
+        sizeof(proto));
     std::decay_t<decltype(proto)> t{};
     memcpy(&t, raw, sizeof(proto));
     return FromWord(t);
diff --git a/flang/lib/Evaluate/integer-value-impl.h b/flang/lib/Evaluate/integer-value-impl.h
index 0ad4608619046..c3e8928c55b73 100644
--- a/flang/lib/Evaluate/integer-value-impl.h
+++ b/flang/lib/Evaluate/integer-value-impl.h
@@ -22,6 +22,7 @@
 #undef HUGE
 
 namespace Fortran::evaluate::value {
+using common::KindsEnum;
 
 class IntegerValueImpl {
 public:
diff --git a/flang/lib/Evaluate/integer-value.cpp b/flang/lib/Evaluate/integer-value.cpp
index 2ec567a0423ac..d079bdf194fce 100644
--- a/flang/lib/Evaluate/integer-value.cpp
+++ b/flang/lib/Evaluate/integer-value.cpp
@@ -38,13 +38,15 @@ IntegerValue &IntegerValue::operator=(IntegerValue &&x) {
   return *this;
 }
 
-IntegerValue IntegerValue::Zero(int kind) {
-  return FromImpl(IntegerValueImpl::Zero(kind));
+IntegerValue IntegerValue::Zero(KindsEnum kind) {
+  return FromImpl(IntegerValueImpl::Zero(static_cast<int>(kind)));
 }
 
 bool IntegerValue::IsMonostate() const { return impl().IsMonostate(); }
 
-int IntegerValue::kind() const { return impl().kind(); }
+KindsEnum IntegerValue::kind() const {
+  return static_cast<KindsEnum>(impl().kind());
+}
 
 void IntegerValue::print(llvm::raw_ostream &os) const { impl().print(os); }
 
@@ -56,17 +58,17 @@ bool IntegerValue::operator==(const IntegerValue &y) const {
   return impl() == y.impl();
 }
 
-IntegerValue IntegerValue::MASKL(int kind, int places) {
-  return FromImpl(IntegerValueImpl::MASKL(kind, places));
+IntegerValue IntegerValue::MASKL(KindsEnum kind, int places) {
+  return FromImpl(IntegerValueImpl::MASKL(static_cast<int>(kind), places));
 }
 
-IntegerValue IntegerValue::MASKR(int kind, int places) {
-  return FromImpl(IntegerValueImpl::MASKR(kind, places));
+IntegerValue IntegerValue::MASKR(KindsEnum kind, int places) {
+  return FromImpl(IntegerValueImpl::MASKR(static_cast<int>(kind), places));
 }
 
 IntegerValue::ValueWithOverflow IntegerValue::Read(
-    int kind, const char *&pp, int base, bool isSigned) {
-  auto r{IntegerValueImpl::Read(kind, pp, base, isSigned)};
+    KindsEnum kind, const char *&pp, int base, bool isSigned) {
+  auto r{IntegerValueImpl::Read(static_cast<int>(kind), pp, base, isSigned)};
   return {FromImpl(std::move(r.value)), r.overflow};
 }
 
@@ -92,17 +94,21 @@ std::string IntegerValue::SignedDecimal() const {
 
 std::string IntegerValue::Hexadecimal() const { return impl().Hexadecimal(); }
 
-IntegerValue IntegerValue::HUGE(int kind) {
-  return FromImpl(IntegerValueImpl::HUGE(kind));
+IntegerValue IntegerValue::HUGE(KindsEnum kind) {
+  return FromImpl(IntegerValueImpl::HUGE(static_cast<int>(kind)));
 }
 
-IntegerValue IntegerValue::Least(int kind) {
-  return FromImpl(IntegerValueImpl::Least(kind));
+IntegerValue IntegerValue::Least(KindsEnum kind) {
+  return FromImpl(IntegerValueImpl::Least(static_cast<int>(kind)));
 }
 
-int IntegerValue::RANGE(int kind) { return DecimalRange(kind * 8 - 1); }
+int IntegerValue::RANGE(KindsEnum kind) {
+  return DecimalRange(static_cast<int>(kind) * 8 - 1);
+}
 
-int IntegerValue::UnsignedRANGE(int kind) { return DecimalRange(kind * 8); }
+int IntegerValue::UnsignedRANGE(KindsEnum kind) {
+  return DecimalRange(static_cast<int>(kind) * 8);
+}
 
 bool IntegerValue::IsZero() const { return impl().IsZero(); }
 
@@ -282,8 +288,9 @@ typename IntegerValue::PowerWithErrors IntegerValue::Power(
 }
 
 IntegerValue IntegerValue::FromRawBytes(
-    int kind, const void *raw, std::size_t expectedSize) {
-  return FromImpl(IntegerValueImpl::FromRawBytes(kind, raw, expectedSize));
+    KindsEnum kind, const void *raw, std::size_t expectedSize) {
+  return FromImpl(IntegerValueImpl::FromRawBytes(
+      static_cast<int>(kind), raw, expectedSize));
 }
 
 void IntegerValue::StoreRawBytes(void *dst, size_t size, bool *changed) const {
@@ -291,13 +298,13 @@ void IntegerValue::StoreRawBytes(void *dst, size_t size, bool *changed) const {
 }
 
 void IntegerValue::ConstructFromIntegral(
-    int kind, std::uint64_t v, bool isSigned) {
-  new (this) IntegerValueImpl(kind, v, isSigned);
+    KindsEnum kind, std::uint64_t v, bool isSigned) {
+  new (this) IntegerValueImpl(static_cast<int>(kind), v, isSigned);
 }
 
 void IntegerValue::ConstructFromIntegral(
-    int kind, Fortran::common::uint128_t v) {
-  new (this) IntegerValueImpl(kind, v);
+    KindsEnum kind, Fortran::common::uint128_t v) {
+  new (this) IntegerValueImpl(static_cast<int>(kind), v);
 }
 
 IntegerValue IntegerValue::FromImpl(const IntegerValueImpl &x) {
diff --git a/flang/lib/Evaluate/intrinsics-library.cpp b/flang/lib/Evaluate/intrinsics-library.cpp
index adfc295b97a2c..264f145edf268 100644
--- a/flang/lib/Evaluate/intrinsics-library.cpp
+++ b/flang/lib/Evaluate/intrinsics-library.cpp
@@ -152,7 +152,7 @@ template <typename FuncType, typename TR, typename... TA, size_t... I>
 static Expr<SomeType> ApplyHostFunctionHelper(FuncType func,
     FoldingContext &context, std::vector<Expr<SomeType>> &&args,
     std::index_sequence<I...>) {
-  const int kind{TR::kind};
+  const KindsEnum kind{static_cast<KindsEnum>(TR::kind)};
   host::HostFloatingPointEnvironment hostFPE;
   hostFPE.SetUpHostFloatingPointEnvironment(context);
   host::HostType<TR> hostResult{};
@@ -787,13 +787,18 @@ static DynamicType BiggerType(DynamicType type) {
   if (type.category() == TypeCategory::Real ||
       type.category() == TypeCategory::Complex) {
     // 16 bits floats to IEEE 32 bits float
-    if (type.kind() == common::RealKindForPrecision(11) ||
-        type.kind() == common::RealKindForPrecision(8)) {
-      return {type.category(), common::RealKindForPrecision(24)};
+    if (type.kind() ==
+            static_cast<KindsEnum>(common::RealKindForPrecision(11)) ||
+        type.kind() ==
+            static_cast<KindsEnum>(common::RealKindForPrecision(8))) {
+      return {type.category(),
+          static_cast<KindsEnum>(common::RealKindForPrecision(24))};
     }
     // x87 float to IEEE 128 bits float
-    if (type.kind() == common::RealKindForPrecision(64)) {
-      return {type.category(), common::RealKindForPrecision(113)};
+    if (type.kind() ==
+        static_cast<KindsEnum>(common::RealKindForPrecision(64))) {
+      return {type.category(),
+          static_cast<KindsEnum>(common::RealKindForPrecision(113))};
     }
   }
   return type;
@@ -825,12 +830,12 @@ static const Expr<SomeType> &GetArg(
 
 template <typename T>
 static bool IsInRange(const Expr<T> &expr, int lb, int ub) {
-  const int kind{expr.kind()};
+  const auto kind{static_cast<KindsEnum>(expr.kind())};
   if (auto scalar{GetScalarConstantValue<T>(expr)}) {
     auto lbValue{
-        Scalar<T>::FromInteger(kind, value::IntegerValue{1, lb}).value};
+        Scalar<T>::FromInteger(kind, value::IntegerValue{Kind1, lb}).value};
     auto ubValue{
-        Scalar<T>::FromInteger(kind, value::IntegerValue{1, ub}).value};
+        Scalar<T>::FromInteger(kind, value::IntegerValue{Kind1, ub}).value};
     return Satisfies(RelationalOperator::LE, lbValue.Compare(*scalar)) &&
         Satisfies(RelationalOperator::LE, scalar->Compare(ubValue));
   }
@@ -864,7 +869,7 @@ static bool VerifyStrictlyPositiveIfReal(
     const bool isStrictlyPositive{std::visit(
         [&](const auto &x) -> bool {
           using T = typename std::decay_t<decltype(x)>::Result;
-          const int kind{x.kind()};
+          const auto kind{static_cast<KindsEnum>(x.kind())};
           auto scalar{GetScalarConstantValue<T>(x)};
           return Satisfies(
               RelationalOperator::LT, Scalar<T>::Zero(kind).Compare(*scalar));
diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp
index 748c6e0d14402..85ee4011a1024 100644
--- a/flang/lib/Evaluate/intrinsics.cpp
+++ b/flang/lib/Evaluate/intrinsics.cpp
@@ -9,6 +9,7 @@
 #include "flang/Evaluate/intrinsics.h"
 #include "flang/Common/enum-set.h"
 #include "flang/Common/idioms.h"
+#include "flang/Common/type-kinds.h"
 #include "flang/Evaluate/check-expression.h"
 #include "flang/Evaluate/common.h"
 #include "flang/Evaluate/expression.h"
@@ -1821,7 +1822,7 @@ static DynamicType GetBuiltinDerivedType(
   return DynamicType{derived};
 }
 
-static std::int64_t GetBuiltinKind(
+static KindsEnum GetBuiltinKind(
     const semantics::Scope *builtinsScope, const char *which) {
   if (!builtinsScope) {
     common::die("INTERNAL: The __fortran_builtins module was not found, and "
@@ -1839,12 +1840,12 @@ static std::int64_t GetBuiltinKind(
   const auto &details{
       DEREF(symbol.detailsIf<semantics::ObjectEntityDetails>())};
   if (const auto kind{ToInt64(details.init())}) {
-    return *kind;
+    return static_cast<KindsEnum>(*kind);
   } else {
     common::die(
         "INTERNAL: The __fortran_builtins module does not define the kind '%s'",
         which);
-    return -1;
+    return KindsEnum::InvalidKind;
   }
 }
 
@@ -2279,10 +2280,10 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
       argOk = true;
       break;
     case KindCode::exactKind:
-      argOk = type->kind() == d.typePattern.kindValue;
+      argOk = type->kind() == static_cast<KindsEnum>(d.typePattern.kindValue);
       break;
     case KindCode::greaterOrEqualToKind:
-      argOk = type->kind() >= d.typePattern.kindValue;
+      argOk = type->kind() >= static_cast<KindsEnum>(d.typePattern.kindValue);
       break;
     case KindCode::sameAtom:
       if (!sameArg) {
@@ -2546,7 +2547,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
         messages.Say(
             "%s(KIND=%jd) type not supported on this target."_err_en_US,
             parser::ToUpperCaseLetters(EnumToString(*category)),
-            defaults.quadPrecisionKind());
+            static_cast<std::intmax_t>(defaults.quadPrecisionKind()));
       }
       break;
     case KindCode::defaultLogicalKind:
@@ -2567,7 +2568,8 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
         if (result.categorySet.test(aType->category())) {
           if (const auto *sameChar{UnwrapExpr<Expr<SomeCharacter>>(*sameArg)}) {
             if (auto len{ToInt64(Fold(context, sameChar->LEN()))}) {
-              resultType = DynamicType{aType->kind(), *len};
+              resultType =
+                  DynamicType{static_cast<KindsEnum>(aType->kind()), *len};
             } else {
               resultType = *aType;
             }
@@ -2575,14 +2577,16 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
             resultType = *aType;
           }
         } else {
-          resultType = DynamicType{*category, aType->kind()};
+          resultType =
+              DynamicType{*category, static_cast<KindsEnum>(aType->kind())};
         }
       }
       break;
     case KindCode::sameKind:
       CHECK(sameArg);
       if (std::optional<DynamicType> aType{sameArg->GetType()}) {
-        resultType = DynamicType{*category, aType->kind()};
+        resultType =
+            DynamicType{*category, static_cast<KindsEnum>(aType->kind())};
       }
       break;
     case KindCode::operand:
@@ -2599,11 +2603,12 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
           CHECK(expr->Rank() == 0);
           if (auto code{ToInt64(Fold(context, common::Clone(*expr)))}) {
             if (context.targetCharacteristics().IsTypeEnabled(
-                    *category, *code)) {
+                    *category, static_cast<KindsEnum>(*code))) {
               if (*category == TypeCategory::Character) { // ACHAR & CHAR
-                resultType = DynamicType{static_cast<int>(*code), 1};
+                resultType = DynamicType{static_cast<KindsEnum>(*code), 1};
               } else {
-                resultType = DynamicType{*category, static_cast<int>(*code)};
+                resultType =
+                    DynamicType{*category, static_cast<KindsEnum>(*code)};
               }
               break;
             }
@@ -2631,7 +2636,7 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
         CHECK(kindDummyArg->flags.test(ArgFlag::defaultsToDefaultForResult));
       }
       if (!resultType) {
-        int kind{defaults.GetDefaultKind(*category)};
+        KindsEnum kind{defaults.GetDefaultKind(*category)};
         if (*category == TypeCategory::Character) { // ACHAR & CHAR
           resultType = DynamicType{kind, 1};
         } else {
@@ -2649,14 +2654,14 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
     case KindCode::subscript:
       CHECK(result.categorySet == IntType);
       CHECK(*category == TypeCategory::Integer);
-      resultType =
-          DynamicType{TypeCategory::Integer, defaults.subscriptIntegerKind()};
+      resultType = DynamicType{TypeCategory::Integer,
+          static_cast<KindsEnum>(defaults.subscriptIntegerKind())};
       break;
     case KindCode::size:
       CHECK(result.categorySet == IntType);
       CHECK(*category == TypeCategory::Integer);
-      resultType =
-          DynamicType{TypeCategory::Integer, defaults.sizeIntegerKind()};
+      resultType = DynamicType{TypeCategory::Integer,
+          static_cast<KindsEnum>(defaults.sizeIntegerKind())};
       break;
     case KindCode::teamType:
       CHECK(result.categorySet == DerivedType);
@@ -2666,7 +2671,8 @@ std::optional<SpecificCall> IntrinsicInterface::Match(
       break;
     case KindCode::greaterOrEqualToKind:
     case KindCode::exactKind:
-      resultType = DynamicType{*category, result.kindValue};
+      resultType =
+          DynamicType{*category, static_cast<KindsEnum>(result.kindValue)};
       break;
     case KindCode::typeless:
     case KindCode::any:
@@ -3262,7 +3268,8 @@ IntrinsicProcTable::Implementation::HandleC_F_Pointer(
   }
   if (dummies.size() == 2) {
     // Handle SHAPE
-    DynamicType shapeType{TypeCategory::Integer, defaults_.sizeIntegerKind()};
+    DynamicType shapeType{TypeCategory::Integer,
+        static_cast<KindsEnum>(defaults_.sizeIntegerKind())};
     if (arguments.size() >= 3 && arguments[2]) {
       if (auto type{arguments[2]->GetType()}) {
         if (type->category() == TypeCategory::Integer) {
@@ -3277,7 +3284,8 @@ IntrinsicProcTable::Implementation::HandleC_F_Pointer(
     dummies.emplace_back("shape"s, std::move(shape));
 
     // Handle LOWER
-    DynamicType lowerType{TypeCategory::Integer, defaults_.sizeIntegerKind()};
+    DynamicType lowerType{TypeCategory::Integer,
+        static_cast<KindsEnum>(defaults_.sizeIntegerKind())};
     if (arguments.size() >= 4 && arguments[3]) {
       if (auto type{arguments[3]->GetType()}) {
         if (type->category() == TypeCategory::Integer) {
@@ -3331,7 +3339,8 @@ IntrinsicProcTable::Implementation::HandleC_F_Strpointer(
   if (CheckAndRearrangeArguments(arguments, context.messages(), keywords, 1)) {
     CHECK(arguments.size() == 3);
     const bool hasNchars{arguments[2].has_value()};
-    const int cCharKind = defaults_.GetDefaultKind(TypeCategory::Character);
+    const KindsEnum cCharKind{
+        defaults_.GetDefaultKind(TypeCategory::Character)};
 
     // Check first argument (CSTRARRAY or CSTRPTR) and optional third argument
     // (NCHARS)
@@ -3477,7 +3486,8 @@ IntrinsicProcTable::Implementation::HandleC_F_Strpointer(
   }
   if (dummies.size() == 2) {
     // Add NCHARS dummy
-    DynamicType ncharsType{TypeCategory::Integer, defaults_.sizeIntegerKind()};
+    DynamicType ncharsType{TypeCategory::Integer,
+        static_cast<KindsEnum>(defaults_.sizeIntegerKind())};
     if (arguments.size() >= 3 && arguments[2]) {
       if (const auto type{arguments[2]->GetType()}) {
         if (type->category() == TypeCategory::Integer) {
@@ -3554,7 +3564,7 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Loc(
       } else if (typeAndShape->type().category() != TypeCategory::Derived &&
           !IsInteroperableIntrinsicType(typeAndShape->type()).value_or(true)) {
         if (typeAndShape->type().category() == TypeCategory::Character &&
-            typeAndShape->type().kind() == 1) {
+            typeAndShape->type().kind() == Kind1) {
           // Default character kind, but length is not known to be 1
           context.Warn(common::UsageWarning::CharacterInteroperability,
               arguments[0]->sourceLocation(),
@@ -3624,7 +3634,7 @@ std::optional<SpecificCall> IntrinsicProcTable::Implementation::HandleC_Devloc(
       } else if (typeAndShape->type().category() != TypeCategory::Derived &&
           !IsInteroperableIntrinsicType(typeAndShape->type()).value_or(true)) {
         if (typeAndShape->type().category() == TypeCategory::Character &&
-            typeAndShape->type().kind() == 1) {
+            typeAndShape->type().kind() == Kind1) {
           // Default character kind, but length is not known to be 1
           context.Warn(common::UsageWarning::CharacterInteroperability,
               arguments[0]->sourceLocation(),
@@ -3805,7 +3815,7 @@ static DynamicType GetReturnType(const SpecificIntrinsicInterface &interface,
   default:
     CRASH_NO_CASE;
   }
-  int kind{interface.result.kindCode == KindCode::doublePrecision
+  KindsEnum kind{interface.result.kindCode == KindCode::doublePrecision
           ? defaults.doublePrecisionKind()
           : interface.result.kindCode == KindCode::quadPrecision
           ? defaults.quadPrecisionKind()
diff --git a/flang/lib/Evaluate/real-value-impl.cpp b/flang/lib/Evaluate/real-value-impl.cpp
index 738f461af2d88..3d682a780d1a9 100644
--- a/flang/lib/Evaluate/real-value-impl.cpp
+++ b/flang/lib/Evaluate/real-value-impl.cpp
@@ -38,7 +38,7 @@ RealValueImpl::RealValueImpl(int kind, double x) {
     storage_ = std::signbit(x) ? RealValueImpl::NegativeZero(kind).storage_
                                : RealValueImpl::Zero(kind).storage_;
   } else if (std::isnan(x)) {
-    storage_ = RealValueImpl::NotANumber(kind).storage_;
+    storage_ = RealValueImpl::NotANumber(static_cast<KindsEnum>(kind)).storage_;
   } else if (std::isinf(x)) {
     storage_ = RealValueImpl::Infinity(kind, x < 0).storage_;
   } else {
@@ -54,9 +54,11 @@ RealValueImpl::RealValueImpl(int kind, double x) {
     // Convert() then applies the target kind's own IEEE rounding/overflow
     // semantics for the final narrowing (or widening).
     constexpr int wideKind{8};
-    RealValueImpl magnitude{
-        RealValueImpl::FromInteger(wideKind, IntegerValue{8, mantissa}).value};
-    magnitude = magnitude.SCALE(IntegerValue{4, exp - fracBits}).value;
+    RealValueImpl magnitude{RealValueImpl::FromInteger(
+        wideKind, IntegerValue{KindsEnum::Kind8, mantissa})
+            .value};
+    magnitude =
+        magnitude.SCALE(IntegerValue{KindsEnum::Kind4, exp - fracBits}).value;
     if (negative) {
       magnitude = magnitude.SetSign(true);
     }
@@ -74,12 +76,13 @@ RealValueImpl RealValueImpl::Zero(int kind) {
 
 RealValueImpl RealValueImpl::FromRawBytes(
     int kind, const void *raw, std::size_t expectedSize) {
-  return RealValueImpl{
-      kind, IntegerValue::FromRawBytes(kind, raw, expectedSize)};
+  return RealValueImpl{kind,
+      IntegerValue::FromRawBytes(
+          static_cast<KindsEnum>(kind), raw, expectedSize)};
 }
 
 void RealValueImpl::print(llvm::raw_ostream &os) const {
-  AsFortran(os, kind());
+  AsFortran(os, static_cast<int>(kind()));
 }
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -89,17 +92,17 @@ LLVM_DUMP_METHOD void RealValueImpl::dump() const {
 }
 #endif
 
-int RealValueImpl::kind() const {
+KindsEnum RealValueImpl::kind() const {
   if (IsMonostate()) {
     llvm_unreachable("uninitialized value has not a defined kind");
   }
 
-  return withWord([](const auto &v) -> int {
+  return withWord([](const auto &v) -> KindsEnum {
     using R = std::decay_t<decltype(v)>;
     if constexpr (std::is_same_v<R, R3>) {
-      return 3;
+      return KindsEnum::Kind3;
     }
-    return R::bits / 8;
+    return static_cast<KindsEnum>(R::bits / 8);
   });
 }
 
@@ -166,9 +169,9 @@ RealValueImpl RealValueImpl::TINY(int kind) {
       kind, [](auto p) { return FromWord(decltype(p)::TINY()); });
 }
 
-RealValueImpl RealValueImpl::NotANumber(int kind) {
-  return withWordProto(
-      kind, [](auto p) { return FromWord(decltype(p)::NotANumber()); });
+RealValueImpl RealValueImpl::NotANumber(KindsEnum kind) {
+  return withWordProto(static_cast<int>(kind),
+      [](auto p) { return FromWord(decltype(p)::NotANumber()); });
 }
 
 RealValueImpl RealValueImpl::SignalingNaN(int kind) {
diff --git a/flang/lib/Evaluate/real-value-impl.h b/flang/lib/Evaluate/real-value-impl.h
index 15f4dae3ded47..b1b670cb65e01 100644
--- a/flang/lib/Evaluate/real-value-impl.h
+++ b/flang/lib/Evaluate/real-value-impl.h
@@ -9,6 +9,7 @@
 #ifndef FORTRAN_EVALUATE_REAL_VALUE_IMPL_H_
 #define FORTRAN_EVALUATE_REAL_VALUE_IMPL_H_
 
+#include "flang/Common/type-kinds.h"
 #include "flang/Evaluate/real.h"
 #include "llvm/Support/ErrorHandling.h"
 #include <type_traits>
@@ -25,6 +26,7 @@ class raw_ostream;
 
 namespace Fortran::evaluate::value {
 class IntegerValue;
+using common::KindsEnum;
 
 class RealValueImpl {
 public:
@@ -78,19 +80,19 @@ class RealValueImpl {
 #endif
 
   bool IsMonostate() const { return storage_.index() == 0; }
-  int kind() const;
+  KindsEnum kind() const;
 
   int bits() const;
 
   std::size_t bytesStored() const { return bytesStored(kind()); }
-  static constexpr std::size_t bytesStored(int kind) {
+  static constexpr std::size_t bytesStored(KindsEnum kind) {
     switch (kind) {
-    case 3:
+    case KindsEnum::Kind3:
       return 2;
-    case 10:
+    case KindsEnum::Kind10:
       return 16;
     default:
-      return kind;
+      return static_cast<std::size_t>(kind);
     }
   }
 
@@ -111,7 +113,7 @@ class RealValueImpl {
   static RealValueImpl HUGE(int kind);
   static RealValueImpl EPSILON(int kind);
   static RealValueImpl TINY(int kind);
-  static RealValueImpl NotANumber(int kind);
+  static RealValueImpl NotANumber(KindsEnum kind);
   static RealValueImpl SignalingNaN(int kind);
   static RealValueImpl Infinity(int kind, bool negative = false);
   static RealValueImpl NegativeZero(int kind);
diff --git a/flang/lib/Evaluate/real-value.cpp b/flang/lib/Evaluate/real-value.cpp
index 8f8f9bbaefd99..b6aec10be12ba 100644
--- a/flang/lib/Evaluate/real-value.cpp
+++ b/flang/lib/Evaluate/real-value.cpp
@@ -38,31 +38,33 @@ RealValue &RealValue::operator=(RealValue &&x) {
   return *this;
 }
 
-RealValue::RealValue(int kind, const Word &w) {
-  new (this) RealValueImpl(kind, w);
+RealValue::RealValue(KindsEnum kind, const Word &w) {
+  new (this) RealValueImpl(static_cast<int>(kind), w);
 }
 
-RealValue::RealValue(int kind, double x) { new (this) RealValueImpl(kind, x); }
+RealValue::RealValue(KindsEnum kind, double x) {
+  new (this) RealValueImpl(static_cast<int>(kind), x);
+}
 
-RealValue RealValue::Zero(int kind) {
-  return FromImpl(RealValueImpl::Zero(kind));
+RealValue RealValue::Zero(KindsEnum kind) {
+  return FromImpl(RealValueImpl::Zero(static_cast<int>(kind)));
 }
 
-RealValue RealValue::NegativeZero(int kind) {
-  return FromImpl(RealValueImpl::NegativeZero(kind));
+RealValue RealValue::NegativeZero(KindsEnum kind) {
+  return FromImpl(RealValueImpl::NegativeZero(static_cast<int>(kind)));
 }
 
-RealValue RealValue::Infinity(int kind, bool negative) {
-  return FromImpl(RealValueImpl::Infinity(kind, negative));
+RealValue RealValue::Infinity(KindsEnum kind, bool negative) {
+  return FromImpl(RealValueImpl::Infinity(static_cast<int>(kind), negative));
 }
 
-RealValue RealValue::SignalingNaN(int kind) {
-  return FromImpl(RealValueImpl::SignalingNaN(kind));
+RealValue RealValue::SignalingNaN(KindsEnum kind) {
+  return FromImpl(RealValueImpl::SignalingNaN(static_cast<int>(kind)));
 }
 
 bool RealValue::IsMonostate() const { return impl().IsMonostate(); }
 
-int RealValue::kind() const { return impl().kind(); }
+KindsEnum RealValue::kind() const { return impl().kind(); }
 
 void RealValue::print(llvm::raw_ostream &os) const { impl().print(os); }
 
@@ -149,30 +151,36 @@ ValueWithRealFlags<RealValue> RealValue::KahanSummation(
 
 IntegerValue RealValue::EXPONENT() const { return impl().EXPONENT(); }
 
-RealValue RealValue::EPSILON(int kind) {
-  return FromImpl(RealValueImpl::EPSILON(kind));
+RealValue RealValue::EPSILON(KindsEnum kind) {
+  return FromImpl(RealValueImpl::EPSILON(static_cast<int>(kind)));
 }
 
-RealValue RealValue::HUGE(int kind) {
-  return FromImpl(RealValueImpl::HUGE(kind));
+RealValue RealValue::HUGE(KindsEnum kind) {
+  return FromImpl(RealValueImpl::HUGE(static_cast<int>(kind)));
 }
 
-RealValue RealValue::TINY(int kind) {
-  return FromImpl(RealValueImpl::TINY(kind));
+RealValue RealValue::TINY(KindsEnum kind) {
+  return FromImpl(RealValueImpl::TINY(static_cast<int>(kind)));
 }
 
-int RealValue::DIGITS(int kind) { return RealValueImpl::DIGITS(kind); }
+int RealValue::DIGITS(KindsEnum kind) {
+  return RealValueImpl::DIGITS(static_cast<int>(kind));
+}
 
-int RealValue::PRECISION(int kind) { return RealValueImpl::PRECISION(kind); }
+int RealValue::PRECISION(KindsEnum kind) {
+  return RealValueImpl::PRECISION(static_cast<int>(kind));
+}
 
-int RealValue::RANGE(int kind) { return RealValueImpl::RANGE(kind); }
+int RealValue::RANGE(KindsEnum kind) {
+  return RealValueImpl::RANGE(static_cast<int>(kind));
+}
 
-int RealValue::MAXEXPONENT(int kind) {
-  return RealValueImpl::MAXEXPONENT(kind);
+int RealValue::MAXEXPONENT(KindsEnum kind) {
+  return RealValueImpl::MAXEXPONENT(static_cast<int>(kind));
 }
 
-int RealValue::MINEXPONENT(int kind) {
-  return RealValueImpl::MINEXPONENT(kind);
+int RealValue::MINEXPONENT(KindsEnum kind) {
+  return RealValueImpl::MINEXPONENT(static_cast<int>(kind));
 }
 
 RealValue RealValue::RRSPACING() const { return FromImpl(impl().RRSPACING()); }
@@ -194,13 +202,14 @@ RealValue RealValue::FlushSubnormalToZero() const {
   return FromImpl(impl().FlushSubnormalToZero());
 }
 
-RealValue RealValue::NotANumber(int kind) {
+RealValue RealValue::NotANumber(KindsEnum kind) {
   return FromImpl(RealValueImpl::NotANumber(kind));
 }
 
 ValueWithRealFlags<RealValue> RealValue::FromInteger(
-    int kind, const IntegerValue &n, bool isUnsigned, Rounding rounding) {
-  return FromImpl(RealValueImpl::FromInteger(kind, n, isUnsigned, rounding));
+    KindsEnum kind, const IntegerValue &n, bool isUnsigned, Rounding rounding) {
+  return FromImpl(RealValueImpl::FromInteger(
+      static_cast<int>(kind), n, isUnsigned, rounding));
 }
 
 ValueWithRealFlags<RealValue> RealValue::ToWholeNumber(
@@ -213,8 +222,9 @@ ValueWithRealFlags<IntegerValue> RealValue::ToInteger(
 }
 
 ValueWithRealFlags<RealValue> RealValue::Convert(
-    int kind, const RealValue &from, Rounding rounding) {
-  return FromImpl(RealValueImpl::Convert(kind, from.impl(), rounding));
+    KindsEnum kind, const RealValue &from, Rounding rounding) {
+  return FromImpl(
+      RealValueImpl::Convert(static_cast<int>(kind), from.impl(), rounding));
 }
 
 IntegerValue RealValue::RawBits() const { return impl().RawBits(); }
@@ -222,8 +232,8 @@ IntegerValue RealValue::RawBits() const { return impl().RawBits(); }
 int RealValue::Exponent() const { return impl().Exponent(); }
 
 ValueWithRealFlags<RealValue> RealValue::Read(
-    int kind, const char *&pp, Rounding rounding) {
-  return FromImpl(RealValueImpl::Read(kind, pp, rounding));
+    KindsEnum kind, const char *&pp, Rounding rounding) {
+  return FromImpl(RealValueImpl::Read(static_cast<int>(kind), pp, rounding));
 }
 
 std::string RealValue::DumpHexadecimal() const {
@@ -236,8 +246,9 @@ llvm::raw_ostream &RealValue::AsFortran(
 }
 
 RealValue RealValue::FromRawBytes(
-    int kind, const void *raw, std::size_t expectedSize) {
-  return FromImpl(RealValueImpl::FromRawBytes(kind, raw, expectedSize));
+    KindsEnum kind, const void *raw, std::size_t expectedSize) {
+  return FromImpl(
+      RealValueImpl::FromRawBytes(static_cast<int>(kind), raw, expectedSize));
 }
 
 void RealValue::StoreRawBytes(void *dst, size_t size, bool *changed) const {
diff --git a/flang/lib/Evaluate/shape.cpp b/flang/lib/Evaluate/shape.cpp
index 4d10951fe6501..a0cb21a543987 100644
--- a/flang/lib/Evaluate/shape.cpp
+++ b/flang/lib/Evaluate/shape.cpp
@@ -1087,8 +1087,8 @@ auto GetShapeHelper::operator()(const ProcedureRef &call) const -> Result {
             }
           } else {
             // Non-scalar MASK= -> [COUNT(mask, KIND=extent_kind)]
-            ActualArgument kindArg{
-                AsGenericExpr(MakeExtentConstant(ExtentIntKind))};
+            ActualArgument kindArg{AsGenericExpr(
+                MakeExtentConstant(static_cast<int64_t>(ExtentIntKind)))};
             kindArg.set_keyword(context_->SaveTempName("kind"));
             ActualArguments toCount{
                 ActualArgument{common::Clone(
diff --git a/flang/lib/Evaluate/target.cpp b/flang/lib/Evaluate/target.cpp
index 47442634355c2..3698fbf8aa3a4 100644
--- a/flang/lib/Evaluate/target.cpp
+++ b/flang/lib/Evaluate/target.cpp
@@ -18,15 +18,16 @@ Rounding TargetCharacteristics::defaultRounding;
 
 TargetCharacteristics::TargetCharacteristics() {
   auto enableCategoryKinds{[this](TypeCategory category) {
-    for (int kind{1}; kind <= maxKind; ++kind) {
+    for (int i{1}; i <= maxKind; ++i) {
+      KindsEnum kind{static_cast<KindsEnum>(i)};
       if (CanSupportType(category, kind)) {
-        auto byteSize{
-            static_cast<std::size_t>(common::TypeSizeInBytes(category, kind))};
+        auto byteSize{static_cast<std::size_t>(
+            common::TypeSizeInBytes(category, static_cast<KindsEnum>(kind)))};
         std::size_t align{byteSize};
         if (category == TypeCategory::Complex) {
           align /= 2;
         }
-        EnableType(category, kind, byteSize, align);
+        EnableType(category, static_cast<KindsEnum>(kind), byteSize, align);
       }
     }
   }};
@@ -43,15 +44,15 @@ TargetCharacteristics::TargetCharacteristics() {
 }
 
 bool TargetCharacteristics::CanSupportType(
-    TypeCategory category, std::int64_t kind) {
+    TypeCategory category, KindsEnum kind) {
   return common::IsValidKindOfIntrinsicType(category, kind);
 }
 
 bool TargetCharacteristics::EnableType(common::TypeCategory category,
-    std::int64_t kind, std::size_t byteSize, std::size_t align) {
+    KindsEnum kind, std::size_t byteSize, std::size_t align) {
   if (CanSupportType(category, kind)) {
-    byteSize_[static_cast<int>(category)][kind] = byteSize;
-    align_[static_cast<int>(category)][kind] = align;
+    byteSize_[static_cast<int>(category)][static_cast<int>(kind)] = byteSize;
+    align_[static_cast<int>(category)][static_cast<int>(kind)] = align;
     maxByteSize_ = std::max(maxByteSize_, byteSize);
     maxAlignment_ = std::max(maxAlignment_, align);
     return true;
@@ -61,32 +62,32 @@ bool TargetCharacteristics::EnableType(common::TypeCategory category,
 }
 
 void TargetCharacteristics::DisableType(
-    common::TypeCategory category, std::int64_t kind) {
-  if (kind > 0 && kind <= maxKind) {
-    align_[static_cast<int>(category)][kind] = 0;
+    common::TypeCategory category, KindsEnum kind) {
+  if (IsValidKind(kind)) {
+    align_[static_cast<int>(category)][static_cast<int>(kind)] = 0;
   }
 }
 
 std::size_t TargetCharacteristics::GetByteSize(
-    common::TypeCategory category, std::int64_t kind) const {
-  if (kind > 0 && kind <= maxKind) {
-    return byteSize_[static_cast<int>(category)][kind];
+    common::TypeCategory category, KindsEnum kind) const {
+  if (IsValidKind(kind)) {
+    return byteSize_[static_cast<int>(category)][static_cast<int>(kind)];
   } else {
     return 0;
   }
 }
 
 std::size_t TargetCharacteristics::GetAlignment(
-    common::TypeCategory category, std::int64_t kind) const {
-  if (kind > 0 && kind <= maxKind) {
-    return align_[static_cast<int>(category)][kind];
+    common::TypeCategory category, KindsEnum kind) const {
+  if (IsValidKind(kind)) {
+    return align_[static_cast<int>(category)][static_cast<int>(kind)];
   } else {
     return 0;
   }
 }
 
 bool TargetCharacteristics::IsTypeEnabled(
-    common::TypeCategory category, std::int64_t kind) const {
+    common::TypeCategory category, KindsEnum kind) const {
   return GetAlignment(category, kind) > 0;
 }
 
@@ -102,16 +103,16 @@ void TargetCharacteristics::set_areSubnormalsFlushedToZero(bool yes) {
 }
 
 // Check if a given real kind has flushing control.
-bool TargetCharacteristics::hasSubnormalFlushingControl(int kind) const {
-  CHECK(kind > 0 && kind <= maxKind);
+bool TargetCharacteristics::hasSubnormalFlushingControl(KindsEnum kind) const {
+  CHECK(IsValidKind(kind));
   CHECK(CanSupportType(TypeCategory::Real, kind));
-  return hasSubnormalFlushingControl_[kind];
+  return hasSubnormalFlushingControl_[static_cast<int>(kind)];
 }
 
 // Check if any or all real kinds have flushing control.
 bool TargetCharacteristics::hasSubnormalFlushingControl(bool any) const {
   for (int kind{1}; kind <= maxKind; ++kind) {
-    if (CanSupportType(TypeCategory::Real, kind) &&
+    if (CanSupportType(TypeCategory::Real, static_cast<KindsEnum>(kind)) &&
         hasSubnormalFlushingControl_[kind] == any) {
       return any;
     }
@@ -120,22 +121,22 @@ bool TargetCharacteristics::hasSubnormalFlushingControl(bool any) const {
 }
 
 void TargetCharacteristics::set_hasSubnormalFlushingControl(
-    int kind, bool yes) {
-  CHECK(kind > 0 && kind <= maxKind);
-  hasSubnormalFlushingControl_[kind] = yes;
+    KindsEnum kind, bool yes) {
+  CHECK(IsValidKind(kind));
+  hasSubnormalFlushingControl_[static_cast<int>(kind)] = yes;
 }
 
 // Check if a given real kind has (nonstandard) ieee_denorm exception control.
-bool TargetCharacteristics::hasSubnormalExceptionSupport(int kind) const {
-  CHECK(kind > 0 && kind <= maxKind);
+bool TargetCharacteristics::hasSubnormalExceptionSupport(KindsEnum kind) const {
+  CHECK(IsValidKind(kind));
   CHECK(CanSupportType(TypeCategory::Real, kind));
-  return hasSubnormalExceptionSupport_[kind];
+  return hasSubnormalExceptionSupport_[static_cast<int>(kind)];
 }
 
 // Check if all real kinds have support for the ieee_denorm exception.
 bool TargetCharacteristics::hasSubnormalExceptionSupport() const {
   for (int kind{1}; kind <= maxKind; ++kind) {
-    if (CanSupportType(TypeCategory::Real, kind) &&
+    if (CanSupportType(TypeCategory::Real, static_cast<KindsEnum>(kind)) &&
         !hasSubnormalExceptionSupport_[kind]) {
       return false;
     }
@@ -144,9 +145,9 @@ bool TargetCharacteristics::hasSubnormalExceptionSupport() const {
 }
 
 void TargetCharacteristics::set_hasSubnormalExceptionSupport(
-    int kind, bool yes) {
-  CHECK(kind > 0 && kind <= maxKind);
-  hasSubnormalExceptionSupport_[kind] = yes;
+    KindsEnum kind, bool yes) {
+  CHECK(IsValidKind(kind));
+  hasSubnormalExceptionSupport_[static_cast<int>(kind)] = yes;
 }
 
 void TargetCharacteristics::set_roundingMode(Rounding rounding) {
@@ -160,9 +161,9 @@ class SelectedIntKindVisitor {
   SelectedIntKindVisitor(
       const TargetCharacteristics &targetCharacteristics, std::int64_t p)
       : targetCharacteristics_{targetCharacteristics}, precision_{p} {}
-  using Result = std::optional<int>;
+  using Result = std::optional<KindsEnum>;
   using Types = IntegerTypes;
-  template <typename T> Result Test(int kind) const {
+  template <typename T> Result Test(KindsEnum kind) const {
     if (Scalar<T>::RANGE(kind) >= precision_ &&
         targetCharacteristics_.IsTypeEnabled(T::category, kind)) {
       return kind;
@@ -178,7 +179,7 @@ class SelectedIntKindVisitor {
 
 int TargetCharacteristics::SelectedIntKind(std::int64_t precision) const {
   if (auto kind{SearchTypes(SelectedIntKindVisitor{*this, precision})}) {
-    return *kind;
+    return static_cast<int>(*kind);
   } else {
     return -1;
   }
@@ -190,9 +191,9 @@ class SelectedLogicalKindVisitor {
   SelectedLogicalKindVisitor(
       const TargetCharacteristics &targetCharacteristics, std::int64_t bits)
       : targetCharacteristics_{targetCharacteristics}, bits_{bits} {}
-  using Result = std::optional<int>;
+  using Result = std::optional<KindsEnum>;
   using Types = LogicalTypes;
-  template <typename T> Result Test(int kind) const {
+  template <typename T> Result Test(KindsEnum kind) const {
     if (Scalar<T>::bits(kind) >= bits_ &&
         targetCharacteristics_.IsTypeEnabled(T::category, kind)) {
       return kind;
@@ -208,7 +209,7 @@ class SelectedLogicalKindVisitor {
 
 int TargetCharacteristics::SelectedLogicalKind(std::int64_t bits) const {
   if (auto kind{SearchTypes(SelectedLogicalKindVisitor{*this, bits})}) {
-    return *kind;
+    return static_cast<int>(*kind);
   } else {
     return -1;
   }
@@ -221,13 +222,13 @@ class SelectedRealKindVisitor {
       std::int64_t p, std::int64_t r)
       : targetCharacteristics_{targetCharacteristics}, precision_{p}, range_{
                                                                           r} {}
-  using Result = std::optional<int>;
+  using Result = std::optional<KindsEnum>;
   using Types = RealTypes;
-  template <typename T> Result Test(int kind) const {
+  template <typename T> Result Test(KindsEnum kind) const {
     if (Scalar<T>::PRECISION(kind) >= precision_ &&
         Scalar<T>::RANGE(kind) >= range_ &&
         targetCharacteristics_.IsTypeEnabled(T::category, kind)) {
-      return {kind};
+      return kind;
     } else {
       return std::nullopt;
     }
@@ -245,7 +246,7 @@ int TargetCharacteristics::SelectedRealKind(
   }
   if (auto kind{
           SearchTypes(SelectedRealKindVisitor{*this, precision, range})}) {
-    return *kind;
+    return static_cast<int>(*kind);
   }
   // No kind has both sufficient precision and sufficient range.
   // The negative return value encodes whether any kinds exist that
diff --git a/flang/lib/Evaluate/tools.cpp b/flang/lib/Evaluate/tools.cpp
index 85d2a8f752d0f..0d0381be5dfda 100644
--- a/flang/lib/Evaluate/tools.cpp
+++ b/flang/lib/Evaluate/tools.cpp
@@ -116,7 +116,8 @@ auto IsVariableHelper::operator()(const ProcedureDesignator &x) const
 // Conversions of COMPLEX component expressions to REAL.
 ConvertRealOperandsResult ConvertRealOperands(
     parser::ContextualMessages &messages, Expr<SomeType> &&x,
-    Expr<SomeType> &&y, int defaultRealKind) {
+    Expr<SomeType> &&y, KindsEnum defaultRealKind) {
+  const KindsEnum defaultRealKindEnum{defaultRealKind};
   return common::visit(
       common::visitors{
           [&](Expr<SomeInteger> &&ix,
@@ -125,33 +126,33 @@ ConvertRealOperandsResult ConvertRealOperands(
             // both integer operands are converted to default REAL.
             return {AsSameKindExprs<TypeCategory::Real>(
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(ix)),
+                    defaultRealKindEnum, std::move(ix)),
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(iy)))};
+                    defaultRealKindEnum, std::move(iy)))};
           },
           [&](Expr<SomeInteger> &&ix,
               Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult {
             return {AsSameKindExprs<TypeCategory::Real>(
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(ix)),
+                    defaultRealKindEnum, std::move(ix)),
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(iy)))};
+                    defaultRealKindEnum, std::move(iy)))};
           },
           [&](Expr<SomeUnsigned> &&ix,
               Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
             return {AsSameKindExprs<TypeCategory::Real>(
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(ix)),
+                    defaultRealKindEnum, std::move(ix)),
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(iy)))};
+                    defaultRealKindEnum, std::move(iy)))};
           },
           [&](Expr<SomeUnsigned> &&ix,
               Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult {
             return {AsSameKindExprs<TypeCategory::Real>(
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(ix)),
+                    defaultRealKindEnum, std::move(ix)),
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(iy)))};
+                    defaultRealKindEnum, std::move(iy)))};
           },
           [&](Expr<SomeInteger> &&ix,
               Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
@@ -186,33 +187,33 @@ ConvertRealOperandsResult ConvertRealOperands(
               BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
             return {AsSameKindExprs<TypeCategory::Real>(
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(ix)),
+                    defaultRealKindEnum, std::move(ix)),
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(by)))};
+                    defaultRealKindEnum, std::move(by)))};
           },
           [&](Expr<SomeUnsigned> &&ix,
               BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
             return {AsSameKindExprs<TypeCategory::Real>(
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(ix)),
+                    defaultRealKindEnum, std::move(ix)),
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(by)))};
+                    defaultRealKindEnum, std::move(by)))};
           },
           [&](BOZLiteralConstant &&bx,
               Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
             return {AsSameKindExprs<TypeCategory::Real>(
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(bx)),
+                    defaultRealKindEnum, std::move(bx)),
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(iy)))};
+                    defaultRealKindEnum, std::move(iy)))};
           },
           [&](BOZLiteralConstant &&bx,
               Expr<SomeUnsigned> &&iy) -> ConvertRealOperandsResult {
             return {AsSameKindExprs<TypeCategory::Real>(
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(bx)),
+                    defaultRealKindEnum, std::move(bx)),
                 ConvertToKind<TypeCategory::Real>(
-                    defaultRealKind, std::move(iy)))};
+                    defaultRealKindEnum, std::move(iy)))};
           },
           [&](Expr<SomeReal> &&rx,
               BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
@@ -265,7 +266,7 @@ std::optional<Expr<SomeType>> MixedRealLeft(
   return Package(common::visit(
       [&](auto &&rxk) -> Expr<SomeReal> {
         using resultType = ResultType<decltype(rxk)>;
-        const int resultKind = rxk.kind();
+        const KindsEnum resultKind{rxk.kind()};
         if constexpr (std::is_same_v<OPR<resultType>, Power<resultType>>) {
           return AsCategoryExpr(RealToIntPower<resultType>{
               resultKind, std::move(rxk), std::move(iy)});
@@ -286,7 +287,7 @@ static Expr<SomeComplex> MakeComplex(
 
 std::optional<Expr<SomeComplex>> ConstructComplex(
     parser::ContextualMessages &messages, Expr<SomeType> &&real,
-    Expr<SomeType> &&imaginary, int defaultRealKind) {
+    Expr<SomeType> &&imaginary, KindsEnum defaultRealKind) {
   if (auto converted{ConvertRealOperands(
           messages, std::move(real), std::move(imaginary), defaultRealKind)}) {
     return {common::visit(
@@ -300,7 +301,7 @@ std::optional<Expr<SomeComplex>> ConstructComplex(
 
 std::optional<Expr<SomeComplex>> ConstructComplex(
     parser::ContextualMessages &messages, std::optional<Expr<SomeType>> &&real,
-    std::optional<Expr<SomeType>> &&imaginary, int defaultRealKind) {
+    std::optional<Expr<SomeType>> &&imaginary, KindsEnum defaultRealKind) {
   if (auto parts{common::AllPresent(std::move(real), std::move(imaginary))}) {
     return ConstructComplex(messages, std::get<0>(std::move(*parts)),
         std::get<1>(std::move(*parts)), defaultRealKind);
@@ -332,7 +333,7 @@ Expr<SomeComplex> PromoteRealToComplex(Expr<SomeReal> &&someX) {
   return common::visit(
       [](auto &&x) {
         using RT = ResultType<decltype(x)>;
-        int rtKind{x.kind()};
+        auto rtKind{x.kind()};
         return AsCategoryExpr(ComplexConstructor{std::move(x),
             AsExpr(
                 Constant<RT>{rtKind, Scalar<RT>::Zero(rtKind), RT{rtKind}})});
@@ -346,13 +347,14 @@ Expr<SomeComplex> PromoteRealToComplex(Expr<SomeReal> &&someX) {
 template <template <typename> class OPR, TypeCategory RCAT>
 std::optional<Expr<SomeType>> MixedComplexLeft(
     parser::ContextualMessages &messages, const Expr<SomeComplex> &zx,
-    const Expr<SomeKind<RCAT>> &iry, [[maybe_unused]] int defaultRealKind) {
+    const Expr<SomeKind<RCAT>> &iry,
+    [[maybe_unused]] KindsEnum defaultRealKind) {
   if constexpr (RCAT == TypeCategory::Integer &&
       std::is_same_v<OPR<LargestReal>, Power<LargestReal>>) {
     // COMPLEX**INTEGER is a special case that doesn't convert the exponent.
     return Package(common::visit(
         [&](const auto &zxk) {
-          const int zxkKind{zxk.kind()};
+          const KindsEnum zxkKind{static_cast<KindsEnum>(zxk.kind())};
           using Ty = ResultType<decltype(zxk)>;
           return AsCategoryExpr(AsExpr(RealToIntPower<Ty>{
               zxkKind, common::Clone(zxk), common::Clone(iry)}));
@@ -398,7 +400,7 @@ std::optional<Expr<SomeType>> MixedComplexLeft(
 template <template <typename> class OPR, TypeCategory LCAT>
 std::optional<Expr<SomeType>> MixedComplexRight(
     parser::ContextualMessages &messages, const Expr<SomeKind<LCAT>> &irx,
-    const Expr<SomeComplex> &zy, [[maybe_unused]] int defaultRealKind) {
+    const Expr<SomeComplex> &zy, [[maybe_unused]] KindsEnum defaultRealKind) {
   if constexpr (std::is_same_v<OPR<LargestReal>, Add<LargestReal>>) {
     // x + (a,b) -> (a,b) + x -> (a+x, b)
     return MixedComplexLeft<OPR, LCAT>(messages, zy, irx, defaultRealKind);
@@ -432,7 +434,7 @@ Expr<SomeComplex> PromoteMixedComplexReal(
   static_assert(XCAT == TypeCategory::Real || YCAT == TypeCategory::Real);
   return common::visit(
       [&](const auto &kx, const auto &ky) {
-        int maxKind{std::max(kx.kind(), ky.kind())};
+        KindsEnum maxKind{std::max(kx.kind(), ky.kind())};
         using ZTy = Type<TypeCategory::Complex>;
         auto cx{ConvertToType<ZTy>(maxKind, std::move(x))};
         auto cy{ConvertToType<ZTy>(maxKind, std::move(y))};
@@ -448,7 +450,7 @@ Expr<SomeComplex> PromoteMixedComplexReal(
 template <template <typename> class OPR>
 std::optional<Expr<SomeType>> NumericOperation(
     parser::ContextualMessages &messages, Expr<SomeType> &&x,
-    Expr<SomeType> &&y, int defaultRealKind) {
+    Expr<SomeType> &&y, KindsEnum defaultRealKind) {
   return common::visit(
       common::visitors{
           [](Expr<SomeInteger> &&ix, Expr<SomeInteger> &&iy) {
@@ -471,7 +473,7 @@ std::optional<Expr<SomeType>> NumericOperation(
             return Package(common::visit(
                 [&](auto &&ryk) -> Expr<SomeReal> {
                   using resultType = ResultType<decltype(ryk)>;
-                  const int resultKind{ryk.kind()};
+                  const KindsEnum resultKind{ryk.kind()};
                   auto converted{
                       ConvertToType<resultType>(resultKind, std::move(ix))};
                   return AsCategoryExpr(OPR<resultType>{
@@ -577,19 +579,19 @@ std::optional<Expr<SomeType>> NumericOperation(
 
 template std::optional<Expr<SomeType>> NumericOperation<Power>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 template std::optional<Expr<SomeType>> NumericOperation<Divide>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 template std::optional<Expr<SomeType>> NumericOperation<Add>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
     parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&,
-    int defaultRealKind);
+    KindsEnum defaultRealKind);
 
 std::optional<Expr<SomeType>> Negation(
     parser::ContextualMessages &messages, Expr<SomeType> &&x) {
@@ -771,9 +773,9 @@ std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
                 auto makeIntCall =
                     [&](Expr<SomeDerived> &&operand) -> Expr<SomeType> {
                   using IntType = Type<TypeCategory::Integer>;
-                  constexpr int intKind{4};
+                  constexpr KindsEnum intKind{Kind4};
                   DynamicType enumType{*xDerived};
-                  DynamicType intResultType{TypeCategory::Integer, 4};
+                  DynamicType intResultType{TypeCategory::Integer, Kind4};
                   characteristics::DummyDataObject ddo{
                       characteristics::TypeAndShape{enumType}};
                   ddo.intent = common::Intent::In;
@@ -821,7 +823,8 @@ Expr<SomeLogical> BinaryLogicalOperation(
 }
 
 template <TypeCategory TO>
-std::optional<Expr<SomeType>> ConvertToNumeric(int kind, Expr<SomeType> &&x) {
+std::optional<Expr<SomeType>> ConvertToNumeric(
+    KindsEnum kind, Expr<SomeType> &&x) {
   static_assert(common::IsNumericTypeCategory(TO));
   return common::visit(
       [=](auto &&cx) -> std::optional<Expr<SomeType>> {
@@ -841,42 +844,44 @@ std::optional<Expr<SomeType>> ConvertToType(
   if (type.IsTypelessIntrinsicArgument()) {
     return std::nullopt;
   }
+  const bool hasKind{type.category() != TypeCategory::Derived};
+  const KindsEnum kind{hasKind ? static_cast<KindsEnum>(type.kind()) : NoKind};
   switch (type.category()) {
   case TypeCategory::Integer:
     if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) {
       // Extension to C7109: allow BOZ literals to appear in integer contexts
       // when the type is unambiguous.
       return Expr<SomeType>{
-          ConvertToKind<TypeCategory::Integer>(type.kind(), std::move(*boz))};
+          ConvertToKind<TypeCategory::Integer>(kind, std::move(*boz))};
     }
-    return ConvertToNumeric<TypeCategory::Integer>(type.kind(), std::move(x));
+    return ConvertToNumeric<TypeCategory::Integer>(kind, std::move(x));
   case TypeCategory::Unsigned:
     if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) {
       return Expr<SomeType>{
-          ConvertToKind<TypeCategory::Unsigned>(type.kind(), std::move(*boz))};
+          ConvertToKind<TypeCategory::Unsigned>(kind, std::move(*boz))};
     }
     if (auto *cx{UnwrapExpr<Expr<SomeUnsigned>>(x)}) {
       return Expr<SomeType>{
-          ConvertToKind<TypeCategory::Unsigned>(type.kind(), std::move(*cx))};
+          ConvertToKind<TypeCategory::Unsigned>(kind, std::move(*cx))};
     }
     break;
   case TypeCategory::Real:
     if (auto *boz{std::get_if<BOZLiteralConstant>(&x.u)}) {
       return Expr<SomeType>{
-          ConvertToKind<TypeCategory::Real>(type.kind(), std::move(*boz))};
+          ConvertToKind<TypeCategory::Real>(kind, std::move(*boz))};
     }
-    return ConvertToNumeric<TypeCategory::Real>(type.kind(), std::move(x));
+    return ConvertToNumeric<TypeCategory::Real>(kind, std::move(x));
   case TypeCategory::Complex:
-    return ConvertToNumeric<TypeCategory::Complex>(type.kind(), std::move(x));
+    return ConvertToNumeric<TypeCategory::Complex>(kind, std::move(x));
   case TypeCategory::Character:
     if (auto *cx{UnwrapExpr<Expr<SomeCharacter>>(x)}) {
       auto converted{
-          ConvertToKind<TypeCategory::Character>(type.kind(), std::move(*cx))};
+          ConvertToKind<TypeCategory::Character>(kind, std::move(*cx))};
       if (auto length{type.GetCharLength()}) {
         converted = common::visit(
             [&](auto &&x) {
               using CharacterType = ResultType<decltype(x)>;
-              const int characterKind{x.kind()};
+              const KindsEnum characterKind{x.kind()};
               return Expr<SomeCharacter>{Expr<CharacterType>{
                   SetLength{characterKind, std::move(x), std::move(*length)}}};
             },
@@ -888,7 +893,7 @@ std::optional<Expr<SomeType>> ConvertToType(
   case TypeCategory::Logical:
     if (auto *cx{UnwrapExpr<Expr<SomeLogical>>(x)}) {
       return Expr<SomeType>{
-          ConvertToKind<TypeCategory::Logical>(type.kind(), std::move(*cx))};
+          ConvertToKind<TypeCategory::Logical>(kind, std::move(*cx))};
     }
     break;
   case TypeCategory::Derived:
@@ -1351,14 +1356,14 @@ template <common::TypeCategory CAT> using Numeric = Type<CAT>;
 template <common::TypeCategory CAT> using NumericExpr = Expr<Numeric<CAT>>;
 
 template <common::TypeCategory CAT> struct SignedNumericTerm {
-  int kind() const { return expr.kind(); };
+  KindsEnum kind() const { return static_cast<KindsEnum>(expr.kind()); };
 
   NumericExpr<CAT> expr;
   bool isPositive;
 };
 
 template <common::TypeCategory CAT> struct SignedNumericExpr {
-  int kind() const { return expr.kind(); };
+  KindsEnum kind() const { return static_cast<KindsEnum>(expr.kind()); };
 
   NumericExpr<CAT> expr;
   bool isPositive;
@@ -1387,7 +1392,7 @@ template <common::TypeCategory CAT>
 static SignedNumericExpr<CAT> buildRightAssociatedSignedFold(
     llvm::MutableArrayRef<SignedNumericTerm<CAT>> terms) {
   assert(!terms.empty() && "cannot build empty signed fold");
-  const int kind{terms.front().kind()};
+  const KindsEnum kind{terms.front().kind()};
   const bool isPositive{terms.front().isPositive};
   NumericExpr<CAT> result{std::move(terms.back().expr)};
   for (std::size_t i{terms.size() - 1}; i > 0; --i) {
@@ -1407,7 +1412,7 @@ template <common::TypeCategory CAT>
 static SignedNumericExpr<CAT> buildSignedAdd(
     SignedNumericExpr<CAT> left, SignedNumericExpr<CAT> right) {
   CHECK(left.kind() == right.kind());
-  const int kind{left.kind()};
+  const KindsEnum kind{left.kind()};
   if (left.isPositive == right.isPositive) {
     return SignedNumericExpr<CAT>{
         NumericExpr<CAT>{Add<Numeric<CAT>>{
@@ -1730,17 +1735,18 @@ template <TypeCategory TO, TypeCategory FROM>
 static std::optional<Expr<SomeType>> DataConstantConversionHelper(
     FoldingContext &context, const DynamicType &toType,
     const Expr<SomeType> &expr) {
-  if (!common::IsValidKindOfIntrinsicType(FROM, toType.kind())) {
+  if (!common::IsValidKindOfIntrinsicType(
+          FROM, static_cast<KindsEnum>(toType.kind()))) {
     return std::nullopt;
   }
-  DynamicType sizedType{FROM, toType.kind()};
+  DynamicType sizedType{FROM, static_cast<KindsEnum>(toType.kind())};
   if (auto sized{
           Fold(context, ConvertToType(sizedType, Expr<SomeType>{expr}))}) {
     if (const auto *someExpr{UnwrapExpr<Expr<SomeKind<FROM>>>(*sized)}) {
       return common::visit(
           [](const auto &w) -> std::optional<Expr<SomeType>> {
             using FromType = ResultType<decltype(w)>;
-            const int kind{w.kind()};
+            const KindsEnum kind{static_cast<KindsEnum>(w.kind())};
             if (IsValidKindOfIntrinsicType(TO, kind)) {
               if (const auto *fromConst{UnwrapExpr<Constant<FromType>>(w)}) {
                 using LogicalType = value::LogicalValue;
@@ -1854,7 +1860,7 @@ std::optional<Expr<SomeType>> HollerithToBOZ(FoldingContext &context,
 template <typename T> const Symbol *GetBoundSymbol(const Expr<T> &);
 const Symbol *GetBoundSymbol(const Expr<Type<TypeCategory::Integer>> &expr) {
   using T = Type<TypeCategory::Integer>;
-  const int kind{expr.kind()};
+  const KindsEnum kind{static_cast<KindsEnum>(expr.kind())};
   return common::visit(
       common::visitors{
           [](const Extremum<T> &max) -> const Symbol * {
@@ -1875,7 +1881,9 @@ const Symbol *GetBoundSymbol(const Expr<Type<TypeCategory::Integer>> &expr) {
           [kind](const Convert<T, TypeCategory::Integer> &x) {
             return common::visit(
                 [kind](const auto &y) -> const Symbol * {
-                  int yKind{y.GetType() ? y.GetType()->kind() : 0};
+                  KindsEnum yKind{y.GetType()
+                          ? static_cast<KindsEnum>(y.GetType()->kind())
+                          : NoKind};
                   if (yKind <= kind) {
                     return GetBoundSymbol(y);
                   } else {
diff --git a/flang/lib/Evaluate/type.cpp b/flang/lib/Evaluate/type.cpp
index b9ed5305ad75c..ff050d8782c58 100644
--- a/flang/lib/Evaluate/type.cpp
+++ b/flang/lib/Evaluate/type.cpp
@@ -21,6 +21,14 @@
 #include <optional>
 #include <string>
 
+using Fortran::common::KindsEnum;
+
+namespace Fortran::common {
+llvm::raw_ostream &operator<<(llvm::raw_ostream &os, KindsEnum kind) {
+  return os << static_cast<int>(kind);
+}
+} // namespace Fortran::common
+
 // IsDescriptor() predicate: true when a symbol is implemented
 // at runtime with a descriptor.
 namespace Fortran::semantics {
@@ -117,7 +125,7 @@ bool IsPassedViaDescriptor(const Symbol &symbol) {
 
 namespace Fortran::evaluate {
 
-DynamicType::DynamicType(int k, const semantics::ParamValue &pv)
+DynamicType::DynamicType(KindsEnum k, const semantics::ParamValue &pv)
     : category_{TypeCategory::Character}, kind_{k} {
   CHECK(common::IsValidKindOfIntrinsicType(category_, kind_));
   if (auto n{ToInt64(pv.GetExplicit())}) {
@@ -252,7 +260,8 @@ bool DynamicType::IsNonConstantLengthCharacter() const {
 }
 
 bool DynamicType::IsTypelessIntrinsicArgument() const {
-  return category_ == TypeCategory::Integer && kind_ == TypelessKind;
+  return category_ == TypeCategory::Integer &&
+      kind_ == static_cast<KindsEnum>(TypelessKind);
 }
 
 bool DynamicType::IsLengthlessIntrinsicType() const {
@@ -703,12 +712,13 @@ std::optional<DynamicType> DynamicType::From(
   if (const auto *intrinsic{type.AsIntrinsic()}) {
     if (auto kind{ToInt64(intrinsic->kind())}) {
       TypeCategory category{intrinsic->category()};
-      if (common::IsValidKindOfIntrinsicType(category, *kind)) {
+      auto kindsEnum{static_cast<KindsEnum>(*kind)};
+      if (common::IsValidKindOfIntrinsicType(category, kindsEnum)) {
         if (category == TypeCategory::Character) {
           const auto &charType{type.characterTypeSpec()};
-          return DynamicType{static_cast<int>(*kind), charType.length()};
+          return DynamicType{kindsEnum, charType.length()};
         } else {
-          return DynamicType{category, static_cast<int>(*kind)};
+          return DynamicType{category, kindsEnum};
         }
       }
     }
@@ -734,7 +744,8 @@ DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
   case TypeCategory::Integer:
     switch (that.category_) {
     case TypeCategory::Integer:
-      return DynamicType{TypeCategory::Integer, std::max(kind(), that.kind())};
+      return DynamicType{TypeCategory::Integer,
+          static_cast<KindsEnum>(std::max(kind(), that.kind()))};
     case TypeCategory::Real:
     case TypeCategory::Complex:
       return that;
@@ -745,7 +756,8 @@ DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
   case TypeCategory::Unsigned:
     switch (that.category_) {
     case TypeCategory::Unsigned:
-      return DynamicType{TypeCategory::Unsigned, std::max(kind(), that.kind())};
+      return DynamicType{TypeCategory::Unsigned,
+          static_cast<KindsEnum>(std::max(kind(), that.kind()))};
     default:
       CRASH_NO_CASE;
     }
@@ -755,9 +767,11 @@ DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
     case TypeCategory::Integer:
       return *this;
     case TypeCategory::Real:
-      return DynamicType{TypeCategory::Real, std::max(kind(), that.kind())};
+      return DynamicType{TypeCategory::Real,
+          static_cast<KindsEnum>(std::max(kind(), that.kind()))};
     case TypeCategory::Complex:
-      return DynamicType{TypeCategory::Complex, std::max(kind(), that.kind())};
+      return DynamicType{TypeCategory::Complex,
+          static_cast<KindsEnum>(std::max(kind(), that.kind()))};
     default:
       CRASH_NO_CASE;
     }
@@ -768,7 +782,8 @@ DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
       return *this;
     case TypeCategory::Real:
     case TypeCategory::Complex:
-      return DynamicType{TypeCategory::Complex, std::max(kind(), that.kind())};
+      return DynamicType{TypeCategory::Complex,
+          static_cast<KindsEnum>(std::max(kind(), that.kind()))};
     default:
       CRASH_NO_CASE;
     }
@@ -776,7 +791,8 @@ DynamicType DynamicType::ResultTypeForMultiply(const DynamicType &that) const {
   case TypeCategory::Logical:
     switch (that.category_) {
     case TypeCategory::Logical:
-      return DynamicType{TypeCategory::Logical, std::max(kind(), that.kind())};
+      return DynamicType{TypeCategory::Logical,
+          static_cast<KindsEnum>(std::max(kind(), that.kind()))};
     default:
       CRASH_NO_CASE;
     }
@@ -821,7 +837,8 @@ bool SomeKind<TypeCategory::Derived>::operator==(
   return PointeeComparison(derivedTypeSpec_, that.derivedTypeSpec_);
 }
 
-int SelectedCharKind(const std::string &s, int defaultKind) { // F'2023 16.9.180
+int SelectedCharKind(
+    const std::string &s, KindsEnum defaultKind) { // F'2023 16.9.180
   auto lower{parser::ToLowerCaseLetters(s)};
   auto n{lower.size()};
   while (n > 0 && lower[0] == ' ') {
@@ -838,7 +855,7 @@ int SelectedCharKind(const std::string &s, int defaultKind) { // F'2023 16.9.180
   } else if (lower == "iso_10646" || lower == "ucs-4") {
     return 4;
   } else if (lower == "default") {
-    return defaultKind;
+    return static_cast<int>(defaultKind);
   } else {
     return -1;
   }
@@ -850,7 +867,8 @@ std::optional<DynamicType> ComparisonType(
   case TypeCategory::Integer:
     switch (t2.category()) {
     case TypeCategory::Integer:
-      return DynamicType{TypeCategory::Integer, std::max(t1.kind(), t2.kind())};
+      return DynamicType{TypeCategory::Integer,
+          static_cast<KindsEnum>(std::max(t1.kind(), t2.kind()))};
     case TypeCategory::Real:
     case TypeCategory::Complex:
       return t2;
@@ -863,7 +881,8 @@ std::optional<DynamicType> ComparisonType(
       return t1;
     case TypeCategory::Real:
     case TypeCategory::Complex:
-      return DynamicType{t2.category(), std::max(t1.kind(), t2.kind())};
+      return DynamicType{t2.category(),
+          static_cast<KindsEnum>(std::max(t1.kind(), t2.kind()))};
     default:
       return std::nullopt;
     }
@@ -873,15 +892,16 @@ std::optional<DynamicType> ComparisonType(
       return t1;
     case TypeCategory::Real:
     case TypeCategory::Complex:
-      return DynamicType{TypeCategory::Complex, std::max(t1.kind(), t2.kind())};
+      return DynamicType{TypeCategory::Complex,
+          static_cast<KindsEnum>(std::max(t1.kind(), t2.kind()))};
     default:
       return std::nullopt;
     }
   case TypeCategory::Character:
     switch (t2.category()) {
     case TypeCategory::Character:
-      return DynamicType{
-          TypeCategory::Character, std::max(t1.kind(), t2.kind())};
+      return DynamicType{TypeCategory::Character,
+          static_cast<KindsEnum>(std::max(t1.kind(), t2.kind()))};
     default:
       return std::nullopt;
     }
@@ -908,12 +928,12 @@ std::optional<bool> IsInteroperableIntrinsicType(const DynamicType &type,
     // KIND=2 is IEEE-754 binary16, interoperable with C _Float16.
     // KIND=3 (bfloat16) has no standard interoperable C type, so it is
     // accepted only under CUDA (or when reading a module file).
-    return type.kind() == 2 || type.kind() >= 4 || !features ||
+    return type.kind() == Kind2 || type.kind() >= Kind4 || !features ||
         features->IsEnabled(common::LanguageFeature::CUDA);
   case TypeCategory::Logical:
-    return type.kind() == 1; // C_BOOL
+    return type.kind() == Kind1; // C_BOOL
   case TypeCategory::Character:
-    if (type.kind() != 1) { // C_CHAR
+    if (type.kind() != Kind1) { // C_CHAR
       return false;
     } else if (checkCharLength) {
       if (type.knownLength()) {
@@ -934,13 +954,13 @@ bool IsCUDAIntrinsicType(const DynamicType &type) {
   switch (type.category()) {
   case TypeCategory::Integer:
   case TypeCategory::Logical:
-    return type.kind() <= 8;
+    return type.kind() <= Kind8;
   case TypeCategory::Real:
-    return type.kind() >= 2 && type.kind() <= 8;
+    return type.kind() >= Kind2 && type.kind() <= Kind8;
   case TypeCategory::Complex:
-    return type.kind() == 2 || type.kind() == 4 || type.kind() == 8;
+    return type.kind() == Kind2 || type.kind() == Kind4 || type.kind() == Kind8;
   case TypeCategory::Character:
-    return type.kind() == 1;
+    return type.kind() == Kind1;
   default:
     // Derived types are tested in Semantics/check-declarations.cpp
     return false;
diff --git a/flang/lib/Evaluate/variable.cpp b/flang/lib/Evaluate/variable.cpp
index 7ea509efdd5ca..4dd6a059be7e1 100644
--- a/flang/lib/Evaluate/variable.cpp
+++ b/flang/lib/Evaluate/variable.cpp
@@ -647,16 +647,17 @@ template <typename T> const Symbol *Designator<T>::GetLastSymbol() const {
 template <typename T>
 std::optional<DynamicType> Designator<T>::GetType() const {
   if constexpr (IsLengthlessIntrinsicType<Result>) {
-    return DynamicType{Result::category, kind()};
+    return DynamicType{Result::category, static_cast<KindsEnum>(kind())};
   }
   if constexpr (Result::category == TypeCategory::Character) {
     if (std::holds_alternative<Substring>(u)) {
       if (auto len{LEN()}) {
         if (auto n{ToInt64(*len)}) {
-          return DynamicType{kind(), *n};
+          return DynamicType{static_cast<KindsEnum>(kind()), *n};
         }
       }
-      return DynamicType{TypeCategory::Character, kind()};
+      return DynamicType{
+          TypeCategory::Character, static_cast<KindsEnum>(kind())};
     }
   }
   if (const Symbol * symbol{GetLastSymbol()}) {
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index b57bc4583be38..69c1aad1d03b4 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -589,16 +589,16 @@ static void parseTargetArgs(TargetOptions &opts, llvm::opt::ArgList &args) {
     opts.featuresAsWritten.emplace_back(currentArg->getValue());
 
   if (args.hasArg(clang::options::OPT_fdisable_real_10))
-    opts.disabledRealKinds.push_back(10);
+    opts.disabledRealKinds.push_back(Fortran::common::KindsEnum::Kind10);
 
   if (args.hasArg(clang::options::OPT_fdisable_real_3))
-    opts.disabledRealKinds.push_back(3);
+    opts.disabledRealKinds.push_back(Fortran::common::KindsEnum::Kind3);
 
   if (args.hasArg(clang::options::OPT_fdisable_integer_2))
-    opts.disabledIntegerKinds.push_back(2);
+    opts.disabledIntegerKinds.push_back(Fortran::common::KindsEnum::Kind2);
 
   if (args.hasArg(clang::options::OPT_fdisable_integer_16))
-    opts.disabledIntegerKinds.push_back(16);
+    opts.disabledIntegerKinds.push_back(Fortran::common::KindsEnum::Kind16);
 
   if (const llvm::opt::Arg *a = args.getLastArg(clang::options::OPT_mabi_EQ)) {
     opts.abi = a->getValue();
@@ -1176,11 +1176,15 @@ static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
                           clang::options::OPT_fdefault_real_4)) {
     const llvm::opt::Option &opt = arg->getOption();
     if (opt.matches(clang::options::OPT_fdefault_real_8)) {
-      res.getDefaultKinds().set_defaultRealKind(8);
-      res.getDefaultKinds().set_doublePrecisionKind(16);
+      res.getDefaultKinds().set_defaultRealKind(
+          Fortran::common::KindsEnum::Kind8);
+      res.getDefaultKinds().set_doublePrecisionKind(
+          Fortran::common::KindsEnum::Kind16);
     } else if (opt.matches(clang::options::OPT_fdefault_real_4)) {
-      res.getDefaultKinds().set_defaultRealKind(4);
-      res.getDefaultKinds().set_doublePrecisionKind(8);
+      res.getDefaultKinds().set_defaultRealKind(
+          Fortran::common::KindsEnum::Kind4);
+      res.getDefaultKinds().set_doublePrecisionKind(
+          Fortran::common::KindsEnum::Kind8);
     }
   }
   if (const llvm::opt::Arg *arg =
@@ -1188,17 +1192,25 @@ static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
                           clang::options::OPT_fdefault_integer_4)) {
     const llvm::opt::Option &opt = arg->getOption();
     if (opt.matches(clang::options::OPT_fdefault_integer_8)) {
-      res.getDefaultKinds().set_defaultIntegerKind(8);
-      res.getDefaultKinds().set_subscriptIntegerKind(8);
-      res.getDefaultKinds().set_sizeIntegerKind(8);
-      res.getDefaultKinds().set_defaultLogicalKind(8);
+      res.getDefaultKinds().set_defaultIntegerKind(
+          Fortran::common::KindsEnum::Kind8);
+      res.getDefaultKinds().set_subscriptIntegerKind(
+          Fortran::common::KindsEnum::Kind8);
+      res.getDefaultKinds().set_sizeIntegerKind(
+          Fortran::common::KindsEnum::Kind8);
+      res.getDefaultKinds().set_defaultLogicalKind(
+          Fortran::common::KindsEnum::Kind8);
     } else if (opt.matches(clang::options::OPT_fdefault_integer_4)) {
       // Note that the subscript integer kind is set to 8 here. If a
       // default-integer-kind is not provided, it is also set to 8.
-      res.getDefaultKinds().set_defaultIntegerKind(4);
-      res.getDefaultKinds().set_subscriptIntegerKind(8);
-      res.getDefaultKinds().set_sizeIntegerKind(4);
-      res.getDefaultKinds().set_defaultLogicalKind(4);
+      res.getDefaultKinds().set_defaultIntegerKind(
+          Fortran::common::KindsEnum::Kind4);
+      res.getDefaultKinds().set_subscriptIntegerKind(
+          Fortran::common::KindsEnum::Kind8);
+      res.getDefaultKinds().set_sizeIntegerKind(
+          Fortran::common::KindsEnum::Kind4);
+      res.getDefaultKinds().set_defaultLogicalKind(
+          Fortran::common::KindsEnum::Kind4);
     }
   }
   if (args.hasArg(clang::options::OPT_fdefault_double_8)) {
@@ -1211,10 +1223,12 @@ static bool parseDialectArgs(CompilerInvocation &res, llvm::opt::ArgList &args,
       diags.Report(diagID);
     }
     // https://gcc.gnu.org/onlinedocs/gfortran/Fortran-Dialect-Options.html
-    res.getDefaultKinds().set_doublePrecisionKind(8);
+    res.getDefaultKinds().set_doublePrecisionKind(
+        Fortran::common::KindsEnum::Kind8);
   }
   if (args.hasArg(clang::options::OPT_flarge_sizes))
-    res.getDefaultKinds().set_sizeIntegerKind(8);
+    res.getDefaultKinds().set_sizeIntegerKind(
+        Fortran::common::KindsEnum::Kind8);
 
   // -x cuda
   auto language = args.getLastArgValue(clang::options::OPT_x);
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 4759ad9b6fb3c..1ef864fc8a52d 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -875,7 +875,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   }
   mlir::Type genType(Fortran::common::TypeCategory tc) override final {
     return Fortran::lower::getFIRType(
-        &getMLIRContext(), tc, bridge.getDefaultKinds().GetDefaultKind(tc), {});
+        &getMLIRContext(), tc,
+        static_cast<int>(bridge.getDefaultKinds().GetDefaultKind(tc)), {});
   }
 
   Fortran::lower::TypeConstructionStack &
diff --git a/flang/lib/Lower/CallInterface.cpp b/flang/lib/Lower/CallInterface.cpp
index 4d1f8c29dc9f7..25c5bfb035ad4 100644
--- a/flang/lib/Lower/CallInterface.cpp
+++ b/flang/lib/Lower/CallInterface.cpp
@@ -1066,8 +1066,8 @@ class Fortran::lower::CallInterfaceImpl {
     } else {
       // All result other than characters/derived are simply returned by value
       // in implicit interfaces
-      mlir::Type mlirType =
-          getConverter().genType(dynamicType.category(), dynamicType.kind());
+      mlir::Type mlirType = getConverter().genType(
+          dynamicType.category(), static_cast<int>(dynamicType.kind()));
       addFirResult(mlirType, FirPlaceHolder::resultEntityPosition,
                    Property::Value);
     }
@@ -1081,9 +1081,10 @@ class Fortran::lower::CallInterfaceImpl {
     std::optional<std::int64_t> constantLen = type.knownLength();
     fir::CharacterType::LenType len =
         constantLen ? *constantLen : fir::CharacterType::unknownLen();
-    mlir::Type charRefTy = fir::ReferenceType::get(
-        fir::CharacterType::get(&mlirContext, type.kind(), len));
-    mlir::Type boxCharTy = fir::BoxCharType::get(&mlirContext, type.kind());
+    mlir::Type charRefTy = fir::ReferenceType::get(fir::CharacterType::get(
+        &mlirContext, static_cast<int>(type.kind()), len));
+    mlir::Type boxCharTy =
+        fir::BoxCharType::get(&mlirContext, static_cast<int>(type.kind()));
     addFirOperand(charRefTy, resultPosition, Property::CharAddress);
     addFirOperand(lenTy, resultPosition, Property::CharLength);
     /// For now, also return it by boxchar
@@ -1145,8 +1146,8 @@ class Fortran::lower::CallInterfaceImpl {
       }
     }
     if (dynamicType.category() == Fortran::common::TypeCategory::Character) {
-      mlir::Type boxCharTy =
-          fir::BoxCharType::get(&mlirContext, dynamicType.kind());
+      mlir::Type boxCharTy = fir::BoxCharType::get(
+          &mlirContext, static_cast<int>(dynamicType.kind()));
       addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
                     dummyNameAttr(entity));
       addPassedArg(PassEntityBy::BoxChar, entity, characteristics);
@@ -1179,9 +1180,10 @@ class Fortran::lower::CallInterfaceImpl {
     if (cat == Fortran::common::TypeCategory::Character)
       if (std::optional<std::int64_t> constantLen =
               toInt64(dynamicType.GetCharLength()))
-        return getConverter().genType(cat, dynamicType.kind(), {*constantLen});
+        return getConverter().genType(cat, static_cast<int>(dynamicType.kind()),
+                                      {*constantLen});
     // INTEGER, REAL, LOGICAL, COMPLEX, and CHARACTER with dynamic length.
-    return getConverter().genType(cat, dynamicType.kind());
+    return getConverter().genType(cat, static_cast<int>(dynamicType.kind()));
   }
 
   void handleExplicitDummy(
@@ -1259,14 +1261,14 @@ class Fortran::lower::CallInterfaceImpl {
                Fortran::common::TypeCategory::Character) {
       if (isValueAttr && isBindC) {
         // Pass as fir.char<1>
-        mlir::Type charTy =
-            fir::CharacterType::getSingleton(&mlirContext, dynamicType.kind());
+        mlir::Type charTy = fir::CharacterType::getSingleton(
+            &mlirContext, static_cast<int>(dynamicType.kind()));
         addFirOperand(charTy, nextPassedArgPosition(), Property::Value, attrs);
         addPassedArg(PassEntityBy::Value, entity, characteristics);
       } else {
         // Pass as fir.box_char
-        mlir::Type boxCharTy =
-            fir::BoxCharType::get(&mlirContext, dynamicType.kind());
+        mlir::Type boxCharTy = fir::BoxCharType::get(
+            &mlirContext, static_cast<int>(dynamicType.kind()));
         addFirOperand(boxCharTy, nextPassedArgPosition(), Property::BoxChar,
                       attrs);
         addPassedArg(isValueAttr ? PassEntityBy::CharBoxValueAttribute
diff --git a/flang/lib/Lower/ConvertArrayConstructor.cpp b/flang/lib/Lower/ConvertArrayConstructor.cpp
index a76527b4e7454..c840355fc1783 100644
--- a/flang/lib/Lower/ConvertArrayConstructor.cpp
+++ b/flang/lib/Lower/ConvertArrayConstructor.cpp
@@ -508,7 +508,7 @@ struct LengthAndTypeCollector {
           const Fortran::evaluate::ArrayConstructor<T> &arrayCtorExpr,
           Fortran::lower::SymMap &, Fortran::lower::StatementContext &,
           mlir::SmallVectorImpl<mlir::Value> &) {
-    const int kind{arrayCtorExpr.kind()};
+    const int kind{static_cast<int>(arrayCtorExpr.kind())};
     // Numerical and Logical types.
     return Fortran::lower::getFIRType(&converter.getMLIRContext(), T::category,
                                       kind,
@@ -541,7 +541,7 @@ struct LengthAndTypeCollector<Character> {
           Fortran::lower::SymMap &symMap,
           Fortran::lower::StatementContext &stmtCtx,
           mlir::SmallVectorImpl<mlir::Value> &lengths) {
-    const int kind{arrayCtorExpr.kind()};
+    const int kind{static_cast<int>(arrayCtorExpr.kind())};
     llvm::SmallVector<Fortran::lower::LenParameterTy> typeLengths;
     if (const Fortran::evaluate::ExtentExpr *lenExpr = arrayCtorExpr.LEN()) {
       lengths.push_back(
diff --git a/flang/lib/Lower/ConvertConstant.cpp b/flang/lib/Lower/ConvertConstant.cpp
index 31f706803de3e..49faf2460d443 100644
--- a/flang/lib/Lower/ConvertConstant.cpp
+++ b/flang/lib/Lower/ConvertConstant.cpp
@@ -54,7 +54,7 @@ static mlir::Attribute convertToAttribute(
     fir::FirOpBuilder &builder,
     const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC>> &value,
     mlir::Type type) {
-  const int kind{value.kind()};
+  const int kind{static_cast<int>(value.kind())};
   if constexpr (TC == Fortran::common::TypeCategory::Integer) {
     if (kind <= 8)
       return builder.getIntegerAttr(type, value.ToInt64());
@@ -154,7 +154,7 @@ class DenseGlobalBuilder {
 
     static_assert(TC != Fortran::common::TypeCategory::Character,
                   "must be numerical or logical");
-    const int kind = constant.kind();
+    const int kind = static_cast<int>(constant.kind());
     auto attrTc = TC == Fortran::common::TypeCategory::Logical
                       ? Fortran::common::TypeCategory::Integer
                       : TC;
@@ -253,7 +253,7 @@ template <Fortran::common::TypeCategory TC>
 static mlir::Value genScalarLit(
     fir::FirOpBuilder &builder, mlir::Location loc,
     const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC>> &value) {
-  int kind = value.kind();
+  int kind = static_cast<int>(value.kind());
   if constexpr (TC == Fortran::common::TypeCategory::Integer ||
                 TC == Fortran::common::TypeCategory::Unsigned) {
     // MLIR requires constants to be signless
@@ -325,15 +325,15 @@ createStringLitOp(fir::FirOpBuilder &builder, mlir::Location loc,
                   const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
                       Fortran::common::TypeCategory::Character>> &value,
                   [[maybe_unused]] int64_t len) {
-  int kind = value.kind();
-  if (kind == 1) {
+  Fortran::common::KindsEnum kind = value.kind();
+  if (kind == Fortran::common::KindsEnum::Kind1) {
     assert(value.size() == static_cast<std::uint64_t>(len));
     return builder.createStringLitOp(loc, *value.AsStringRef());
   } else {
     return value.withStdString([&](const auto &value) -> fir::StringLitOp {
       using ET = typename std::decay_t<decltype(value)>::value_type;
-      fir::CharacterType type =
-          fir::CharacterType::get(builder.getContext(), kind, len);
+      fir::CharacterType type = fir::CharacterType::get(
+          builder.getContext(), static_cast<int>(kind), len);
       mlir::MLIRContext *context = builder.getContext();
       std::int64_t size = static_cast<std::int64_t>(value.size());
       mlir::ShapedType shape = mlir::RankedTensorType::get(
@@ -359,7 +359,7 @@ genScalarLit(fir::FirOpBuilder &builder, mlir::Location loc,
              const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
                  Fortran::common::TypeCategory::Character>> &value,
              int64_t len, bool outlineInReadOnlyMemory) {
-  int kind = value.kind();
+  int kind = static_cast<int>(value.kind());
   // When in an initializer context, construct the literal op itself and do
   // not construct another constant object in rodata.
   if (!outlineInReadOnlyMemory)
@@ -751,7 +751,7 @@ static fir::ExtendedValue
 genArrayLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
             const Fortran::evaluate::Constant<T> &con,
             bool outlineInReadOnlyMemory) {
-  const int kind{con.kind()};
+  const int kind{static_cast<int>(con.kind())};
   fir::FirOpBuilder &builder = converter.getFirOpBuilder();
   Fortran::evaluate::ConstantSubscript size =
       Fortran::evaluate::GetSize(con.shape());
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index fd46a4b721563..9797ac35a220f 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -1198,7 +1198,7 @@ struct BinaryOp<Fortran::evaluate::Divide<
                                          fir::FirOpBuilder &builder,
                                          const Op &op, hlfir::Entity lhs,
                                          hlfir::Entity rhs) {
-    const int kind = op.kind();
+    const int kind = static_cast<int>(op.kind());
     mlir::Type ty = Fortran::lower::getFIRType(
         builder.getContext(), Fortran::common::TypeCategory::Complex, kind,
         /*params=*/{});
@@ -1223,7 +1223,7 @@ struct BinaryOp<Fortran::evaluate::Power<Fortran::evaluate::Type<TC>>> {
                                          fir::FirOpBuilder &builder,
                                          const Op &op, hlfir::Entity lhs,
                                          hlfir::Entity rhs) {
-    const int kind = op.kind();
+    const int kind = static_cast<int>(op.kind());
     mlir::Type ty = Fortran::lower::getFIRType(builder.getContext(), TC, kind,
                                                /*params=*/{});
     return hlfir::EntityWithAttributes{fir::genPow(builder, loc, ty, lhs, rhs)};
@@ -1238,7 +1238,7 @@ struct BinaryOp<
                                          fir::FirOpBuilder &builder,
                                          const Op &op, hlfir::Entity lhs,
                                          hlfir::Entity rhs) {
-    const int kind = op.kind();
+    const int kind = static_cast<int>(op.kind());
     mlir::Type ty = Fortran::lower::getFIRType(builder.getContext(), TC, kind,
                                                /*params=*/{});
     return hlfir::EntityWithAttributes{fir::genPow(builder, loc, ty, lhs, rhs)};
@@ -1327,7 +1327,7 @@ struct BinaryOp<Fortran::evaluate::Relational<
                                          fir::FirOpBuilder &builder,
                                          const Op &op, hlfir::Entity lhs,
                                          hlfir::Entity rhs) {
-    int bits = 8 * op.left().GetType().value().kind();
+    int bits = 8 * static_cast<int>(op.left().GetType().value().kind());
     auto signlessType = mlir::IntegerType::get(
         builder.getContext(), bits,
         mlir::IntegerType::SignednessSemantics::Signless);
@@ -1535,7 +1535,7 @@ struct UnaryOp<Fortran::evaluate::Negate<
   static hlfir::EntityWithAttributes gen(mlir::Location loc,
                                          fir::FirOpBuilder &builder,
                                          const Op &op, hlfir::Entity lhs) {
-    const int kind = op.kind();
+    const int kind = static_cast<int>(op.kind());
     // Like LLVM, integer negation is the binary op "0 - value"
     mlir::Type type = Fortran::lower::getFIRType(
         builder.getContext(), Fortran::common::TypeCategory::Integer, kind,
@@ -1554,9 +1554,9 @@ struct UnaryOp<Fortran::evaluate::Negate<
   static hlfir::EntityWithAttributes gen(mlir::Location loc,
                                          fir::FirOpBuilder &builder,
                                          const Op &op, hlfir::Entity lhs) {
-    const int kind = op.kind();
-    int bits = Fortran::evaluate::Type<
-        Fortran::common::TypeCategory::Integer>::Scalar::bits(kind);
+    const int kind = static_cast<int>(op.kind());
+    int bits = Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>::
+        Scalar::bits(static_cast<Fortran::common::KindsEnum>(kind));
     mlir::Type signlessType = mlir::IntegerType::get(
         builder.getContext(), bits,
         mlir::IntegerType::SignednessSemantics::Signless);
@@ -1633,7 +1633,7 @@ struct UnaryOp<Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1>, TC2>> {
   static hlfir::EntityWithAttributes gen(mlir::Location loc,
                                          fir::FirOpBuilder &builder,
                                          const Op &op, hlfir::Entity lhs) {
-    int kind = op.kind();
+    int kind = static_cast<int>(op.kind());
     if constexpr (TC1 == Fortran::common::TypeCategory::Character &&
                   TC2 == TC1) {
       return hlfir::convertCharacterKind(loc, builder, lhs, kind);
@@ -1765,7 +1765,7 @@ class HlfirBuilder {
   template <typename D, typename R, typename O>
   hlfir::EntityWithAttributes
   gen(const Fortran::evaluate::Operation<D, R, O> &op) {
-    const int rKind = op.kind();
+    const int rKind = static_cast<int>(op.kind());
     auto &builder = getBuilder();
     mlir::Location loc = getLoc();
     const int rank = op.Rank();
@@ -1811,7 +1811,7 @@ class HlfirBuilder {
   template <typename D, typename R, typename LO, typename RO>
   hlfir::EntityWithAttributes
   gen(const Fortran::evaluate::Operation<D, R, LO, RO> &op) {
-    const int rKind = op.kind();
+    const int rKind = static_cast<int>(op.kind());
     auto &builder = getBuilder();
     mlir::Location loc = getLoc();
     const int rank = op.Rank();
@@ -1910,7 +1910,8 @@ class HlfirBuilder {
                                getStmtCtx())
             .genNamedEntity(desc.base());
     using ResTy = Fortran::evaluate::DescriptorInquiry::Result;
-    const int resKind = Fortran::evaluate::DescriptorInquiry::kind();
+    const int resKind =
+        static_cast<int>(Fortran::evaluate::DescriptorInquiry::kind());
     mlir::Type resultType = getConverter().genType(ResTy::category, resKind);
     auto castResult = [&](mlir::Value v) {
       return hlfir::EntityWithAttributes{
diff --git a/flang/lib/Lower/ConvertType.cpp b/flang/lib/Lower/ConvertType.cpp
index a2c10f0bd821f..0f1fac1a84574 100644
--- a/flang/lib/Lower/ConvertType.cpp
+++ b/flang/lib/Lower/ConvertType.cpp
@@ -34,7 +34,8 @@ using Fortran::common::VectorElementCategory;
 
 static mlir::Type genRealType(mlir::MLIRContext *context, int kind) {
   if (Fortran::common::IsValidKindOfIntrinsicType(
-          Fortran::common::TypeCategory::Real, kind)) {
+          Fortran::common::TypeCategory::Real,
+          static_cast<Fortran::common::KindsEnum>(kind))) {
     switch (kind) {
     case 2:
       return mlir::Float16Type::get(context);
@@ -54,13 +55,14 @@ static mlir::Type genRealType(mlir::MLIRContext *context, int kind) {
 }
 
 static int getIntegerBits(int kind) {
-  return Fortran::evaluate::Type<
-      Fortran::common::TypeCategory::Integer>::Scalar::bits(kind);
+  return Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer>::
+      Scalar::bits(static_cast<Fortran::common::KindsEnum>(kind));
 }
 static mlir::Type genIntegerType(mlir::MLIRContext *context, int kind,
                                  bool isUnsigned = false) {
   if (Fortran::common::IsValidKindOfIntrinsicType(
-          Fortran::common::TypeCategory::Integer, kind)) {
+          Fortran::common::TypeCategory::Integer,
+          static_cast<Fortran::common::KindsEnum>(kind))) {
     mlir::IntegerType::SignednessSemantics signedness =
         (isUnsigned ? mlir::IntegerType::SignednessSemantics::Unsigned
                     : mlir::IntegerType::SignednessSemantics::Signless);
@@ -72,7 +74,8 @@ static mlir::Type genIntegerType(mlir::MLIRContext *context, int kind,
 
 static mlir::Type genLogicalType(mlir::MLIRContext *context, int KIND) {
   if (Fortran::common::IsValidKindOfIntrinsicType(
-          Fortran::common::TypeCategory::Logical, KIND))
+          Fortran::common::TypeCategory::Logical,
+          static_cast<Fortran::common::KindsEnum>(KIND)))
     return fir::LogicalType::get(context, KIND);
   return {};
 }
@@ -81,7 +84,8 @@ static mlir::Type genCharacterType(
     mlir::MLIRContext *context, int KIND,
     Fortran::lower::LenParameterTy len = fir::CharacterType::unknownLen()) {
   if (Fortran::common::IsValidKindOfIntrinsicType(
-          Fortran::common::TypeCategory::Character, KIND))
+          Fortran::common::TypeCategory::Character,
+          static_cast<Fortran::common::KindsEnum>(KIND)))
     return fir::CharacterType::get(context, KIND, len);
   return {};
 }
@@ -152,7 +156,8 @@ struct TypeBuilderImpl {
       // INTEGER, UNSIGNED, REAL, COMPLEX, CHARACTER, LOGICAL
       llvm::SmallVector<Fortran::lower::LenParameterTy> params;
       translateLenParameters(params, category, expr);
-      baseType = genFIRType(context, category, dynamicType->kind(), params);
+      baseType = genFIRType(context, category,
+                            static_cast<int>(dynamicType->kind()), params);
     }
     std::optional<Fortran::evaluate::Shape> shapeExpr =
         Fortran::evaluate::GetShape(converter.getFoldingContext(), expr);
@@ -487,8 +492,8 @@ struct TypeBuilderImpl {
         Fortran::evaluate::Type<Fortran::common::TypeCategory::Character>;
     auto designator = Fortran::evaluate::Fold(
         converter.getFoldingContext(),
-        Fortran::evaluate::Expr<TC>{
-            Fortran::evaluate::Designator<TC>{kind, symbol}});
+        Fortran::evaluate::Expr<TC>{Fortran::evaluate::Designator<TC>{
+            static_cast<Fortran::common::KindsEnum>(kind), symbol}});
     if (auto len = toInt64(designator.LEN()))
       return *len;
     return fir::SequenceType::getUnknownExtent();
diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp
index 490db8cc1d1ba..f3222b42472c3 100644
--- a/flang/lib/Lower/IO.cpp
+++ b/flang/lib/Lower/IO.cpp
@@ -1116,7 +1116,7 @@ lowerStringLit(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
   auto [buff, len] = genBuffer(converter, loc, *expr, strTy, lenTy, stmtCtx);
   mlir::Value kind;
   if (ty2) {
-    auto kindVal = expr->GetType().value().kind();
+    auto kindVal = static_cast<std::int64_t>(expr->GetType().value().kind());
     kind = mlir::arith::ConstantOp::create(
         builder, loc, builder.getIntegerAttr(ty2, kindVal));
   }
@@ -1954,8 +1954,9 @@ genNewunitSpec(Fortran::lower::AbstractConverter &converter, mlir::Location loc,
       mlir::Value addr = builder.createConvert(
           loc, ioFuncTy.getInput(1),
           fir::getBase(converter.genExprAddr(loc, var, stmtCtx)));
-      auto kind = builder.createIntegerConstant(loc, ioFuncTy.getInput(2),
-                                                var->GetType().value().kind());
+      auto kind = builder.createIntegerConstant(
+          loc, ioFuncTy.getInput(2),
+          static_cast<std::int64_t>(var->GetType().value().kind()));
       llvm::SmallVector<mlir::Value> ioArgs = {cookie, addr, kind};
       return fir::CallOp::create(builder, loc, ioFunc, ioArgs).getResult(0);
     }
diff --git a/flang/lib/Lower/OpenMP/Atomic.cpp b/flang/lib/Lower/OpenMP/Atomic.cpp
index c2ac17c21c34d..0d876e66abdcd 100644
--- a/flang/lib/Lower/OpenMP/Atomic.cpp
+++ b/flang/lib/Lower/OpenMP/Atomic.cpp
@@ -678,7 +678,6 @@ void Fortran::lower::omp::lowerAtomic(
                   *cond)) {
         common::visit(
             [&](const auto &kindLogical) {
-              using LogicalExpr = std::decay_t<decltype(kindLogical)>;
               if (const auto *logOp =
                       std::get_if<evaluate::LogicalOperation>(&kindLogical.u)) {
                 if (logOp->logicalOperator == common::LogicalOperator::Eqv ||
diff --git a/flang/lib/Lower/Support/Utils.cpp b/flang/lib/Lower/Support/Utils.cpp
index b54bb9b3886dd..c5277c0eda08a 100644
--- a/flang/lib/Lower/Support/Utils.cpp
+++ b/flang/lib/Lower/Support/Utils.cpp
@@ -101,12 +101,12 @@ class HashEvaluateExpr {
             Fortran::common::TypeCategory TC2>
   static unsigned getHashValue(
       const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1>, TC2> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return getHashValue(x.left()) - (static_cast<unsigned>(TC1) + 2u) -
            (static_cast<unsigned>(kind) + 5u);
   }
   static unsigned getHashValue(const Fortran::evaluate::ComplexComponent &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return getHashValue(x.left()) -
            (static_cast<unsigned>(x.isImaginaryPart) + 1u) * 3u +
            static_cast<unsigned>(kind);
@@ -118,49 +118,49 @@ class HashEvaluateExpr {
   template <Fortran::common::TypeCategory TC>
   static unsigned getHashValue(
       const Fortran::evaluate::Negate<Fortran::evaluate::Type<TC>> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return getHashValue(x.left()) - (static_cast<unsigned>(TC) + 5u) -
            (static_cast<unsigned>(kind) + 7u);
   }
   template <Fortran::common::TypeCategory TC>
   static unsigned
   getHashValue(const Fortran::evaluate::Add<Fortran::evaluate::Type<TC>> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) + getHashValue(x.right())) * 23u +
            static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
   }
   template <Fortran::common::TypeCategory TC>
   static unsigned getHashValue(
       const Fortran::evaluate::Subtract<Fortran::evaluate::Type<TC>> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) - getHashValue(x.right())) * 19u +
            static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
   }
   template <Fortran::common::TypeCategory TC>
   static unsigned getHashValue(
       const Fortran::evaluate::Multiply<Fortran::evaluate::Type<TC>> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) + getHashValue(x.right())) * 29u +
            static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
   }
   template <Fortran::common::TypeCategory TC>
   static unsigned getHashValue(
       const Fortran::evaluate::Divide<Fortran::evaluate::Type<TC>> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) - getHashValue(x.right())) * 31u +
            static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
   }
   template <Fortran::common::TypeCategory TC>
   static unsigned
   getHashValue(const Fortran::evaluate::Power<Fortran::evaluate::Type<TC>> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) - getHashValue(x.right())) * 37u +
            static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
   }
   template <Fortran::common::TypeCategory TC>
   static unsigned getHashValue(
       const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC>> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) + getHashValue(x.right())) * 41u +
            static_cast<unsigned>(TC) + static_cast<unsigned>(kind) +
            static_cast<unsigned>(x.ordering) * 7u;
@@ -173,22 +173,22 @@ class HashEvaluateExpr {
   template <Fortran::common::TypeCategory TC>
   static unsigned getHashValue(
       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC>> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) - getHashValue(x.right())) * 43u +
            static_cast<unsigned>(TC) + static_cast<unsigned>(kind);
   }
   static unsigned getHashValue(const Fortran::evaluate::ComplexConstructor &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) - getHashValue(x.right())) * 47u +
            static_cast<unsigned>(kind);
   }
   static unsigned getHashValue(const Fortran::evaluate::Concat &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) - getHashValue(x.right())) * 53u +
            static_cast<unsigned>(kind);
   }
   static unsigned getHashValue(const Fortran::evaluate::SetLength &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) - getHashValue(x.right())) * 59u +
            static_cast<unsigned>(kind);
   }
@@ -267,7 +267,7 @@ class HashEvaluateExpr {
   template <Fortran::common::TypeCategory TC>
   static unsigned getHashValue(
       const Fortran::evaluate::Relational<Fortran::evaluate::Type<TC>> &x) {
-    const int kind{x.kind()};
+    const Fortran::common::KindsEnum kind{x.kind()};
     return (getHashValue(x.left()) + getHashValue(x.right())) * 71u +
            static_cast<unsigned>(TC) + static_cast<unsigned>(kind) +
            static_cast<unsigned>(x.opr) * 11u;
diff --git a/flang/lib/Semantics/check-allocate.cpp b/flang/lib/Semantics/check-allocate.cpp
index 2ac242abd5788..2532106498744 100644
--- a/flang/lib/Semantics/check-allocate.cpp
+++ b/flang/lib/Semantics/check-allocate.cpp
@@ -422,7 +422,9 @@ static bool HaveCompatibleTypeParameters(
     return true;
   }
   if (const IntrinsicTypeSpec * intrinsicType1{type1.AsIntrinsic()}) {
-    return evaluate::ToInt64(intrinsicType1->kind()).value() == type2.kind();
+    return static_cast<KindsEnum>(
+               evaluate::ToInt64(intrinsicType1->kind()).value()) ==
+        type2.kind();
   } else if (type2.IsUnlimitedPolymorphic()) {
     return false;
   } else if (const DerivedTypeSpec * derivedType1{type1.AsDerived()}) {
diff --git a/flang/lib/Semantics/check-call.cpp b/flang/lib/Semantics/check-call.cpp
index 7f9095159183a..2e3cad90ddc80 100644
--- a/flang/lib/Semantics/check-call.cpp
+++ b/flang/lib/Semantics/check-call.cpp
@@ -199,15 +199,16 @@ static void CheckCharacterActual(evaluate::Expr<evaluate::SomeType> &actual,
                         .value_or(false)) {
                   // If substring, take rest of substring
                   if (*actualLength > 0) {
-                    actualChars -=
-                        (actualOffset->offset() / actualType.type().kind()) %
+                    actualChars -= (actualOffset->offset() /
+                                       static_cast<std::int64_t>(
+                                           actualType.type().kind())) %
                         *actualLength;
                   }
                 } else {
                   actualChars = (static_cast<std::int64_t>(
                                      actualOffset->symbol().size()) -
                                     actualOffset->offset()) /
-                      actualType.type().kind();
+                      static_cast<std::int64_t>(actualType.type().kind());
                 }
                 if (actualChars < dummyChars) {
                   if (extentErrors) {
@@ -303,12 +304,14 @@ static void ConvertIntegerActual(evaluate::Expr<evaluate::SomeType> &actual,
               common::LanguageFeature::ActualIntegerConvertedToSmallerKind)) {
         messages.Say(
             "Actual argument scalar expression of type INTEGER(%d) cannot be implicitly converted to smaller dummy argument type INTEGER(%d)"_err_en_US,
-            actualType.type().kind(), dummyType.type().kind());
+            static_cast<int>(actualType.type().kind()),
+            static_cast<int>(dummyType.type().kind()));
       } else {
         semanticsContext.Warn(messages,
             common::LanguageFeature::ActualIntegerConvertedToSmallerKind,
             "Actual argument scalar expression of type INTEGER(%d) was converted to smaller dummy argument type INTEGER(%d)"_port_en_US,
-            actualType.type().kind(), dummyType.type().kind());
+            static_cast<int>(actualType.type().kind()),
+            static_cast<int>(dummyType.type().kind()));
       }
     }
     actualType = dummyType;
@@ -633,7 +636,7 @@ static void CheckExplicitDataArg(const characteristics::DummyDataObject &dummy,
       bool actualIsArrayElement{IsArrayElement(actual) != nullptr};
       bool actualIsCKindCharacter{
           actualType.type().category() == TypeCategory::Character &&
-          actualType.type().kind() == 1};
+          actualType.type().kind() == KindsEnum::Kind1};
       if (!actualIsCKindCharacter) {
         if (!actualIsArrayElement && !dummyIsAssumedRank &&
             !dummy.ignoreTKR.test(common::IgnoreTKR::Rank)) {
@@ -2075,19 +2078,20 @@ static void CheckEvent_Query(evaluate::ActualArguments &arguments,
   }
   if (arguments.size() > 1 && arguments[1]) {
     if (auto dyType{arguments[1]->GetType()}) {
-      int defaultInt{
+      KindsEnum defaultInt{
           foldingContext.defaults().GetDefaultKind(TypeCategory::Integer)};
       if (dyType->category() == TypeCategory::Integer &&
           dyType->kind() < defaultInt) {
         foldingContext.messages().Say(arguments[1]->sourceLocation(),
             "COUNT= argument to EVENT_QUERY must be an integer with kind >= %d"_err_en_US,
-            defaultInt);
+            static_cast<int>(defaultInt));
       }
     }
   }
   if (arguments.size() > 2 && arguments[2]) {
     if (auto dyType{arguments[2]->GetType()}) {
-      if (dyType->category() == TypeCategory::Integer && dyType->kind() < 2) {
+      if (dyType->category() == TypeCategory::Integer &&
+          dyType->kind() < KindsEnum::Kind2) {
         foldingContext.messages().Say(arguments[2]->sourceLocation(),
             "STAT= argument to EVENT_QUERY must be an integer with kind >= 2 when present"_err_en_US);
       }
diff --git a/flang/lib/Semantics/check-case.cpp b/flang/lib/Semantics/check-case.cpp
index 38eab344ed48f..4f358ce5ba677 100644
--- a/flang/lib/Semantics/check-case.cpp
+++ b/flang/lib/Semantics/check-case.cpp
@@ -22,7 +22,8 @@ namespace Fortran::semantics {
 
 template <typename T> class CaseValues {
 public:
-  CaseValues(int kind, SemanticsContext &c, const evaluate::DynamicType &t)
+  CaseValues(
+      KindsEnum kind, SemanticsContext &c, const evaluate::DynamicType &t)
       : kind_{kind}, context_{c}, caseExprType_{t} {}
 
   void Check(const std::list<parser::CaseConstruct::Case> &cases) {
@@ -87,7 +88,8 @@ template <typename T> class CaseValues {
         auto folded{evaluate::Fold(foldingContext, SomeExpr{*x->v})};
         if (auto converted{evaluate::Fold(foldingContext,
                 evaluate::ConvertToType(
-                    {T::category, kind_}, SomeExpr{folded}))}) {
+                    evaluate::DynamicType{T::category, kind_},
+                    SomeExpr{folded}))}) {
           if (auto value{evaluate::GetScalarConstantValue<T>(*converted)}) {
             auto back{evaluate::Fold(foldingContext,
                 evaluate::ConvertToType(*type, SomeExpr{*converted}))};
@@ -144,7 +146,7 @@ template <typename T> class CaseValues {
   }
 
   struct Case {
-    explicit Case(int kind, const parser::Statement<parser::CaseStmt> &s)
+    explicit Case(KindsEnum kind, const parser::Statement<parser::CaseStmt> &s)
         : kind_{kind}, stmt{s} {}
     bool IsDefault() const { return !lower && !upper; }
     std::string AsFortran() const {
@@ -168,7 +170,7 @@ template <typename T> class CaseValues {
       return result;
     }
 
-    int kind_;
+    KindsEnum kind_;
     const parser::Statement<parser::CaseStmt> &stmt;
     std::optional<Value> lower, upper;
   };
@@ -218,7 +220,7 @@ template <typename T> class CaseValues {
     }
   }
 
-  int kind_;
+  KindsEnum kind_;
   SemanticsContext &context_;
   const evaluate::DynamicType &caseExprType_;
   std::list<Case> cases_;
@@ -228,7 +230,7 @@ template <typename T> class CaseValues {
 template <TypeCategory CAT> struct TypeVisitor {
   using Result = bool;
   using Types = evaluate::CategoryTypes<CAT>;
-  template <typename T> Result Test(int kind) {
+  template <typename T> Result Test(KindsEnum kind) {
     if (kind == exprType.kind()) {
       CaseValues<T>(kind, context, exprType).Check(caseList);
       return true;
@@ -353,7 +355,8 @@ void CaseChecker::Enter(const parser::CaseConstruct &construct) {
           TypeVisitor<TypeCategory::Unsigned>{context_, *exprType, caseList});
       return;
     case TypeCategory::Logical:
-      CaseValues<evaluate::Type<TypeCategory::Logical>>{1, context_, *exprType}
+      CaseValues<evaluate::Type<TypeCategory::Logical>>{
+          KindsEnum::Kind1, context_, *exprType}
           .Check(caseList);
       return;
     case TypeCategory::Character:
@@ -364,9 +367,10 @@ void CaseChecker::Enter(const parser::CaseConstruct &construct) {
       if (const auto *derived{evaluate::GetDerivedTypeSpec(*exprType)}) {
         if (derived->IsEnumerationType()) {
           if (ConvertEnumCaseValues(context_, caseList, *derived)) {
-            evaluate::DynamicType intType{TypeCategory::Integer, 4};
+            evaluate::DynamicType intType{
+                TypeCategory::Integer, KindsEnum::Kind4};
             CaseValues<evaluate::Type<TypeCategory::Integer>>{
-                4, context_, intType}
+                KindsEnum::Kind4, context_, intType}
                 .Check(caseList);
           }
           return;
diff --git a/flang/lib/Semantics/check-data.cpp b/flang/lib/Semantics/check-data.cpp
index 8adf758e319af..bc503ea85fc83 100644
--- a/flang/lib/Semantics/check-data.cpp
+++ b/flang/lib/Semantics/check-data.cpp
@@ -25,7 +25,7 @@ namespace Fortran::semantics {
 void DataChecker::Enter(const parser::DataImpliedDo &x) {
   const auto &name{parser::UnwrapRef<parser::Name>(
       std::get<parser::DataImpliedDo::Bounds>(x.t).Name())};
-  int kind{evaluate::ResultKind<evaluate::ImpliedDoIndex>};
+  KindsEnum kind{evaluate::ResultKind<evaluate::ImpliedDoIndex>};
   if (const auto dynamicType{evaluate::DynamicType::From(DEREF(name.symbol))}) {
     if (dynamicType->category() == TypeCategory::Integer) {
       kind = dynamicType->kind();
diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp
index 07c6b88f0562a..a127a055542c4 100644
--- a/flang/lib/Semantics/check-declarations.cpp
+++ b/flang/lib/Semantics/check-declarations.cpp
@@ -1603,7 +1603,7 @@ bool CheckHelper::IsResultOkToDiffer(const FunctionResult &result) {
       category == TypeCategory::Derived) {
     return false;
   }
-  int kind{typeAndShape->type().kind()};
+  KindsEnum kind{typeAndShape->type().kind()};
   return kind == context_.GetDefaultKind(category) ||
       (category == TypeCategory::Real &&
           kind == context_.doublePrecisionKind());
@@ -3295,7 +3295,7 @@ parser::Messages CheckHelper::WhyNotInteroperableDerivedType(
                 component.name(),
                 "A LOGICAL component of an interoperable type should have the interoperable KIND=C_BOOL"_port_en_US);
           } else if (type->category() == DeclTypeSpec::Character && dyType &&
-              dyType->kind() == 1) {
+              dyType->kind() == KindsEnum::Kind1) {
             context().Warn(msgs, common::UsageWarning::BindCCharLength,
                 component.name(),
                 "A CHARACTER component of an interoperable type should have length 1"_port_en_US);
@@ -3712,7 +3712,10 @@ void CheckHelper::CheckDioDummyIsDefaultInteger(
   if (const DeclTypeSpec *type{arg.GetType()};
       type && type->IsNumeric(TypeCategory::Integer)) {
     if (const auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())};
-        kind && *kind == context_.GetDefaultKind(TypeCategory::Integer)) {
+        kind &&
+        *kind ==
+            static_cast<std::int64_t>(
+                context_.GetDefaultKind(TypeCategory::Integer))) {
       return;
     }
   }
@@ -3799,8 +3802,9 @@ void CheckHelper::CheckDioAssumedLenCharacterArg(const Symbol &subp,
     if (!IsAssumedLengthCharacter(*arg) ||
         (!kind ||
             *kind !=
-                context_.defaultKinds().GetDefaultKind(
-                    TypeCategory::Character))) {
+                static_cast<std::int64_t>(
+                    context_.defaultKinds().GetDefaultKind(
+                        TypeCategory::Character)))) {
       messages_.Say(arg->name(),
           "Dummy argument '%s' of a defined input/output procedure must be assumed-length CHARACTER of default kind"_err_en_US,
           arg->name());
diff --git a/flang/lib/Semantics/check-io.cpp b/flang/lib/Semantics/check-io.cpp
index 5a5bee2b80e3c..e4dd309469bf1 100644
--- a/flang/lib/Semantics/check-io.cpp
+++ b/flang/lib/Semantics/check-io.cpp
@@ -71,20 +71,20 @@ void IoChecker::Enter(
   FormatErrorReporter reporter{context_, reporterCharBlock};
   auto reporterWrapper{[&](const auto &msg) { return reporter.Say(msg); }};
   switch (context_.GetDefaultKind(TypeCategory::Character)) {
-  case 1: {
+  case KindsEnum::Kind1: {
     common::FormatValidator<char> validator{formatStart,
         stmt.source.size() - (formatStart - stmt.source.begin()),
         reporterWrapper};
     validator.Check();
     break;
   }
-  case 2: { // TODO: Get this to work.
+  case KindsEnum::Kind2: { // TODO: Get this to work.
     common::FormatValidator<char16_t> validator{
         /*???*/ nullptr, /*???*/ 0, reporterWrapper};
     validator.Check();
     break;
   }
-  case 4: { // TODO: Get this to work.
+  case KindsEnum::Kind4: { // TODO: Get this to work.
     common::FormatValidator<char32_t> validator{
         /*???*/ nullptr, /*???*/ 0, reporterWrapper};
     validator.Check();
@@ -277,20 +277,20 @@ void IoChecker::Enter(const parser::Format &spec) {
             auto reporterWrapper{
                 [&](const auto &msg) { return reporter.Say(msg); }};
             switch (context_.GetDefaultKind(TypeCategory::Character)) {
-            case 1: {
+            case KindsEnum::Kind1: {
               common::FormatValidator<char> validator{constantFormat->c_str(),
                   constantFormat->length(), reporterWrapper, stmt_};
               validator.Check();
               break;
             }
-            case 2: {
+            case KindsEnum::Kind2: {
               // TODO: Get this to work.  (Maybe combine with earlier instance?)
               common::FormatValidator<char16_t> validator{
                   /*???*/ nullptr, /*???*/ 0, reporterWrapper, stmt_};
               validator.Check();
               break;
             }
-            case 4: {
+            case KindsEnum::Kind4: {
               // TODO: Get this to work.  (Maybe combine with earlier instance?)
               common::FormatValidator<char32_t> validator{
                   /*???*/ nullptr, /*???*/ 0, reporterWrapper, stmt_};
@@ -314,8 +314,8 @@ void IoChecker::Enter(const parser::IdVariable &spec) {
     return;
   }
   CheckForDefinableVariable(spec, "ID");
-  int kind{expr->GetType()->kind()};
-  int defaultKind{context_.GetDefaultKind(TypeCategory::Integer)};
+  KindsEnum kind{expr->GetType()->kind()};
+  KindsEnum defaultKind{context_.GetDefaultKind(TypeCategory::Integer)};
   if (kind < defaultKind) {
     context_.Say(
         "ID kind (%d) is smaller than default INTEGER kind (%d)"_err_en_US,
diff --git a/flang/lib/Semantics/check-omp-atomic.cpp b/flang/lib/Semantics/check-omp-atomic.cpp
index aa0ac59ac8bdf..eac84a685c8b6 100644
--- a/flang/lib/Semantics/check-omp-atomic.cpp
+++ b/flang/lib/Semantics/check-omp-atomic.cpp
@@ -202,7 +202,7 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
   evaluate::Expr<T> Reconstruct(const S &op, evaluate::Expr<T> atom,
       evaluate::Expr<T> op1, evaluate::Expr<T> op2) {
     using TypeS = llvm::remove_cvref_t<decltype(op)>;
-    const int KindS{op.kind()};
+    const auto KindS{op.kind()};
     // This function has to be semantically correct for all possible types
     // of S even though at runtime s will only be one of the matched types.
     // Limit the construction to the operation types that we tried to match
@@ -211,7 +211,7 @@ struct ReassocRewriter : public evaluate::rewrite::Identity {
       return evaluate::Expr<T>(TypeS(op));
     } else if constexpr (is_logical_v<T>) {
       CHECK(op1.kind() == op2.kind());
-      const int K{op1.kind()};
+      const KindsEnum K{op1.kind()};
       if constexpr (std::is_same_v<TypeS, evaluate::LogicalOperation>) {
         CHECK(K == KindS);
         // Logical operators take an extra argument in their constructor,
@@ -1739,7 +1739,7 @@ struct MinMaxRewriter : public evaluate::rewrite::Identity {
   template <typename T>
   evaluate::Expr<T> operator()(
       evaluate::Expr<T> &&x, const evaluate::FunctionRef<T> &f) {
-    const int kind{f.kind()};
+    const KindsEnum kind{f.kind()};
     const evaluate::ProcedureDesignator &proc = f.proc();
     if (!IsMinMax(proc) || f.arguments().size() <= 2) {
       return Id::operator()(std::move(x), f);
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 8826ba617d397..7af3013883341 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -6518,7 +6518,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
     // The interop-var must be a scalar integer of omp_interop_kind
     // (c_intptr_t): the runtime uses its storage as a pointer-sized handle.
     std::optional<evaluate::DynamicType> type{expr->GetType()};
-    int interopKind{static_cast<int>(
+    KindsEnum interopKind{static_cast<KindsEnum>(
         context_.targetCharacteristics().integerKindForPointer())};
     if (!type || type->category() != evaluate::TypeCategory::Integer ||
         type->kind() != interopKind) {
diff --git a/flang/lib/Semantics/compute-offsets.cpp b/flang/lib/Semantics/compute-offsets.cpp
index f72bc844e89b6..8cdb05118c49f 100644
--- a/flang/lib/Semantics/compute-offsets.cpp
+++ b/flang/lib/Semantics/compute-offsets.cpp
@@ -381,7 +381,8 @@ std::size_t ComputeOffsetsHelper::ComputeOffset(
   }
   auto result{offset * GetSizeAndAlignment(object.symbol, false).size};
   if (object.substringStart) {
-    int kind{context_.defaultKinds().GetDefaultKind(TypeCategory::Character)};
+    int kind{static_cast<int>(
+        context_.defaultKinds().GetDefaultKind(TypeCategory::Character))};
     if (const DeclTypeSpec * type{object.symbol.GetType()}) {
       if (const IntrinsicTypeSpec * intrinsic{type->AsIntrinsic()}) {
         kind = ToInt64(intrinsic->kind()).value_or(kind);
diff --git a/flang/lib/Semantics/data-to-inits.cpp b/flang/lib/Semantics/data-to-inits.cpp
index ad893735eae6c..952466ac040fd 100644
--- a/flang/lib/Semantics/data-to-inits.cpp
+++ b/flang/lib/Semantics/data-to-inits.cpp
@@ -207,7 +207,7 @@ bool DataInitializationCompiler<DSV>::Scan(const parser::DataImpliedDo &ido) {
     auto foldedUpper{evaluate::Fold(context, SomeExpr{*upperExpr})};
     auto upper{ToInt64(foldedUpper)};
     if (lower && upper) {
-      int kind{evaluate::ResultKind<evaluate::ImpliedDoIndex>};
+      KindsEnum kind{evaluate::ResultKind<evaluate::ImpliedDoIndex>};
       if (const auto dynamicType{evaluate::DynamicType::From(*name.symbol)}) {
         if (dynamicType->category() == TypeCategory::Integer) {
           kind = dynamicType->kind();
@@ -824,12 +824,12 @@ static bool CombineEquivalencedInitialization(
     std::size_t minElementBytes{
         ComputeMinElementBytes(associated, foldingContext)};
     if (!exprAnalyzer.GetFoldingContext().targetCharacteristics().IsTypeEnabled(
-            TypeCategory::Integer, minElementBytes) ||
+            TypeCategory::Integer, static_cast<KindsEnum>(minElementBytes)) ||
         (bytes % minElementBytes) != 0) {
       minElementBytes = 1;
     }
-    const DeclTypeSpec &typeSpec{scope.MakeNumericType(
-        TypeCategory::Integer, MakeKindExpr(minElementBytes))};
+    const DeclTypeSpec &typeSpec{scope.MakeNumericType(TypeCategory::Integer,
+        MakeKindExpr(static_cast<KindsEnum>(minElementBytes)))};
     details.set_type(typeSpec);
     ArraySpec arraySpec;
     arraySpec.emplace_back(ShapeSpec::MakeExplicit(Bound{
diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp
index d007d990514ca..df5b00f315a02 100644
--- a/flang/lib/Semantics/expression.cpp
+++ b/flang/lib/Semantics/expression.cpp
@@ -74,7 +74,7 @@ static std::optional<DynamicTypeWithLength> AnalyzeTypeSpec(
               typeSpec->AsIntrinsic()}) {
         TypeCategory category{intrinsic->category()};
         if (auto optKind{ToInt64(intrinsic->kind())}) {
-          int kind{static_cast<int>(*optKind)};
+          KindsEnum kind{static_cast<KindsEnum>(*optKind)};
           if (category == TypeCategory::Character) {
             const semantics::CharacterTypeSpec &cts{
                 typeSpec->characterTypeSpec()};
@@ -698,8 +698,8 @@ template <typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) {
 }
 
 // Type kind parameter values for literal constants.
-int ExpressionAnalyzer::AnalyzeKindParam(
-    const std::optional<parser::KindParam> &kindParam, int defaultKind) {
+KindsEnum ExpressionAnalyzer::AnalyzeKindParam(
+    const std::optional<parser::KindParam> &kindParam, KindsEnum defaultKind) {
   if (!kindParam) {
     return defaultKind;
   }
@@ -709,7 +709,8 @@ int ExpressionAnalyzer::AnalyzeKindParam(
           [&](const parser::Scalar<
               parser::Integer<parser::Constant<parser::Name>>> &n) {
             if (MaybeExpr ie{Analyze(n)}) {
-              return ToInt64(*ie).value_or(defaultKind);
+              return ToInt64(*ie).value_or(
+                  static_cast<std::int64_t>(defaultKind));
             }
             return static_cast<std::int64_t>(defaultKind);
           },
@@ -718,9 +719,9 @@ int ExpressionAnalyzer::AnalyzeKindParam(
   if (kind != static_cast<int>(kind)) {
     Say("Unsupported type kind value (%jd)"_err_en_US,
         static_cast<std::intmax_t>(kind));
-    kind = defaultKind;
+    kind = static_cast<std::int64_t>(defaultKind);
   }
-  return static_cast<int>(kind);
+  return static_cast<KindsEnum>(kind);
 }
 
 // Common handling of parser::IntLiteralConstant, SignedIntLiteralConstant,
@@ -728,7 +729,7 @@ int ExpressionAnalyzer::AnalyzeKindParam(
 template <typename TYPES, TypeCategory CAT> struct IntTypeVisitor {
   using Result = MaybeExpr;
   using Types = TYPES;
-  template <typename T> Result Test(int testKind) {
+  template <typename T> Result Test(KindsEnum testKind) {
     if (testKind >= kind) {
       const char *p{digits.begin()};
       using Int = typename T::Scalar;
@@ -776,7 +777,7 @@ template <typename TYPES, TypeCategory CAT> struct IntTypeVisitor {
   }
   ExpressionAnalyzer &analyzer;
   parser::CharBlock digits;
-  std::int64_t kind;
+  KindsEnum kind;
   bool isDefaultKind;
   bool isNegated;
 };
@@ -786,7 +787,7 @@ MaybeExpr ExpressionAnalyzer::IntLiteralConstant(
     const PARSED &x, bool isNegated) {
   const auto &kindParam{std::get<std::optional<parser::KindParam>>(x.t)};
   bool isDefaultKind{!kindParam};
-  int kind{AnalyzeKindParam(kindParam, GetDefaultKind(CAT))};
+  KindsEnum kind{AnalyzeKindParam(kindParam, GetDefaultKind(CAT))};
   const char *typeName{CAT == TypeCategory::Integer ? "INTEGER" : "UNSIGNED"};
   if (CheckIntrinsicKind(CAT, kind)) {
     auto digits{std::get<parser::CharBlock>(x.t)};
@@ -831,7 +832,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
 }
 
 template <typename TYPE>
-Constant<TYPE> ReadRealLiteral(int kind, parser::CharBlock source,
+Constant<TYPE> ReadRealLiteral(KindsEnum kind, parser::CharBlock source,
     FoldingContext &context, bool isDefaultKind) {
   const char *p{source.begin()};
   auto valWithFlags{Scalar<TYPE>::Read(
@@ -853,10 +854,10 @@ struct RealTypeVisitor {
   using Types = RealTypes;
 
   RealTypeVisitor(
-      int k, parser::CharBlock lit, FoldingContext &ctx, bool isDeftKind)
+      KindsEnum k, parser::CharBlock lit, FoldingContext &ctx, bool isDeftKind)
       : kind{k}, literal{lit}, context{ctx}, isDefaultKind{isDeftKind} {}
 
-  template <typename T> Result Test(int testKind) {
+  template <typename T> Result Test(KindsEnum testKind) {
     if (kind == testKind) {
       return {AsCategoryExpr(
           ReadRealLiteral<T>(testKind, literal, context, isDefaultKind))};
@@ -864,7 +865,7 @@ struct RealTypeVisitor {
     return std::nullopt;
   }
 
-  int kind;
+  KindsEnum kind;
   parser::CharBlock literal;
   FoldingContext &context;
   bool isDefaultKind;
@@ -881,10 +882,10 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) {
   // "6.02214E+23").  In the absence of an explicit kind parameter, any
   // exponent letter determines the kind.  Otherwise, defaults apply.
   auto &defaults{context_.defaultKinds()};
-  int defaultKind{defaults.GetDefaultKind(TypeCategory::Real)};
+  KindsEnum defaultKind{defaults.GetDefaultKind(TypeCategory::Real)};
   const char *end{xreal.source.end()};
   char expoLetter{' '};
-  std::optional<int> letterKind;
+  std::optional<KindsEnum> letterKind;
   for (const char *p{xreal.source.begin()}; p < end; ++p) {
     if (parser::IsLetter(*p)) {
       expoLetter = *p;
@@ -920,7 +921,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::RealLiteralConstant &x) {
           "Explicit kind parameter together with non-'E' exponent letter is not standard"_port_en_US);
     }
   }
-  bool isDefaultKind{!xkind && letterKind.value_or('e') == 'e'};
+  bool isDefaultKind{!xkind && !letterKind.has_value()};
   auto result{SearchTypes(
       RealTypeVisitor{kind, xreal.source, GetFoldingContext(), isDefaultKind})};
   if (!result) { // C717
@@ -965,38 +966,42 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::ComplexLiteralConstant &z) {
 }
 
 // CHARACTER literal processing.
-MaybeExpr ExpressionAnalyzer::AnalyzeString(std::string &&string, int kind) {
+MaybeExpr ExpressionAnalyzer::AnalyzeString(
+    std::string &&string, KindsEnum kind) {
   if (!CheckIntrinsicKind(TypeCategory::Character, kind)) {
     return std::nullopt;
   }
   switch (kind) {
-  case 1:
-    return AsGenericExpr(MakeConstant<Type<TypeCategory::Character>>(1,
-        parser::DecodeString<std::string, parser::Encoding::LATIN_1>(
-            string, true)));
-  case 2:
-    return AsGenericExpr(MakeConstant<Type<TypeCategory::Character>>(2,
-        parser::DecodeString<std::u16string, parser::Encoding::UTF_8>(
-            string, true)));
-  case 4:
-    return AsGenericExpr(MakeConstant<Type<TypeCategory::Character>>(4,
-        parser::DecodeString<std::u32string, parser::Encoding::UTF_8>(
-            string, true)));
+  case KindsEnum::Kind1:
+    return AsGenericExpr(
+        MakeConstant<Type<TypeCategory::Character>>(KindsEnum::Kind1,
+            parser::DecodeString<std::string, parser::Encoding::LATIN_1>(
+                string, true)));
+  case KindsEnum::Kind2:
+    return AsGenericExpr(
+        MakeConstant<Type<TypeCategory::Character>>(KindsEnum::Kind2,
+            parser::DecodeString<std::u16string, parser::Encoding::UTF_8>(
+                string, true)));
+  case KindsEnum::Kind4:
+    return AsGenericExpr(
+        MakeConstant<Type<TypeCategory::Character>>(KindsEnum::Kind4,
+            parser::DecodeString<std::u32string, parser::Encoding::UTF_8>(
+                string, true)));
   default:
     CRASH_NO_CASE;
   }
 }
 
 MaybeExpr ExpressionAnalyzer::Analyze(const parser::CharLiteralConstant &x) {
-  int kind{
-      AnalyzeKindParam(std::get<std::optional<parser::KindParam>>(x.t), 1)};
+  KindsEnum kind{AnalyzeKindParam(
+      std::get<std::optional<parser::KindParam>>(x.t), KindsEnum::Kind1)};
   auto value{std::get<std::string>(x.t)};
   return AnalyzeString(std::move(value), kind);
 }
 
 MaybeExpr ExpressionAnalyzer::Analyze(
     const parser::HollerithLiteralConstant &x) {
-  int kind{GetDefaultKind(TypeCategory::Character)};
+  KindsEnum kind{GetDefaultKind(TypeCategory::Character)};
   auto result{AnalyzeString(std::string{x.v}, kind)};
   if (auto *constant{UnwrapConstantValue<Ascii>(result)}) {
     constant->set_wasHollerith(true);
@@ -1011,7 +1016,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::LogicalLiteralConstant &x) {
   bool value{std::get<bool>(x.t)};
   auto result{SearchTypes(
       TypeKindVisitor<TypeCategory::Logical, Constant, value::LogicalValue>{
-          kind, value::LogicalValue{kind, value}})};
+          static_cast<KindsEnum>(kind),
+          value::LogicalValue{static_cast<KindsEnum>(kind), value}})};
   if (!result) {
     Say("unsupported LOGICAL(KIND=%d)"_err_en_US, kind); // C728
   }
@@ -1057,7 +1063,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
   auto restorer{GetContextualMessages().SetLocation(n.source)};
   if (std::optional<int> kind{IsImpliedDo(n.source)}) {
     return AsMaybeExpr(ConvertToKind<TypeCategory::Integer>(
-        *kind, AsExpr(ImpliedDoIndex{n.source})));
+        static_cast<KindsEnum>(*kind), AsExpr(ImpliedDoIndex{n.source})));
   }
   if (context_.HasError(n.symbol)) { // includes case of no symbol
     return std::nullopt;
@@ -1074,14 +1080,15 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Name &n) {
         // while processing other specification expressions in the PDT
         // definition; the right kind value will be used later in each of its
         // instantiations.
-        int kind{SubscriptIntegerKind};
+        KindsEnum kind{SubscriptIntegerKind};
         if (const auto *typeSpec{ultimate.GetType()}) {
           if (const semantics::IntrinsicTypeSpec *
               intrinType{typeSpec->AsIntrinsic()}) {
             if (auto k{ToInt64(Fold(semantics::KindExpr{intrinType->kind()}))};
                 k &&
-                common::IsValidKindOfIntrinsicType(TypeCategory::Integer, *k)) {
-              kind = *k;
+                common::IsValidKindOfIntrinsicType(
+                    TypeCategory::Integer, static_cast<KindsEnum>(*k))) {
+              kind = static_cast<KindsEnum>(*k);
             }
           }
         }
@@ -1236,7 +1243,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Substring &ss) {
                 return std::nullopt;
               }
               return WrapperHelper<TypeCategory::Character, Designator,
-                  Substring>(dynamicType->kind(),
+                  Substring>(static_cast<KindsEnum>(dynamicType->kind()),
                   Substring{std::move(checked.value()), std::move(first),
                       std::move(last)});
             }
@@ -1271,7 +1278,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
       return common::visit(
           [&](auto &&ckExpr) -> MaybeExpr {
             using Result = ResultType<decltype(ckExpr)>;
-            const int resultKind{ckExpr.kind()};
+            const int resultKind{static_cast<int>(ckExpr.kind())};
             auto *cp{std::get_if<Constant<Result>>(&ckExpr.u)};
             CHECK(DEREF(cp).size() == 1);
             StaticDataObject::Pointer staticData{StaticDataObject::Create()};
@@ -1281,8 +1288,8 @@ MaybeExpr ExpressionAnalyzer::Analyze(
                     foldingContext_.targetCharacteristics().isBigEndian());
             Substring substring{std::move(staticData), std::move(lower.value()),
                 std::move(upper.value())};
-            return AsGenericExpr(Expr<Result>{
-                Designator<Result>{resultKind, std::move(substring)}});
+            return AsGenericExpr(Expr<Result>{Designator<Result>{
+                static_cast<KindsEnum>(resultKind), std::move(substring)}});
           },
           std::move(charExpr->u));
     }
@@ -1552,8 +1559,9 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::StructureComponent &sc) {
                 auto part{kind == MiscKind::ComplexPartRe
                         ? ComplexPart::Part::RE
                         : ComplexPart::Part::IM};
-                return AsCategoryExpr(Designator<PartType>{
-                    z.kind(), ComplexPart{std::move(*dataRef), part}});
+                return AsCategoryExpr(
+                    Designator<PartType>{static_cast<KindsEnum>(z.kind()),
+                        ComplexPart{std::move(*dataRef), part}});
               },
               zExpr->u)};
           return AsGenericExpr(std::move(realExpr));
@@ -1661,12 +1669,12 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::CoindexedNamedObject &x) {
   return std::nullopt;
 }
 
-int ExpressionAnalyzer::IntegerTypeSpecKind(
+KindsEnum ExpressionAnalyzer::IntegerTypeSpecKind(
     const parser::IntegerTypeSpec &spec) {
   Expr<SubscriptInteger> value{
       AnalyzeKindSelector(TypeCategory::Integer, spec.v)};
   if (auto kind{ToInt64(value)}) {
-    return static_cast<int>(*kind);
+    return static_cast<KindsEnum>(*kind);
   }
   SayAt(spec, "Constant INTEGER kind value required here"_err_en_US);
   return GetDefaultKind(TypeCategory::Integer);
@@ -1713,7 +1721,7 @@ class ArrayConstructorContext {
   // expression in ToExpr().
   using Result = MaybeExpr;
   using Types = AllTypes;
-  template <typename T> Result Test(int kind) {
+  template <typename T> Result Test(KindsEnum kind) {
     if (type_ && type_->category() == T::category) {
       if constexpr (T::category == TypeCategory::Derived) {
         if (!type_->IsUnlimitedPolymorphic()) {
@@ -1748,7 +1756,7 @@ class ArrayConstructorContext {
 
 private:
   using ImpliedDoIntType = ResultType<ImpliedDoIndex>;
-  static constexpr int ImpliedDoIntKind = ResultKind<ImpliedDoIndex>;
+  static constexpr KindsEnum ImpliedDoIntKind = ResultKind<ImpliedDoIndex>;
 
   std::optional<Expr<SubscriptInteger>> LengthIfGood() const {
     if (type_) {
@@ -1810,7 +1818,8 @@ void ArrayConstructorContext::Push(MaybeExpr &&x) {
       exprAnalyzer_.Warn(common::LanguageFeature::BOZAsDefaultInteger,
           "BOZ literal in array constructor without explicit type is assumed to be default INTEGER"_port_en_US);
       x = AsGenericExpr(ConvertToKind<TypeCategory::Integer>(
-          exprAnalyzer_.GetDefaultKind(TypeCategory::Integer),
+          static_cast<KindsEnum>(
+              exprAnalyzer_.GetDefaultKind(TypeCategory::Integer)),
           std::move(*boz)));
     }
   }
@@ -1822,7 +1831,8 @@ void ArrayConstructorContext::Push(MaybeExpr &&x) {
         exprAnalyzer_.Warn(common::LanguageFeature::BOZAsDefaultInteger,
             "BOZ literal in array constructor without explicit type is assumed to be default INTEGER"_port_en_US);
         x = AsGenericExpr(ConvertToKind<TypeCategory::Integer>(
-            exprAnalyzer_.GetDefaultKind(TypeCategory::Integer),
+            static_cast<KindsEnum>(
+                exprAnalyzer_.GetDefaultKind(TypeCategory::Integer)),
             std::move(*boz)));
         dyType = x.value().GetType();
       } else if (auto cast{ConvertToType(*type_, std::move(*x))}) {
@@ -1948,7 +1958,7 @@ void ArrayConstructorContext::Add(const parser::AcValue::Triplet &triplet) {
     auto upperType{upperExpr->GetType()};
     auto strideType{strideExpr ? strideExpr->GetType() : lowerType};
     if (lowerType && upperType && strideType) {
-      int kind{lowerType->kind()};
+      KindsEnum kind{lowerType->kind()};
       if (upperType->kind() > kind) {
         kind = upperType->kind();
       }
@@ -1962,7 +1972,7 @@ void ArrayConstructorContext::Add(const parser::AcValue::Triplet &triplet) {
         if (!stride) {
           stride = MakeConstantExpr<ImpliedDoIntType>(ImpliedDoIntKind, 1);
         }
-        DynamicType type{TypeCategory::Integer, kind};
+        DynamicType type{TypeCategory::Integer, static_cast<KindsEnum>(kind)};
         if (!type_) {
           type_ = DynamicTypeWithLength{type};
         }
@@ -1994,7 +2004,7 @@ void ArrayConstructorContext::Add(const parser::AcImpliedDo &impliedDo) {
   exprAnalyzer_.Analyze(bounds.Name());
   const auto &parsedName{parser::UnwrapRef<parser::Name>(bounds.Name())};
   parser::CharBlock name{parsedName.source};
-  int kind{ImpliedDoIntKind};
+  KindsEnum kind{ImpliedDoIntKind};
   if (const Symbol *symbol{parsedName.symbol}) {
     if (auto dynamicType{DynamicType::From(symbol)}) {
       if (dynamicType->category() == TypeCategory::Integer) {
@@ -4373,7 +4383,8 @@ MaybeExpr RelationHelper(ExpressionAnalyzer &context, RelationalOperator opr,
       if (auto cmp{Relate(context.GetContextualMessages(), opr,
               analyzer.MoveExpr(0), analyzer.MoveExpr(1))}) {
         return AsMaybeExpr(ConvertToKind<TypeCategory::Logical>(
-            context.GetDefaultKind(TypeCategory::Logical),
+            static_cast<KindsEnum>(
+                context.GetDefaultKind(TypeCategory::Logical)),
             AsExpr(std::move(*cmp))));
       }
     } else {
@@ -4729,16 +4740,17 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::PointerObject &x) {
 Expr<SubscriptInteger> ExpressionAnalyzer::AnalyzeKindSelector(
     TypeCategory category,
     const std::optional<parser::KindSelector> &selector) {
-  int defaultKind{GetDefaultKind(category)};
+  KindsEnum defaultKind{GetDefaultKind(category)};
   if (!selector) {
-    return MakeSubscriptIntExpr(defaultKind);
+    return MakeKindExpr(defaultKind);
   }
   return common::visit(
       common::visitors{
           [&](const parser::ScalarIntConstantExpr &x) {
             if (MaybeExpr kind{Analyze(x)}) {
               if (std::optional<std::int64_t> code{ToInt64(*kind)}) {
-                if (CheckIntrinsicKind(category, *code)) {
+                if (CheckIntrinsicKind(
+                        category, static_cast<KindsEnum>(*code))) {
                   return MakeSubscriptIntExpr(*code);
                 }
               } else if (auto *intExpr{UnwrapExpr<Expr<SomeInteger>>(*kind)}) {
@@ -4746,12 +4758,12 @@ Expr<SubscriptInteger> ExpressionAnalyzer::AnalyzeKindSelector(
                     SubscriptIntegerKind, std::move(*intExpr));
               }
             }
-            return MakeSubscriptIntExpr(defaultKind);
+            return MakeKindExpr(defaultKind);
           },
           [&](const parser::KindSelector::StarSize &x) {
             std::intmax_t size = x.v;
             if (!CheckIntrinsicSize(category, size)) {
-              size = defaultKind;
+              size = static_cast<int>(defaultKind);
             } else if (category == TypeCategory::Complex) {
               size /= 2;
             }
@@ -4761,7 +4773,7 @@ Expr<SubscriptInteger> ExpressionAnalyzer::AnalyzeKindSelector(
       selector->u);
 }
 
-int ExpressionAnalyzer::GetDefaultKind(common::TypeCategory category) {
+KindsEnum ExpressionAnalyzer::GetDefaultKind(common::TypeCategory category) {
   return context_.GetDefaultKind(category);
 }
 
@@ -4771,7 +4783,7 @@ DynamicType ExpressionAnalyzer::GetDefaultKindOfType(
 }
 
 bool ExpressionAnalyzer::CheckIntrinsicKind(
-    TypeCategory category, std::int64_t kind) {
+    TypeCategory category, KindsEnum kind) {
   if (foldingContext_.targetCharacteristics().IsTypeEnabled(
           category, kind)) { // C712, C714, C715, C727
     return true;
@@ -4786,12 +4798,13 @@ bool ExpressionAnalyzer::CheckIntrinsicKind(
       // compilation with a disabled REAL kind.
     } else {
       Say("%s(KIND=%jd) is not an enabled type for this target"_err_en_US,
-          ToUpperCase(EnumToString(category)), kind);
+          ToUpperCase(EnumToString(category)),
+          static_cast<std::intmax_t>(kind));
     }
     return true;
   } else {
     Say("%s(KIND=%jd) is not a supported type"_err_en_US,
-        ToUpperCase(EnumToString(category)), kind);
+        ToUpperCase(EnumToString(category)), static_cast<std::intmax_t>(kind));
     return false;
   }
 }
@@ -4808,11 +4821,12 @@ bool ExpressionAnalyzer::CheckIntrinsicSize(
       return false;
     }
   }
-  return CheckIntrinsicKind(category, kind);
+  return CheckIntrinsicKind(category, static_cast<KindsEnum>(kind));
 }
 
-bool ExpressionAnalyzer::AddImpliedDo(parser::CharBlock name, int kind) {
-  return impliedDos_.insert(std::make_pair(name, kind)).second;
+bool ExpressionAnalyzer::AddImpliedDo(parser::CharBlock name, KindsEnum kind) {
+  return impliedDos_.insert(std::make_pair(name, static_cast<int>(kind)))
+      .second;
 }
 
 void ExpressionAnalyzer::RemoveImpliedDo(parser::CharBlock name) {
@@ -4842,7 +4856,7 @@ bool ExpressionAnalyzer::EnforceTypeConstraint(parser::CharBlock at,
             ToUpperCase(type->AsFortran()));
         return false;
       } else if (defaultKind) {
-        int kind{context_.GetDefaultKind(category)};
+        KindsEnum kind{context_.GetDefaultKind(category)};
         if (type->kind() != kind) {
           Say(at, "Must have default kind(%d) of %s type, but is %s"_err_en_US,
               kind, ToUpperCase(EnumToString(category)),
@@ -5856,7 +5870,8 @@ void ArgumentAnalyzer::ConvertBOZOperand(std::optional<DynamicType> *thisType,
     Expr<SomeType> &&argExpr{MoveExpr(i)};
     auto *boz{std::get_if<BOZLiteralConstant>(&argExpr.u)};
     if (otherType && otherType->category() == TypeCategory::Real) {
-      int kind{context_.context().GetDefaultKind(TypeCategory::Real)};
+      KindsEnum kind{static_cast<KindsEnum>(
+          context_.context().GetDefaultKind(TypeCategory::Real))};
       MaybeExpr realExpr{
           ConvertToKind<TypeCategory::Real>(kind, std::move(*boz))};
       actuals_[i] = std::move(realExpr.value());
@@ -5864,7 +5879,8 @@ void ArgumentAnalyzer::ConvertBOZOperand(std::optional<DynamicType> *thisType,
         thisType->emplace(TypeCategory::Real, kind);
       }
     } else if (otherType && otherType->category() == TypeCategory::Unsigned) {
-      int kind{context_.context().GetDefaultKind(TypeCategory::Unsigned)};
+      KindsEnum kind{static_cast<KindsEnum>(
+          context_.context().GetDefaultKind(TypeCategory::Unsigned))};
       MaybeExpr unsignedExpr{
           ConvertToKind<TypeCategory::Unsigned>(kind, std::move(*boz))};
       actuals_[i] = std::move(unsignedExpr.value());
@@ -5872,7 +5888,8 @@ void ArgumentAnalyzer::ConvertBOZOperand(std::optional<DynamicType> *thisType,
         thisType->emplace(TypeCategory::Unsigned, kind);
       }
     } else {
-      int kind{context_.context().GetDefaultKind(TypeCategory::Integer)};
+      KindsEnum kind{static_cast<KindsEnum>(
+          context_.context().GetDefaultKind(TypeCategory::Integer))};
       MaybeExpr intExpr{
           ConvertToKind<TypeCategory::Integer>(kind, std::move(*boz))};
       actuals_[i] = std::move(*intExpr);
@@ -5893,7 +5910,7 @@ void ArgumentAnalyzer::ConvertBOZAssignmentRHS(const DynamicType &lhsType) {
       if (const auto *boz{std::get_if<BOZLiteralConstant>(&rhs.u)}; boz &&
           evaluate::value::IntegerValue::bits(BOZLiteralConstantKind) -
                   boz->LEADZ() >
-              lhsType.kind() * 8) {
+              static_cast<int>(lhsType.kind()) * 8) {
         context_.Warn(common::UsageWarning::BOZLiteralTruncation,
             "BOZ literal constant is too large for %s(KIND=%d) assignment target; truncated"_warn_en_US,
             lhsType.category() == TypeCategory::Unsigned ? "UNSIGNED"
@@ -5955,7 +5972,8 @@ std::string ArgumentAnalyzer::TypeAsFortran(std::size_t i) {
                   ? type->AsFortran()
                   : "TYPE("s + type->AsFortran() + ')')
         : type->category() == TypeCategory::Character
-        ? "CHARACTER(KIND="s + std::to_string(type->kind()) + ')'
+        ? "CHARACTER(KIND="s + std::to_string(static_cast<int>(type->kind())) +
+            ')'
         : ToUpperCase(type->AsFortran());
   } else {
     return "untyped";
@@ -6045,7 +6063,7 @@ bool ExprChecker::Pre(const parser::DataImpliedDo &ido) {
   parser::Walk(std::get<parser::DataImpliedDo::Bounds>(ido.t), *this);
   const auto &bounds{std::get<parser::DataImpliedDo::Bounds>(ido.t)};
   const auto &name{parser::UnwrapRef<parser::Name>(bounds.Name())};
-  int kind{evaluate::ResultKind<evaluate::ImpliedDoIndex>};
+  KindsEnum kind{evaluate::ResultKind<evaluate::ImpliedDoIndex>};
   if (const auto dynamicType{evaluate::DynamicType::From(DEREF(name.symbol))}) {
     if (dynamicType->category() == TypeCategory::Integer) {
       kind = dynamicType->kind();
diff --git a/flang/lib/Semantics/resolve-names-utils.cpp b/flang/lib/Semantics/resolve-names-utils.cpp
index deb11bdeb96fb..45abb0ab2130c 100644
--- a/flang/lib/Semantics/resolve-names-utils.cpp
+++ b/flang/lib/Semantics/resolve-names-utils.cpp
@@ -933,7 +933,9 @@ bool EquivalenceSets::IsCharacterSequenceType(const DeclTypeSpec *type) {
   return IsSequenceType(type, [&](const IntrinsicTypeSpec &type) {
     auto kind{evaluate::ToInt64(type.kind())};
     return type.category() == TypeCategory::Character && kind &&
-        kind.value() == context_.GetDefaultKind(TypeCategory::Character);
+        kind.value() ==
+        static_cast<std::int64_t>(
+            context_.GetDefaultKind(TypeCategory::Character));
   });
 }
 
@@ -943,11 +945,15 @@ bool EquivalenceSets::IsDefaultKindNumericType(const IntrinsicTypeSpec &type) {
     switch (type.category()) {
     case TypeCategory::Integer:
     case TypeCategory::Logical:
-      return *kind == context_.GetDefaultKind(TypeCategory::Integer);
+      return *kind ==
+          static_cast<std::int64_t>(
+              context_.GetDefaultKind(TypeCategory::Integer));
     case TypeCategory::Real:
     case TypeCategory::Complex:
-      return *kind == context_.GetDefaultKind(TypeCategory::Real) ||
-          *kind == context_.doublePrecisionKind();
+      return *kind ==
+          static_cast<std::int64_t>(
+              context_.GetDefaultKind(TypeCategory::Real)) ||
+          *kind == static_cast<std::int64_t>(context_.doublePrecisionKind());
     default:
       return false;
     }
diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp
index bd3a6222710b4..0525728196e66 100644
--- a/flang/lib/Semantics/resolve-names.cpp
+++ b/flang/lib/Semantics/resolve-names.cpp
@@ -2777,14 +2777,17 @@ void DeclTypeSpecVisitor::Post(const parser::TypeSpec &typeSpec) {
 
 void DeclTypeSpecVisitor::Post(
     const parser::IntrinsicTypeSpec::DoublePrecision &) {
-  MakeNumericType(TypeCategory::Real, context().doublePrecisionKind());
+  MakeNumericType(
+      TypeCategory::Real, static_cast<int>(context().doublePrecisionKind()));
 }
 void DeclTypeSpecVisitor::Post(
     const parser::IntrinsicTypeSpec::DoubleComplex &) {
-  MakeNumericType(TypeCategory::Complex, context().doublePrecisionKind());
+  MakeNumericType(
+      TypeCategory::Complex, static_cast<int>(context().doublePrecisionKind()));
 }
 void DeclTypeSpecVisitor::MakeNumericType(TypeCategory category, int kind) {
-  SetDeclTypeSpec(context().MakeNumericType(category, kind));
+  SetDeclTypeSpec(
+      context().MakeNumericType(category, static_cast<KindsEnum>(kind)));
 }
 
 void DeclTypeSpecVisitor::CheckForAbstractType(const Symbol &typeSymbol) {
@@ -3572,7 +3575,9 @@ bool ScopeHandler::ImplicitlyTypeForwardRef(Symbol &symbol) {
       return false;
     }
     auto kind{evaluate::ToInt64(type->numericTypeSpec().kind())};
-    if (!kind || *kind != context().GetDefaultKind(TypeCategory::Integer)) {
+    if (!kind ||
+        static_cast<KindsEnum>(*kind) !=
+            context().GetDefaultKind(TypeCategory::Integer)) {
       return false;
     }
   }
@@ -3706,7 +3711,7 @@ const DeclTypeSpec &ScopeHandler::MakeNumericType(
 
 const DeclTypeSpec &ScopeHandler::MakeNumericType(
     TypeCategory category, int kind) {
-  return context().MakeNumericType(category, kind);
+  return context().MakeNumericType(category, static_cast<KindsEnum>(kind));
 }
 
 const DeclTypeSpec &ScopeHandler::MakeLogicalType(
@@ -3720,7 +3725,7 @@ const DeclTypeSpec &ScopeHandler::MakeLogicalType(
 }
 
 const DeclTypeSpec &ScopeHandler::MakeLogicalType(int kind) {
-  return context().MakeLogicalType(kind);
+  return context().MakeLogicalType(static_cast<KindsEnum>(kind));
 }
 
 void ScopeHandler::NotePossibleBadForwardRef(const parser::Name &name) {
@@ -6326,8 +6331,8 @@ void DeclarationVisitor::Post(const parser::EnumerationTypeStmt &x) {
       std::string{DerivedTypeDetails::ordinalComponentName})};
   Symbol &ordinalSym{MakeSymbol(currScope(), ordinalName, Attrs{})};
   ordinalSym.set_details(ObjectEntityDetails{});
-  ordinalSym.SetType(
-      currScope().MakeNumericType(TypeCategory::Integer, MakeKindExpr(4)));
+  ordinalSym.SetType(currScope().MakeNumericType(
+      TypeCategory::Integer, MakeKindExpr(KindsEnum::Kind4)));
   ordinalSym.set(Symbol::Flag::CompilerCreated);
   symbol.get<DerivedTypeDetails>().add_component(ordinalSym);
 }
@@ -6773,8 +6778,8 @@ void DeclarationVisitor::Post(const parser::CharSelector::LengthAndKind &x) {
   charInfo_.kind = EvaluateSubscriptIntExpr(kind);
   std::optional<std::int64_t> intKind{ToInt64(charInfo_.kind)};
   if (intKind &&
-      !context().targetCharacteristics().IsTypeEnabled(
-          TypeCategory::Character, *intKind)) { // C715, C719
+      !context().targetCharacteristics().IsTypeEnabled(TypeCategory::Character,
+          static_cast<KindsEnum>(*intKind))) { // C715, C719
     Say(currStmtSource().value(),
         "KIND value (%jd) not valid for CHARACTER"_err_en_US, *intKind);
     charInfo_.kind = std::nullopt; // prevent further errors
@@ -7875,7 +7880,8 @@ void DeclarationVisitor::Post(const parser::BasedPointer &bp) {
   }
   pointer->set(Symbol::Flag::CrayPointer);
   const DeclTypeSpec &pointerType{MakeNumericType(TypeCategory::Integer,
-      context().targetCharacteristics().integerKindForPointer())};
+      static_cast<int>(
+          context().targetCharacteristics().integerKindForPointer()))};
   const auto *type{pointer->GetType()};
   if (!type) {
     pointer->SetType(pointerType);
@@ -8167,7 +8173,7 @@ bool DeclarationVisitor::HandleUnrestrictedSpecificIntrinsicFunction(
         DEREF(interface->functionResult->GetTypeAndShape()).type()};
     CHECK(common::IsNumericTypeCategory(dyType.category()));
     const DeclTypeSpec &typeSpec{
-        MakeNumericType(dyType.category(), dyType.kind())};
+        MakeNumericType(dyType.category(), static_cast<int>(dyType.kind()))};
     ProcEntityDetails details;
     details.set_type(typeSpec);
     symbol.set_details(std::move(details));
diff --git a/flang/lib/Semantics/runtime-type-info.cpp b/flang/lib/Semantics/runtime-type-info.cpp
index ef56ce90ff0a7..a47e715430bee 100644
--- a/flang/lib/Semantics/runtime-type-info.cpp
+++ b/flang/lib/Semantics/runtime-type-info.cpp
@@ -312,15 +312,15 @@ static int GetIntegerKind(const Symbol &symbol, bool canBeUninstantiated) {
   auto dyType{evaluate::DynamicType::From(symbol)};
   CHECK((dyType && dyType->category() == TypeCategory::Integer) ||
       symbol.owner().context().HasError(symbol) || canBeUninstantiated);
-  return dyType && dyType->category() == TypeCategory::Integer
-      ? dyType->kind()
-      : symbol.owner().context().GetDefaultKind(TypeCategory::Integer);
+  return static_cast<int>(dyType && dyType->category() == TypeCategory::Integer
+          ? dyType->kind()
+          : symbol.owner().context().GetDefaultKind(TypeCategory::Integer));
 }
 
 // Save a rank-1 array constant of some numeric type as an
 // initialized data object in a scope.
 template <typename T>
-static SomeExpr SaveNumericPointerTarget(int kind, Scope &scope,
+static SomeExpr SaveNumericPointerTarget(KindsEnum kind, Scope &scope,
     SourceName name, std::vector<typename T::Scalar> &&x) {
   if (x.empty()) {
     return SomeExpr{evaluate::NullPointer{}};
@@ -357,10 +357,10 @@ static SomeExpr SaveObjectInit(
   CHECK(symbol.get<ObjectEntityDetails>().init().has_value());
   SetReadOnlyCompilerCreatedFlags(symbol);
   return evaluate::AsGenericExpr(
-      evaluate::Designator<evaluate::SomeDerived>{0, symbol});
+      evaluate::Designator<evaluate::SomeDerived>{KindsEnum::NoKind, symbol});
 }
 
-static SomeExpr IntExpr(int kind, std::int64_t n) {
+static SomeExpr IntExpr(KindsEnum kind, std::int64_t n) {
   return evaluate::AsGenericExpr(
       evaluate::MakeConstant<evaluate::Type<TypeCategory::Integer>>(kind, n));
 }
@@ -514,20 +514,20 @@ const Symbol *RuntimeTableBuilder::DescribeType(
               }
             }
           }
-          kinds.emplace_back(8, value);
+          kinds.emplace_back(KindsEnum::Kind8, value);
         } else { // LEN= parameter
-          lenKinds.emplace_back(
-              1, GetIntegerKind(*inst, isPDTDefinitionWithKindParameters));
+          lenKinds.emplace_back(KindsEnum::Kind1,
+              GetIntegerKind(*inst, isPDTDefinitionWithKindParameters));
         }
       }
     }
   }
   AddValue(dtValues, derivedTypeSchema_, "kindparameter"s,
-      SaveNumericPointerTarget<Int8>(8, scope,
+      SaveNumericPointerTarget<Int8>(KindsEnum::Kind8, scope,
           SaveObjectName((fir::kKindParameterSeparator + distinctName).str()),
           std::move(kinds)));
   AddValue(dtValues, derivedTypeSchema_, "lenparameterkind"s,
-      SaveNumericPointerTarget<Int1>(1, scope,
+      SaveNumericPointerTarget<Int1>(KindsEnum::Kind1, scope,
           SaveObjectName((fir::kLenKindSeparator + distinctName).str()),
           std::move(lenKinds)));
   // Traverse the components of the derived type
@@ -645,28 +645,29 @@ const Symbol *RuntimeTableBuilder::DescribeType(
                   static_cast<evaluate::ConstantSubscript>(specials.size())}));
     }
     AddValue(dtValues, derivedTypeSchema_, "specialbitset"s,
-        IntExpr(4, specialBitSet));
+        IntExpr(KindsEnum::Kind4, specialBitSet));
     // Note the presence/absence of a parent component
     AddValue(dtValues, derivedTypeSchema_, "hasparent"s,
-        IntExpr(1, dtScope.GetDerivedTypeParent() != nullptr));
+        IntExpr(KindsEnum::Kind1, dtScope.GetDerivedTypeParent() != nullptr));
     // To avoid wasting run time attempting to initialize derived type
     // instances without any initialized components, analyze the type
     // and set a flag if there's nothing to do for it at run time.
     AddValue(dtValues, derivedTypeSchema_, "noinitializationneeded"s,
-        IntExpr(1,
+        IntExpr(KindsEnum::Kind1,
             derivedTypeSpec &&
                 !derivedTypeSpec->HasDefaultInitialization(false, false)));
     // Similarly, a flag to short-circuit destruction when not needed.
     AddValue(dtValues, derivedTypeSchema_, "nodestructionneeded"s,
-        IntExpr(1, derivedTypeSpec && !derivedTypeSpec->HasDestruction()));
+        IntExpr(KindsEnum::Kind1,
+            derivedTypeSpec && !derivedTypeSpec->HasDestruction()));
     // Similarly, a flag to short-circuit finalization when not needed.
     AddValue(dtValues, derivedTypeSchema_, "nofinalizationneeded"s,
-        IntExpr(
-            1, derivedTypeSpec && !MayRequireFinalization(*derivedTypeSpec)));
+        IntExpr(KindsEnum::Kind1,
+            derivedTypeSpec && !MayRequireFinalization(*derivedTypeSpec)));
     // Similarly, a flag to enable optimized runtime assignment.
     AddValue(dtValues, derivedTypeSchema_, "nodefinedassignment"s,
-        IntExpr(
-            1, derivedTypeSpec && !MayHaveDefinedAssignment(*derivedTypeSpec)));
+        IntExpr(KindsEnum::Kind1,
+            derivedTypeSpec && !MayHaveDefinedAssignment(*derivedTypeSpec)));
   }
   dtObject.get<ObjectEntityDetails>().set_init(MaybeExpr{
       StructureExpr(Structure(derivedTypeSchema_, std::move(dtValues)))});
@@ -716,7 +717,7 @@ SomeExpr RuntimeTableBuilder::GetEnumValue(const char *name) const {
   const Symbol &symbol{GetSchemaSymbol(name)};
   auto value{evaluate::ToInt64(symbol.get<ObjectEntityDetails>().init())};
   CHECK(value.has_value());
-  return IntExpr(1, *value);
+  return IntExpr(KindsEnum::Kind1, *value);
 }
 
 Symbol &RuntimeTableBuilder::CreateObject(
@@ -741,12 +742,14 @@ SomeExpr RuntimeTableBuilder::SaveNameAsPointerTarget(
   CHECK(name.front() != '.' || context_.IsTempName(name));
   ObjectEntityDetails object;
   auto len{static_cast<common::ConstantSubscript>(name.size())};
-  if (const auto *spec{scope.FindType(DeclTypeSpec{CharacterTypeSpec{
-          ParamValue{len, common::TypeParamAttr::Len}, MakeKindExpr(1)}})}) {
+  if (const auto *spec{scope.FindType(DeclTypeSpec{
+          CharacterTypeSpec{ParamValue{len, common::TypeParamAttr::Len},
+              MakeKindExpr(KindsEnum::Kind1)}})}) {
     object.set_type(*spec);
   } else {
-    object.set_type(scope.MakeCharacterType(
-        ParamValue{len, common::TypeParamAttr::Len}, MakeKindExpr(1)));
+    object.set_type(
+        scope.MakeCharacterType(ParamValue{len, common::TypeParamAttr::Len},
+            MakeKindExpr(KindsEnum::Kind1)));
   }
   using evaluate::Ascii, evaluate::AsciiKind, evaluate::MakeConstantExpr;
   using AsciiExpr = evaluate::Expr<Ascii>;
@@ -783,14 +786,16 @@ evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
   AddValue(values, componentSchema_, "name"s,
       SaveNameAsPointerTarget(scope, symbol.name().ToString()));
   AddValue(values, componentSchema_, "category"s,
-      IntExpr(1, static_cast<int>(dyType.category())));
+      IntExpr(KindsEnum::Kind1, static_cast<int>(dyType.category())));
   if (dyType.IsUnlimitedPolymorphic() ||
       dyType.category() == TypeCategory::Derived) {
-    AddValue(values, componentSchema_, "kind"s, IntExpr(1, 0));
+    AddValue(values, componentSchema_, "kind"s, IntExpr(KindsEnum::Kind1, 0));
   } else {
-    AddValue(values, componentSchema_, "kind"s, IntExpr(1, dyType.kind()));
+    AddValue(values, componentSchema_, "kind"s,
+        IntExpr(KindsEnum::Kind1, static_cast<std::int64_t>(dyType.kind())));
   }
-  AddValue(values, componentSchema_, "offset"s, IntExpr(8, symbol.offset()));
+  AddValue(values, componentSchema_, "offset"s,
+      IntExpr(KindsEnum::Kind8, symbol.offset()));
   // CHARACTER length
   auto len{typeAndShape->LEN()};
   if (const semantics::DerivedTypeSpec *
@@ -864,7 +869,7 @@ evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
         SomeExpr{evaluate::NullPointer{}});
   }
   // Shape information
-  AddValue(values, componentSchema_, "rank"s, IntExpr(1, rank));
+  AddValue(values, componentSchema_, "rank"s, IntExpr(KindsEnum::Kind1, rank));
   if (rank > 0 && !IsAllocatable(symbol) && !IsPointer(symbol)) {
     std::vector<evaluate::StructureConstructor> bounds;
     evaluate::NamedEntity entity{symbol};
@@ -929,7 +934,8 @@ evaluate::StructureConstructor RuntimeTableBuilder::DescribeComponent(
   evaluate::StructureConstructorValues values;
   AddValue(values, procPtrSchema_, "name"s,
       SaveNameAsPointerTarget(scope, symbol.name().ToString()));
-  AddValue(values, procPtrSchema_, "offset"s, IntExpr(8, symbol.offset()));
+  AddValue(values, procPtrSchema_, "offset"s,
+      IntExpr(KindsEnum::Kind8, symbol.offset()));
   if (auto init{proc.init()}; init && *init) {
     AddValue(values, procPtrSchema_, "initialization"s,
         SomeExpr{evaluate::ProcedureDesignator{**init}});
@@ -1204,7 +1210,7 @@ void RuntimeTableBuilder::DescribeSpecialProc(
         } else {
           which = scalarFinalEnum_;
           if (int rank{typeAndShape.Rank()}; rank > 0) {
-            which = IntExpr(1, ToInt64(which).value() + rank);
+            which = IntExpr(KindsEnum::Kind1, ToInt64(which).value() + rank);
             if (dummyData.IsPassedByDescriptor(proc->IsBindC())) {
               argThatMightBeDescriptor = 1;
             }
@@ -1247,7 +1253,8 @@ void RuntimeTableBuilder::DescribeSpecialProc(
         which = writeUnformattedEnum_;
         break;
       }
-      if (context_.defaultKinds().GetDefaultKind(TypeCategory::Integer) == 8) {
+      if (context_.defaultKinds().GetDefaultKind(TypeCategory::Integer) ==
+          KindsEnum::Kind8) {
         specialCaseFlag = true; // UNIT= & IOSTAT= INTEGER(8)
       }
     }
@@ -1266,7 +1273,7 @@ void RuntimeTableBuilder::DescribeSpecialProc(
     AddValue(
         values, specialSchema_, "which"s, SomeExpr{std::move(which.value())});
     AddValue(values, specialSchema_, "isargdescriptorset"s,
-        IntExpr(1, isArgDescriptorSet));
+        IntExpr(KindsEnum::Kind1, isArgDescriptorSet));
     int bindingIndex{0};
     if (bindings) {
       int j{0};
@@ -1279,9 +1286,10 @@ void RuntimeTableBuilder::DescribeSpecialProc(
       }
     }
     CHECK(bindingIndex <= 255);
-    AddValue(values, specialSchema_, "istypebound"s, IntExpr(1, bindingIndex));
+    AddValue(values, specialSchema_, "istypebound"s,
+        IntExpr(KindsEnum::Kind1, bindingIndex));
     AddValue(values, specialSchema_, "specialcaseflag"s,
-        IntExpr(1, specialCaseFlag));
+        IntExpr(KindsEnum::Kind1, specialCaseFlag));
     AddValue(values, specialSchema_, procCompName,
         SomeExpr{evaluate::ProcedureDesignator{specific}});
     // index might already be present in the case of an override
@@ -1431,7 +1439,7 @@ CollectNonTbpDefinedIoGenericInterfaces(
                   flags |= IsDtvArgPolymorphic;
                 }
                 if (scope.context().GetDefaultKind(TypeCategory::Integer) ==
-                    8) {
+                    KindsEnum::Kind8) {
                   flags |= DefinedIoInteger8;
                 }
                 for (auto [iter, end]{result.equal_range(dtDesc)}; iter != end;
diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp
index 1ccbf79750be2..439403c688251 100644
--- a/flang/lib/Semantics/semantics.cpp
+++ b/flang/lib/Semantics/semantics.cpp
@@ -397,19 +397,19 @@ SemanticsContext::SemanticsContext(
 
 SemanticsContext::~SemanticsContext() {}
 
-int SemanticsContext::GetDefaultKind(TypeCategory category) const {
+KindsEnum SemanticsContext::GetDefaultKind(TypeCategory category) const {
   return defaultKinds_.GetDefaultKind(category);
 }
 
 const DeclTypeSpec &SemanticsContext::MakeNumericType(
-    TypeCategory category, int kind) {
-  if (kind == 0) {
+    TypeCategory category, KindsEnum kind) {
+  if (kind == KindsEnum::NoKind) {
     kind = GetDefaultKind(category);
   }
   return globalScope_.MakeNumericType(category, MakeKindExpr(kind));
 }
-const DeclTypeSpec &SemanticsContext::MakeLogicalType(int kind) {
-  if (kind == 0) {
+const DeclTypeSpec &SemanticsContext::MakeLogicalType(KindsEnum kind) {
+  if (kind == KindsEnum::NoKind) {
     kind = GetDefaultKind(TypeCategory::Logical);
   }
   return globalScope_.MakeLogicalType(MakeKindExpr(kind));
diff --git a/flang/lib/Semantics/type.cpp b/flang/lib/Semantics/type.cpp
index d8bafc2d26aa6..8e062dc7ba877 100644
--- a/flang/lib/Semantics/type.cpp
+++ b/flang/lib/Semantics/type.cpp
@@ -23,8 +23,6 @@
 
 namespace Fortran::semantics {
 
-KindExpr MakeKindExpr(int v) { return evaluate::MakeSubscriptIntExpr(v); }
-
 DerivedTypeSpec::DerivedTypeSpec(SourceName name, const Symbol &typeSymbol)
     : name_{name}, originalTypeSymbol_{typeSymbol},
       typeSymbol_{typeSymbol.GetUltimate()} {
@@ -116,7 +114,7 @@ void DerivedTypeSpec::EvaluateParameters(SemanticsContext &context) {
   auto &messages{foldingContext.messages()};
   for (const Symbol &symbol : OrderParameterDeclarations(typeSymbol_)) {
     SourceName name{symbol.name()};
-    int parameterKind{evaluate::TypeParamInquiry::ResultKind};
+    KindsEnum parameterKind{evaluate::TypeParamInquiry::ResultKind};
     // Compute the integer kind value of the type parameter,
     // which may depend on the values of earlier ones.
     if (const auto *typeSpec{symbol.GetType()}) {
@@ -125,8 +123,9 @@ void DerivedTypeSpec::EvaluateParameters(SemanticsContext &context) {
         auto restorer{foldingContext.WithPDTInstance(*this)};
         auto folded{Fold(foldingContext, KindExpr{intrinType->kind()})};
         if (auto k{evaluate::ToInt64(folded)}; k &&
-            common::IsValidKindOfIntrinsicType(TypeCategory::Integer, *k)) {
-          parameterKind = static_cast<int>(*k);
+            common::IsValidKindOfIntrinsicType(
+                TypeCategory::Integer, static_cast<KindsEnum>(*k))) {
+          parameterKind = static_cast<KindsEnum>(*k);
         } else {
           messages.Say(
               "Type of type parameter '%s' (%s) is not a valid kind of INTEGER"_err_en_US,
@@ -672,11 +671,11 @@ const DeclTypeSpec &InstantiateHelper::InstantiateIntrinsicType(
     CHECK(kindExpr.has_value());
   }
   KindExpr folded{Fold(std::move(*kindExpr))};
-  int kind{context().GetDefaultKind(intrinsic.category())};
+  KindsEnum kind{context().GetDefaultKind(intrinsic.category())};
   if (auto value{evaluate::ToInt64(folded)}) {
     if (foldingContext().targetCharacteristics().IsTypeEnabled(
-            intrinsic.category(), *value)) {
-      kind = *value;
+            intrinsic.category(), static_cast<KindsEnum>(*value))) {
+      kind = static_cast<KindsEnum>(*value);
     } else {
       foldingContext().messages().Say(symbolName,
           "KIND parameter value (%jd) of intrinsic type %s did not resolve to a supported value"_err_en_US,
diff --git a/flang/lib/Support/default-kinds.cpp b/flang/lib/Support/default-kinds.cpp
index b763f8ec19418..4d7b1afd138b3 100644
--- a/flang/lib/Support/default-kinds.cpp
+++ b/flang/lib/Support/default-kinds.cpp
@@ -14,54 +14,55 @@ namespace Fortran::common {
 IntrinsicTypeDefaultKinds::IntrinsicTypeDefaultKinds() {}
 
 IntrinsicTypeDefaultKinds &IntrinsicTypeDefaultKinds::set_defaultIntegerKind(
-    int k) {
+    KindsEnum k) {
   defaultIntegerKind_ = k;
   return *this;
 }
 
 IntrinsicTypeDefaultKinds &IntrinsicTypeDefaultKinds::set_subscriptIntegerKind(
-    int k) {
+    KindsEnum k) {
   subscriptIntegerKind_ = k;
   return *this;
 }
 
 IntrinsicTypeDefaultKinds &IntrinsicTypeDefaultKinds::set_sizeIntegerKind(
-    int k) {
+    KindsEnum k) {
   sizeIntegerKind_ = k;
   return *this;
 }
 
 IntrinsicTypeDefaultKinds &IntrinsicTypeDefaultKinds::set_defaultRealKind(
-    int k) {
+    KindsEnum k) {
   defaultRealKind_ = k;
   return *this;
 }
 
 IntrinsicTypeDefaultKinds &IntrinsicTypeDefaultKinds::set_doublePrecisionKind(
-    int k) {
+    KindsEnum k) {
   doublePrecisionKind_ = k;
   return *this;
 }
 
 IntrinsicTypeDefaultKinds &IntrinsicTypeDefaultKinds::set_quadPrecisionKind(
-    int k) {
+    KindsEnum k) {
   quadPrecisionKind_ = k;
   return *this;
 }
 
 IntrinsicTypeDefaultKinds &IntrinsicTypeDefaultKinds::set_defaultCharacterKind(
-    int k) {
+    KindsEnum k) {
   defaultCharacterKind_ = k;
   return *this;
 }
 
 IntrinsicTypeDefaultKinds &IntrinsicTypeDefaultKinds::set_defaultLogicalKind(
-    int k) {
+    KindsEnum k) {
   defaultLogicalKind_ = k;
   return *this;
 }
 
-int IntrinsicTypeDefaultKinds::GetDefaultKind(TypeCategory category) const {
+KindsEnum IntrinsicTypeDefaultKinds::GetDefaultKind(
+    TypeCategory category) const {
   switch (category) {
   case TypeCategory::Integer:
   case TypeCategory::Unsigned:
@@ -75,7 +76,7 @@ int IntrinsicTypeDefaultKinds::GetDefaultKind(TypeCategory category) const {
     return defaultLogicalKind_;
   default:
     CRASH_NO_CASE;
-    return 0;
+    return KindsEnum::NoKind;
   }
 }
 } // namespace Fortran::common
diff --git a/flang/tools/f18-parse-demo/f18-parse-demo.cpp b/flang/tools/f18-parse-demo/f18-parse-demo.cpp
index 07280da15d7d9..7a68932fddbdb 100644
--- a/flang/tools/f18-parse-demo/f18-parse-demo.cpp
+++ b/flang/tools/f18-parse-demo/f18-parse-demo.cpp
@@ -406,10 +406,10 @@ int main(int argc, char *const argv[]) {
       options.predefinitions.emplace_back(
           arg.substr(2), std::optional<std::string>{});
     } else if (arg == "-r8" || arg == "-fdefault-real-8") {
-      defaultKinds.set_defaultRealKind(8);
+      defaultKinds.set_defaultRealKind(Fortran::common::KindsEnum::Kind8);
     } else if (arg == "-i8" || arg == "-fdefault-integer-8") {
-      defaultKinds.set_defaultIntegerKind(8);
-      defaultKinds.set_defaultLogicalKind(8);
+      defaultKinds.set_defaultIntegerKind(Fortran::common::KindsEnum::Kind8);
+      defaultKinds.set_defaultLogicalKind(Fortran::common::KindsEnum::Kind8);
     } else if (arg == "-help" || arg == "--help" || arg == "-?") {
       llvm::errs()
           << "f18-parse-demo options:\n"
diff --git a/flang/unittests/Evaluate/CharacterValueTest.cpp b/flang/unittests/Evaluate/CharacterValueTest.cpp
index 803a3e106a80a..111ebafd519f2 100644
--- a/flang/unittests/Evaluate/CharacterValueTest.cpp
+++ b/flang/unittests/Evaluate/CharacterValueTest.cpp
@@ -37,11 +37,11 @@ struct KindName {
 template <typename T> class CharacterValueTypedKind : public testing::Test {};
 TYPED_TEST_SUITE(CharacterValueTypedKind, CharacterTypedKinds, KindName);
 
-class CharacterValueKind : public testing::TestWithParam<int> {};
+class CharacterValueKind : public testing::TestWithParam<KindsEnum> {};
 INSTANTIATE_TEST_SUITE_P(CharacterValueKind, CharacterValueKind,
     testing::ValuesIn(CharacterKinds),
-    [](const testing::TestParamInfo<int> &info) {
-      return "CHARACTER(" + std::to_string(info.param) + ")";
+    [](const testing::TestParamInfo<KindsEnum> &info) {
+      return "CHARACTER(" + std::to_string(static_cast<int>(info.param)) + ")";
     });
 
 //===----------------------------------------------------------------------===//
@@ -63,7 +63,7 @@ static testing::AssertionResult CharsEqual(const char *expectedExpr,
   EXPECT_PRED_FORMAT2(CharsEqual, expected, value)
 
 /// Writes one character of the value's own character type at "dst".
-static void PutChar(int kind, void *dst, char32_t c) {
+static void PutChar(KindsEnum kind, void *dst, char32_t c) {
   CharacterValue::withCharProto(kind, [=](auto proto) {
     using CharT = std::decay_t<decltype(proto)>;
     CharT raw{static_cast<CharT>(c)};
@@ -96,7 +96,7 @@ TEST(CharacterValue, Monostate) {
 TYPED_TEST(CharacterValueTypedKind, ConstructFromStdBasicString) {
   using CharT = typename TypeParam::CharT;
   using StringT = typename TypeParam::StringT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   CharT buffer[] = {'a', 'b', 'c', '\0'};
   CharacterValue v{kind, StringT{buffer}};
@@ -106,7 +106,7 @@ TYPED_TEST(CharacterValueTypedKind, ConstructFromStdBasicString) {
 }
 
 TEST_P(CharacterValueKind, Zero) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue zero{CharacterValue::Zero(kind)};
   CharacterValue empty{kind, ""};
   CharacterValue monostate;
@@ -121,7 +121,7 @@ TEST_P(CharacterValueKind, Zero) {
 }
 
 TEST_P(CharacterValueKind, FillConstructor) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   CharacterValue v(kind, 3, U'x');
   EXPECT_FALSE(v.IsMonostate());
@@ -137,18 +137,18 @@ TEST_P(CharacterValueKind, FillConstructor) {
 }
 
 TEST(CharacterValue, SubscriptWidensToChar32) {
-  CharacterValue u{1, std::string{"\x80"}};
+  CharacterValue u{KindsEnum::Kind1, std::string{"\x80"}};
   EXPECT_EQ(char32_t('\x80'), u[0]);
 
-  CharacterValue w{2, std::u16string{u"\u0100"}};
+  CharacterValue w{KindsEnum::Kind2, std::u16string{u"\u0100"}};
   EXPECT_EQ(char32_t{u'\u0100'}, w[0]);
 
-  CharacterValue v{4, std::u32string{U"\U0001F600"}};
+  CharacterValue v{KindsEnum::Kind4, std::u32string{U"\U0001F600"}};
   EXPECT_EQ(U'\U0001F600', v[0]);
 }
 
 TEST_P(CharacterValueKind, CopyAndMove) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abc"};
 
   CharacterValue copyConstructed{v};
@@ -171,7 +171,7 @@ TEST_P(CharacterValueKind, CopyAndMove) {
 }
 
 TEST_P(CharacterValueKind, CharSize) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   CharacterValue v{kind, "abcd"};
   EXPECT_EQ(kind, v.kind());
@@ -179,7 +179,7 @@ TEST_P(CharacterValueKind, CharSize) {
 }
 
 TEST_P(CharacterValueKind, SizeAndLength) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   CharacterValue v{kind, "hello"};
   EXPECT_FALSE(v.empty());
@@ -192,26 +192,28 @@ TEST_P(CharacterValueKind, SizeAndLength) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(CharacterValueKind, AsStringConversions) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abc"};
 
   // Only the conversion matching the stored character type is available.
-  EXPECT_EQ(kind == 1, v.AsStringRef().has_value());
-  EXPECT_EQ(kind == 1, v.AsStdString().has_value());
-  EXPECT_EQ(kind == 2, v.AsU16String().has_value());
-  EXPECT_EQ(kind == 4, v.AsU32String().has_value());
+  EXPECT_EQ(kind == KindsEnum::Kind1, v.AsStringRef().has_value());
+  EXPECT_EQ(kind == KindsEnum::Kind1, v.AsStdString().has_value());
+  EXPECT_EQ(kind == KindsEnum::Kind2, v.AsU16String().has_value());
+  EXPECT_EQ(kind == KindsEnum::Kind4, v.AsU32String().has_value());
 
   switch (kind) {
-  case 1:
+  case KindsEnum::Kind1:
     EXPECT_EQ("abc", *v.AsStringRef());
     EXPECT_EQ("abc", *v.AsStdString());
     break;
-  case 2:
+  case KindsEnum::Kind2:
     EXPECT_EQ(std::u16string{u"abc"}, *v.AsU16String());
     break;
-  case 4:
+  case KindsEnum::Kind4:
     EXPECT_EQ(std::u32string{U"abc"}, *v.AsU32String());
     break;
+  default:
+    break;
   }
   EXPECT_EQ("abc", v.ToStdString());
 }
@@ -219,7 +221,7 @@ TEST_P(CharacterValueKind, AsStringConversions) {
 TYPED_TEST(CharacterValueTypedKind, ToBasicString) {
   using CharT = typename TypeParam::CharT;
   using StringT = typename TypeParam::StringT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   const CharT data[] = {'a', 'b', 'c', '\0'};
   CharacterValue v1{kind, data};
@@ -227,7 +229,7 @@ TYPED_TEST(CharacterValueTypedKind, ToBasicString) {
 }
 
 TEST_P(CharacterValueKind, WithStdString) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abcde"};
 
   // The callable sees the concrete std::basic_string<> for the stored kind.
@@ -238,11 +240,11 @@ TEST_P(CharacterValueKind, WithStdString) {
 }
 
 TEST_P(CharacterValueKind, ToAscii) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // conversion to possible kinds
   CharacterValue v{kind, "abc"};
-  for (int to : std::initializer_list<int> FORTRAN_CHARACTER_KINDS) {
+  for (KindsEnum to : CharacterKinds) {
     CharacterValue converted{v.ToAscii(to)};
     EXPECT_EQ(to, converted.kind());
     EXPECT_CHARS_EQ("abc", converted);
@@ -250,7 +252,7 @@ TEST_P(CharacterValueKind, ToAscii) {
 
   // Conversion between kinds is defined only for 7-bit ASCII; anything else
   // yields an empty string.
-  CharacterValue nonascii{4, std::u32string{U"a\u0100b"}};
+  CharacterValue nonascii{KindsEnum::Kind4, std::u32string{U"a\u0100b"}};
   EXPECT_TRUE(nonascii.ToAscii(kind).empty());
   EXPECT_EQ(kind, nonascii.ToAscii(kind).kind());
 
@@ -265,7 +267,7 @@ TEST_P(CharacterValueKind, ToAscii) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(CharacterValueKind, Compare) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   CharacterValue abc{kind, "abc"};
   CharacterValue abd{kind, "abd"};
@@ -292,7 +294,7 @@ TEST_P(CharacterValueKind, Compare) {
 }
 
 TEST_P(CharacterValueKind, RelationalOperators) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue abc{kind, "abc"};
   CharacterValue abd{kind, "abd"};
 
@@ -326,7 +328,7 @@ TEST_P(CharacterValueKind, RelationalOperators) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(CharacterValueKind, AssignFill) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abc"};
 
   v.assign(kind, 2, 'z');
@@ -345,22 +347,22 @@ TEST_P(CharacterValueKind, AssignFromPointerAndLength) {
 
   // char
   v.assign("abcd", 3);
-  EXPECT_EQ(1, v.kind());
+  EXPECT_EQ(KindsEnum::Kind1, v.kind());
   EXPECT_CHARS_EQ("abc", v);
 
   // char16_t
   v.assign(u"abcd", 2);
-  EXPECT_EQ(2, v.kind());
+  EXPECT_EQ(KindsEnum::Kind2, v.kind());
   EXPECT_CHARS_EQ("ab", v);
 
   // char32_t
   v.assign(U"abcd", 4);
-  EXPECT_EQ(4, v.kind());
+  EXPECT_EQ(KindsEnum::Kind4, v.kind());
   EXPECT_CHARS_EQ("abcd", v);
 }
 
 TEST_P(CharacterValueKind, Erase) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abcdef"};
 
   v.erase(3);
@@ -373,7 +375,7 @@ TEST_P(CharacterValueKind, Erase) {
 }
 
 TEST_P(CharacterValueKind, Append) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "ab"};
 
   v.append(3, '!');
@@ -384,7 +386,7 @@ TEST_P(CharacterValueKind, Append) {
 }
 
 TEST_P(CharacterValueKind, Replace) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abcdef"};
 
   CharacterValue xy{kind, "XY"};
@@ -398,7 +400,7 @@ TEST_P(CharacterValueKind, Replace) {
 }
 
 TEST_P(CharacterValueKind, Substr) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abcdef"};
 
   EXPECT_CHARS_EQ("cdef", v.substr(2));
@@ -414,7 +416,7 @@ TEST_P(CharacterValueKind, Substr) {
 }
 
 TEST_P(CharacterValueKind, Reserve) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abc"};
 
   // Reserving capacity does not change the value.
@@ -425,7 +427,7 @@ TEST_P(CharacterValueKind, Reserve) {
 }
 
 TEST_P(CharacterValueKind, Subscript) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abc"};
   EXPECT_EQ(U'a', v[0]);
   EXPECT_EQ(U'b', v[1]);
@@ -433,7 +435,7 @@ TEST_P(CharacterValueKind, Subscript) {
 }
 
 TEST_P(CharacterValueKind, Concatenation) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue ab{kind, "ab"};
   CharacterValue cd{kind, "cd"};
   CharacterValue empty = CharacterValue::Zero(kind);
@@ -447,7 +449,7 @@ TEST_P(CharacterValueKind, Concatenation) {
 }
 
 TEST_P(CharacterValueKind, AppendAssignString) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   CharacterValue v{kind, "ab"};
   CharacterValue cd{kind, "cd"};
@@ -456,7 +458,7 @@ TEST_P(CharacterValueKind, AppendAssignString) {
 }
 
 TEST_P(CharacterValueKind, AppendAssignChar) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   CharacterValue v{kind, "ab"};
   EXPECT_EQ(kind, v.kind());
@@ -473,7 +475,7 @@ TEST(CharacterValue, Npos) {
 }
 
 TEST_P(CharacterValueKind, Find) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue abcabc{kind, "abcabc"};
   CharacterValue bc{kind, "bc"};
   CharacterValue abc{kind, "abc"};
@@ -498,7 +500,7 @@ TEST_P(CharacterValueKind, Find) {
 }
 
 TEST_P(CharacterValueKind, RFind) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "abcabc"};
   CharacterValue bc{kind, "bc"};
   CharacterValue abc{kind, "abc"};
@@ -510,7 +512,7 @@ TEST_P(CharacterValueKind, RFind) {
 }
 
 TEST_P(CharacterValueKind, FindFirstOf) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "hello"};
   CharacterValue le{kind, "le"};
   CharacterValue h{kind, "he"};
@@ -524,7 +526,7 @@ TEST_P(CharacterValueKind, FindFirstOf) {
 }
 
 TEST_P(CharacterValueKind, FindLastOf) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "hello"};
   CharacterValue le{kind, "le"};
   CharacterValue o{kind, "o"};
@@ -536,7 +538,7 @@ TEST_P(CharacterValueKind, FindLastOf) {
 }
 
 TEST_P(CharacterValueKind, FindFirstNotOfCharacter) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue aab{kind, "aab"};
   CharacterValue aaa{kind, "aaa"};
 
@@ -546,7 +548,7 @@ TEST_P(CharacterValueKind, FindFirstNotOfCharacter) {
 }
 
 TEST_P(CharacterValueKind, FindLastNotOfCharacter) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue abb{kind, "abb"};
   CharacterValue bbb{kind, "bbb"};
 
@@ -556,7 +558,7 @@ TEST_P(CharacterValueKind, FindLastNotOfCharacter) {
 }
 
 TEST_P(CharacterValueKind, FindFirstNotOfSet) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "aabbc"};
   CharacterValue ab{kind, "ab"};
   CharacterValue abc{kind, "abc"};
@@ -573,7 +575,7 @@ TEST_P(CharacterValueKind, FindFirstNotOfSet) {
 }
 
 TEST_P(CharacterValueKind, FindLastNotOfSet) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue v{kind, "aabbc"};
   CharacterValue abc{kind, "abc"};
   CharacterValue bc{kind, "bc"};
@@ -594,13 +596,13 @@ TEST_P(CharacterValueKind, FindLastNotOfSet) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(CharacterValueKind, Data) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   CharacterValue abc{kind, "abc"};
   CharacterValue same{abc};
   CharacterValue v{kind, "abc"};
   const CharacterValue &constRef{v};
 
-  ASSERT_EQ(v.bytesStored(), std::size_t(3 * kind));
+  ASSERT_EQ(v.bytesStored(), std::size_t(3) * static_cast<std::size_t>(kind));
   ASSERT_EQ(same.bytesStored(), v.bytesStored());
   EXPECT_EQ(v.data(), static_cast<void *>(v.charData()));
   EXPECT_EQ(constRef.data(), static_cast<const void *>(constRef.charData()));
@@ -612,7 +614,7 @@ TEST_P(CharacterValueKind, Data) {
 }
 
 TYPED_TEST(CharacterValueTypedKind, At) {
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   CharacterValue v{kind, "abc"};
   const CharacterValue &constRef{v};
 
@@ -629,7 +631,7 @@ TYPED_TEST(CharacterValueTypedKind, At) {
 
 TYPED_TEST(CharacterValueTypedKind, StoreRawBytes) {
   using CharT = typename TypeParam::CharT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   CharacterValue v{kind, "abc"};
 
   CharT buffer[4]{};
@@ -671,7 +673,7 @@ TYPED_TEST(CharacterValueTypedKind, StoreRawBytes) {
 
 TYPED_TEST(CharacterValueTypedKind, FromRawBytes) {
   using CharT = typename TypeParam::CharT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   CharT data[] = {'a', 'b', 'c', '\0'};
   CharacterValue reference{kind, std::basic_string<CharT>(data)};
@@ -689,7 +691,7 @@ TYPED_TEST(CharacterValueTypedKind, FromRawBytes) {
 
 TYPED_TEST(CharacterValueTypedKind, Print) {
   using CharT = typename TypeParam::CharT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int pos{CharacterKindPos<TypeParam>};
 
   llvm::SmallString<128> buf;
diff --git a/flang/unittests/Evaluate/ComplexValueTest.cpp b/flang/unittests/Evaluate/ComplexValueTest.cpp
index 93a882f551d4a..e3b166c8dc143 100644
--- a/flang/unittests/Evaluate/ComplexValueTest.cpp
+++ b/flang/unittests/Evaluate/ComplexValueTest.cpp
@@ -18,17 +18,18 @@ using namespace Fortran::evaluate::value;
 
 namespace {
 
-class ComplexValueKind : public testing::TestWithParam<int> {};
+class ComplexValueKind : public testing::TestWithParam<KindsEnum> {};
 INSTANTIATE_TEST_SUITE_P(ComplexValueKind, ComplexValueKind,
-    testing::ValuesIn(RealKinds), [](const testing::TestParamInfo<int> &info) {
-      return "COMPLEX(" + std::to_string(info.param) + ")";
+    testing::ValuesIn(RealKinds),
+    [](const testing::TestParamInfo<KindsEnum> &info) {
+      return "COMPLEX(" + std::to_string(static_cast<int>(info.param)) + ")";
     });
 
-RealValue Real(int kind, std::int64_t n) {
-  return RealValue::FromInteger(kind, IntegerValue{8, n}).value;
+RealValue Real(KindsEnum kind, std::int64_t n) {
+  return RealValue::FromInteger(kind, IntegerValue{KindsEnum::Kind8, n}).value;
 }
 
-ComplexValue Complex(int kind, std::int64_t re, std::int64_t im) {
+ComplexValue Complex(KindsEnum kind, std::int64_t re, std::int64_t im) {
   return ComplexValue{Real(kind, re), Real(kind, im)};
 }
 
@@ -45,14 +46,14 @@ testing::AssertionResult ComplexValuesEqual(const char *lhsExpr,
 #define EXPECT_COMPLEX_EQ(lhs, rhs) \
   EXPECT_PRED_FORMAT2(ComplexValuesEqual, lhs, rhs)
 
-std::string AsFortranString(const ComplexValue &z, int kind) {
+std::string AsFortranString(const ComplexValue &z, KindsEnum kind) {
   std::string s;
   llvm::raw_string_ostream os{s};
-  z.AsFortran(os, kind);
+  z.AsFortran(os, static_cast<int>(kind));
   return s;
 }
 
-constexpr int KindPos(int kind) {
+constexpr int KindPos(KindsEnum kind) {
   for (std::size_t i{0}; i < std::size(RealKinds); ++i) {
     if (RealKinds[i] == kind) {
       return static_cast<int>(i);
@@ -75,7 +76,7 @@ TEST(ComplexValue, DefaultConstructionIsMonostate) {
 }
 
 TEST_P(ComplexValueKind, ConstructFromParts) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   ComplexValue z{Real(kind, 1), Real(kind, 2)};
   EXPECT_FALSE(z.IsMonostate());
   EXPECT_EQ(kind, z.kind());
@@ -84,7 +85,7 @@ TEST_P(ComplexValueKind, ConstructFromParts) {
 }
 
 TEST_P(ComplexValueKind, ConstructFromRealPartOnly) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   ComplexValue z{Real(kind, 3)};
   EXPECT_EQ(kind, z.kind());
   EXPECT_TRUE(z.REAL() == Real(kind, 3));
@@ -94,15 +95,15 @@ TEST_P(ComplexValueKind, ConstructFromRealPartOnly) {
 }
 
 TEST_P(ComplexValueKind, ImaginaryPartIsConvertedToTheRealPartsKind) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   // The imaginary operand is converted to the kind of the real operand.
-  ComplexValue z{Real(kind, 1), Real(8, 2)};
+  ComplexValue z{Real(kind, 1), Real(KindsEnum::Kind8, 2)};
   EXPECT_EQ(kind, z.kind());
   EXPECT_TRUE(z.AIMAG() == Real(kind, 2));
 }
 
 TEST(ComplexValue, CopyAndMove) {
-  ComplexValue z{Complex(4, 1, 2)};
+  ComplexValue z{Complex(KindsEnum::Kind4, 1, 2)};
   ComplexValue copyConstructed{z};
   EXPECT_COMPLEX_EQ(z, copyConstructed);
   ComplexValue copyAssigned;
@@ -116,16 +117,16 @@ TEST(ComplexValue, CopyAndMove) {
 }
 
 TEST(ComplexValue, KindCheckingConstructors) {
-  ComplexValue z{Complex(4, 1, 2)};
-  EXPECT_EQ(4, ComplexValue(4, z).kind());
-  EXPECT_COMPLEX_EQ(z, ComplexValue(4, z));
-  ComplexValue y{Complex(8, 1, 2)};
-  ComplexValue moved{8, std::move(y)};
-  EXPECT_EQ(8, moved.kind());
+  ComplexValue z{Complex(KindsEnum::Kind4, 1, 2)};
+  EXPECT_EQ(KindsEnum::Kind4, ComplexValue(KindsEnum::Kind4, z).kind());
+  EXPECT_COMPLEX_EQ(z, ComplexValue(KindsEnum::Kind4, z));
+  ComplexValue y{Complex(KindsEnum::Kind8, 1, 2)};
+  ComplexValue moved{KindsEnum::Kind8, std::move(y)};
+  EXPECT_EQ(KindsEnum::Kind8, moved.kind());
 }
 
 TEST_P(ComplexValueKind, Zero) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   ComplexValue zero{ComplexValue::Zero(kind)};
   EXPECT_FALSE(zero.IsMonostate());
   EXPECT_EQ(kind, zero.kind());
@@ -135,13 +136,13 @@ TEST_P(ComplexValueKind, Zero) {
 }
 
 TEST(ComplexValue, BytesStored) {
-  EXPECT_EQ(4u, ComplexValue::bytesStored(2));
-  EXPECT_EQ(4u, ComplexValue::bytesStored(3));
-  EXPECT_EQ(8u, ComplexValue::bytesStored(4));
-  EXPECT_EQ(16u, ComplexValue::bytesStored(8));
-  EXPECT_EQ(32u, ComplexValue::bytesStored(10));
-  EXPECT_EQ(32u, ComplexValue::bytesStored(16));
-  EXPECT_EQ(8u, Complex(4, 1, 2).bytesStored());
+  EXPECT_EQ(4u, ComplexValue::bytesStored(KindsEnum::Kind2));
+  EXPECT_EQ(4u, ComplexValue::bytesStored(KindsEnum::Kind3));
+  EXPECT_EQ(8u, ComplexValue::bytesStored(KindsEnum::Kind4));
+  EXPECT_EQ(16u, ComplexValue::bytesStored(KindsEnum::Kind8));
+  EXPECT_EQ(32u, ComplexValue::bytesStored(KindsEnum::Kind10));
+  EXPECT_EQ(32u, ComplexValue::bytesStored(KindsEnum::Kind16));
+  EXPECT_EQ(8u, Complex(KindsEnum::Kind4, 1, 2).bytesStored());
 }
 
 //===----------------------------------------------------------------------===//
@@ -149,25 +150,25 @@ TEST(ComplexValue, BytesStored) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(ComplexValueKind, REAL) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_TRUE(Complex(kind, 1, 2).REAL() == Real(kind, 1));
   EXPECT_EQ(kind, Complex(kind, 1, 2).REAL().kind());
 }
 
 TEST_P(ComplexValueKind, AIMAG) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_TRUE(Complex(kind, 1, 2).AIMAG() == Real(kind, 2));
   EXPECT_EQ(kind, Complex(kind, 1, 2).AIMAG().kind());
 }
 
 TEST_P(ComplexValueKind, CONJG) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_COMPLEX_EQ(Complex(kind, 1, -2), Complex(kind, 1, 2).CONJG());
   EXPECT_COMPLEX_EQ(Complex(kind, 1, 2), Complex(kind, 1, 2).CONJG().CONJG());
 }
 
 TEST_P(ComplexValueKind, Negate) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_COMPLEX_EQ(Complex(kind, -1, -2), Complex(kind, 1, 2).Negate());
   // Negating a zero flips both sign bits.
   ComplexValue negZero{ComplexValue::Zero(kind).Negate()};
@@ -181,7 +182,7 @@ TEST_P(ComplexValueKind, Negate) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(ComplexValueKind, Equals) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   // Equals() compares numerically, so +0.0 and -0.0 are equal ...
   EXPECT_TRUE(
       ComplexValue::Zero(kind).Equals(ComplexValue::Zero(kind).Negate()));
@@ -193,7 +194,7 @@ TEST_P(ComplexValueKind, Equals) {
 }
 
 TEST_P(ComplexValueKind, EqualityOperators) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   // The operators compare bit patterns, so -0.0 differs from +0.0 ...
   EXPECT_FALSE(ComplexValue::Zero(kind) == ComplexValue::Zero(kind).Negate());
   EXPECT_TRUE(ComplexValue::Zero(kind) != ComplexValue::Zero(kind).Negate());
@@ -204,14 +205,14 @@ TEST_P(ComplexValueKind, EqualityOperators) {
 }
 
 TEST_P(ComplexValueKind, IsZero) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_TRUE(ComplexValue::Zero(kind).IsZero());
   EXPECT_FALSE(Complex(kind, 1, 0).IsZero());
   EXPECT_FALSE(Complex(kind, 0, 1).IsZero());
 }
 
 TEST_P(ComplexValueKind, IsInfinite) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   RealValue inf{Real(kind, 1).Divide(RealValue::Zero(kind)).value};
   ASSERT_TRUE(inf.IsInfinite());
   EXPECT_FALSE(ComplexValue::Zero(kind).IsInfinite());
@@ -221,7 +222,7 @@ TEST_P(ComplexValueKind, IsInfinite) {
 }
 
 TEST_P(ComplexValueKind, IsNotANumber) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   RealValue nan{RealValue::NotANumber(kind)};
   EXPECT_FALSE(ComplexValue::Zero(kind).IsNotANumber());
   EXPECT_TRUE(ComplexValue::NotANumber(kind).IsNotANumber());
@@ -231,14 +232,14 @@ TEST_P(ComplexValueKind, IsNotANumber) {
 }
 
 TEST_P(ComplexValueKind, IsSignalingNaN) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_FALSE(ComplexValue::Zero(kind).IsSignalingNaN());
   // NotANumber() produces quiet NaNs.
   EXPECT_FALSE(ComplexValue::NotANumber(kind).IsSignalingNaN());
 }
 
 TEST_P(ComplexValueKind, NotANumber) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   ComplexValue nan{ComplexValue::NotANumber(kind)};
   EXPECT_EQ(kind, nan.kind());
   EXPECT_TRUE(nan.REAL().IsNotANumber());
@@ -250,22 +251,23 @@ TEST_P(ComplexValueKind, NotANumber) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(ComplexValueKind, FromInteger) {
-  const int kind{GetParam()};
-  auto z{ComplexValue::FromInteger(kind, IntegerValue{8, 3})};
+  const KindsEnum kind{GetParam()};
+  auto z{ComplexValue::FromInteger(kind, IntegerValue{KindsEnum::Kind8, 3})};
   EXPECT_TRUE(z.flags.empty());
   EXPECT_EQ(kind, z.value.kind());
   EXPECT_COMPLEX_EQ(Complex(kind, 3, 0), z.value);
-  auto negative{ComplexValue::FromInteger(kind, IntegerValue{8, -3})};
+  auto negative{
+      ComplexValue::FromInteger(kind, IntegerValue{KindsEnum::Kind8, -3})};
   EXPECT_COMPLEX_EQ(Complex(kind, -3, 0), negative.value);
   // Reading the same bits as unsigned gives a large positive real part.
   auto asUnsigned{ComplexValue::FromInteger(
-      kind, IntegerValue{8, -1}, /*isUnsigned=*/true)};
+      kind, IntegerValue{KindsEnum::Kind8, -1}, /*isUnsigned=*/true)};
   EXPECT_FALSE(asUnsigned.value.REAL().IsNegative());
   EXPECT_TRUE(asUnsigned.value.AIMAG().IsZero());
 }
 
 TEST_P(ComplexValueKind, Add) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   auto sum{Complex(kind, 1, 2).Add(Complex(kind, 3, 4))};
   EXPECT_TRUE(sum.flags.empty());
   EXPECT_COMPLEX_EQ(Complex(kind, 4, 6), sum.value);
@@ -277,14 +279,14 @@ TEST_P(ComplexValueKind, Add) {
 }
 
 TEST_P(ComplexValueKind, Subtract) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   auto diff{Complex(kind, 1, 2).Subtract(Complex(kind, 3, 4))};
   EXPECT_TRUE(diff.flags.empty());
   EXPECT_COMPLEX_EQ(Complex(kind, -2, -2), diff.value);
 }
 
 TEST_P(ComplexValueKind, Multiply) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   // (1+2i)*(3+4i) = (3-8) + (4+6)i
   auto product{Complex(kind, 1, 2).Multiply(Complex(kind, 3, 4))};
   EXPECT_TRUE(product.flags.empty());
@@ -295,7 +297,7 @@ TEST_P(ComplexValueKind, Multiply) {
 }
 
 TEST_P(ComplexValueKind, Divide) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   // (-5+10i)/(3+4i) = 1+2i
   auto quotient{Complex(kind, -5, 10).Divide(Complex(kind, 3, 4))};
   EXPECT_COMPLEX_EQ(Complex(kind, 1, 2), quotient.value);
@@ -313,7 +315,7 @@ TEST_P(ComplexValueKind, Divide) {
 }
 
 TEST_P(ComplexValueKind, ABS) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   auto abs{Complex(kind, 3, 4).ABS()};
   EXPECT_TRUE(abs.value == Real(kind, 5));
   EXPECT_EQ(kind, abs.value.kind());
@@ -322,7 +324,7 @@ TEST_P(ComplexValueKind, ABS) {
 }
 
 TEST_P(ComplexValueKind, KahanSummation) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   ComplexValue correction{ComplexValue::Zero(kind)};
   auto sum{Complex(kind, 1, 2).KahanSummation(Complex(kind, 3, 4), correction)};
   EXPECT_COMPLEX_EQ(Complex(kind, 4, 6), sum.value);
@@ -337,7 +339,7 @@ TEST_P(ComplexValueKind, KahanSummation) {
 }
 
 TEST_P(ComplexValueKind, FlushSubnormalToZero) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   RealValue subnormal{RealValue{kind, IntegerValue{kind, 1}}};
   ASSERT_FALSE(subnormal.IsZero());
   ComplexValue z{subnormal, subnormal};
@@ -353,12 +355,14 @@ TEST_P(ComplexValueKind, FlushSubnormalToZero) {
 //===----------------------------------------------------------------------===//
 
 TEST(ComplexValue, DumpHexadecimal) {
-  EXPECT_EQ("(0.0,0.0)", ComplexValue::Zero(4).DumpHexadecimal());
-  EXPECT_EQ("(0x1.0p0,-0x1.0p1)", Complex(4, 1, -2).DumpHexadecimal());
+  EXPECT_EQ(
+      "(0.0,0.0)", ComplexValue::Zero(KindsEnum::Kind4).DumpHexadecimal());
+  EXPECT_EQ(
+      "(0x1.0p0,-0x1.0p1)", Complex(KindsEnum::Kind4, 1, -2).DumpHexadecimal());
 }
 
 TEST_P(ComplexValueKind, AsFortran) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   std::string s{AsFortranString(Complex(kind, 1, 2), kind)};
   // The components are emitted as a parenthesized, comma-separated pair.
   EXPECT_EQ('(', s.front());
@@ -367,7 +371,7 @@ TEST_P(ComplexValueKind, AsFortran) {
 }
 
 TEST_P(ComplexValueKind, RawBytesRoundTrip) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   ComplexValue original{Complex(kind, 1, -2)};
   char buffer[32]{};
   ASSERT_EQ(ComplexValue::bytesStored(kind), original.bytesStored());
@@ -384,7 +388,7 @@ TEST_P(ComplexValueKind, RawBytesRoundTrip) {
 }
 
 TEST_P(ComplexValueKind, Print) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   const int pos{KindPos(kind)};
 
   llvm::SmallString<128> buf;
diff --git a/flang/unittests/Evaluate/IntegerValueTest.cpp b/flang/unittests/Evaluate/IntegerValueTest.cpp
index 59b063dfa1686..e267132207344 100644
--- a/flang/unittests/Evaluate/IntegerValueTest.cpp
+++ b/flang/unittests/Evaluate/IntegerValueTest.cpp
@@ -50,11 +50,11 @@ struct KindName {
 template <typename T> class IntegerValueTypedKind : public testing::Test {};
 TYPED_TEST_SUITE(IntegerValueTypedKind, IntegerTypedKinds, KindName);
 
-class IntegerValueKind : public testing::TestWithParam<int> {};
+class IntegerValueKind : public testing::TestWithParam<KindsEnum> {};
 INSTANTIATE_TEST_SUITE_P(IntegerValueKind, IntegerValueKind,
     testing::ValuesIn(IntegerKinds),
-    [](const testing::TestParamInfo<int> &info) {
-      return "INTEGER(" + std::to_string(info.param) + ")";
+    [](const testing::TestParamInfo<KindsEnum> &info) {
+      return "INTEGER(" + std::to_string(static_cast<int>(info.param)) + ")";
     });
 
 //===----------------------------------------------------------------------===//
@@ -78,7 +78,7 @@ TEST(IntegerValue, Monostate) {
 TYPED_TEST(IntegerValueTypedKind, ConstructFromIntegral) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue positive{kind, 42};
   EXPECT_EQ(42, positive.ToInt64());
@@ -102,7 +102,7 @@ TYPED_TEST(IntegerValueTypedKind, ConstructFromIntegral) {
 }
 
 TEST_P(IntegerValueKind, CopyAndMove) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   const IntegerValue x{IntegerValue::HUGE(kind)};
 
   IntegerValue copyConstructed{x};
@@ -127,7 +127,7 @@ TEST_P(IntegerValueKind, CopyAndMove) {
 TYPED_TEST(IntegerValueTypedKind, KindCheckingConstructors) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue x{kind, 7};
   IntegerValue copied{kind, x};
@@ -143,7 +143,7 @@ TYPED_TEST(IntegerValueTypedKind, KindCheckingConstructors) {
 
 TYPED_TEST(IntegerValueTypedKind, Zero) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(kind, zero.kind());
@@ -153,50 +153,51 @@ TYPED_TEST(IntegerValueTypedKind, Zero) {
 }
 
 TEST(IntegerValue, Bits) {
-  EXPECT_EQ(8, IntegerValue::bits(1));
-  EXPECT_EQ(16, IntegerValue::bits(2));
-  EXPECT_EQ(16, IntegerValue::bits(3));
-  EXPECT_EQ(32, IntegerValue::bits(4));
-  EXPECT_EQ(64, IntegerValue::bits(8));
-  EXPECT_EQ(128, IntegerValue::bits(10)); // 80 significant bits, 128 stored
-  EXPECT_EQ(128, IntegerValue::bits(16));
-
-  IntegerValue v{4, 0};
+  EXPECT_EQ(8, IntegerValue::bits(KindsEnum::Kind1));
+  EXPECT_EQ(16, IntegerValue::bits(KindsEnum::Kind2));
+  EXPECT_EQ(16, IntegerValue::bits(KindsEnum::Kind3));
+  EXPECT_EQ(32, IntegerValue::bits(KindsEnum::Kind4));
+  EXPECT_EQ(64, IntegerValue::bits(KindsEnum::Kind8));
+  EXPECT_EQ(128,
+      IntegerValue::bits(KindsEnum::Kind10)); // 80 significant bits, 128 stored
+  EXPECT_EQ(128, IntegerValue::bits(KindsEnum::Kind16));
+
+  IntegerValue v{KindsEnum::Kind4, 0};
   EXPECT_EQ(32, v.bits());
 }
 
 TEST(IntegerValue, BytesStored) {
-  EXPECT_EQ(1u, IntegerValue::bytesStored(1));
-  EXPECT_EQ(2u, IntegerValue::bytesStored(2));
-  EXPECT_EQ(2u, IntegerValue::bytesStored(3));
-  EXPECT_EQ(4u, IntegerValue::bytesStored(4));
-  EXPECT_EQ(8u, IntegerValue::bytesStored(8));
-  EXPECT_EQ(16u, IntegerValue::bytesStored(10));
-  EXPECT_EQ(16u, IntegerValue::bytesStored(16));
-
-  IntegerValue v{4, 0};
+  EXPECT_EQ(1u, IntegerValue::bytesStored(KindsEnum::Kind1));
+  EXPECT_EQ(2u, IntegerValue::bytesStored(KindsEnum::Kind2));
+  EXPECT_EQ(2u, IntegerValue::bytesStored(KindsEnum::Kind3));
+  EXPECT_EQ(4u, IntegerValue::bytesStored(KindsEnum::Kind4));
+  EXPECT_EQ(8u, IntegerValue::bytesStored(KindsEnum::Kind8));
+  EXPECT_EQ(16u, IntegerValue::bytesStored(KindsEnum::Kind10));
+  EXPECT_EQ(16u, IntegerValue::bytesStored(KindsEnum::Kind16));
+
+  IntegerValue v{KindsEnum::Kind4, 0};
   EXPECT_EQ(4u, v.bytesStored());
 }
 
 TYPED_TEST(IntegerValueTypedKind, DIGITS) {
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   EXPECT_EQ(TypeParam::bits - 1, IntegerValue::DIGITS(kind));
 }
 
 TEST(IntegerValue, RANGE) {
-  EXPECT_EQ(2, IntegerValue::RANGE(1));
-  EXPECT_EQ(4, IntegerValue::RANGE(2));
-  EXPECT_EQ(9, IntegerValue::RANGE(4));
-  EXPECT_EQ(18, IntegerValue::RANGE(8));
-  EXPECT_EQ(38, IntegerValue::RANGE(16));
+  EXPECT_EQ(2, IntegerValue::RANGE(KindsEnum::Kind1));
+  EXPECT_EQ(4, IntegerValue::RANGE(KindsEnum::Kind2));
+  EXPECT_EQ(9, IntegerValue::RANGE(KindsEnum::Kind4));
+  EXPECT_EQ(18, IntegerValue::RANGE(KindsEnum::Kind8));
+  EXPECT_EQ(38, IntegerValue::RANGE(KindsEnum::Kind16));
 }
 
 TEST(IntegerValue, UnsignedRANGE) {
-  EXPECT_EQ(2, IntegerValue::UnsignedRANGE(1));
-  EXPECT_EQ(4, IntegerValue::UnsignedRANGE(2));
-  EXPECT_EQ(9, IntegerValue::UnsignedRANGE(4));
-  EXPECT_EQ(19, IntegerValue::UnsignedRANGE(8));
-  EXPECT_EQ(38, IntegerValue::UnsignedRANGE(16));
+  EXPECT_EQ(2, IntegerValue::UnsignedRANGE(KindsEnum::Kind1));
+  EXPECT_EQ(4, IntegerValue::UnsignedRANGE(KindsEnum::Kind2));
+  EXPECT_EQ(9, IntegerValue::UnsignedRANGE(KindsEnum::Kind4));
+  EXPECT_EQ(19, IntegerValue::UnsignedRANGE(KindsEnum::Kind8));
+  EXPECT_EQ(38, IntegerValue::UnsignedRANGE(KindsEnum::Kind16));
 }
 
 //===----------------------------------------------------------------------===//
@@ -205,9 +206,9 @@ TEST(IntegerValue, UnsignedRANGE) {
 
 TYPED_TEST(IntegerValueTypedKind, UnsignedDecimal) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ("0", zero.UnsignedDecimal());
 
   IntegerValue one{kind, 1};
@@ -250,9 +251,9 @@ TYPED_TEST(IntegerValueTypedKind, UnsignedDecimal) {
 
 TYPED_TEST(IntegerValueTypedKind, SignedDecimal) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ("0", zero.SignedDecimal());
 
   IntegerValue one{kind, 1};
@@ -297,9 +298,9 @@ TYPED_TEST(IntegerValueTypedKind, SignedDecimal) {
 
 TYPED_TEST(IntegerValueTypedKind, Hexadecimal) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ("0", zero.Hexadecimal());
 
   IntegerValue one{kind, 1};
@@ -342,7 +343,7 @@ TYPED_TEST(IntegerValueTypedKind, Hexadecimal) {
 TYPED_TEST(IntegerValueTypedKind, Read) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   {
     // Leading blanks are skipped and trailing text is left for the caller.
@@ -404,7 +405,7 @@ TYPED_TEST(IntegerValueTypedKind, Read) {
 
 TYPED_TEST(IntegerValueTypedKind, MASKL) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
   static constexpr UnsignedT nobits{0};
   static constexpr UnsignedT allbits{UnsignedT(~UnsignedT(0))};
@@ -442,7 +443,7 @@ TYPED_TEST(IntegerValueTypedKind, MASKL) {
 
 TYPED_TEST(IntegerValueTypedKind, MASKR) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
   static constexpr UnsignedT nobits{0};
   static constexpr UnsignedT allbits{UnsignedT(~UnsignedT(0))};
@@ -473,7 +474,7 @@ TYPED_TEST(IntegerValueTypedKind, MASKR) {
 
 TYPED_TEST(IntegerValueTypedKind, HUGE) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue huge{IntegerValue::HUGE(kind)};
   EXPECT_EQ(kind, huge.kind());
@@ -482,7 +483,7 @@ TYPED_TEST(IntegerValueTypedKind, HUGE) {
 
 TYPED_TEST(IntegerValueTypedKind, Least) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue least{IntegerValue::Least(kind)};
   EXPECT_EQ(kind, least.kind());
@@ -496,9 +497,9 @@ TYPED_TEST(IntegerValueTypedKind, Least) {
 TYPED_TEST(IntegerValueTypedKind, IsZero) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_TRUE(zero.IsZero());
 
   IntegerValue one{kind, 1};
@@ -532,9 +533,9 @@ TYPED_TEST(IntegerValueTypedKind, IsZero) {
 TYPED_TEST(IntegerValueTypedKind, IsNegative) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_FALSE(zero.IsNegative());
 
   IntegerValue one{kind, 1};
@@ -568,9 +569,9 @@ TYPED_TEST(IntegerValueTypedKind, IsNegative) {
 TYPED_TEST(IntegerValueTypedKind, CompareToZeroSigned) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(Ordering::Equal, zero.CompareToZeroSigned());
 
   IntegerValue one{kind, 1};
@@ -604,10 +605,10 @@ TYPED_TEST(IntegerValueTypedKind, CompareToZeroSigned) {
 TYPED_TEST(IntegerValueTypedKind, LEADZ) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(bits, zero.LEADZ());
 
   IntegerValue one{kind, 1};
@@ -632,7 +633,7 @@ TYPED_TEST(IntegerValueTypedKind, LEADZ) {
   EXPECT_EQ(0, sminv.LEADZ());
 
   IntegerValue patternv{kind, 0x7FFFFFFF7FFF7F7Full};
-  EXPECT_EQ((kind == 16) ? 65 : 1, patternv.LEADZ());
+  EXPECT_EQ((kind == KindsEnum::Kind16) ? 65 : 1, patternv.LEADZ());
 
   IntegerValue invpatternv{kind, ~UnsignedT(0x7FFFFFFF7FFF7F7Full)};
   EXPECT_EQ(0, invpatternv.LEADZ());
@@ -641,10 +642,10 @@ TYPED_TEST(IntegerValueTypedKind, LEADZ) {
 TYPED_TEST(IntegerValueTypedKind, POPCNT) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(0, zero.POPCNT());
 
   IntegerValue one{kind, 1};
@@ -670,19 +671,21 @@ TYPED_TEST(IntegerValueTypedKind, POPCNT) {
 
   IntegerValue patternv{kind, 0x7FFFFFFF7FFF7F7Full};
   const int kindPos{IntKindPos<TypeParam>};
-  EXPECT_EQ((kind == 16) ? 60 : bits - kindPos - 1, patternv.POPCNT());
+  EXPECT_EQ(
+      (kind == KindsEnum::Kind16) ? 60 : bits - kindPos - 1, patternv.POPCNT());
 
   IntegerValue invpatternv{kind, ~UnsignedT(0x7FFFFFFF7FFF7F7Full)};
-  EXPECT_EQ((kind == 16) ? 68 : 1 + kindPos, invpatternv.POPCNT());
+  EXPECT_EQ(
+      (kind == KindsEnum::Kind16) ? 68 : 1 + kindPos, invpatternv.POPCNT());
 }
 
 TYPED_TEST(IntegerValueTypedKind, POPPAR) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_FALSE(zero.POPPAR());
 
   IntegerValue one{kind, 1};
@@ -716,10 +719,10 @@ TYPED_TEST(IntegerValueTypedKind, POPPAR) {
 TYPED_TEST(IntegerValueTypedKind, TRAILZ) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
-  IntegerValue zero{IntegerValue::Zero(TypeParam::kind)};
+  IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(bits, zero.TRAILZ());
 
   IntegerValue one{kind, 1};
@@ -751,7 +754,7 @@ TYPED_TEST(IntegerValueTypedKind, TRAILZ) {
 }
 
 TYPED_TEST(IntegerValueTypedKind, BTEST) {
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
@@ -770,7 +773,7 @@ TYPED_TEST(IntegerValueTypedKind, BTEST) {
 
 TYPED_TEST(IntegerValueTypedKind, CompareUnsigned) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(Ordering::Equal, zero.CompareUnsigned(zero));
@@ -796,7 +799,7 @@ TYPED_TEST(IntegerValueTypedKind, CompareUnsigned) {
 
 TYPED_TEST(IntegerValueTypedKind, CompareSigned) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(Ordering::Equal, zero.CompareSigned(zero));
@@ -820,7 +823,7 @@ TYPED_TEST(IntegerValueTypedKind, CompareSigned) {
 
 TYPED_TEST(IntegerValueTypedKind, BitwiseComparisons) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   IntegerValue one{kind, 1};
@@ -851,7 +854,7 @@ TYPED_TEST(IntegerValueTypedKind, BitwiseComparisons) {
 
 TYPED_TEST(IntegerValueTypedKind, RelationalOperators) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_FALSE(zero < zero);
@@ -898,7 +901,7 @@ TYPED_TEST(IntegerValueTypedKind, RelationalOperators) {
 TYPED_TEST(IntegerValueTypedKind, ToUInt64) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(0u, zero.ToUInt64());
@@ -946,7 +949,7 @@ TYPED_TEST(IntegerValueTypedKind, ToUInt64) {
 TYPED_TEST(IntegerValueTypedKind, ToInt64) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(0, zero.ToInt64());
@@ -988,7 +991,7 @@ TYPED_TEST(IntegerValueTypedKind, ToInt64) {
 TYPED_TEST(IntegerValueTypedKind, ToUInt) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   // Small values fit in every host width, regardless of kind.
   IntegerValue zero{IntegerValue::Zero(kind)};
@@ -1031,7 +1034,7 @@ TYPED_TEST(IntegerValueTypedKind, ToUInt) {
 
   // Least truncates to zero in any host width narrower than the kind.
   IntegerValue sminv{kind, std::numeric_limits<SignedT>::min()};
-  EXPECT_EQ(kind == 1 ? 0x80u : 0, sminv.ToUInt<uint8_t>());
+  EXPECT_EQ(kind == KindsEnum::Kind1 ? 0x80u : 0, sminv.ToUInt<uint8_t>());
   EXPECT_EQ(uint128_t{UnsignedT(std::numeric_limits<SignedT>::min())},
       sminv.ToUInt<uint128_t>());
 
@@ -1055,7 +1058,7 @@ TYPED_TEST(IntegerValueTypedKind, ToUInt) {
 TYPED_TEST(IntegerValueTypedKind, ToSInt) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   // Small values fit in every host width, regardless of kind.
   IntegerValue zero{IntegerValue::Zero(kind)};
@@ -1104,7 +1107,7 @@ TYPED_TEST(IntegerValueTypedKind, ToSInt) {
   // kind; a narrower width truncates it away, along with the sign. Widening
   // to the widest kind's own width (16) always sign-extends the true value.
   IntegerValue sminv{kind, std::numeric_limits<SignedT>::min()};
-  EXPECT_EQ(kind == 1 ? -128 : 0, sminv.ToSInt<int8_t>());
+  EXPECT_EQ(kind == KindsEnum::Kind1 ? -128 : 0, sminv.ToSInt<int8_t>());
   EXPECT_EQ(
       int8_t(std::numeric_limits<SignedT>::min()), sminv.ToSInt<int8_t>());
   EXPECT_EQ(
@@ -1140,7 +1143,7 @@ TYPED_TEST(IntegerValueTypedKind, ToSInt) {
 TYPED_TEST(IntegerValueTypedKind, NOT) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   EXPECT_EQ(UnsignedT(~UnsignedT{0}), zero.NOT().ToUInt<UnsignedT>());
@@ -1168,7 +1171,7 @@ TYPED_TEST(IntegerValueTypedKind, NOT) {
 
 TYPED_TEST(IntegerValueTypedKind, IAND) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue theanswer{kind, 42};
   EXPECT_EQ(theanswer, theanswer.IAND(theanswer));
@@ -1188,7 +1191,7 @@ TYPED_TEST(IntegerValueTypedKind, IAND) {
 
 TYPED_TEST(IntegerValueTypedKind, IOR) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue theanswer{kind, 42};
   EXPECT_EQ(theanswer, theanswer.IOR(theanswer));
@@ -1208,7 +1211,7 @@ TYPED_TEST(IntegerValueTypedKind, IOR) {
 
 TYPED_TEST(IntegerValueTypedKind, IEOR) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   IntegerValue zero{IntegerValue::Zero(kind)};
 
   IntegerValue theanswer{kind, 42};
@@ -1225,7 +1228,7 @@ TYPED_TEST(IntegerValueTypedKind, IEOR) {
 
 TYPED_TEST(IntegerValueTypedKind, MERGE_BITS) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue negone{kind, -1};
   IntegerValue zero{IntegerValue::Zero(kind)};
@@ -1240,7 +1243,7 @@ TYPED_TEST(IntegerValueTypedKind, MERGE_BITS) {
 
 TYPED_TEST(IntegerValueTypedKind, MAX) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   IntegerValue one{kind, 1};
@@ -1259,7 +1262,7 @@ TYPED_TEST(IntegerValueTypedKind, MAX) {
 
 TYPED_TEST(IntegerValueTypedKind, MIN) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   IntegerValue one{kind, 1};
@@ -1279,7 +1282,7 @@ TYPED_TEST(IntegerValueTypedKind, MIN) {
 TYPED_TEST(IntegerValueTypedKind, IBCLR) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   IntegerValue theanswer{kind, 0b101010};
@@ -1299,7 +1302,7 @@ TYPED_TEST(IntegerValueTypedKind, IBCLR) {
 
 TYPED_TEST(IntegerValueTypedKind, IBSET) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   IntegerValue theanswer{kind, 42}; // 0b101010
@@ -1318,7 +1321,7 @@ TYPED_TEST(IntegerValueTypedKind, IBSET) {
 }
 
 TYPED_TEST(IntegerValueTypedKind, IBITS) {
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   // 0x...ef: low byte is 0b11101111.
@@ -1340,7 +1343,7 @@ TYPED_TEST(IntegerValueTypedKind, IBITS) {
 TYPED_TEST(IntegerValueTypedKind, ISHFT) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   // A positive count shifts left; a negative count shifts right.
@@ -1366,7 +1369,7 @@ TYPED_TEST(IntegerValueTypedKind, ISHFT) {
 TYPED_TEST(IntegerValueTypedKind, SHIFTL) {
   using UnsignedT = typename TypeParam::UnsignedT;
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   IntegerValue one{kind, 1};
@@ -1385,7 +1388,7 @@ TYPED_TEST(IntegerValueTypedKind, SHIFTL) {
 
 TYPED_TEST(IntegerValueTypedKind, SHIFTR) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   IntegerValue negone{kind, -1};
@@ -1404,7 +1407,7 @@ TYPED_TEST(IntegerValueTypedKind, SHIFTR) {
 
 TYPED_TEST(IntegerValueTypedKind, SHIFTA) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   IntegerValue negone{kind, -1};
@@ -1423,7 +1426,7 @@ TYPED_TEST(IntegerValueTypedKind, SHIFTA) {
 
 TYPED_TEST(IntegerValueTypedKind, ISHFTC) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   // Rotating a uniform bit pattern leaves it unchanged.
@@ -1451,7 +1454,7 @@ TYPED_TEST(IntegerValueTypedKind, ISHFTC) {
 
 TYPED_TEST(IntegerValueTypedKind, DSHIFTL) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
   const UnsignedT i{UnsignedT(0x0123456789abcdefull)};
   const UnsignedT j{UnsignedT(~i)};
@@ -1470,7 +1473,7 @@ TYPED_TEST(IntegerValueTypedKind, DSHIFTL) {
 
 TYPED_TEST(IntegerValueTypedKind, DSHIFTR) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
   const UnsignedT i{UnsignedT(0x0123456789abcdefull)};
   const UnsignedT j{UnsignedT(~i)};
@@ -1494,7 +1497,7 @@ TYPED_TEST(IntegerValueTypedKind, DSHIFTR) {
 
 TYPED_TEST(IntegerValueTypedKind, Negate) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   auto negZero{zero.Negate()};
@@ -1530,7 +1533,7 @@ TYPED_TEST(IntegerValueTypedKind, Negate) {
 
 TYPED_TEST(IntegerValueTypedKind, ABS) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   auto absZero{zero.ABS()};
@@ -1568,7 +1571,7 @@ TYPED_TEST(IntegerValueTypedKind, ABS) {
 
 TYPED_TEST(IntegerValueTypedKind, AddUnsigned) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   auto zeroPlusZero{zero.AddUnsigned(zero)};
@@ -1605,7 +1608,7 @@ TYPED_TEST(IntegerValueTypedKind, AddUnsigned) {
 
 TYPED_TEST(IntegerValueTypedKind, AddSigned) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   auto zeroPlusZero{zero.AddSigned(zero)};
@@ -1639,7 +1642,7 @@ TYPED_TEST(IntegerValueTypedKind, AddSigned) {
 
 TYPED_TEST(IntegerValueTypedKind, SubtractSigned) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   auto zeroMinusZero{zero.SubtractSigned(zero)};
@@ -1672,7 +1675,7 @@ TYPED_TEST(IntegerValueTypedKind, SubtractSigned) {
 
 TYPED_TEST(IntegerValueTypedKind, DIM) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue one{kind, 1};
   IntegerValue theanswer{kind, 42};
@@ -1705,7 +1708,7 @@ TYPED_TEST(IntegerValueTypedKind, DIM) {
 
 TYPED_TEST(IntegerValueTypedKind, SIGN) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue one{kind, 1};
   IntegerValue negone{kind, -1};
@@ -1740,7 +1743,7 @@ TYPED_TEST(IntegerValueTypedKind, SIGN) {
 
 TYPED_TEST(IntegerValueTypedKind, MultiplyUnsigned) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int bits{TypeParam::bits};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
@@ -1779,7 +1782,7 @@ TYPED_TEST(IntegerValueTypedKind, MultiplyUnsigned) {
 
 TYPED_TEST(IntegerValueTypedKind, MultiplySigned) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue zero{IntegerValue::Zero(kind)};
   IntegerValue theanswer{kind, 42};
@@ -1812,7 +1815,7 @@ TYPED_TEST(IntegerValueTypedKind, MultiplySigned) {
 
 TYPED_TEST(IntegerValueTypedKind, DivideUnsigned) {
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   IntegerValue patternv{kind, 0x0123456789abcdefull};
   IntegerValue zero{IntegerValue::Zero(kind)};
@@ -1846,7 +1849,7 @@ TYPED_TEST(IntegerValueTypedKind, DivideUnsigned) {
 
 TYPED_TEST(IntegerValueTypedKind, DivideSigned) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   // A nonzero remainder has the sign of the dividend: this is MOD, not MODULO.
   struct {
@@ -1892,7 +1895,7 @@ TYPED_TEST(IntegerValueTypedKind, DivideSigned) {
 
 TYPED_TEST(IntegerValueTypedKind, MODULO) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   // The result has the sign of the divisor.
   struct {
     int64_t x, y, modulo;
@@ -1929,7 +1932,7 @@ TYPED_TEST(IntegerValueTypedKind, MODULO) {
 }
 
 TYPED_TEST(IntegerValueTypedKind, Power) {
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   IntegerValue three{kind, 3};
   IntegerValue two{kind, 2};
   auto square{three.Power(two)};
@@ -1977,7 +1980,7 @@ TYPED_TEST(IntegerValueTypedKind, Power) {
 
 TYPED_TEST(IntegerValueTypedKind, RawBytesRoundTrip) {
   using SignedT = typename TypeParam::SignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   ASSERT_EQ(
       IntegerValue::bytesStored(kind), IntegerValue::Zero(kind).bytesStored());
 
@@ -2045,19 +2048,19 @@ TYPED_TEST(IntegerValueTypedKind, RawBytesRoundTrip) {
 //===----------------------------------------------------------------------===//
 
 class IntegerValueKindPair
-    : public testing::TestWithParam<std::tuple<int, int>> {};
+    : public testing::TestWithParam<std::tuple<KindsEnum, KindsEnum>> {};
 
 INSTANTIATE_TEST_SUITE_P(AllKindPairs, IntegerValueKindPair,
     testing::Combine(
-        testing::ValuesIn(std::initializer_list<int> FORTRAN_INTEGER_KINDS),
-        testing::ValuesIn(std::initializer_list<int> FORTRAN_INTEGER_KINDS)),
-    [](const testing::TestParamInfo<std::tuple<int, int>> &info) {
-      return "KIND" + std::to_string(std::get<0>(info.param)) + "AndKIND" +
-          std::to_string(std::get<1>(info.param));
+        testing::ValuesIn(IntegerKinds), testing::ValuesIn(IntegerKinds)),
+    [](const testing::TestParamInfo<std::tuple<KindsEnum, KindsEnum>> &info) {
+      return "KIND" +
+          std::to_string(static_cast<int>(std::get<0>(info.param))) +
+          "AndKIND" + std::to_string(static_cast<int>(std::get<1>(info.param)));
     });
 
 TEST_P(IntegerValueKindPair, ConvertUnsigned) {
-  const int from{std::get<0>(GetParam())}, to{std::get<1>(GetParam())};
+  const KindsEnum from{std::get<0>(GetParam())}, to{std::get<1>(GetParam())};
   const int fromBits{IntegerValue::bits(from)}, toBits{IntegerValue::bits(to)};
   const int common{std::min(fromBits, toBits)};
 
@@ -2076,7 +2079,7 @@ TEST_P(IntegerValueKindPair, ConvertUnsigned) {
 }
 
 TEST_P(IntegerValueKindPair, ConvertSigned) {
-  const int from{std::get<0>(GetParam())}, to{std::get<1>(GetParam())};
+  const KindsEnum from{std::get<0>(GetParam())}, to{std::get<1>(GetParam())};
   const int fromBits{IntegerValue::bits(from)}, toBits{IntegerValue::bits(to)};
   // All ones stays all ones: it sign-extends and truncates to itself.
   auto ones{IntegerValue::ConvertSigned(IntegerValue{from, -1}, toBits)};
@@ -2095,8 +2098,8 @@ TEST_P(IntegerValueKindPair, ConvertSigned) {
 }
 
 TEST_P(IntegerValueKindPair, MixedKindOperandsAreCoerced) {
-  const int receiver{std::get<0>(GetParam())};
-  const int other{std::get<1>(GetParam())};
+  const KindsEnum receiver{std::get<0>(GetParam())};
+  const KindsEnum other{std::get<1>(GetParam())};
   IntegerValue x{receiver, 0x5a};
   IntegerValue allOnes{other, -1};
   // The result takes the receiver's kind; the argument is converted to it,
@@ -2111,7 +2114,7 @@ TEST_P(IntegerValueKindPair, MixedKindOperandsAreCoerced) {
 }
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-TEST(IntegerValue, Dump) { IntegerValue(4, -1).dump(); }
+TEST(IntegerValue, Dump) { IntegerValue(KindsEnum::Kind4, -1).dump(); }
 #endif
 
 // Not an IntegerValue method, but the ordering that its comparisons return;
@@ -2131,7 +2134,7 @@ TEST(Ordering, Reverse) {
 // dyadic operations, every pair of values) of a narrow kind.
 //===----------------------------------------------------------------------===//
 
-void ExhaustiveUnary(int kind) {
+void ExhaustiveUnary(KindsEnum kind) {
   const int bits{IntegerValue::bits(kind)};
   ASSERT_LE(bits, 16); // the reference arithmetic below assumes a narrow kind
   const uint64_t maxUnsigned{(uint64_t{1} << bits) - 1};
@@ -2231,12 +2234,12 @@ void ExhaustiveUnary(int kind) {
   }
 }
 
-TEST(IntegerValue, ExhaustiveUnaryKind1) { ExhaustiveUnary(1); }
+TEST(IntegerValue, ExhaustiveUnaryKind1) { ExhaustiveUnary(KindsEnum::Kind1); }
 
-TEST(IntegerValue, ExhaustiveUnaryKind2) { ExhaustiveUnary(2); }
+TEST(IntegerValue, ExhaustiveUnaryKind2) { ExhaustiveUnary(KindsEnum::Kind2); }
 
 TEST(IntegerValue, ExhaustiveDyadicKind1) {
-  constexpr int kind{1};
+  constexpr KindsEnum kind{KindsEnum::Kind1};
   constexpr int bits{8};
   constexpr uint64_t maxUnsigned{0xff};
   constexpr int64_t maxPositiveSigned{0x7f};
@@ -2331,7 +2334,7 @@ TEST(IntegerValue, ExhaustiveDyadicKind1) {
 }
 
 TYPED_TEST(IntegerValueTypedKind, Print) {
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
   constexpr int pos{IntKindPos<TypeParam>};
 
   llvm::SmallString<128> buf;
diff --git a/flang/unittests/Evaluate/LogicalValueTest.cpp b/flang/unittests/Evaluate/LogicalValueTest.cpp
index bb0e9d9575f56..811906a83a6c9 100644
--- a/flang/unittests/Evaluate/LogicalValueTest.cpp
+++ b/flang/unittests/Evaluate/LogicalValueTest.cpp
@@ -19,14 +19,14 @@ using namespace Fortran::evaluate::value;
 
 namespace {
 
-class LogicalValueKind : public testing::TestWithParam<int> {};
+class LogicalValueKind : public testing::TestWithParam<KindsEnum> {};
 INSTANTIATE_TEST_SUITE_P(LogicalValueKind, LogicalValueKind,
     testing::ValuesIn(LogicalKinds),
-    [](const testing::TestParamInfo<int> &info) {
-      return "LOGICAL(" + std::to_string(info.param) + ")";
+    [](const testing::TestParamInfo<KindsEnum> &info) {
+      return "LOGICAL(" + std::to_string(static_cast<int>(info.param)) + ")";
     });
 
-constexpr int KindPos(int kind) {
+constexpr int KindPos(KindsEnum kind) {
   for (std::size_t i{0}; i < std::size(LogicalKinds); ++i) {
     if (LogicalKinds[i] == kind) {
       return static_cast<int>(i);
@@ -46,7 +46,7 @@ TEST(LogicalValue, DefaultConstructionIsMonostate) {
 }
 
 TEST_P(LogicalValueKind, ConstructFromBool) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   LogicalValue truth{kind, true};
   EXPECT_FALSE(truth.IsMonostate());
@@ -59,7 +59,7 @@ TEST_P(LogicalValueKind, ConstructFromBool) {
 }
 
 TEST_P(LogicalValueKind, ConstructFromWord) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   LogicalValue zero{kind, IntegerValue{kind, 0}};
   EXPECT_FALSE(zero.IsTrue());
@@ -75,7 +75,7 @@ TEST_P(LogicalValueKind, ConstructFromWord) {
 }
 
 TEST_P(LogicalValueKind, CopyAndMove) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   LogicalValue x{kind, true};
   LogicalValue copyConstructed{x};
@@ -95,7 +95,7 @@ TEST_P(LogicalValueKind, CopyAndMove) {
 }
 
 TEST_P(LogicalValueKind, KindCheckingConstructors) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   LogicalValue x{kind, true};
   EXPECT_EQ(kind, LogicalValue(kind, x).kind());
@@ -108,7 +108,7 @@ TEST_P(LogicalValueKind, KindCheckingConstructors) {
 }
 
 TEST_P(LogicalValueKind, Zero) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   LogicalValue zero{LogicalValue::Zero(kind)};
   EXPECT_FALSE(zero.IsMonostate());
@@ -118,23 +118,23 @@ TEST_P(LogicalValueKind, Zero) {
 }
 
 TEST(LogicalValue, Bits) {
-  EXPECT_EQ(8, LogicalValue::bits(1));
-  EXPECT_EQ(16, LogicalValue::bits(2));
-  EXPECT_EQ(32, LogicalValue::bits(4));
-  EXPECT_EQ(64, LogicalValue::bits(8));
-  EXPECT_EQ(32, LogicalValue(4, true).bits());
+  EXPECT_EQ(8, LogicalValue::bits(KindsEnum::Kind1));
+  EXPECT_EQ(16, LogicalValue::bits(KindsEnum::Kind2));
+  EXPECT_EQ(32, LogicalValue::bits(KindsEnum::Kind4));
+  EXPECT_EQ(64, LogicalValue::bits(KindsEnum::Kind8));
+  EXPECT_EQ(32, LogicalValue(KindsEnum::Kind4, true).bits());
 }
 
 TEST(LogicalValue, BytesStored) {
-  EXPECT_EQ(1u, LogicalValue::bytesStored(1));
-  EXPECT_EQ(2u, LogicalValue::bytesStored(2));
-  EXPECT_EQ(4u, LogicalValue::bytesStored(4));
-  EXPECT_EQ(8u, LogicalValue::bytesStored(8));
-  EXPECT_EQ(4u, LogicalValue(4, true).bytesStored());
+  EXPECT_EQ(1u, LogicalValue::bytesStored(KindsEnum::Kind1));
+  EXPECT_EQ(2u, LogicalValue::bytesStored(KindsEnum::Kind2));
+  EXPECT_EQ(4u, LogicalValue::bytesStored(KindsEnum::Kind4));
+  EXPECT_EQ(8u, LogicalValue::bytesStored(KindsEnum::Kind8));
+  EXPECT_EQ(4u, LogicalValue(KindsEnum::Kind4, true).bytesStored());
 }
 
 TEST_P(LogicalValueKind, Word_) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // .TRUE. is represented canonically as 1 and .FALSE. as 0.
   EXPECT_EQ(1, LogicalValue(kind, true).word().ToInt64());
@@ -145,7 +145,7 @@ TEST_P(LogicalValueKind, Word_) {
 }
 
 TEST_P(LogicalValueKind, IsCanonical) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   EXPECT_TRUE(LogicalValue(kind, true).IsCanonical());
   EXPECT_TRUE(LogicalValue(kind, false).IsCanonical());
@@ -156,7 +156,7 @@ TEST_P(LogicalValueKind, IsCanonical) {
 }
 
 TEST_P(LogicalValueKind, IsTrue) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   EXPECT_FALSE(LogicalValue{}.IsTrue());
   EXPECT_FALSE(LogicalValue(kind, false).IsTrue());
@@ -165,7 +165,7 @@ TEST_P(LogicalValueKind, IsTrue) {
 }
 
 TEST_P(LogicalValueKind, RelationalOperators) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   LogicalValue f{kind, false}, t{kind, true};
 
   EXPECT_TRUE(f < t);
@@ -198,7 +198,7 @@ TEST_P(LogicalValueKind, RelationalOperators) {
 }
 
 TEST_P(LogicalValueKind, NOT) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   EXPECT_TRUE(LogicalValue(kind, false).NOT().IsTrue());
   EXPECT_FALSE(LogicalValue(kind, true).NOT().IsTrue());
@@ -206,7 +206,7 @@ TEST_P(LogicalValueKind, NOT) {
 }
 
 TEST_P(LogicalValueKind, AND) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   LogicalValue f{kind, false}, t{kind, true};
   EXPECT_FALSE(f.AND(f).IsTrue());
@@ -217,7 +217,7 @@ TEST_P(LogicalValueKind, AND) {
 }
 
 TEST_P(LogicalValueKind, OR) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   LogicalValue f{kind, false}, t{kind, true};
   EXPECT_FALSE(f.OR(f).IsTrue());
@@ -228,7 +228,7 @@ TEST_P(LogicalValueKind, OR) {
 }
 
 TEST_P(LogicalValueKind, EQV) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   LogicalValue f{kind, false}, t{kind, true};
   EXPECT_TRUE(f.EQV(f).IsTrue());
@@ -239,7 +239,7 @@ TEST_P(LogicalValueKind, EQV) {
 }
 
 TEST_P(LogicalValueKind, NEQV) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   LogicalValue f{kind, false}, t{kind, true};
   EXPECT_FALSE(f.NEQV(f).IsTrue());
@@ -250,7 +250,7 @@ TEST_P(LogicalValueKind, NEQV) {
 }
 
 TEST_P(LogicalValueKind, RawBytesRoundTrip) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   for (bool truth : {false, true}) {
     SCOPED_TRACE(testing::Message() << "truth=" << truth);
@@ -271,7 +271,7 @@ TEST_P(LogicalValueKind, RawBytesRoundTrip) {
 }
 
 TEST_P(LogicalValueKind, Print) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   const int pos{KindPos(kind)};
 
   struct Case {
@@ -299,9 +299,9 @@ TEST_P(LogicalValueKind, Print) {
 // Replicates the coverage of the legacy non-GTest test
 // flang/unittests/Evaluate/logical.cpp.
 TEST_P(LogicalValueKind, TruthTables) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
-  EXPECT_EQ(8 * kind, LogicalValue::bits(kind));
+  EXPECT_EQ(8 * static_cast<int>(kind), LogicalValue::bits(kind));
   EXPECT_FALSE(LogicalValue{}.IsTrue());
   EXPECT_FALSE(LogicalValue(kind, false).IsTrue());
   EXPECT_TRUE(LogicalValue(kind, true).IsTrue());
diff --git a/flang/unittests/Evaluate/RealValueTest.cpp b/flang/unittests/Evaluate/RealValueTest.cpp
index 69119522b5f86..f7a0a499d4870 100644
--- a/flang/unittests/Evaluate/RealValueTest.cpp
+++ b/flang/unittests/Evaluate/RealValueTest.cpp
@@ -44,17 +44,18 @@ using RealHostTypedKinds = testing::Types<TypeKind<TypeCategory::Real, 4>,
 template <typename T> class RealValueHostTypedKind : public testing::Test {};
 TYPED_TEST_SUITE(RealValueHostTypedKind, RealHostTypedKinds, KindName);
 
-class RealValueKind : public testing::TestWithParam<int> {};
+class RealValueKind : public testing::TestWithParam<KindsEnum> {};
 INSTANTIATE_TEST_SUITE_P(RealValueKind, RealValueKind,
-    testing::ValuesIn(RealKinds), [](const testing::TestParamInfo<int> &info) {
-      return "REAL(" + std::to_string(info.param) + ")";
+    testing::ValuesIn(RealKinds),
+    [](const testing::TestParamInfo<KindsEnum> &info) {
+      return "REAL(" + std::to_string(static_cast<int>(info.param)) + ")";
     });
 
 //===----------------------------------------------------------------------===//
 // Helpers
 //===----------------------------------------------------------------------===//
 
-constexpr int KindPos(int kind) {
+constexpr int KindPos(KindsEnum kind) {
   for (std::size_t i{0}; i < std::size(RealKinds); ++i) {
     if (RealKinds[i] == kind) {
       return static_cast<int>(i);
@@ -75,10 +76,10 @@ testing::AssertionResult RealValuesEqual(const char *lhsExpr,
 
 #define EXPECT_REAL_EQ(lhs, rhs) EXPECT_PRED_FORMAT2(RealValuesEqual, lhs, rhs)
 
-std::string AsFortranString(const RealValue &x, int kind, bool minimal) {
+std::string AsFortranString(const RealValue &x, KindsEnum kind, bool minimal) {
   std::string s;
   llvm::raw_string_ostream os{s};
-  x.AsFortran(os, kind, minimal);
+  x.AsFortran(os, static_cast<int>(kind), minimal);
   return s;
 }
 
@@ -145,7 +146,7 @@ TEST(RealValue, DefaultConstructionIsMonostate) {
 }
 
 TEST_P(RealValueKind, ConstructFromWord) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   // The word is the raw bit pattern, not a numeric value.
   RealValue zero{kind, IntegerValue::Zero(kind)};
   EXPECT_EQ(kind, zero.kind());
@@ -157,7 +158,7 @@ TEST_P(RealValueKind, ConstructFromWord) {
 }
 
 TEST(RealValue, CopyAndMove) {
-  RealValue x{4, 3.0};
+  RealValue x{KindsEnum::Kind4, 3.0};
   RealValue copyConstructed{x};
   EXPECT_REAL_EQ(x, copyConstructed);
   RealValue copyAssigned;
@@ -171,16 +172,16 @@ TEST(RealValue, CopyAndMove) {
 }
 
 TEST(RealValue, KindCheckingConstructors) {
-  RealValue x{4, 3.0};
-  EXPECT_EQ(4, RealValue(4, x).kind());
-  EXPECT_REAL_EQ(x, RealValue(4, x));
-  RealValue y{8, 3.0};
-  RealValue moved{8, std::move(y)};
-  EXPECT_EQ(8, moved.kind());
+  RealValue x{KindsEnum::Kind4, 3.0};
+  EXPECT_EQ(KindsEnum::Kind4, RealValue(KindsEnum::Kind4, x).kind());
+  EXPECT_REAL_EQ(x, RealValue(KindsEnum::Kind4, x));
+  RealValue y{KindsEnum::Kind8, 3.0};
+  RealValue moved{KindsEnum::Kind8, std::move(y)};
+  EXPECT_EQ(KindsEnum::Kind8, moved.kind());
 }
 
 TEST_P(RealValueKind, Zero) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   RealValue zero{RealValue::Zero(kind)};
   EXPECT_FALSE(zero.IsMonostate());
   EXPECT_EQ(kind, zero.kind());
@@ -192,38 +193,40 @@ TEST_P(RealValueKind, Zero) {
 }
 
 TEST(RealValue, Bits) {
-  EXPECT_EQ(16, RealValue::bits(2));
-  EXPECT_EQ(16, RealValue::bits(3));
-  EXPECT_EQ(32, RealValue::bits(4));
-  EXPECT_EQ(64, RealValue::bits(8));
-  EXPECT_EQ(128, RealValue::bits(10)); // 80 significant bits, 128 stored
-  EXPECT_EQ(128, RealValue::bits(16));
-  EXPECT_EQ(32, (RealValue{4, 1.0}.bits()));
+  EXPECT_EQ(16, RealValue::bits(KindsEnum::Kind2));
+  EXPECT_EQ(16, RealValue::bits(KindsEnum::Kind3));
+  EXPECT_EQ(32, RealValue::bits(KindsEnum::Kind4));
+  EXPECT_EQ(64, RealValue::bits(KindsEnum::Kind8));
+  EXPECT_EQ(128,
+      RealValue::bits(KindsEnum::Kind10)); // 80 significant bits, 128 stored
+  EXPECT_EQ(128, RealValue::bits(KindsEnum::Kind16));
+  EXPECT_EQ(32, (RealValue{KindsEnum::Kind4, 1.0}.bits()));
 }
 
 TEST(RealValue, BytesStored) {
-  EXPECT_EQ(2u, RealValue::bytesStored(2));
-  EXPECT_EQ(2u, RealValue::bytesStored(3));
-  EXPECT_EQ(4u, RealValue::bytesStored(4));
-  EXPECT_EQ(8u, RealValue::bytesStored(8));
-  EXPECT_EQ(16u, RealValue::bytesStored(10));
-  EXPECT_EQ(16u, RealValue::bytesStored(16));
-  EXPECT_EQ(4u, (RealValue{4, 1.0}.bytesStored()));
+  EXPECT_EQ(2u, RealValue::bytesStored(KindsEnum::Kind2));
+  EXPECT_EQ(2u, RealValue::bytesStored(KindsEnum::Kind3));
+  EXPECT_EQ(4u, RealValue::bytesStored(KindsEnum::Kind4));
+  EXPECT_EQ(8u, RealValue::bytesStored(KindsEnum::Kind8));
+  EXPECT_EQ(16u, RealValue::bytesStored(KindsEnum::Kind10));
+  EXPECT_EQ(16u, RealValue::bytesStored(KindsEnum::Kind16));
+  EXPECT_EQ(4u, (RealValue{KindsEnum::Kind4, 1.0}.bytesStored()));
 }
 
 TEST(RealValue, KindProperties) {
   struct {
-    int kind, digits, precision, range, maxExponent, minExponent;
+    KindsEnum kind;
+    int digits, precision, range, maxExponent, minExponent;
   } expected[]{
-      {2, 11, 3, 4, 16, -13},
-      {3, 8, 2, 37, 128, -125},
-      {4, 24, 6, 37, 128, -125},
-      {8, 53, 15, 307, 1024, -1021},
-      {10, 64, 18, 4931, 16384, -16381},
-      {16, 113, 33, 4931, 16384, -16381},
+      {KindsEnum::Kind2, 11, 3, 4, 16, -13},
+      {KindsEnum::Kind3, 8, 2, 37, 128, -125},
+      {KindsEnum::Kind4, 24, 6, 37, 128, -125},
+      {KindsEnum::Kind8, 53, 15, 307, 1024, -1021},
+      {KindsEnum::Kind10, 64, 18, 4931, 16384, -16381},
+      {KindsEnum::Kind16, 113, 33, 4931, 16384, -16381},
   };
   for (auto &e : expected) {
-    SCOPED_TRACE(testing::Message() << "kind=" << e.kind);
+    SCOPED_TRACE(testing::Message() << "kind=" << static_cast<int>(e.kind));
     EXPECT_EQ(e.digits, RealValue::DIGITS(e.kind));
     EXPECT_EQ(e.precision, RealValue::PRECISION(e.kind));
     EXPECT_EQ(e.range, RealValue::RANGE(e.kind));
@@ -237,7 +240,7 @@ TEST(RealValue, KindProperties) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(RealValueKind, IsZero) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_TRUE(RealValue::Zero(kind).IsZero());
   EXPECT_TRUE(RealValue::NegativeZero(kind).IsZero());
   EXPECT_FALSE((RealValue{kind, 1.0}.IsZero()));
@@ -246,7 +249,7 @@ TEST_P(RealValueKind, IsZero) {
 }
 
 TEST_P(RealValueKind, IsNegative) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_FALSE(RealValue::Zero(kind).IsNegative());
   EXPECT_TRUE(RealValue::NegativeZero(kind).IsNegative());
   EXPECT_FALSE((RealValue{kind, 1.0}.IsNegative()));
@@ -257,7 +260,7 @@ TEST_P(RealValueKind, IsNegative) {
 }
 
 TEST_P(RealValueKind, IsNotANumber) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_FALSE(RealValue::Zero(kind).IsNotANumber());
   EXPECT_FALSE(RealValue::Infinity(kind).IsNotANumber());
   EXPECT_FALSE(RealValue::Infinity(kind, /*negative=*/true).IsNotANumber());
@@ -266,7 +269,7 @@ TEST_P(RealValueKind, IsNotANumber) {
 }
 
 TEST_P(RealValueKind, IsSignalingNaN) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_FALSE(RealValue::Zero(kind).IsSignalingNaN());
   EXPECT_FALSE(RealValue::Infinity(kind).IsSignalingNaN());
   EXPECT_TRUE(RealValue::SignalingNaN(kind).IsSignalingNaN());
@@ -275,7 +278,7 @@ TEST_P(RealValueKind, IsSignalingNaN) {
 }
 
 TEST_P(RealValueKind, IsInfinite) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_FALSE(RealValue::Zero(kind).IsInfinite());
   EXPECT_FALSE(RealValue::HUGE(kind).IsInfinite());
   EXPECT_TRUE(RealValue::Infinity(kind).IsInfinite());
@@ -284,7 +287,7 @@ TEST_P(RealValueKind, IsInfinite) {
 }
 
 TEST_P(RealValueKind, IsFinite) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_TRUE(RealValue::Zero(kind).IsFinite());
   EXPECT_TRUE(RealValue::HUGE(kind).IsFinite());
   EXPECT_TRUE(RealValue::TINY(kind).IsFinite());
@@ -294,7 +297,7 @@ TEST_P(RealValueKind, IsFinite) {
 }
 
 TEST_P(RealValueKind, IsNormal) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_TRUE(RealValue::Zero(kind).IsNormal()); // zero counts as normal here
   EXPECT_TRUE(RealValue::TINY(kind).IsNormal());
   EXPECT_TRUE(RealValue::HUGE(kind).IsNormal());
@@ -310,7 +313,7 @@ TEST_P(RealValueKind, IsNormal) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(RealValueKind, ABS) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_REAL_EQ((RealValue{kind, 3.0}), (RealValue{kind, -3.0}.ABS()));
   EXPECT_REAL_EQ((RealValue{kind, 3.0}), (RealValue{kind, 3.0}.ABS()));
   EXPECT_TRUE(RealValue::NegativeZero(kind).ABS().RawBits().IsZero());
@@ -319,7 +322,7 @@ TEST_P(RealValueKind, ABS) {
 }
 
 TEST_P(RealValueKind, SetSign) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_REAL_EQ((RealValue{kind, -3.0}), (RealValue{kind, 3.0}.SetSign(true)));
   EXPECT_REAL_EQ(
       (RealValue{kind, 3.0}), (RealValue{kind, -3.0}.SetSign(false)));
@@ -329,7 +332,7 @@ TEST_P(RealValueKind, SetSign) {
 }
 
 TEST_P(RealValueKind, SIGN) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_REAL_EQ((RealValue{kind, -3.0}),
       (RealValue{kind, 3.0}.SIGN(RealValue{kind, -1.0})));
   EXPECT_REAL_EQ((RealValue{kind, 3.0}),
@@ -340,7 +343,7 @@ TEST_P(RealValueKind, SIGN) {
 }
 
 TEST_P(RealValueKind, Negate) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   EXPECT_REAL_EQ((RealValue{kind, -3.0}), (RealValue{kind, 3.0}.Negate()));
   EXPECT_REAL_EQ((RealValue{kind, 3.0}), (RealValue{kind, -3.0}.Negate()));
   EXPECT_REAL_EQ(RealValue::NegativeZero(kind), RealValue::Zero(kind).Negate());
@@ -354,7 +357,7 @@ TEST_P(RealValueKind, Negate) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(RealValueKind, Compare) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   RealValue zero{RealValue::Zero(kind)};
   RealValue minusZero{RealValue::NegativeZero(kind)};
   RealValue one{kind, 1.0};
@@ -385,7 +388,7 @@ TEST_P(RealValueKind, Compare) {
 }
 
 TEST_P(RealValueKind, EqualityOperators) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   // operator== compares bit patterns, unlike Compare().
   EXPECT_TRUE((RealValue{kind, 1.0} == RealValue{kind, 1.0}));
   EXPECT_FALSE((RealValue{kind, 1.0} == RealValue{kind, 2.0}));
@@ -399,7 +402,7 @@ TEST_P(RealValueKind, EqualityOperators) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(RealValueKind, Add) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   auto sum{RealValue{kind, 1.0}.Add(RealValue{kind, 2.0})};
   EXPECT_TRUE(sum.flags.empty());
   EXPECT_REAL_EQ((RealValue{kind, 3.0}), sum.value);
@@ -418,7 +421,7 @@ TEST_P(RealValueKind, Add) {
 }
 
 TEST_P(RealValueKind, Subtract) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   auto diff{RealValue{kind, 3.0}.Subtract(RealValue{kind, 5.0})};
   EXPECT_TRUE(diff.flags.empty());
   EXPECT_REAL_EQ((RealValue{kind, -2.0}), diff.value);
@@ -428,7 +431,7 @@ TEST_P(RealValueKind, Subtract) {
 }
 
 TEST_P(RealValueKind, Multiply) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   auto product{RealValue{kind, 3.0}.Multiply(RealValue{kind, 5.0})};
   EXPECT_TRUE(product.flags.empty());
   EXPECT_REAL_EQ((RealValue{kind, 15.0}), product.value);
@@ -448,7 +451,7 @@ TEST_P(RealValueKind, Multiply) {
 }
 
 TEST_P(RealValueKind, Divide) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   auto quotient{RealValue{kind, 15.0}.Divide(RealValue{kind, 5.0})};
   EXPECT_TRUE(quotient.flags.empty());
   EXPECT_REAL_EQ((RealValue{kind, 3.0}), quotient.value);
@@ -470,7 +473,7 @@ TEST_P(RealValueKind, Divide) {
 }
 
 TEST_P(RealValueKind, SQRT) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   auto four{RealValue{kind, 4.0}.SQRT()};
   EXPECT_TRUE(four.flags.empty());
   EXPECT_REAL_EQ((RealValue{kind, 2.0}), four.value);
@@ -483,7 +486,7 @@ TEST_P(RealValueKind, SQRT) {
 }
 
 TEST_P(RealValueKind, NEAREST) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   RealValue one{kind, 1.0};
 
   // The next value above 1.0 is 1.0+EPSILON.
@@ -502,7 +505,7 @@ TEST_P(RealValueKind, NEAREST) {
 }
 
 TEST_P(RealValueKind, HYPOT) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   auto hypot{RealValue{kind, 3.0}.HYPOT(RealValue{kind, 4.0})};
   EXPECT_REAL_EQ((RealValue{kind, 5.0}), hypot.value);
@@ -513,7 +516,7 @@ TEST_P(RealValueKind, HYPOT) {
 }
 
 TEST_P(RealValueKind, DIM) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   auto positive{RealValue{kind, 7.0}.DIM(RealValue{kind, 5.0})};
   EXPECT_REAL_EQ((RealValue{kind, 2.0}), positive.value);
@@ -527,7 +530,7 @@ TEST_P(RealValueKind, DIM) {
 }
 
 TEST_P(RealValueKind, MOD) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // The result has the sign of the dividend.
   RealValue m1{RealValue{kind, 8.0}.MOD(RealValue{kind, 5.0}).value};
@@ -545,7 +548,7 @@ TEST_P(RealValueKind, MOD) {
 }
 
 TEST_P(RealValueKind, MODULO) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // The result has the sign of the divisor.
   RealValue m1{RealValue{kind, 8.0}.MODULO(RealValue{kind, 5.0}).value};
@@ -559,7 +562,7 @@ TEST_P(RealValueKind, MODULO) {
 }
 
 TEST_P(RealValueKind, KahanSummation) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   RealValue correction{RealValue::Zero(kind)};
   auto sum{
@@ -581,7 +584,7 @@ TEST_P(RealValueKind, KahanSummation) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(RealValueKind, EPSILON) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   RealValue eps{RealValue::EPSILON(kind)};
   EXPECT_EQ(kind, eps.kind());
@@ -596,7 +599,7 @@ TEST_P(RealValueKind, EPSILON) {
 }
 
 TEST_P(RealValueKind, HUGE) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   RealValue huge{RealValue::HUGE(kind)};
   EXPECT_EQ(kind, huge.kind());
@@ -610,7 +613,7 @@ TEST_P(RealValueKind, HUGE) {
 }
 
 TEST_P(RealValueKind, TINY) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   RealValue tiny{RealValue::TINY(kind)};
   EXPECT_EQ(kind, tiny.kind());
@@ -622,7 +625,7 @@ TEST_P(RealValueKind, TINY) {
 }
 
 TEST_P(RealValueKind, NotANumber) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   RealValue nan{RealValue::NotANumber(kind)};
   EXPECT_EQ(kind, nan.kind());
@@ -632,7 +635,7 @@ TEST_P(RealValueKind, NotANumber) {
 }
 
 TEST_P(RealValueKind, Exponent) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // Exponent() is the raw, biased exponent field.  The bias is recovered
   // from the (unbiased) Fortran MAXEXPONENT and the raw exponent of
@@ -647,22 +650,23 @@ TEST_P(RealValueKind, Exponent) {
 }
 
 TEST_P(RealValueKind, EXPONENT) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // The Fortran EXPONENT() intrinsic returns the unbiased exponent, plus one.
   EXPECT_EQ(1, (RealValue{kind, 1.0}.EXPONENT().ToInt64()));
   EXPECT_EQ(2, (RealValue{kind, 2.0}.EXPONENT().ToInt64()));
   EXPECT_EQ(3, (RealValue{kind, 4.0}.EXPONENT().ToInt64()));
   EXPECT_EQ(0, RealValue::Zero(kind).EXPONENT().ToInt64());
-  EXPECT_EQ(4, (RealValue{kind, 1.0}.EXPONENT().kind())); // INTEGER(4) result
+  EXPECT_EQ(KindsEnum::Kind4,
+      (RealValue{kind, 1.0}.EXPONENT().kind())); // INTEGER(4) result
 }
 
 TEST_P(RealValueKind, RRSPACING) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // RRSPACING(1.0) is 2**(DIGITS-1).
   RealValue scaled{RealValue{kind, 1.0}
-          .SCALE(IntegerValue{4, RealValue::DIGITS(kind) - 1})
+          .SCALE(IntegerValue{KindsEnum::Kind4, RealValue::DIGITS(kind) - 1})
           .value};
   EXPECT_REAL_EQ(scaled, (RealValue{kind, 1.0}.RRSPACING()));
   EXPECT_FALSE((RealValue{kind, -1.0}.RRSPACING().IsNegative()));
@@ -670,7 +674,7 @@ TEST_P(RealValueKind, RRSPACING) {
 }
 
 TEST_P(RealValueKind, SPACING) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   EXPECT_REAL_EQ(RealValue::EPSILON(kind), (RealValue{kind, 1.0}.SPACING()));
   // The spacing of a zero or subnormal value is defined to be TINY.
@@ -679,7 +683,7 @@ TEST_P(RealValueKind, SPACING) {
 }
 
 TEST_P(RealValueKind, SET_EXPONENT) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // SET_EXPONENT(X,I) is FRACTION(X)*2**I.
   EXPECT_REAL_EQ(
@@ -691,7 +695,7 @@ TEST_P(RealValueKind, SET_EXPONENT) {
 }
 
 TEST_P(RealValueKind, FRACTION) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // FRACTION() normalizes into [0.5, 1.0).
   EXPECT_REAL_EQ((RealValue{kind, 0.5}), (RealValue{kind, 1.0}.FRACTION()));
@@ -700,20 +704,22 @@ TEST_P(RealValueKind, FRACTION) {
 }
 
 TEST_P(RealValueKind, SCALE) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
-  auto scaled{RealValue{kind, 3.0}.SCALE(IntegerValue{4, 4})};
+  auto scaled{RealValue{kind, 3.0}.SCALE(IntegerValue{KindsEnum::Kind4, 4})};
   EXPECT_TRUE(scaled.flags.empty());
   EXPECT_REAL_EQ((RealValue{kind, 48.0}), scaled.value);
-  RealValue rescaled{RealValue{kind, 48.0}.SCALE(IntegerValue{4, -4}).value};
+  RealValue rescaled{
+      RealValue{kind, 48.0}.SCALE(IntegerValue{KindsEnum::Kind4, -4}).value};
   EXPECT_REAL_EQ((RealValue{kind, 3.0}), rescaled);
   // Scaling a zero ignores the factor.
-  EXPECT_TRUE(
-      RealValue::Zero(kind).SCALE(IntegerValue{4, 1000}).value.IsZero());
+  EXPECT_TRUE(RealValue::Zero(kind)
+          .SCALE(IntegerValue{KindsEnum::Kind4, 1000})
+          .value.IsZero());
 }
 
 TEST_P(RealValueKind, FlushSubnormalToZero) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   RealValue subnormal{kind, IntegerValue{kind, 1}};
   ASSERT_FALSE(subnormal.IsZero());
@@ -730,27 +736,28 @@ TEST_P(RealValueKind, FlushSubnormalToZero) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(RealValueKind, FromInteger) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
-  auto exact{RealValue::FromInteger(kind, IntegerValue{8, 3})};
+  auto exact{RealValue::FromInteger(kind, IntegerValue{KindsEnum::Kind8, 3})};
   EXPECT_TRUE(exact.flags.empty());
   EXPECT_EQ(kind, exact.value.kind());
   EXPECT_EQ(Relation::Equal, exact.value.Compare(RealValue{kind, 3.0}));
-  EXPECT_TRUE(
-      RealValue::FromInteger(kind, IntegerValue::Zero(8)).value.IsZero());
+  EXPECT_TRUE(RealValue::FromInteger(kind, IntegerValue::Zero(KindsEnum::Kind8))
+          .value.IsZero());
 
-  auto negative{RealValue::FromInteger(kind, IntegerValue{8, -3})};
+  auto negative{
+      RealValue::FromInteger(kind, IntegerValue{KindsEnum::Kind8, -3})};
   EXPECT_TRUE(negative.value.IsNegative());
 
   // The same bit pattern read as unsigned is a large positive number.
-  auto asUnsigned{
-      RealValue::FromInteger(kind, IntegerValue{8, -1}, /*isUnsigned=*/true)};
+  auto asUnsigned{RealValue::FromInteger(
+      kind, IntegerValue{KindsEnum::Kind8, -1}, /*isUnsigned=*/true)};
   EXPECT_FALSE(asUnsigned.value.IsNegative());
   EXPECT_FALSE(asUnsigned.value.IsZero());
 }
 
 TEST_P(RealValueKind, ToWholeNumber) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // 3.5 is representable in every supported format.
   RealValue x{kind, 3.5};
@@ -778,13 +785,13 @@ TEST_P(RealValueKind, ToWholeNumber) {
 }
 
 TEST_P(RealValueKind, ToInteger) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   auto exact{RealValue{kind, 42.0}.ToInteger()};
   EXPECT_TRUE(exact.flags.empty());
   EXPECT_EQ(42, exact.value.ToInt64());
-  EXPECT_EQ(8, exact.value.kind()); // an INTEGER(8) by default
-  EXPECT_EQ(4,
+  EXPECT_EQ(KindsEnum::Kind8, exact.value.kind()); // an INTEGER(8) by default
+  EXPECT_EQ(KindsEnum::Kind4,
       (RealValue{kind, 42.0}.ToInteger(RoundingMode::ToZero, 32).value.kind()));
   EXPECT_EQ(-42, (RealValue{kind, -42.0}.ToInteger().value.ToInt64()));
 
@@ -798,7 +805,7 @@ TEST_P(RealValueKind, ToInteger) {
   // A NaN is invalid and yields HUGE.
   auto nan{RealValue::NotANumber(kind).ToInteger()};
   EXPECT_TRUE(nan.flags.test(RealFlag::InvalidArgument));
-  EXPECT_TRUE(nan.value == IntegerValue::HUGE(8));
+  EXPECT_TRUE(nan.value == IntegerValue::HUGE(KindsEnum::Kind8));
   // An infinity overflows.
   EXPECT_TRUE(
       RealValue::Infinity(kind).ToInteger().flags.test(RealFlag::Overflow));
@@ -809,11 +816,11 @@ TEST_P(RealValueKind, ToInteger) {
 }
 
 TEST_P(RealValueKind, Convert) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // Widening to REAL(16) and narrowing back is lossless.
-  auto widened{RealValue::Convert(16, RealValue{kind, 3.0})};
-  EXPECT_EQ(16, widened.value.kind());
+  auto widened{RealValue::Convert(KindsEnum::Kind16, RealValue{kind, 3.0})};
+  EXPECT_EQ(KindsEnum::Kind16, widened.value.kind());
   auto restored{RealValue::Convert(kind, widened.value)};
   EXPECT_REAL_EQ((RealValue{kind, 3.0}), restored.value);
   // Converting to the same kind is the identity.
@@ -821,12 +828,13 @@ TEST_P(RealValueKind, Convert) {
       (RealValue::Convert(kind, RealValue{kind, 3.0}).value));
 
   // A NaN is invalid but stays a NaN.
-  auto nan{RealValue::Convert(kind, RealValue::NotANumber(16))};
+  auto nan{RealValue::Convert(kind, RealValue::NotANumber(KindsEnum::Kind16))};
   EXPECT_TRUE(nan.flags.test(RealFlag::InvalidArgument));
   EXPECT_TRUE(nan.value.IsNotANumber());
   // Overflow when the source magnitude exceeds the destination's range.
-  if (kind != 16) {
-    auto overflowed{RealValue::Convert(kind, RealValue::HUGE(16))};
+  if (kind != KindsEnum::Kind16) {
+    auto overflowed{
+        RealValue::Convert(kind, RealValue::HUGE(KindsEnum::Kind16))};
     EXPECT_TRUE(overflowed.flags.test(RealFlag::Overflow));
     EXPECT_TRUE(overflowed.value.IsInfinite());
   }
@@ -837,12 +845,13 @@ TEST_P(RealValueKind, Convert) {
 //===----------------------------------------------------------------------===//
 
 TEST_P(RealValueKind, RawBits) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   EXPECT_TRUE(RealValue::Zero(kind).RawBits().IsZero());
 
   // REAL(10) stores 128 bits, but only 80 of them are significant.
-  const int significantBits{kind == 10 ? 80 : RealValue::bits(kind)};
+  const int significantBits{
+      kind == KindsEnum::Kind10 ? 80 : RealValue::bits(kind)};
   RealValue allOnes{kind, IntegerValue::MASKR(kind, significantBits)};
   EXPECT_EQ(significantBits, allOnes.RawBits().POPCNT());
   EXPECT_EQ(1, RealValue::NegativeZero(kind).RawBits().POPCNT());
@@ -854,7 +863,7 @@ TEST_P(RealValueKind, RawBits) {
 }
 
 TEST_P(RealValueKind, RawBytesRoundTrip) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   RealValue original{kind, -3.0};
   char buffer[16]{};
   ASSERT_EQ(RealValue::bytesStored(kind), original.bytesStored());
@@ -894,24 +903,25 @@ TEST(RealValue, DumpHexadecimal) {
       {0x00000001, "0x0.000002p-126"},
   };
   for (auto &e : table) {
-    EXPECT_EQ(
-        e.expected, (RealValue{4, IntegerValue{4, e.raw}}.DumpHexadecimal()))
+    EXPECT_EQ(e.expected,
+        (RealValue{KindsEnum::Kind4, IntegerValue{KindsEnum::Kind4, e.raw}}
+                .DumpHexadecimal()))
         << "raw=" << e.raw;
   }
 }
 
 TEST_P(RealValueKind, AsFortran) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // NaNs and infinities are emitted as parenthesized expressions.
   std::string nan{AsFortranString(RealValue::NotANumber(kind), kind, false)};
-  EXPECT_EQ("(0._" + std::to_string(kind) + "/0.)", nan);
+  EXPECT_EQ("(0._" + std::to_string(static_cast<int>(kind)) + "/0.)", nan);
   std::string inf{AsFortranString(RealValue::Infinity(kind), kind, false)};
-  EXPECT_EQ("(1._" + std::to_string(kind) + "/0.)", inf);
+  EXPECT_EQ("(1._" + std::to_string(static_cast<int>(kind)) + "/0.)", inf);
 
   std::string negInf{
       AsFortranString(RealValue::Infinity(kind, true), kind, false)};
-  EXPECT_EQ("(-1._" + std::to_string(kind) + "/0.)", negInf);
+  EXPECT_EQ("(-1._" + std::to_string(static_cast<int>(kind)) + "/0.)", negInf);
 
   // A finite value reads back as itself.
   RealValue x{kind, 0.375};
@@ -934,7 +944,7 @@ TEST_P(RealValueKind, AsFortran) {
 }
 
 TEST_P(RealValueKind, Read) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   const char *text{"1.0rest"};
   const char *p{text};
   auto one{RealValue::Read(kind, p)};
@@ -954,7 +964,7 @@ TEST_P(RealValueKind, Read) {
 }
 
 TEST_P(RealValueKind, RoundingModes) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
 
   // 1 + EPSILON/2 is exactly halfway between 1 and the next value up, so each
   // rounding mode picks a different result.
@@ -971,7 +981,7 @@ TEST_P(RealValueKind, RoundingModes) {
 }
 
 TEST_P(RealValueKind, Print) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   const int pos{KindPos(kind)};
 
   llvm::SmallString<128> buf;
@@ -991,13 +1001,13 @@ TEST_P(RealValueKind, Print) {
 // Mirrors basicTests() from the legacy test: converts every power of two that
 // fits in an INTEGER(8) and converts it back.
 TEST_P(RealValueKind, FromIntegerPowersOfTwo) {
-  const int kind{GetParam()};
+  const KindsEnum kind{GetParam()};
   const int bias{
       RealValue::Infinity(kind).Exponent() - RealValue::MAXEXPONENT(kind)};
   for (int j{0}; j < 63; ++j) {
     SCOPED_TRACE(testing::Message() << "kind=" << kind << " 2**" << j);
     const std::uint64_t x{std::uint64_t{1} << j};
-    IntegerValue ix{8, x};
+    IntegerValue ix{KindsEnum::Kind8, x};
     ASSERT_FALSE(ix.IsNegative());
     ASSERT_EQ(x, ix.ToUInt64());
 
@@ -1055,7 +1065,7 @@ TEST_P(RealValueKind, FromIntegerPowersOfTwo) {
 TYPED_TEST(RealValueHostTypedKind, CompareUnaryWithHost) {
   using HostT = typename TypeParam::HostT;
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   union {
     UnsignedT ui;
@@ -1087,7 +1097,7 @@ TYPED_TEST(RealValueHostTypedKind, CompareUnaryWithHost) {
     }
 
     // Every value is emitted as a Fortran constant that reads back exactly.
-    const std::string kindSuffix{std::to_string(kind)};
+    const std::string kindSuffix{std::to_string(static_cast<int>(kind))};
     std::string text{AsFortranString(x, kind, false)};
     if (std::isnan(f)) {
       EXPECT_EQ("(0._" + kindSuffix + "/0.)", text);
@@ -1110,7 +1120,7 @@ TYPED_TEST(RealValueHostTypedKind, CompareUnaryWithHost) {
 TYPED_TEST(RealValueHostTypedKind, CompareDyadicWithHost) {
   using HostT = typename TypeParam::HostT;
   using UnsignedT = typename TypeParam::UnsignedT;
-  constexpr int kind{TypeParam::kind};
+  constexpr KindsEnum kind{TypeParam::kindsEnum};
 
   union {
     UnsignedT ui;
diff --git a/flang/unittests/Evaluate/expression.cpp b/flang/unittests/Evaluate/expression.cpp
index 8d570f517bc1f..abc09ccc55c78 100644
--- a/flang/unittests/Evaluate/expression.cpp
+++ b/flang/unittests/Evaluate/expression.cpp
@@ -12,12 +12,13 @@
 using namespace Fortran::evaluate;
 
 static Expr<Type<TypeCategory::Integer>> MakeDefaultIntegerExpr(int32_t v) {
-  return MakeConstantExpr<Type<TypeCategory::Integer>>(4, v);
+  return MakeConstantExpr<Type<TypeCategory::Integer>>(
+      Fortran::common::KindsEnum::Kind4, v);
 }
 
 int main() {
   using DefaultIntegerExpr = Expr<Type<TypeCategory::Integer>>;
-  TEST(DefaultIntegerExpr::Result{4}.AsFortran() == "INTEGER(4)");
+  TEST(DefaultIntegerExpr::Result{Kind4}.AsFortran() == "INTEGER(4)");
   MATCH("666_4", MakeDefaultIntegerExpr(666).AsFortran());
   MATCH("-1_4", (-MakeDefaultIntegerExpr(1)).AsFortran());
   auto ex1{MakeDefaultIntegerExpr(2) +
diff --git a/flang/unittests/Evaluate/folding.cpp b/flang/unittests/Evaluate/folding.cpp
index 7837e32df6994..161e64b99a76c 100644
--- a/flang/unittests/Evaluate/folding.cpp
+++ b/flang/unittests/Evaluate/folding.cpp
@@ -9,6 +9,7 @@
 #include "flang/Testing/testing.h"
 #include <tuple>
 
+using namespace Fortran::common;
 using namespace Fortran::evaluate;
 
 // helper to call functions on all types from tuple
@@ -16,7 +17,7 @@ template <typename... T> struct RunOnTypes {};
 template <typename Test, typename... T>
 struct RunOnTypes<Test, std::tuple<T...>> {
   template <typename U> static void RunOnKinds() {
-    for (int kind : KindsByType<U::category>::kinds) {
+    for (KindsEnum kind : KindsByType<U::category>::kinds) {
       Test::template Run<U>(kind);
     }
   }
@@ -26,7 +27,7 @@ struct RunOnTypes<Test, std::tuple<T...>> {
 
 // test for fold.h GetScalarConstantValue function
 struct TestGetScalarConstantValue {
-  template <typename T> static void Run(int kind) {
+  template <typename T> static void Run(KindsEnum kind) {
     Expr<T> exprFullyTyped{MakeZeroExpr<T>(kind)};
     Expr<SomeKind<T::category>> exprSomeKind{exprFullyTyped};
     Expr<SomeType> exprSomeType{exprSomeKind};
@@ -39,7 +40,7 @@ struct TestGetScalarConstantValue {
 template <typename T>
 Scalar<T> CallHostRt(
     HostRuntimeWrapper func, FoldingContext &context, Scalar<T> x) {
-  const int kind{x.kind()};
+  const KindsEnum kind{x.kind()};
   return GetScalarConstantValue<T>(
       func(context, {AsGenericExpr(Constant<T>{kind, x})}))
       .value();
@@ -68,7 +69,8 @@ void TestHostRuntimeSubnormalFlushing() {
     // Test subnormal argument flushing
     if (auto callable{GetHostRuntimeWrapper("log", r4, {r4})}) {
       // Biggest IEEE 32bits subnormal power of two
-      const Scalar<FR4> x1{4, Scalar<FR4>::Word{4, 0x00400000}};
+      const Scalar<FR4> x1{
+          KindsEnum::Kind4, Scalar<FR4>::Word{KindsEnum::Kind4, 0x00400000}};
       Scalar<FR4> y1Flushing{CallHostRt<FR4>(*callable, flushingContext, x1)};
       Scalar<FR4> y1NoFlushing{
           CallHostRt<FR4>(*callable, noFlushingContext, x1)};
diff --git a/flang/unittests/Evaluate/intrinsics.cpp b/flang/unittests/Evaluate/intrinsics.cpp
index 69c8aa4246950..f8fa90d4fba67 100644
--- a/flang/unittests/Evaluate/intrinsics.cpp
+++ b/flang/unittests/Evaluate/intrinsics.cpp
@@ -44,7 +44,7 @@ class CookedStrings {
 };
 
 template <typename A> auto Const(A &&x) -> Constant<TypeOf<A>> {
-  const int kind{x.kind()};
+  const common::KindsEnum kind{x.kind()};
   return Constant<TypeOf<A>>{kind, std::move(x)};
 }
 
@@ -148,76 +148,80 @@ struct TestCall {
 
 void TestIntrinsics() {
   common::IntrinsicTypeDefaultKinds defaults;
-  MATCH(4, defaults.GetDefaultKind(TypeCategory::Integer));
-  MATCH(4, defaults.GetDefaultKind(TypeCategory::Real));
+  MATCH(4, static_cast<int>(defaults.GetDefaultKind(TypeCategory::Integer)));
+  MATCH(4, static_cast<int>(defaults.GetDefaultKind(TypeCategory::Real)));
   IntrinsicProcTable table{IntrinsicProcTable::Configure(defaults)};
   table.Dump(llvm::outs());
 
   using Int1 = Type<TypeCategory::Integer>;
-  Int1 int1{1};
+  Int1 int1{Kind1};
   using Int4 = Type<TypeCategory::Integer>;
-  Int4 int4{4};
+  Int4 int4{Kind4};
   using Int8 = Type<TypeCategory::Integer>;
-  Int8 int8{8};
+  Int8 int8{Kind8};
   using Real4 = Type<TypeCategory::Real>;
-  Real4 real4{4};
+  Real4 real4{Kind4};
   using Real8 = Type<TypeCategory::Real>;
-  Real8 real8{8};
+  Real8 real8{Kind8};
   using Complex4 = Type<TypeCategory::Complex>;
-  Complex4 complex4{4};
+  Complex4 complex4{Kind4};
   using Complex8 = Type<TypeCategory::Complex>;
-  Complex8 complex8{8};
+  Complex8 complex8{Kind8};
   using Char = Type<TypeCategory::Character>;
   using Log4 = Type<TypeCategory::Logical>;
 
   TestCall{defaults, table, "bad"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // bad intrinsic name
   TestCall{defaults, table, "abs"}
-      .Push(Named("a", Const(Scalar<Int4>::Zero(4))))
+      .Push(Named("a", Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4))))
       .DoCall(int4.GetType());
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(int4.GetType());
   TestCall{defaults, table, "abs"}
-      .Push(Named("bad", Const(Scalar<Int4>::Zero(4))))
+      .Push(Named("bad", Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4))))
       .DoCall(); // bad keyword
   TestCall{defaults, table, "abs"}.DoCall(); // insufficient args
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // too many args
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
-      .Push(Named("a", Const(Scalar<Int4>::Zero(4))))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
+      .Push(Named("a", Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4))))
       .DoCall();
   TestCall{defaults, table, "abs"}
-      .Push(Named("a", Const(Scalar<Int4>::Zero(4))))
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Named("a", Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4))))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall();
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Int1>::Zero(1)))
+      .Push(Const(Scalar<Int1>::Zero(common::KindsEnum::Kind1)))
       .DoCall(int1.GetType());
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(int4.GetType());
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Int8>::Zero(8)))
+      .Push(Const(Scalar<Int8>::Zero(common::KindsEnum::Kind8)))
       .DoCall(int8.GetType());
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Real4>::Zero(4)))
+      .Push(Const(Scalar<Real4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(real4.GetType());
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Real8>::Zero(8)))
+      .Push(Const(Scalar<Real8>::Zero(common::KindsEnum::Kind8)))
       .DoCall(real8.GetType());
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Complex4>::Zero(4)))
+      .Push(Const(Scalar<Complex4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(real4.GetType());
   TestCall{defaults, table, "abs"}
-      .Push(Const(Scalar<Complex8>::Zero(8)))
+      .Push(Const(Scalar<Complex8>::Zero(common::KindsEnum::Kind8)))
       .DoCall(real8.GetType());
-  TestCall{defaults, table, "abs"}.Push(Const(Scalar<Char>::Zero(1))).DoCall();
-  TestCall{defaults, table, "abs"}.Push(Const(Scalar<Log4>::Zero(4))).DoCall();
+  TestCall{defaults, table, "abs"}
+      .Push(Const(Scalar<Char>::Zero(common::KindsEnum::Kind1)))
+      .DoCall();
+  TestCall{defaults, table, "abs"}
+      .Push(Const(Scalar<Log4>::Zero(common::KindsEnum::Kind4)))
+      .DoCall();
 
   // "Ext" in names for calls allowed as extensions
   TestCall maxCallR{defaults, table, "max"}, maxCallI{defaults, table, "min"},
@@ -226,14 +230,14 @@ void TestIntrinsics() {
       max0ExtCall{defaults, table, "max0"},
       amin1ExtCall{defaults, table, "amin1"};
   for (int j{0}; j < 10; ++j) {
-    maxCallR.Push(Const(Scalar<Real4>::Zero(4)));
-    maxCallI.Push(Const(Scalar<Int4>::Zero(4)));
-    max0Call.Push(Const(Scalar<Int4>::Zero(4)));
-    max0ExtCall.Push(Const(Scalar<Real4>::Zero(4)));
-    max1Call.Push(Const(Scalar<Real4>::Zero(4)));
-    amin0Call.Push(Const(Scalar<Int4>::Zero(4)));
-    amin1ExtCall.Push(Const(Scalar<Int4>::Zero(4)));
-    amin1Call.Push(Const(Scalar<Real4>::Zero(4)));
+    maxCallR.Push(Const(Scalar<Real4>::Zero(common::KindsEnum::Kind4)));
+    maxCallI.Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)));
+    max0Call.Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)));
+    max0ExtCall.Push(Const(Scalar<Real4>::Zero(common::KindsEnum::Kind4)));
+    max1Call.Push(Const(Scalar<Real4>::Zero(common::KindsEnum::Kind4)));
+    amin0Call.Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)));
+    amin1ExtCall.Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)));
+    amin1Call.Push(Const(Scalar<Real4>::Zero(common::KindsEnum::Kind4)));
   }
   maxCallR.DoCall(real4.GetType());
   maxCallI.DoCall(int4.GetType());
@@ -245,67 +249,68 @@ void TestIntrinsics() {
   amin1ExtCall.DoCall(real4.GetType());
 
   TestCall{defaults, table, "conjg"}
-      .Push(Const(Scalar<Complex4>::Zero(4)))
+      .Push(Const(Scalar<Complex4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(complex4.GetType());
   TestCall{defaults, table, "conjg"}
-      .Push(Const(Scalar<Complex8>::Zero(8)))
+      .Push(Const(Scalar<Complex8>::Zero(common::KindsEnum::Kind8)))
       .DoCall(complex8.GetType());
   TestCall{defaults, table, "dconjg"}
-      .Push(Const(Scalar<Complex8>::Zero(8)))
+      .Push(Const(Scalar<Complex8>::Zero(common::KindsEnum::Kind8)))
       .DoCall(complex8.GetType());
 
   TestCall{defaults, table, "float"}
-      .Push(Const(Scalar<Real4>::Zero(4)))
+      .Push(Const(Scalar<Real4>::Zero(common::KindsEnum::Kind4)))
       .DoCall();
   TestCall{defaults, table, "float"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(real4.GetType());
   TestCall{defaults, table, "idint"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall();
   TestCall{defaults, table, "idint"}
-      .Push(Const(Scalar<Real8>::Zero(8)))
+      .Push(Const(Scalar<Real8>::Zero(common::KindsEnum::Kind8)))
       .DoCall(int4.GetType());
 
   // Allowed as extensions
   TestCall{defaults, table, "float"}
-      .Push(Const(Scalar<Int8>::Zero(8)))
+      .Push(Const(Scalar<Int8>::Zero(common::KindsEnum::Kind8)))
       .DoCall(real4.GetType());
   TestCall{defaults, table, "idint"}
-      .Push(Const(Scalar<Real4>::Zero(4)))
+      .Push(Const(Scalar<Real4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(int4.GetType());
 
   TestCall{defaults, table, "num_images"}.DoCall(int4.GetType());
   TestCall{defaults, table, "num_images"}
-      .Push(Const(Scalar<Int1>::Zero(1)))
+      .Push(Const(Scalar<Int1>::Zero(common::KindsEnum::Kind1)))
       .DoCall(int4.GetType());
   TestCall{defaults, table, "num_images"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(int4.GetType());
   TestCall{defaults, table, "num_images"}
-      .Push(Const(Scalar<Int8>::Zero(8)))
+      .Push(Const(Scalar<Int8>::Zero(common::KindsEnum::Kind8)))
       .DoCall(int4.GetType());
   TestCall{defaults, table, "num_images"}
-      .Push(Named("team_number", Const(Scalar<Int4>::Zero(4))))
+      .Push(Named(
+          "team_number", Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4))))
       .DoCall(int4.GetType());
   TestCall{defaults, table, "num_images"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // too many args
   TestCall{defaults, table, "num_images"}
-      .Push(Named("bad", Const(Scalar<Int4>::Zero(4))))
+      .Push(Named("bad", Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4))))
       .DoCall(); // bad keyword
   TestCall{defaults, table, "num_images"}
-      .Push(Const(Scalar<Char>::Zero(1)))
+      .Push(Const(Scalar<Char>::Zero(common::KindsEnum::Kind1)))
       .DoCall(); // bad type
   TestCall{defaults, table, "num_images"}
-      .Push(Const(Scalar<Log4>::Zero(4)))
+      .Push(Const(Scalar<Log4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // bad type
   TestCall{defaults, table, "num_images"}
-      .Push(Const(Scalar<Complex8>::Zero(8)))
+      .Push(Const(Scalar<Complex8>::Zero(common::KindsEnum::Kind8)))
       .DoCall(); // bad type
   TestCall{defaults, table, "num_images"}
-      .Push(Const(Scalar<Real4>::Zero(4)))
+      .Push(Const(Scalar<Real4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // bad type
 
   // This test temporarily removed because it requires access to
@@ -314,26 +319,26 @@ void TestIntrinsics() {
   // TestCall{defaults, table, "team_number"}.DoCall(int4.GetType());
 
   TestCall{defaults, table, "team_number"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // too many args
   TestCall{defaults, table, "team_number"}
-      .Push(Named("bad", Const(Scalar<Int4>::Zero(4))))
+      .Push(Named("bad", Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4))))
       .DoCall(); // bad keyword
   TestCall{defaults, table, "team_number"}
-      .Push(Const(Scalar<Int4>::Zero(4)))
+      .Push(Const(Scalar<Int4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // bad type
   TestCall{defaults, table, "team_number"}
-      .Push(Const(Scalar<Char>::Zero(1)))
+      .Push(Const(Scalar<Char>::Zero(common::KindsEnum::Kind1)))
       .DoCall(); // bad type
   TestCall{defaults, table, "team_number"}
-      .Push(Const(Scalar<Log4>::Zero(4)))
+      .Push(Const(Scalar<Log4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // bad type
   TestCall{defaults, table, "team_number"}
-      .Push(Const(Scalar<Complex8>::Zero(4)))
+      .Push(Const(Scalar<Complex8>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // bad type
   TestCall{defaults, table, "team_number"}
-      .Push(Const(Scalar<Real4>::Zero(4)))
+      .Push(Const(Scalar<Real4>::Zero(common::KindsEnum::Kind4)))
       .DoCall(); // bad type
 
   // TODO: test other intrinsics
diff --git a/flang/unittests/Evaluate/logical.cpp b/flang/unittests/Evaluate/logical.cpp
index c8bf850d6c08e..a58082a9db70f 100644
--- a/flang/unittests/Evaluate/logical.cpp
+++ b/flang/unittests/Evaluate/logical.cpp
@@ -2,13 +2,13 @@
 #include "flang/Testing/testing.h"
 #include <cstdio>
 
-static void testKind(int kind) {
+static void testKind(Fortran::common::KindsEnum kind) {
   using Type = Fortran::evaluate::Type<Fortran::common::TypeCategory::Logical>;
   TEST(Fortran::evaluate::IsSpecificIntrinsicType<Type>);
   TEST(Type::category == Fortran::common::TypeCategory::Logical);
   TEST(Type{kind}.kind() == kind);
   using Value = Fortran::evaluate::Scalar<Type>;
-  MATCH(8 * kind, Value::Zero(kind).bits());
+  MATCH(8 * static_cast<int>(kind), Value::Zero(kind).bits());
   TEST(!Value{}.IsTrue());
   TEST(!Value(kind, false).IsTrue());
   TEST(Value(kind, true).IsTrue());
@@ -33,9 +33,10 @@ static void testKind(int kind) {
 }
 
 int main() {
-  testKind(1);
-  testKind(2);
-  testKind(4);
-  testKind(8);
+  using Fortran::common::KindsEnum;
+  testKind(KindsEnum::Kind1);
+  testKind(KindsEnum::Kind2);
+  testKind(KindsEnum::Kind4);
+  testKind(KindsEnum::Kind8);
   return testing::Complete();
 }



More information about the llvm-branch-commits mailing list