diff --git a/llvm/include/llvm/ADT/SemanticTypedef.h b/llvm/include/llvm/ADT/SemanticTypedef.h
new file mode 100644
--- /dev/null
+++ b/llvm/include/llvm/ADT/SemanticTypedef.h
@@ -0,0 +1,390 @@
+//===-- SemanticTypedef.h - Typedefs for a type-safe world ------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// This file provides utilities for creating semantic typedefs.
+///
+/// Semantic typedefs are useful when one wants to distinguish between types
+/// that otherwise would have the same signature.  For example:
+///
+///   void foo(int Size, int Alignment);
+///
+/// It is easy to forget the order of arguments.  It would be safer to have
+/// something like:
+///
+///   void frobnicate(SizeType Size, AlignmentType Alignment);
+///
+/// But we can do even better.  Are Size and Alignment specified in bits or
+/// bytes?  We could make it explicit:
+///
+/// void frobnicate(BitSizeType Size, ByteAlignmentType Alignment);
+///
+/// However, defining BitSizeSizeType and BytesAlignmentType (and ByteDistance
+/// and VectorElementLength and...)  is tedious, cumbersome and error-prone.
+/// The semantic typedef utilites here make this at least a little bit easier.
+///
+/// Typical use is as follows:
+///
+///   class MyInt : public SemanticTypedef<MyInt, int> {
+///     using SemanticTypedef<MyInt, int>::SemanticTypedef;
+///   };
+///
+///   MyInt a(10);
+///
+///   int A = int(MyInt);
+///
+/// It is not required that SemanticTypedef be used with CRTP, but it is often
+/// most conveninent.  One may use any type as a SemanticTypedef tag:
+///
+///   struct MyIntTag {};
+///   using MyInt = SemanticTypedef<MyIntTag, int>;
+///
+/// A large downside to not using CRTP is that it's not possible to use the
+/// pre-defined mixin classes to add operators.  One has to define each operator
+/// specially for the new type.
+///
+/// Using the same tag again will make a second alias interoperate with the first
+/// but still prevent implicit conversions to the underlying type:
+///
+///   using MyOtherInt = SemanticTypedef<MyIntTag, int>;
+///
+/// Of course this removes some safety:
+///
+///   void foo(MyInt expected);
+///
+///   MyOtherInt maybeWrong(10);
+///   foo(maybeWrong);  // Oops?
+///
+/// Metafunctions exist to obtain the underlying type and tag of a typedef:
+///
+///   using Underlying = UnderlyingType<MyInt>;
+///   using Tag        = TagType<MyInt>;
+///
+/// The base SemanticTypedef class does not provide any functionality other than
+/// explicit conversion between it and its underlying type along with defaulted
+/// constructors and assignment operators.  Mixins are used to imbue objects
+/// with additional features:
+///
+///   class MyInt : public SemanticTypedef<MyInt, int>,
+///                 public LeCmp<MyInt>  {
+///     using SemanticTypedef::SemanticTypedef;
+///   };
+///
+///   MyInt a(10);
+///   MyInt b(20);
+///
+///   bool LtTrue  = a < b;
+///   bool GeTrue  = b >= a;
+///   bool LtFalse = b < a;
+///   bool GeFalse = a >= b;
+///
+/// Inspired by Jonathan Mueller's post on strong typedef:
+///
+/// https://foonathan.net/blog/2016/10/19/strong-typedefs.html
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_ADT_SEMANTICTYPEDEF_H
+#define LLVM_ADT_SEMANTICTYPEDEF_H
+
+#include "llvm/Support/raw_ostream.h"
+
+#include <type_traits>
+#include <utility>
+
+namespace llvm {
+
+////////////////////////////////////////////////////////////////////////////////
+// Define SemanticTypedef.
+////////////////////////////////////////////////////////////////////////////////
+
+/// This is the basic SemanticTypedef template, providing only explicit
+/// conversion between itself and its underlying type.
+template<typename Tag, typename T>
+class SemanticTypedef {
+  T Value;
+
+public:
+  constexpr SemanticTypedef() : Value() {}
+  explicit SemanticTypedef(const T &V)
+      noexcept(std::is_nothrow_copy_constructible<T>::value) :
+      Value(V) {}
+  explicit SemanticTypedef(T &&V)
+      noexcept(std::is_nothrow_move_constructible<T>::value) :
+      Value(std::move(V)) {}
+
+  explicit operator T &() noexcept {
+    return Value;
+  }
+
+  explicit operator const T &() const noexcept {
+    return Value;
+  }
+
+  friend void swap(SemanticTypedef &A, SemanticTypedef &B) {
+    using std::swap;
+    swap(static_cast<T&>(A), static_cast<T&>(B));
+  }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Internal utilities.
+////////////////////////////////////////////////////////////////////////////////
+
+namespace detail {
+/// Implementation of UnderlyingType.
+template<typename Tag, typename T>
+T UnderlyingTypeImpl(const SemanticTypedef<Tag, T> &);
+
+/// Implementation of TagType.
+template<typename Tag, typename T>
+Tag TagTypeImpl(const SemanticTypedef<Tag, T> &);
+}  // end namespace detail
+
+/// Get the underlying type of a semantic typedef.
+template<typename T>
+using UnderlyingType = decltype(detail::UnderlyingTypeImpl(std::declval<T>()));
+
+/// Get the tag of a semantic typedef.
+template<typename T>
+using TagType = decltype(detail::TagTypeImpl(std::declval<T>()));
+
+namespace detail {
+/// Implmentation of getValue.
+template<typename Tag, typename T>
+T &getValue(SemanticTypedef<Tag, T> &V) {
+  return static_cast<T &>(V);
+}
+
+/// Get the wrapped value of a semantic typedef object.
+template<typename Tag, typename T>
+const T &getValue(const SemanticTypedef<Tag, T> &V) {
+  return static_cast<const T &>(V);
+}
+}  // end namespace detail
+
+////////////////////////////////////////////////////////////////////////////////
+// Define mixins to add functionality to typedefs.
+////////////////////////////////////////////////////////////////////////////////
+
+// The operators shouldn't take a SemanticTypedef<Tag, T> because then any
+// SemanticTypedef would match and we want to restrict these to the specific
+// typedef that opted into them.  In order to do that, we define them as friends
+// within the mixin class.  This makes them accessible only by ADL from the type
+// that defined them (i.e. the typedef subclass).  See the referenced blog post
+// above.
+//
+// Define unary operators.
+
+// Operator(Typedef)
+
+/// Define a macro to create unary operators.
+#define TYPEDEF_UNARY_OP(Op)                                    \
+  friend ResultType operator Op(const Typedef &O) {             \
+    return ResultType(                                          \
+        Op detail::getValue(O));                                \
+  }
+
+/// Define a macro to declare binary operators and the base class.
+#define MAKE_TYPEDEF_UNARY_OP(Name, Op, Result)                 \
+  /* Operator(Typedef) */                                       \
+  template<typename Typedef, typename ResultType = Result>      \
+  struct Name {                                                 \
+    TYPEDEF_UNARY_OP(Op)                                        \
+  };
+
+// Define binary operators.
+
+// Operator(Typedef, Typedef)
+
+/// Define a macro to create straightforward binary operators.
+#define TYPEDEF_TYPEDEF_BINARY_OP(Op)                           \
+  friend ResultType operator Op(const Typedef &LHS,             \
+                                const Typedef &RHS) {           \
+    return ResultType(                                          \
+        detail::getValue(LHS) Op detail::getValue(RHS));        \
+  }
+
+/// Define a macro to create binary operators that modify and return the LHS.
+#define TYPEDEF_TYPEDEF_BINARY_OPEQ(Op)                         \
+  friend Typedef &operator Op(Typedef &LHS,                     \
+                              const Typedef &RHS) {             \
+    detail::getValue(LHS) Op detail::getValue(RHS);             \
+    return LHS;                                                 \
+  }
+
+/// Define a macro to declare binary operators and the base class.
+#define MAKE_TYPEDEF_TYPEDEF_BINARY_OP(Name, Op, Result)                \
+  /* Operator(Typedef, Typedef) */                                      \
+  template<typename Typedef, typename ResultType = Result>              \
+  struct Name {                                                         \
+    TYPEDEF_TYPEDEF_BINARY_OP(Op)                                       \
+  };
+
+/// Define a macro to declare binary operators and the base class.
+#define MAKE_TYPEDEF_TYPEDEF_OPEQ(Name, Op)                             \
+  /* Operator(Typedef, Typedef) */                                      \
+  template<typename Typedef>                                            \
+  struct Name {                                                         \
+    TYPEDEF_TYPEDEF_BINARY_OPEQ(Op)                                     \
+  };
+
+// Define operators
+namespace typedef_operators {
+MAKE_TYPEDEF_UNARY_OP(LogNot, !, bool)
+MAKE_TYPEDEF_UNARY_OP(BitNot, ~, Typedef)
+MAKE_TYPEDEF_UNARY_OP(Pos, +, Typedef)
+MAKE_TYPEDEF_UNARY_OP(Neg, -, Typedef)
+
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(LtCmp, <, bool)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(LeCmp, <=, bool)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(EqCmp, ==, bool)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(NeCmp, !=, bool)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(GeCmp, >=, bool)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(GtCmp, >, bool)
+
+// Note: Not short-circuiting!
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(LogAnd, &&, bool)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(LogOr, ||, bool)
+
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(BitAnd, &, Typedef)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(BitOr, |, Typedef)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(BitXor, ^, Typedef)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(BitShl, <<, Typedef)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(BitShr, >>, Typedef)
+
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(Add, +, Typedef)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(Sub, -, Typedef)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(Mul, *, Typedef)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(Div, /, Typedef)
+MAKE_TYPEDEF_TYPEDEF_BINARY_OP(Mod, %, Typedef)
+
+/// Provide add-assignment operators to semantic typedefs.
+MAKE_TYPEDEF_TYPEDEF_OPEQ(AddAssign, +=)
+MAKE_TYPEDEF_TYPEDEF_OPEQ(SubAssign, -=)
+MAKE_TYPEDEF_TYPEDEF_OPEQ(MulAssign, *=)
+MAKE_TYPEDEF_TYPEDEF_OPEQ(DivAssign, /=)
+MAKE_TYPEDEF_TYPEDEF_OPEQ(ModAssign, %=)
+MAKE_TYPEDEF_TYPEDEF_OPEQ(BitAndAssign, &=)
+MAKE_TYPEDEF_TYPEDEF_OPEQ(BitOrAssign, |=)
+MAKE_TYPEDEF_TYPEDEF_OPEQ(BitXorAssign, ^=)
+MAKE_TYPEDEF_TYPEDEF_OPEQ(BitShlAssign, <<=)
+MAKE_TYPEDEF_TYPEDEF_OPEQ(BitShrAssign, >>=)
+
+/// Provide increment operators to semantic typedefs.
+template<typename Typedef>
+struct Increment {
+  Typedef &operator++() {
+    ++detail::getValue(static_cast<Typedef &>(*this));
+    return static_cast<Typedef &>(*this);
+  }
+
+  Typedef operator++(int) {
+    Typedef ReturnValue(static_cast<Typedef &>(*this));
+    ++*this;
+    return ReturnValue;
+  }
+};
+
+/// Provide decrement operators to semantic typedefs.
+template<typename Typedef>
+struct Decrement {
+  Typedef &operator--() {
+    --detail::getValue(static_cast<Typedef &>(*this));
+    return static_cast<Typedef &>(*this);
+  }
+
+  Typedef operator--(int) {
+    Typedef ReturnValue(static_cast<Typedef &>(*this));
+    --*this;
+    return ReturnValue;
+  }
+};
+
+/// Provide streaming operators to semantic typedefs.
+template<typename Typedef>
+struct StreamOutput {
+  friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+                                       const Typedef &V) {
+    return OS << detail::getValue(V);
+  }
+};
+
+/// Provide arithmetic to semantic typedefs.
+template<typename Typedef>
+struct Arithmetic : private Pos<Typedef>,
+                    private Neg<Typedef>,
+                    private Add<Typedef>,
+                    private Sub<Typedef>,
+                    private Mul<Typedef>,
+                    private Div<Typedef>,
+                    private Mod<Typedef> {};
+
+/// Provide arithmetic-assign operators to semantic typedefs.
+template<typename Typedef>
+struct ArithmeticAssign : private AddAssign<Typedef>,
+                          private SubAssign<Typedef>,
+                          private MulAssign<Typedef>,
+                          private DivAssign<Typedef>,
+                          private ModAssign<Typedef> {};
+
+/// Provide increment and decrement to semantic typedefs.
+template<typename Typedef>
+struct IncDec : public Increment<Typedef>,
+                public Decrement<Typedef> {};
+
+/// Provide bitwise operators to semantic typedefs.
+template<typename Typedef>
+struct Bitwise : private BitNot<Typedef>,
+                 private BitAnd<Typedef>,
+                 private BitOr<Typedef>,
+                 private BitXor<Typedef>,
+                 private BitShl<Typedef>,
+                 private BitShr<Typedef> {};
+
+/// Provide bitwise-assign operators to semantic typedefs.
+template<typename Typedef>
+struct BitwiseAssign : private BitAndAssign<Typedef>,
+                       private BitOrAssign<Typedef>,
+                       private BitXorAssign<Typedef>,
+                       private BitShlAssign<Typedef>,
+                       private BitShrAssign<Typedef> {};
+
+/// Provide logical operators to semantic typedefs.
+template<typename Typedef>
+struct Logical : private LogNot<Typedef>,
+                 private LogAnd<Typedef>,
+                 private LogOr<Typedef> {};
+
+/// Provide operators useful for sorting to semantic typedefs.
+template<typename Typedef>
+struct Sortable : private LtCmp<Typedef> {};
+
+/// Provide compare operators to semantic typedefs.
+template<typename Typedef>
+struct Compare : private Sortable<Typedef>,
+                 private LeCmp<Typedef>,
+                 private EqCmp<Typedef>,
+                 private NeCmp<Typedef>,
+                 private GeCmp<Typedef>,
+                 private GtCmp<Typedef> {};
+}  // end namespace typedef_operators
+
+#undef TYPEDEF_UNARY_OP
+#undef MAKE_TYPEDEFUNARY_OP
+#undef TYPEDEF_TYPEDEF_BINARY_OP
+#undef TYPEDEF_TYPEDEF_BINARY_OP_ALT
+#undef TYPEDEF_TYPEDEF_BINARY_OPEQ
+#undef MAKE_TYPEDEF_TYPEDEF_BINARY_OP
+#undef MAKE_TYPEDEF_TYPEDEF_OPEQ
+
+}  // end namespace llvm
+
+#endif
diff --git a/llvm/unittests/ADT/CMakeLists.txt b/llvm/unittests/ADT/CMakeLists.txt
--- a/llvm/unittests/ADT/CMakeLists.txt
+++ b/llvm/unittests/ADT/CMakeLists.txt
@@ -52,6 +52,7 @@
   SCCIteratorTest.cpp
   STLExtrasTest.cpp
   ScopeExitTest.cpp
+  SemanticTypedef.cpp
   SequenceTest.cpp
   SetVectorTest.cpp
   SimpleIListTest.cpp
diff --git a/llvm/unittests/ADT/SemanticTypedef.cpp b/llvm/unittests/ADT/SemanticTypedef.cpp
new file mode 100644
--- /dev/null
+++ b/llvm/unittests/ADT/SemanticTypedef.cpp
@@ -0,0 +1,320 @@
+//===- unittests/ADT/SemanticTypedef.cpp - Semantic typedef tests ---------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/SemanticTypedef.h"
+#include "llvm/ADT/SmallString.h"
+
+#include "gtest/gtest.h"
+
+#include <cstdint>
+#include <type_traits>
+
+using namespace llvm;
+
+namespace {
+using namespace typedef_operators;
+
+class SemanticTypedefTest : public testing::Test {
+public:
+  // Interoperates with MyInt.
+  class MyInt : public SemanticTypedef<MyInt, int>,
+                private Arithmetic<MyInt>,
+                private ArithmeticAssign<MyInt>,
+                public IncDec<MyInt>,
+                private Bitwise<MyInt>,
+                private BitwiseAssign<MyInt>,
+                private Compare<MyInt>,
+                private StreamOutput<MyInt> {
+  public:
+    using SemanticTypedef<MyInt, int>::SemanticTypedef;
+  };
+
+  class MyOtherInt : public SemanticTypedef<MyOtherInt, int>,
+                     private Arithmetic<MyOtherInt>,
+                     private ArithmeticAssign<MyOtherInt>,
+                     public IncDec<MyOtherInt>,
+                     private Bitwise<MyOtherInt>,
+                     private BitwiseAssign<MyOtherInt>,
+                     private Compare<MyOtherInt>,
+                     private StreamOutput<MyOtherInt> {
+  public:
+    using SemanticTypedef<MyOtherInt, int>::SemanticTypedef;
+  };
+
+  struct NonCRTPInt {};
+  using MyNonCRTPInt = SemanticTypedef<NonCRTPInt, int>;
+
+  using IntBaseType = UnderlyingType<MyInt>;
+
+  // Interoperates with MyBool.
+  class MyBool : public SemanticTypedef<MyBool, bool>,
+                 private EqCmp<MyBool>,
+                 private Logical<MyBool>,
+                 StreamOutput<MyBool> {
+  public:
+    using SemanticTypedef<MyBool, bool>::SemanticTypedef;
+  };
+
+  using BoolBaseType = UnderlyingType<MyBool>;
+
+  SemanticTypedefTest() {}
+
+  // Test copy constructor.
+  MyInt foo(MyInt a) {
+    return a;
+  }
+
+  MyNonCRTPInt foo(MyNonCRTPInt a) {
+    return a;
+  }
+};
+
+TEST_F(SemanticTypedefTest, Conversions) {
+  MyInt a(10);
+  MyOtherInt b(20);
+  MyNonCRTPInt c(30);
+
+  EXPECT_EQ(IntBaseType(a), 10);
+  EXPECT_EQ(IntBaseType(b), 20);
+
+  bool aToUnderlying = std::is_convertible<decltype(a),
+                                           UnderlyingType<decltype(a)>>::value;
+  bool underlyingToa = std::is_convertible<UnderlyingType<decltype(a)>,
+                                           decltype(a)>::value;
+
+  EXPECT_FALSE(aToUnderlying);
+  EXPECT_FALSE(underlyingToa);
+
+  bool aTob = std::is_convertible<decltype(a), decltype(b)>::value;
+  bool bToa = std::is_convertible<decltype(b), decltype(a)>::value;
+
+  EXPECT_FALSE(aTob);
+  EXPECT_FALSE(bToa);
+
+  bool aToc = std::is_convertible<decltype(a), decltype(c)>::value;
+  bool cToa = std::is_convertible<decltype(c), decltype(a)>::value;
+
+  EXPECT_FALSE(aToc);
+  EXPECT_FALSE(cToa);
+}
+
+TEST_F(SemanticTypedefTest, Copy) {
+  int aValue = 10;
+  int bValue = 20;
+
+  MyInt a(aValue);
+  MyNonCRTPInt b(bValue);
+
+  MyInt c(foo(a));
+  MyNonCRTPInt d(foo(b));
+
+  EXPECT_EQ(IntBaseType(c), aValue);
+  EXPECT_EQ(IntBaseType(d), bValue);
+}
+
+TEST_F(SemanticTypedefTest, Assign) {
+  int aValue = 10;
+  int bValue = 20;
+
+  MyInt a(10);
+  MyInt b(20);
+
+  aValue = bValue;
+  a = b;
+
+  EXPECT_EQ(IntBaseType(a), aValue);
+}
+
+TEST_F(SemanticTypedefTest, Swap) {
+  int aValue = 10;
+  int bValue = 20;
+
+  MyInt a(aValue);
+  MyInt b(bValue);
+
+  std::swap(aValue, bValue);
+  swap(a, b);
+
+  EXPECT_EQ(IntBaseType(a), aValue);
+  EXPECT_EQ(IntBaseType(b), bValue);
+}
+
+TEST_F(SemanticTypedefTest, Arithmetic) {
+  int aValue = 10;
+  int bValue = 20;
+
+  MyInt a(aValue);
+  MyInt b(bValue);
+
+  EXPECT_EQ(IntBaseType(+a), aValue);
+  EXPECT_EQ(IntBaseType(-a), -aValue);
+  EXPECT_EQ(IntBaseType(a + b), aValue + bValue);
+  EXPECT_EQ(IntBaseType(a - b), aValue - bValue);
+  EXPECT_EQ(IntBaseType(a * b), aValue * bValue);
+  EXPECT_EQ(IntBaseType(a % b), aValue % bValue);
+
+  a += b;
+  aValue += bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+
+  a -= b;
+  aValue -= bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+
+  a *= b;
+  aValue *= bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+
+  a /= b;
+  aValue /= bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+
+  a %= b;
+  aValue %= bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+}
+
+TEST_F(SemanticTypedefTest, IncDec) {
+  MyInt a(0);
+
+  ASSERT_EQ(++a, MyInt(1));
+  ASSERT_EQ(a++, MyInt(1));
+  ASSERT_EQ(a, MyInt(2));
+
+  ASSERT_EQ(--a, MyInt(1));
+  ASSERT_EQ(a--, MyInt(1));
+  ASSERT_EQ(a, MyInt(0));
+}
+
+TEST_F(SemanticTypedefTest, Bitwise) {
+  int aValue = 10;
+  int bValue = 20;
+
+  MyInt a(aValue);
+  MyInt b(bValue);
+
+  EXPECT_EQ(IntBaseType(~a), ~aValue);
+  EXPECT_EQ(IntBaseType(a & b), aValue & bValue);
+  EXPECT_EQ(IntBaseType(a | b), aValue | bValue);
+  EXPECT_EQ(IntBaseType(a ^ b), aValue ^ bValue);
+  EXPECT_EQ(IntBaseType(a << b), aValue << bValue);
+  EXPECT_EQ(IntBaseType(a >> b), aValue >> bValue);
+
+  a &= b;
+  aValue &= bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+
+  a |= b;
+  aValue |= bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+
+  a ^= b;
+  aValue ^= bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+
+  a <<= b;
+  aValue <<= bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+
+  a >>= b;
+  aValue >>= bValue;
+  EXPECT_EQ(IntBaseType(a), aValue);
+}
+
+TEST_F(SemanticTypedefTest, Compare) {
+  int aValue = 10;
+  int bValue = 20;
+
+  MyInt a(aValue);
+  MyInt b(bValue);
+
+  EXPECT_EQ(IntBaseType(a < b), aValue < bValue);
+  EXPECT_EQ(IntBaseType(a <= b), aValue <= bValue);
+  EXPECT_EQ(IntBaseType(a == b), aValue == bValue);
+  EXPECT_EQ(IntBaseType(a != b), aValue != bValue);
+  EXPECT_EQ(IntBaseType(a >= b), aValue >= bValue);
+  EXPECT_EQ(IntBaseType(a > b), aValue > bValue);
+}
+
+TEST_F(SemanticTypedefTest, LessThan) {
+  MyInt a(10);
+  MyInt b(20);
+
+  EXPECT_TRUE(a < b);
+  EXPECT_FALSE(b < a);
+  EXPECT_FALSE(a < a);
+  EXPECT_FALSE(a >= b);
+  EXPECT_TRUE(b >= a);
+  EXPECT_TRUE(a >= a);
+}
+
+TEST_F(SemanticTypedefTest, IntStreamOutput) {
+  int aValue = 10;
+  MyInt a(aValue);
+
+  SmallString<5> aValueOutput;
+  SmallString<5> aOutput;
+
+  raw_svector_ostream aValueOS(aOutput);
+  raw_svector_ostream aOS(aOutput);
+
+  aValueOS << aValue;
+  aOS << a;
+
+  ASSERT_EQ(aOS.str(), aValueOS.str());
+}
+
+TEST_F(SemanticTypedefTest, Logical) {
+  bool aValue = true;
+  bool bValue = true;
+
+  MyBool a(aValue);
+  MyBool b(bValue);
+
+  EXPECT_EQ(a && a, aValue && aValue);
+  EXPECT_EQ(a && b, aValue && bValue);
+  EXPECT_EQ(b && a, bValue && aValue);
+  EXPECT_EQ(b && b, bValue && bValue);
+
+  EXPECT_EQ(a || a, aValue || aValue);
+  EXPECT_EQ(a || b, aValue || bValue);
+  EXPECT_EQ(b || a, bValue || aValue);
+  EXPECT_EQ(b || b, bValue || bValue);
+
+  EXPECT_EQ(!a, !aValue);
+  EXPECT_EQ(!b, !bValue);
+}
+
+TEST_F(SemanticTypedefTest, BoolStreamOutput) {
+  bool aValue = true;
+  bool bValue = true;
+
+  MyBool a(aValue);
+  MyBool b(bValue);
+
+  SmallString<5> aValueOutput;
+  SmallString<5> aOutput;
+  SmallString<5> bValueOutput;
+  SmallString<5> bOutput;
+
+  raw_svector_ostream aValueOS(aOutput);
+  raw_svector_ostream aOS(aOutput);
+  raw_svector_ostream bValueOS(bOutput);
+  raw_svector_ostream bOS(bOutput);
+
+  aValueOS << aValue;
+  aOS << a;
+  bValueOS << bValue;
+  bOS << b;
+
+  ASSERT_EQ(aOS.str(), aValueOS.str());
+  ASSERT_EQ(bOS.str(), bValueOS.str());
+}
+}  // end anonymous namespace