Introduce a more generic version of IdType for opaque aliases

This one works with strings and other complex types in addition to
integrals.

Bug: 954080
Change-Id: Ic9961e0ccffdce89d160a5b4d8cab9b7d94a100b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1617481
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Ɓukasz Anforowicz <lukasza@chromium.org>
Commit-Queue: Maciej Pawlowski <mpawlowski@opera.com>
Cr-Commit-Position: refs/heads/master@{#662532}
diff --git a/base/util/type_safety/BUILD.gn b/base/util/type_safety/BUILD.gn
index 61ffb6d..011e577 100644
--- a/base/util/type_safety/BUILD.gn
+++ b/base/util/type_safety/BUILD.gn
@@ -10,6 +10,7 @@
 source_set("type_safety") {
   sources = [
     "id_type.h",
+    "strong_alias.h",
   ]
 }
 
@@ -17,6 +18,7 @@
   testonly = true
   sources = [
     "id_type_unittest.cc",
+    "strong_alias_unittest.cc",
   ]
 
   deps = [
diff --git a/base/util/type_safety/id_type.h b/base/util/type_safety/id_type.h
index 2563139..29d1eb8e 100644
--- a/base/util/type_safety/id_type.h
+++ b/base/util/type_safety/id_type.h
@@ -5,11 +5,14 @@
 #ifndef BASE_UTIL_TYPE_SAFETY_ID_TYPE_H_
 #define BASE_UTIL_TYPE_SAFETY_ID_TYPE_H_
 
-#include <stdint.h>
-#include <cstddef>
-#include <ostream>
-#include <type_traits>
+#include <cstdint>
 
+#include "base/util/type_safety/strong_alias.h"
+
+namespace util {
+
+// A specialization of StrongAlias for integer-based identifiers.
+//
 // IdType32<>, IdType64<>, etc. wrap an integer id in a custom, type-safe type.
 //
 // IdType32<Foo> is an alternative to int, for a class Foo with methods like:
@@ -38,72 +41,36 @@
 // - it can be used in IPC messages.
 //
 // IdType32<Foo> has the following differences from a bare int32_t:
-// - it forces coercions to go through GetUnsafeValue and FromUnsafeValue;
+// - it forces coercions to go through the explicit constructor and value()
+//   getter;
 // - it restricts the set of available operations (i.e. no multiplication);
-// - it ensures initialization to zero and allows checking against
-//   default-initialized values via is_null method.
-
-namespace util {
-
+// - it default-constructs to a null value and allows checking against the null
+//   value via is_null method.
 template <typename TypeMarker, typename WrappedType, WrappedType kInvalidValue>
-class IdType {
+class IdType : public StrongAlias<TypeMarker, WrappedType> {
  public:
-  IdType() : value_(kInvalidValue) {}
-  bool is_null() const { return value_ == kInvalidValue; }
+  using StrongAlias<TypeMarker, WrappedType>::StrongAlias;
+  // Default-construct in the null state.
+  IdType() : StrongAlias<TypeMarker, WrappedType>::StrongAlias(kInvalidValue) {}
 
+  bool is_null() const { return this->value() == kInvalidValue; }
+
+  // TODO(mpawlowski) Replace these with constructor/value() getter. The
+  // conversions are safe as long as they're explicit (which is taken care of by
+  // StrongAlias).
   static IdType FromUnsafeValue(WrappedType value) { return IdType(value); }
-  WrappedType GetUnsafeValue() const { return value_; }
-
-  IdType(const IdType& other) = default;
-  IdType& operator=(const IdType& other) = default;
-
-  bool operator==(const IdType& other) const { return value_ == other.value_; }
-  bool operator!=(const IdType& other) const { return value_ != other.value_; }
-  bool operator<(const IdType& other) const { return value_ < other.value_; }
-  bool operator<=(const IdType& other) const { return value_ <= other.value_; }
-
-  // Hasher to use in std::unordered_map, std::unordered_set, etc.
-  struct Hasher {
-    using argument_type = IdType;
-    using result_type = std::size_t;
-    result_type operator()(const argument_type& id) const {
-      return std::hash<WrappedType>()(id.GetUnsafeValue());
-    }
-  };
-
- protected:
-  explicit IdType(WrappedType val) : value_(val) {}
-
- private:
-  // In theory WrappedType could be any type that supports ==, <, <<, std::hash,
-  // etc., but to make things simpler (both for users and for maintainers) we
-  // explicitly restrict the design space to integers.  This means the users
-  // can safely assume that IdType is relatively small and cheap to copy
-  // and the maintainers don't have to worry about WrappedType being a complex
-  // type (i.e. std::string or std::pair or a move-only type).
-  using IntegralWrappedType =
-      typename std::enable_if<std::is_integral<WrappedType>::value,
-                              WrappedType>::type;
-  IntegralWrappedType value_;
+  WrappedType GetUnsafeValue() const { return this->value(); }
 };
 
 // Type aliases for convenience:
 template <typename TypeMarker>
-using IdType32 = IdType<TypeMarker, int32_t, 0>;
+using IdType32 = IdType<TypeMarker, std::int32_t, 0>;
 template <typename TypeMarker>
-using IdTypeU32 = IdType<TypeMarker, uint32_t, 0>;
+using IdTypeU32 = IdType<TypeMarker, std::uint32_t, 0>;
 template <typename TypeMarker>
-using IdType64 = IdType<TypeMarker, int64_t, 0>;
+using IdType64 = IdType<TypeMarker, std::int64_t, 0>;
 template <typename TypeMarker>
-using IdTypeU64 = IdType<TypeMarker, uint64_t, 0>;
-
-template <typename TypeMarker, typename WrappedType, WrappedType kInvalidValue>
-std::ostream& operator<<(
-    std::ostream& stream,
-    const IdType<TypeMarker, WrappedType, kInvalidValue>& id) {
-  return stream << id.GetUnsafeValue();
-}
-
+using IdTypeU64 = IdType<TypeMarker, std::uint64_t, 0>;
 }  // namespace util
 
 #endif  // BASE_UTIL_TYPE_SAFETY_ID_TYPE_H_
diff --git a/base/util/type_safety/id_type_unittest.cc b/base/util/type_safety/id_type_unittest.cc
index 735426d..ba502f62 100644
--- a/base/util/type_safety/id_type_unittest.cc
+++ b/base/util/type_safety/id_type_unittest.cc
@@ -3,11 +3,6 @@
 // found in the LICENSE file.
 
 #include <limits>
-#include <map>
-#include <sstream>
-#include <string>
-#include <type_traits>
-#include <unordered_map>
 
 #include "base/util/type_safety/id_type.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,16 +14,6 @@
 class Foo;
 using FooId = IdType<Foo, int, 0>;
 
-class Bar;
-using BarId = IdType<Bar, int, 0>;
-
-class AnotherIdMarker;
-class DerivedId : public IdType<AnotherIdMarker, int, 0> {
- public:
-  explicit DerivedId(int unsafe_value)
-      : IdType<AnotherIdMarker, int, 0>(unsafe_value) {}
-};
-
 }  // namespace
 
 TEST(IdType, DefaultValueIsInvalid) {
@@ -41,92 +26,6 @@
   EXPECT_FALSE(foo_id.is_null());
 }
 
-TEST(IdType, OutputStreamTest) {
-  FooId foo_id = FooId::FromUnsafeValue(123);
-
-  std::ostringstream ss;
-  ss << foo_id;
-  EXPECT_EQ("123", ss.str());
-}
-
-TEST(IdType, IdType32) {
-  IdType32<Foo> id;
-
-  EXPECT_EQ(0, id.GetUnsafeValue());
-  static_assert(sizeof(int32_t) == sizeof(id), "");
-}
-
-TEST(IdType, IdTypeU32) {
-  IdTypeU32<Foo> id;
-
-  EXPECT_EQ(0u, id.GetUnsafeValue());
-  static_assert(sizeof(uint32_t) == sizeof(id), "");
-}
-
-TEST(IdType, IdType64) {
-  IdType64<Foo> id;
-
-  EXPECT_EQ(0, id.GetUnsafeValue());
-  static_assert(sizeof(int64_t) == sizeof(id), "");
-}
-
-TEST(IdType, IdTypeU64) {
-  IdTypeU64<Foo> id;
-
-  EXPECT_EQ(0u, id.GetUnsafeValue());
-  static_assert(sizeof(uint64_t) == sizeof(id), "");
-}
-
-TEST(IdType, DerivedClasses) {
-  DerivedId derived_id(456);
-
-  std::ostringstream ss;
-  ss << derived_id;
-  EXPECT_EQ("456", ss.str());
-
-  std::map<DerivedId, std::string> ordered_map;
-  ordered_map[derived_id] = "blah";
-  EXPECT_EQ(ordered_map[derived_id], "blah");
-
-  std::unordered_map<DerivedId, std::string, DerivedId::Hasher> unordered_map;
-  unordered_map[derived_id] = "blah2";
-  EXPECT_EQ(unordered_map[derived_id], "blah2");
-}
-
-TEST(IdType, StaticAsserts) {
-  static_assert(!std::is_constructible<FooId, int>::value,
-                "Should be impossible to construct FooId from a raw integer.");
-  static_assert(!std::is_convertible<int, FooId>::value,
-                "Should be impossible to convert a raw integer into FooId.");
-
-  static_assert(!std::is_constructible<FooId, BarId>::value,
-                "Should be impossible to construct FooId from a BarId.");
-  static_assert(!std::is_convertible<BarId, FooId>::value,
-                "Should be impossible to convert a BarId into FooId.");
-
-  // The presence of a custom default constructor means that FooId is not a
-  // "trivial" class and therefore is not a POD type (unlike an int32_t).
-  // At the same time FooId has almost all of the properties of a POD type:
-  // - is "trivially copyable" (i.e. is memcpy-able),
-  // - has "standard layout" (i.e. interops with things expecting C layout).
-  // See http://stackoverflow.com/a/7189821 for more info about these
-  // concepts.
-  static_assert(std::is_standard_layout<FooId>::value,
-                "FooId should have standard layout. "
-                "See http://stackoverflow.com/a/7189821 for more info.");
-  static_assert(sizeof(FooId) == sizeof(int),
-                "FooId should be the same size as the raw integer it wraps.");
-  // TODO(lukasza): Enable these once <type_traits> supports all the standard
-  // C++11 equivalents (i.e. std::is_trivially_copyable instead of the
-  // non-standard std::has_trivial_copy_assign).
-  // static_assert(std::has_trivial_copy_constructor<FooId>::value,
-  //               "FooId should have a trivial copy constructor.");
-  // static_assert(std::has_trivial_copy_assign<FooId>::value,
-  //               "FooId should have a trivial copy assignment operator.");
-  // static_assert(std::has_trivial_destructor<FooId>::value,
-  //               "FooId should have a trivial destructor.");
-}
-
 class IdTypeSpecificValueTest : public ::testing::TestWithParam<int> {
  protected:
   FooId test_id() { return FooId::FromUnsafeValue(GetParam()); }
@@ -139,17 +38,6 @@
   }
 };
 
-TEST_P(IdTypeSpecificValueTest, ComparisonToSelf) {
-  EXPECT_TRUE(test_id() == test_id());
-  EXPECT_FALSE(test_id() != test_id());
-  EXPECT_FALSE(test_id() < test_id());
-}
-
-TEST_P(IdTypeSpecificValueTest, ComparisonToOther) {
-  EXPECT_FALSE(test_id() == other_id());
-  EXPECT_TRUE(test_id() != other_id());
-}
-
 TEST_P(IdTypeSpecificValueTest, UnsafeValueRoundtrips) {
   int original_value = GetParam();
   FooId id = FooId::FromUnsafeValue(original_value);
@@ -157,37 +45,6 @@
   EXPECT_EQ(original_value, final_value);
 }
 
-TEST_P(IdTypeSpecificValueTest, Copying) {
-  FooId original = test_id();
-
-  FooId copy_via_constructor(original);
-  EXPECT_EQ(original, copy_via_constructor);
-
-  FooId copy_via_assignment;
-  copy_via_assignment = original;
-  EXPECT_EQ(original, copy_via_assignment);
-}
-
-TEST_P(IdTypeSpecificValueTest, StdUnorderedMap) {
-  std::unordered_map<FooId, std::string, FooId::Hasher> map;
-
-  map[test_id()] = "test_id";
-  map[other_id()] = "other_id";
-
-  EXPECT_EQ(map[test_id()], "test_id");
-  EXPECT_EQ(map[other_id()], "other_id");
-}
-
-TEST_P(IdTypeSpecificValueTest, StdMap) {
-  std::map<FooId, std::string> map;
-
-  map[test_id()] = "test_id";
-  map[other_id()] = "other_id";
-
-  EXPECT_EQ(map[test_id()], "test_id");
-  EXPECT_EQ(map[other_id()], "other_id");
-}
-
 INSTANTIATE_TEST_SUITE_P(,
                          IdTypeSpecificValueTest,
                          ::testing::Values(std::numeric_limits<int>::min(),
diff --git a/base/util/type_safety/strong_alias.h b/base/util/type_safety/strong_alias.h
new file mode 100644
index 0000000..7b0f38c
--- /dev/null
+++ b/base/util/type_safety/strong_alias.h
@@ -0,0 +1,116 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_UTIL_TYPE_SAFETY_STRONG_ALIAS_H_
+#define BASE_UTIL_TYPE_SAFETY_STRONG_ALIAS_H_
+
+#include <ostream>
+#include <utility>
+
+namespace util {
+
+// A type-safe alternative for a typedef or a 'using' directive.
+//
+// C++ currently does not support type-safe typedefs, despite multiple proposals
+// (ex. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3515.pdf). The
+// next best thing is to try and emulate them in library code.
+//
+// The motivation is to disallow several classes of errors:
+//
+// using Orange = int;
+// using Apple = int;
+// Apple apple(2);
+// Orange orange = apple;  // Orange should not be able to become an Apple.
+// Orange x = orange + apple;  // Shouldn't add Oranges and Apples.
+// if (orange > apple);  // Shouldn't compare Apples to Oranges.
+// void foo(Orange);
+// void foo(Apple);  // Redefinition.
+// etc.
+//
+// StrongAlias may instead be used as follows:
+//
+// using Orange = StrongAlias<class OrangeTag, int>;
+// using Apple = StrongAlias<class AppleTag, int>;
+// Apple apple(2);
+// Orange orange = apple;  // Does not compile.
+// Orange other_orange = orange;  // Compiles, types match.
+// Orange x = orange + apple;  // Does not compile.
+// Orange y = Orange(orange.value() + apple.value());  // Compiles.
+// if (orange > apple);  // Does not compile.
+// if (orange > other_orange);  // Compiles.
+// void foo(Orange);
+// void foo(Apple);  // Compiles into separate overload.
+//
+// StrongAlias is a zero-cost abstraction, it's compiled away.
+//
+// TagType is an empty tag class (also called "phantom type") that only serves
+// the type system to differentiate between different instantiations of the
+// template.
+// UnderlyingType may be almost any value type. Note that some methods of the
+// StrongAlias may be unavailable (ie. produce elaborate compilation errors when
+// used) if UnderlyingType doesn't support them.
+//
+// StrongAlias only directly exposes comparison operators (for convenient use in
+// ordered containers) and a hash function (for unordered_map/set). It's
+// impossible, without reflection, to expose all methods of the UnderlyingType
+// in StrongAlias's interface. It's also potentially unwanted (ex. you don't
+// want to be able to add two StrongAliases that represent socket handles).
+// A getter is provided in case you need to access the UnderlyingType.
+template <typename TagType, typename UnderlyingType>
+class StrongAlias {
+ public:
+  StrongAlias() = default;
+  explicit StrongAlias(const UnderlyingType& v) : value_(v) {}
+  explicit StrongAlias(UnderlyingType&& v) : value_(std::move(v)) {}
+  ~StrongAlias() = default;
+
+  StrongAlias(const StrongAlias& other) = default;
+  StrongAlias& operator=(const StrongAlias& other) = default;
+  StrongAlias(StrongAlias&& other) = default;
+  StrongAlias& operator=(StrongAlias&& other) = default;
+
+  const UnderlyingType& value() const { return value_; }
+
+  bool operator==(const StrongAlias& other) const {
+    return value_ == other.value_;
+  }
+  bool operator!=(const StrongAlias& other) const {
+    return value_ != other.value_;
+  }
+  bool operator<(const StrongAlias& other) const {
+    return value_ < other.value_;
+  }
+  bool operator<=(const StrongAlias& other) const {
+    return value_ <= other.value_;
+  }
+  bool operator>(const StrongAlias& other) const {
+    return value_ > other.value_;
+  }
+  bool operator>=(const StrongAlias& other) const {
+    return value_ >= other.value_;
+  }
+
+  // Hasher to use in std::unordered_map, std::unordered_set, etc.
+  struct Hasher {
+    using argument_type = StrongAlias;
+    using result_type = std::size_t;
+    result_type operator()(const argument_type& id) const {
+      return std::hash<UnderlyingType>()(id.value());
+    }
+  };
+
+ protected:
+  UnderlyingType value_;
+};
+
+// Stream operator for convenience, streams the UnderlyingType.
+template <typename TagType, typename UnderlyingType>
+std::ostream& operator<<(std::ostream& stream,
+                         const StrongAlias<TagType, UnderlyingType>& alias) {
+  return stream << alias.value();
+}
+
+}  // namespace util
+
+#endif  // BASE_UTIL_TYPE_SAFETY_STRONG_ALIAS_H_
diff --git a/base/util/type_safety/strong_alias_unittest.cc b/base/util/type_safety/strong_alias_unittest.cc
new file mode 100644
index 0000000..9b8af3a
--- /dev/null
+++ b/base/util/type_safety/strong_alias_unittest.cc
@@ -0,0 +1,262 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/util/type_safety/strong_alias.h"
+
+#include <cstdint>
+#include <map>
+#include <sstream>
+#include <string>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace util {
+
+namespace {
+
+// For test correctnenss, it's important that these getters return lexically
+// incrementing values as |index| grows.
+template <typename T>
+T GetExampleValue(int index);
+
+template <>
+int GetExampleValue<int>(int index) {
+  return 5 + index;
+}
+template <>
+uint64_t GetExampleValue<uint64_t>(int index) {
+  return 500U + index;
+}
+
+template <>
+std::string GetExampleValue<std::string>(int index) {
+  return std::string('a', index);
+}
+
+template <typename T, typename U>
+bool StreamOutputSame(const T& a, const U& b) {
+  std::stringstream ssa;
+  ssa << a;
+  std::stringstream ssb;
+  ssb << b;
+  return ssa.str() == ssb.str();
+}
+
+}  // namespace
+
+template <typename T>
+class StrongAliasTest : public ::testing::Test {};
+
+using TestedTypes = ::testing::Types<int, uint64_t, std::string>;
+TYPED_TEST_SUITE(StrongAliasTest, TestedTypes);
+
+TYPED_TEST(StrongAliasTest, ValueAccessesUnderlyingValue) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+
+  // Const value getter.
+  const FooAlias const_alias(GetExampleValue<TypeParam>(1));
+  EXPECT_EQ(GetExampleValue<TypeParam>(1), const_alias.value());
+  static_assert(std::is_const<typename std::remove_reference<decltype(
+                    const_alias.value())>::type>::value,
+                "Reference returned by const value getter should be const.");
+}
+
+TYPED_TEST(StrongAliasTest, CanBeCopyConstructed) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  FooAlias alias(GetExampleValue<TypeParam>(0));
+  FooAlias copy_constructed = alias;
+  EXPECT_EQ(copy_constructed, alias);
+
+  FooAlias copy_assigned;
+  copy_assigned = alias;
+  EXPECT_EQ(copy_assigned, alias);
+}
+
+TYPED_TEST(StrongAliasTest, CanBeMoveConstructed) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  FooAlias alias(GetExampleValue<TypeParam>(0));
+  FooAlias move_constructed = std::move(alias);
+  EXPECT_EQ(move_constructed, FooAlias(GetExampleValue<TypeParam>(0)));
+
+  FooAlias alias2(GetExampleValue<TypeParam>(2));
+  FooAlias move_assigned;
+  move_assigned = std::move(alias2);
+  EXPECT_EQ(move_assigned, FooAlias(GetExampleValue<TypeParam>(2)));
+}
+
+TYPED_TEST(StrongAliasTest, CanBeConstructedFromMoveOnlyType) {
+  // Note, using a move-only unique_ptr to T:
+  using FooAlias = StrongAlias<class FooTag, std::unique_ptr<TypeParam>>;
+
+  FooAlias a(std::make_unique<TypeParam>(GetExampleValue<TypeParam>(0)));
+  EXPECT_EQ(*a.value(), GetExampleValue<TypeParam>(0));
+
+  auto bare_value = std::make_unique<TypeParam>(GetExampleValue<TypeParam>(1));
+  FooAlias b(std::move(bare_value));
+  EXPECT_EQ(*b.value(), GetExampleValue<TypeParam>(1));
+}
+
+TYPED_TEST(StrongAliasTest, CanBeWrittenToOutputStream) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+
+  const FooAlias a(GetExampleValue<TypeParam>(0));
+  EXPECT_TRUE(StreamOutputSame(GetExampleValue<TypeParam>(0), a)) << a;
+}
+
+TYPED_TEST(StrongAliasTest, SizeSameAsUnderlyingType) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  static_assert(sizeof(FooAlias) == sizeof(TypeParam),
+                "StrongAlias should be as large as the underlying type.");
+}
+
+TYPED_TEST(StrongAliasTest, IsDefaultConstructible) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  static_assert(std::is_default_constructible<FooAlias>::value,
+                "Should be possible to default-construct a StrongAlias.");
+}
+
+TEST(StrongAliasTest, TrivialTypeAliasIsStandardLayout) {
+  using FooAlias = StrongAlias<class FooTag, int>;
+  static_assert(std::is_standard_layout<FooAlias>::value,
+                "int-based alias should have standard layout. ");
+  static_assert(std::is_trivially_copyable<FooAlias>::value,
+                "int-based alias should be trivially copyable. ");
+}
+
+TYPED_TEST(StrongAliasTest, CannotBeCreatedFromDifferentAlias) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  using BarAlias = StrongAlias<class BarTag, TypeParam>;
+  static_assert(!std::is_constructible<FooAlias, BarAlias>::value,
+                "Should be impossible to construct FooAlias from a BarAlias.");
+  static_assert(!std::is_convertible<BarAlias, FooAlias>::value,
+                "Should be impossible to convert a BarAlias into FooAlias.");
+}
+
+TYPED_TEST(StrongAliasTest, CannotBeImplicitlyConverterToUnderlyingValue) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  static_assert(!std::is_constructible<TypeParam, FooAlias>::value,
+                "Should be impossible to construct an underlying type from a "
+                "StrongAlias.");
+  static_assert(!std::is_convertible<FooAlias, TypeParam>::value,
+                "Should be impossible to implicitly convert a StrongAlias into "
+                "an underlying type.");
+}
+
+TYPED_TEST(StrongAliasTest, ComparesEqualToSameValue) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  // Comparison to self:
+  const FooAlias a = FooAlias(GetExampleValue<TypeParam>(0));
+  EXPECT_EQ(a, a);
+  EXPECT_FALSE(a != a);
+  EXPECT_TRUE(a >= a);
+  EXPECT_TRUE(a <= a);
+  EXPECT_FALSE(a > a);
+  EXPECT_FALSE(a < a);
+  // Comparison to other equal object:
+  const FooAlias b = FooAlias(GetExampleValue<TypeParam>(0));
+  EXPECT_EQ(a, b);
+  EXPECT_FALSE(a != b);
+  EXPECT_TRUE(a >= b);
+  EXPECT_TRUE(a <= b);
+  EXPECT_FALSE(a > b);
+  EXPECT_FALSE(a < b);
+}
+
+TYPED_TEST(StrongAliasTest, ComparesCorrectlyToDifferentValue) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  const FooAlias a = FooAlias(GetExampleValue<TypeParam>(0));
+  const FooAlias b = FooAlias(GetExampleValue<TypeParam>(1));
+  EXPECT_NE(a, b);
+  EXPECT_FALSE(a == b);
+  EXPECT_TRUE(b >= a);
+  EXPECT_TRUE(a <= b);
+  EXPECT_TRUE(b > a);
+  EXPECT_TRUE(a < b);
+}
+
+TEST(StrongAliasTest, CanBeDerivedFrom) {
+  // Aliases can be enriched by custom operations or validations if needed.
+  // Ideally, one could go from a 'using' declaration to a derived class to add
+  // those methods without the need to change any other code.
+  class CountryCode : public StrongAlias<CountryCode, std::string> {
+   public:
+    CountryCode(const std::string& value)
+        : StrongAlias<CountryCode, std::string>::StrongAlias(value) {
+      if (value_.length() != 2) {
+        // Country code invalid!
+        value_.clear();  // is_null() will return true.
+      }
+    }
+
+    bool is_null() const { return value_.empty(); }
+  };
+
+  CountryCode valid("US");
+  EXPECT_FALSE(valid.is_null());
+
+  CountryCode invalid("United States");
+  EXPECT_TRUE(invalid.is_null());
+}
+
+TEST(StrongAliasTest, CanWrapComplexStructures) {
+  // A pair of strings implements odering and can, in principle, be used as
+  // a base of StrongAlias.
+  using PairOfStrings = std::pair<std::string, std::string>;
+  using ComplexAlias = StrongAlias<class FooTag, PairOfStrings>;
+
+  ComplexAlias a1{std::make_pair("aaa", "bbb")};
+  ComplexAlias a2{std::make_pair("ccc", "ddd")};
+  EXPECT_TRUE(a1 < a2);
+
+  EXPECT_TRUE(a1.value() == PairOfStrings("aaa", "bbb"));
+
+  // Note a caveat, an std::pair doesn't have an overload of operator<<, and it
+  // cannot be easily added since ADL rules would require it to be in the std
+  // namespace. So we can't print ComplexAlias.
+}
+
+TYPED_TEST(StrongAliasTest, CanBeKeysInStdUnorderedMap) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  std::unordered_map<FooAlias, std::string, typename FooAlias::Hasher> map;
+
+  FooAlias k1(GetExampleValue<TypeParam>(0));
+  FooAlias k2(GetExampleValue<TypeParam>(1));
+
+  map[k1] = "value1";
+  map[k2] = "value2";
+
+  EXPECT_EQ(map[k1], "value1");
+  EXPECT_EQ(map[k2], "value2");
+}
+
+TYPED_TEST(StrongAliasTest, CanBeKeysInStdMap) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  std::map<FooAlias, std::string> map;
+
+  FooAlias k1(GetExampleValue<TypeParam>(0));
+  FooAlias k2(GetExampleValue<TypeParam>(1));
+
+  map[k1] = "value1";
+  map[k2] = "value2";
+
+  EXPECT_EQ(map[k1], "value1");
+  EXPECT_EQ(map[k2], "value2");
+}
+
+TYPED_TEST(StrongAliasTest, CanDifferentiateOverloads) {
+  using FooAlias = StrongAlias<class FooTag, TypeParam>;
+  using BarAlias = StrongAlias<class BarTag, TypeParam>;
+  class Scope {
+   public:
+    static std::string Overload(FooAlias) { return "FooAlias"; }
+    static std::string Overload(BarAlias) { return "BarAlias"; }
+  };
+  EXPECT_EQ("FooAlias", Scope::Overload(FooAlias()));
+  EXPECT_EQ("BarAlias", Scope::Overload(BarAlias()));
+}
+
+}  // namespace util
diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h
index 53baea4..724539d 100644
--- a/ipc/ipc_message_utils.h
+++ b/ipc/ipc_message_utils.h
@@ -1055,6 +1055,26 @@
   }
 };
 
+template <typename TagType, typename UnderlyingType>
+struct ParamTraits<util::StrongAlias<TagType, UnderlyingType>> {
+  using param_type = util::StrongAlias<TagType, UnderlyingType>;
+  static void Write(base::Pickle* m, const param_type& p) {
+    WriteParam(m, p.value());
+  }
+  static bool Read(const base::Pickle* m,
+                   base::PickleIterator* iter,
+                   param_type* r) {
+    UnderlyingType value;
+    if (!ReadParam(m, iter, &value))
+      return false;
+    *r = param_type::StrongAlias(value);
+    return true;
+  }
+  static void Log(const param_type& p, std::string* l) {
+    LogParam(p.value(), l);
+  }
+};
+
 // IPC types ParamTraits -------------------------------------------------------
 
 // A ChannelHandle is basically a platform-inspecific wrapper around the