Bug 1403083 - Part 1: Properly disable string functions for invalid char types. r=froydnj
authorEric Rahm <erahm@mozilla.com>
Tue, 03 Oct 2017 12:44:44 -0700
changeset 387196 0d2b1057b968860044a6b7c35bf03712d4c155be
parent 387195 381eb8e69ec8fb823177782a3de0a3b2442742fa
child 387197 b871e93e7912a3b51e0f4aa180dc53091918cb18
push id32715
push useracraciun@mozilla.com
push dateFri, 20 Oct 2017 09:04:45 +0000
treeherdermozilla-central@be030533c5af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1403083
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1403083 - Part 1: Properly disable string functions for invalid char types. r=froydnj In order to properly disable template functions with `std::enable_if` we need to use the resulting type. This only works if we use a dependent type in the template params, hence the need to shadow the `T` param. Proper usage is now of the form: template<typename Q = T, typename EnableIfChar = CharOnlyT<Q>> Foo();
xpcom/string/nsStringObsolete.cpp
xpcom/string/nsTDependentString.h
xpcom/string/nsTDependentSubstring.cpp
xpcom/string/nsTDependentSubstring.h
xpcom/string/nsTString.h
xpcom/string/nsTStringObsolete.cpp
xpcom/string/nsTStringRepr.h
xpcom/string/nsTSubstring.h
--- a/xpcom/string/nsStringObsolete.cpp
+++ b/xpcom/string/nsStringObsolete.cpp
@@ -851,81 +851,81 @@ RFind_ComputeSearchRange( uint32_t bigLe
 
 #include "nsTStringObsolete.cpp"
 
 //-----------------------------------------------------------------------------
 
 // specialized methods:
 
 template <typename T>
-template <typename EnableIfChar16>
+template <typename Q, typename EnableIfChar16>
 int32_t
 nsTString<T>::Find(const self_type& aString, int32_t aOffset, int32_t aCount) const
 {
   // this method changes the meaning of aOffset and aCount:
   Find_ComputeSearchRange(this->mLength, aString.Length(), aOffset, aCount);
 
   // Capture the raw buffer locally to help msvc deduce the type.
   const char_type* str = aString.get();
   int32_t result = FindSubstring(this->mData + aOffset, aCount, str, aString.Length(), false);
   if (result != kNotFound)
     result += aOffset;
   return result;
 }
 
 template <typename T>
-template <typename EnableIfChar16>
+template <typename Q, typename EnableIfChar16>
 int32_t
 nsTString<T>::Find(const char_type* aString, int32_t aOffset, int32_t aCount) const
 {
   return Find(nsTDependentString<T>(aString), aOffset, aCount);
 }
 
 template <typename T>
-template <typename EnableIfChar16>
+template <typename Q, typename EnableIfChar16>
 int32_t
 nsTString<T>::RFind(const self_type& aString, int32_t aOffset, int32_t aCount) const
 {
   // this method changes the meaning of aOffset and aCount:
   RFind_ComputeSearchRange(this->mLength, aString.Length(), aOffset, aCount);
 
   // Capture the raw buffer locally to help msvc deduce the type.
   const char_type* str = aString.get();
   int32_t result = RFindSubstring(this->mData + aOffset, aCount, str, aString.Length(), false);
   if (result != kNotFound)
     result += aOffset;
   return result;
 }
 
 template <typename T>
-template <typename EnableIfChar16>
+template <typename Q, typename EnableIfChar16>
 int32_t
 nsTString<T>::RFind(const char_type* aString, int32_t aOffset, int32_t aCount) const
 {
   return RFind(nsTDependentString<T>(aString), aOffset, aCount);
 }
 
 template <typename T>
-template <typename EnableIfChar16>
+template <typename Q, typename EnableIfChar16>
 int32_t
 nsTString<T>::FindCharInSet(const char* aSet, int32_t aOffset) const
 {
   if (aOffset < 0)
     aOffset = 0;
   else if (aOffset >= int32_t(this->mLength))
     return kNotFound;
 
   int32_t result = ::FindCharInSet(this->mData + aOffset, this->mLength - aOffset, aSet);
   if (result != kNotFound)
     result += aOffset;
   return result;
 }
 
 template <typename T>
-template <typename EnableIfChar16>
+template <typename Q, typename EnableIfChar16>
 void
 nsTString<T>::ReplaceChar(const char* aSet, char16_t aNewChar)
 {
   if (!this->EnsureMutable()) // XXX do this lazily?
     this->AllocFailed(this->mLength);
 
   char16_t* data = this->mData;
   uint32_t lenRemaining = this->mLength;
@@ -943,17 +943,17 @@ nsTString<T>::ReplaceChar(const char* aS
 }
 
 
 /**
  * nsTString::Compare,CompareWithConversion,etc.
  */
 
 template <typename T>
-template <typename EnableIfChar>
+template <typename Q, typename EnableIfChar>
 int32_t
 nsTString<T>::Compare(const char_type* aString, bool aIgnoreCase, int32_t aCount) const
 {
   uint32_t strLen = char_traits::length(aString);
 
   int32_t maxCount = int32_t(XPCOM_MIN(this->mLength, strLen));
 
   int32_t compareCount;
@@ -974,17 +974,17 @@ nsTString<T>::Compare(const char_type* a
 
     if (this->mLength != strLen)
       result = (this->mLength < strLen) ? -1 : 1;
   }
   return result;
 }
 
 template <typename T>
-template <typename EnableIfChar16>
+template <typename Q, typename EnableIfChar16>
 bool
 nsTString<T>::EqualsIgnoreCase(const incompatible_char_type* aString, int32_t aCount) const
 {
   uint32_t strLen = nsCharTraits<char>::length(aString);
 
   int32_t maxCount = int32_t(XPCOM_MIN(this->mLength, strLen));
 
   int32_t compareCount;
--- a/xpcom/string/nsTDependentString.h
+++ b/xpcom/string/nsTDependentString.h
@@ -47,54 +47,50 @@ public:
 
   typedef typename base_string_type::index_type index_type;
   typedef typename base_string_type::size_type size_type;
 
   // These are only for internal use within the string classes:
   typedef typename base_string_type::DataFlags DataFlags;
   typedef typename base_string_type::ClassFlags ClassFlags;
 
-  using typename base_string_type::IsChar;
-  using typename base_string_type::IsChar16;
-
-
 public:
 
   /**
    * constructors
    */
 
   nsTDependentString(const char_type* aStart, const char_type* aEnd);
 
   nsTDependentString(const char_type* aData, uint32_t aLength)
     : string_type(const_cast<char_type*>(aData), aLength,
                   DataFlags::TERMINATED, ClassFlags(0))
   {
     this->AssertValidDependentString();
   }
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   nsTDependentString(char16ptr_t aData, uint32_t aLength)
     : nsTDependentString(static_cast<const char16_t*>(aData), aLength)
   {
   }
 #endif
 
   explicit
   nsTDependentString(const char_type* aData)
     : string_type(const_cast<char_type*>(aData),
                   uint32_t(char_traits::length(aData)),
                   DataFlags::TERMINATED, ClassFlags(0))
   {
     string_type::AssertValidDependentString();
   }
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   explicit
   nsTDependentString(char16ptr_t aData)
     : nsTDependentString(static_cast<const char16_t*>(aData))
   {
   }
 #endif
 
   nsTDependentString(const string_type& aStr, uint32_t aStartPos)
--- a/xpcom/string/nsTDependentSubstring.cpp
+++ b/xpcom/string/nsTDependentSubstring.cpp
@@ -55,17 +55,17 @@ nsTDependentSubstring<T>::nsTDependentSu
   : substring_type(const_cast<char_type*>(aStart), uint32_t(aEnd - aStart),
                    DataFlags(0), ClassFlags(0))
 {
   MOZ_RELEASE_ASSERT(aStart <= aEnd, "Overflow!");
 }
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
 template <typename T>
-template <typename EnableIfChar16>
+template <typename Q, typename EnableIfChar16>
 nsTDependentSubstring<T>::nsTDependentSubstring(char16ptr_t aStart,
                                                 char16ptr_t aEnd)
   : substring_type(static_cast<const char16_t*>(aStart),
                    static_cast<const char16_t*>(aEnd))
 {
   MOZ_RELEASE_ASSERT(static_cast<const char16_t*>(aStart) <=
                      static_cast<const char16_t*>(aEnd),
                      "Overflow!");
--- a/xpcom/string/nsTDependentSubstring.h
+++ b/xpcom/string/nsTDependentSubstring.h
@@ -47,19 +47,16 @@ public:
 
   typedef typename substring_type::index_type index_type;
   typedef typename substring_type::size_type size_type;
 
   // These are only for internal use within the string classes:
   typedef typename substring_type::DataFlags DataFlags;
   typedef typename substring_type::ClassFlags ClassFlags;
 
-  using typename substring_type::IsChar;
-  using typename substring_type::IsChar16;
-
 public:
 
   void Rebind(const substring_type&, uint32_t aStartPos,
               uint32_t aLength = size_type(-1));
 
   void Rebind(const char_type* aData, size_type aLength);
 
   void Rebind(const char_type* aStart, const char_type* aEnd);
@@ -75,23 +72,23 @@ public:
     : substring_type(const_cast<char_type*>(aData), aLength,
                      DataFlags(0), ClassFlags(0))
   {
   }
 
   nsTDependentSubstring(const char_type* aStart, const char_type* aEnd);
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   nsTDependentSubstring(char16ptr_t aData, size_type aLength)
     : nsTDependentSubstring(static_cast<const char16_t*>(aData), aLength)
   {
   }
 
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   nsTDependentSubstring(char16ptr_t aStart, char16ptr_t aEnd);
 #endif
 
   nsTDependentSubstring(const const_iterator& aStart,
                         const const_iterator& aEnd);
 
   // Create a nsTDependentSubstring to be bound later
   nsTDependentSubstring()
--- a/xpcom/string/nsTString.h
+++ b/xpcom/string/nsTString.h
@@ -58,19 +58,16 @@ public:
 
   typedef typename substring_type::index_type index_type;
   typedef typename substring_type::size_type size_type;
 
   // These are only for internal use within the string classes:
   typedef typename substring_type::DataFlags DataFlags;
   typedef typename substring_type::ClassFlags ClassFlags;
 
-  using typename substring_type::IsChar;
-  using typename substring_type::IsChar16;
-
 public:
 
   /**
    * constructors
    */
 
   nsTString()
     : substring_type(ClassFlags::NULL_TERMINATED)
@@ -80,17 +77,17 @@ public:
   explicit
   nsTString(const char_type* aData, size_type aLength = size_type(-1))
     : substring_type(ClassFlags::NULL_TERMINATED)
   {
     this->Assign(aData, aLength);
   }
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   explicit
   nsTString(char16ptr_t aStr, size_type aLength = size_type(-1))
     : substring_type(ClassFlags::NULL_TERMINATED)
   {
     this->Assign(static_cast<const char16_t*>(aStr), aLength);
   }
 #endif
 
@@ -152,17 +149,17 @@ public:
     return *this;
   }
   self_type& operator=(self_type&& aStr)
   {
     this->Assign(mozilla::Move(aStr));
     return *this;
   }
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   self_type& operator=(const char16ptr_t aStr)
   {
     this->Assign(static_cast<const char16_t*>(aStr));
     return *this;
   }
 #endif
   self_type& operator=(const substring_type& aStr)
   {
@@ -234,24 +231,24 @@ public:
    *  @return  offset in string, or kNotFound
    */
 
   int32_t Find(const nsTString<char>& aString, bool aIgnoreCase = false,
                int32_t aOffset = 0, int32_t aCount = -1) const;
   int32_t Find(const char* aString, bool aIgnoreCase = false,
                int32_t aOffset = 0, int32_t aCount = -1) const;
 
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   int32_t Find(const self_type& aString, int32_t aOffset = 0,
                int32_t aCount = -1) const;
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   int32_t Find(const char_type* aString, int32_t aOffset = 0,
                int32_t aCount = -1) const;
 #ifdef MOZ_USE_CHAR16_WRAPPER
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   int32_t Find(char16ptr_t aString, int32_t aOffset = 0,
                int32_t aCount = -1) const
   {
     return Find(static_cast<const char16_t*>(aString), aOffset, aCount);
   }
 #endif
 
 
@@ -268,20 +265,20 @@ public:
    */
 
   // Case aIgnoreCase option only with char versions
   int32_t RFind(const nsTString<char>& aString, bool aIgnoreCase = false,
                 int32_t aOffset = -1, int32_t aCount = -1) const;
   int32_t RFind(const char* aCString, bool aIgnoreCase = false,
                 int32_t aOffset = -1, int32_t aCount = -1) const;
 
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   int32_t RFind(const self_type& aString, int32_t aOffset = -1,
                 int32_t aCount = -1) const;
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   int32_t RFind(const char_type* aString, int32_t aOffset = -1,
                 int32_t aCount = -1) const;
 
 
   /**
    *  Search for given char within this string
    *
    *  @param   aChar is the character to search for
@@ -307,17 +304,17 @@ public:
    */
 
   int32_t FindCharInSet(const char_type* aString, int32_t aOffset = 0) const;
   int32_t FindCharInSet(const self_type& aString, int32_t aOffset = 0) const
   {
     return FindCharInSet(aString.get(), aOffset);
   }
 
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   int32_t FindCharInSet(const char* aSet, int32_t aOffset = 0) const;
 
 
   /**
    * This method searches this string for the last character found in
    * the given string.
    *
    * @param aString contains set of chars to be found
@@ -336,36 +333,36 @@ public:
   /**
    * Compares a given string to this string.
    *
    * @param   aString is the string to be compared
    * @param   aIgnoreCase tells us how to treat case
    * @param   aCount tells us how many chars to compare
    * @return  -1,0,1
    */
-  template <typename EnableIfChar = IsChar>
+  template <typename Q = T, typename EnableIfChar = mozilla::CharOnlyT<Q>>
   int32_t Compare(const char_type* aString, bool aIgnoreCase = false,
                   int32_t aCount = -1) const;
 
 
   /**
    * Equality check between given string and this string.
    *
    * @param   aString is the string to check
    * @param   aIgnoreCase tells us how to treat case
    * @param   aCount tells us how many chars to compare
    * @return  boolean
    */
-  template <typename EnableIfChar = IsChar>
+  template <typename Q = T, typename EnableIfChar = mozilla::CharOnlyT<Q>>
   bool EqualsIgnoreCase(const char_type* aString, int32_t aCount = -1) const
   {
     return Compare(aString, true, aCount) == 0;
   }
 
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   bool EqualsIgnoreCase(const incompatible_char_type* aString, int32_t aCount = -1) const;
 
   /**
    * Perform string to double-precision float conversion.
    *
    * @param   aErrorCode will contain error if one occurs
    * @return  double-precision float rep of string value
    */
@@ -441,37 +438,37 @@ public:
   /**
    *  These methods are used to remove all occurrences of the
    *  characters found in aSet from this string.
    *
    *  @param  aSet -- characters to be cut from this
    */
   void StripChars(const char_type* aSet);
 
-  template<typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   bool StripChars(const incompatible_char_type* aSet, const fallible_t&);
 
-  template<typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   void StripChars(const incompatible_char_type* aSet);
 
   /**
    *  This method strips whitespace throughout the string.
    */
   void StripWhitespace();
   bool StripWhitespace(const fallible_t&);
 
 
   /**
    *  swaps occurence of 1 string for another
    */
 
   void ReplaceChar(char_type aOldChar, char_type aNewChar);
   void ReplaceChar(const char_type* aSet, char_type aNewChar);
 
-  template<typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   void ReplaceChar(const char* aSet, char16_t aNewChar);
 
   /**
    * Replace all occurrences of aTarget with aNewValue.
    * The complexity of this function is O(n+m), n being the length of the string
    * and m being the length of aNewValue.
    */
   void ReplaceSubstring(const self_type& aTarget, const self_type& aNewValue);
@@ -591,19 +588,16 @@ public:
   typedef typename base_string_type::size_type size_type;
   typedef typename base_string_type::substring_tuple_type substring_tuple_type;
   typedef typename base_string_type::literalstring_type literalstring_type;
 
   // These are only for internal use within the string classes:
   typedef typename base_string_type::DataFlags DataFlags;
   typedef typename base_string_type::ClassFlags ClassFlags;
 
-  using typename base_string_type::IsChar;
-  using typename base_string_type::IsChar16;
-
 public:
 
   /**
    * constructors
    */
 
   nsTAutoStringN()
     : string_type(mStorage, 0, DataFlags::TERMINATED | DataFlags::INLINE,
@@ -624,17 +618,17 @@ public:
   explicit
   nsTAutoStringN(const char_type* aData, size_type aLength = size_type(-1))
     : self_type()
   {
     this->Assign(aData, aLength);
   }
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   explicit
   nsTAutoStringN(char16ptr_t aData, size_type aLength = size_type(-1))
     : self_type(static_cast<const char16_t*>(aData), aLength)
   {
   }
 #endif
 
   nsTAutoStringN(const self_type& aStr)
@@ -684,17 +678,17 @@ public:
     return *this;
   }
   self_type& operator=(const char_type* aData)
   {
     this->Assign(aData);
     return *this;
   }
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   self_type& operator=(char16ptr_t aStr)
   {
     this->Assign(aStr);
     return *this;
   }
 #endif
   self_type& operator=(const self_type& aStr)
   {
--- a/xpcom/string/nsTStringObsolete.cpp
+++ b/xpcom/string/nsTStringObsolete.cpp
@@ -403,27 +403,27 @@ nsTString<T>::SetCharAt(char16_t aChar, 
 }
 
 
 /**
  * nsTString::StripChars,StripChar,StripWhitespace
  */
 
 template<typename T>
-template<typename EnableIfChar16>
+template<typename Q, typename EnableIfChar16>
 void
 nsTString<T>::StripChars(const incompatible_char_type* aSet)
 {
   if (!StripChars(aSet, mozilla::fallible)) {
     this->AllocFailed(this->mLength);
   }
 }
 
 template<typename T>
-template<typename EnableIfChar16>
+template<typename Q, typename EnableIfChar16>
 bool
 nsTString<T>::StripChars(const incompatible_char_type* aSet, const fallible_t&)
 {
   if (!this->EnsureMutable()) {
     return false;
   }
 
   this->mLength = nsBufferRoutines<T>::strip_chars(this->mData, this->mLength, aSet);
--- a/xpcom/string/nsTStringRepr.h
+++ b/xpcom/string/nsTStringRepr.h
@@ -39,16 +39,46 @@ public:
   virtual int operator()(const char_type*, const char_type*,
                          uint32_t, uint32_t) const override;
 };
 
 extern template class nsTDefaultStringComparator<char>;
 extern template class nsTDefaultStringComparator<char16_t>;
 
 namespace mozilla {
+
+// This is mainly intended to be used in the context of nsTStrings where
+// we want to enable a specific function only for a given character class. In
+// order for this technique to work the member function needs to be templated
+// on something other than `T`. We keep this in the `mozilla` namespace rather
+// than `nsTStringRepr` as it's intentionally not dependent on `T`.
+//
+// The 'T' at the end of `Char[16]OnlyT` is refering to the `::type` portion
+// which will only be defined if the character class is correct. This is similar
+// to `std::enable_if_t` which is available in C++14, but not C++11.
+//
+// `CharType` is generally going to be a shadowed type of `T`.
+//
+// Example usage of a function that will only be defined if `T` == `char`:
+//
+// template <typename T>
+// class nsTSubstring : public nsTStringRepr<T> {
+//   template <typename Q = T, typename EnableForChar = typename CharOnlyT<Q>>
+//   int Foo() { return 42; }
+// };
+//
+// Please note that we had to use a separate type `Q` for this to work. You
+// will get a semi-decent compiler error if you use `T` directly.
+
+template <typename CharType> using CharOnlyT =
+  typename std::enable_if<std::is_same<char, CharType>::value>::type;
+
+template <typename CharType> using Char16OnlyT =
+  typename std::enable_if<std::is_same<char16_t, CharType>::value>::type;
+
 namespace detail {
 
 // nsTStringRepr defines a string's memory layout and some accessor methods.
 // This class exists so that nsTLiteralString can avoid inheriting
 // nsTSubstring's destructor. All methods on this class must be const because
 // literal strings are not writable.
 //
 // This class is an implementation detail and should not be instantiated
@@ -87,21 +117,16 @@ public:
 
   typedef uint32_t index_type;
   typedef uint32_t size_type;
 
   // These are only for internal use within the string classes:
   typedef StringDataFlags DataFlags;
   typedef StringClassFlags ClassFlags;
 
-  // These are used to conditionally enable functions for specific character
-  // types.
-  using IsChar   = std::enable_if<std::is_same<char, T>::value>;
-  using IsChar16 = std::enable_if<std::is_same<char16_t, T>::value>;
-
   // Reading iterators.
   const_char_iterator BeginReading() const
   {
     return mData;
   }
   const_char_iterator EndReading() const
   {
     return mData + mLength;
@@ -207,22 +232,22 @@ public:
   bool NS_FASTCALL Equals(const substring_tuple_type& aTuple,
                           const comparator_type& aComp) const;
 
   bool NS_FASTCALL Equals(const char_type* aData) const;
   bool NS_FASTCALL Equals(const char_type* aData,
                           const comparator_type& aComp) const;
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = Char16OnlyT<Q>>
   bool NS_FASTCALL Equals(char16ptr_t aData) const
   {
     return Equals(static_cast<const char16_t*>(aData));
   }
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = Char16OnlyT<Q>>
   bool NS_FASTCALL Equals(char16ptr_t aData, const comparator_type& aComp) const
   {
     return Equals(static_cast<const char16_t*>(aData), aComp);
   }
 #endif
 
   // An efficient comparison with ASCII that can be used even
   // for wide strings. Call this version when you know the
--- a/xpcom/string/nsTSubstring.h
+++ b/xpcom/string/nsTSubstring.h
@@ -66,19 +66,16 @@ public:
 
   typedef typename base_string_type::index_type index_type;
   typedef typename base_string_type::size_type size_type;
 
   // These are only for internal use within the string classes:
   typedef typename base_string_type::DataFlags DataFlags;
   typedef typename base_string_type::ClassFlags ClassFlags;
 
-  using typename base_string_type::IsChar;
-  using typename base_string_type::IsChar16;
-
   // this acts like a virtual destructor
   ~nsTSubstring()
   {
     Finalize();
   }
 
   /**
    * writing iterators
@@ -182,29 +179,29 @@ public:
   // them.
   void NS_FASTCALL Assign(const literalstring_type&);
 
   void NS_FASTCALL Assign(const substring_tuple_type&);
   MOZ_MUST_USE bool NS_FASTCALL Assign(const substring_tuple_type&,
                                        const fallible_t&);
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   void Assign(char16ptr_t aData)
   {
     Assign(static_cast<const char16_t*>(aData));
   }
 
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   void Assign(char16ptr_t aData, size_type aLength)
   {
     Assign(static_cast<const char16_t*>(aData), aLength);
   }
 
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   MOZ_MUST_USE bool Assign(char16ptr_t aData, size_type aLength,
                            const fallible_t& aFallible)
   {
     return Assign(static_cast<const char16_t*>(aData), aLength,
                   aFallible);
   }
 #endif
 
@@ -232,34 +229,34 @@ public:
   // There are not fallible version of these methods because they only really
   // apply to small allocations that we wouldn't want to check anyway.
   template<int N>
   void AssignLiteral(const char_type (&aStr)[N])
   {
     AssignLiteral(aStr, N - 1);
   }
 
-  template<int N, typename EnableIfChar16 = IsChar16>
+  template<int N, typename Q = T, typename EnableIfChar16 = typename mozilla::Char16OnlyT<Q>>
   void AssignLiteral(const incompatible_char_type (&aStr)[N])
   {
     AssignASCII(aStr, N - 1);
   }
 
   self_type& operator=(char_type aChar)
   {
     Assign(aChar);
     return *this;
   }
   self_type& operator=(const char_type* aData)
   {
     Assign(aData);
     return *this;
   }
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   self_type& operator=(char16ptr_t aData)
   {
     Assign(aData);
     return *this;
   }
 #endif
   self_type& operator=(const self_type& aStr)
   {
@@ -356,17 +353,17 @@ public:
   }
   MOZ_MUST_USE bool Append(const char_type* aData, size_type aLength,
                            const fallible_t& aFallible)
   {
     return Replace(base_string_type::mLength, 0, aData, aLength, aFallible);
   }
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   void Append(char16ptr_t aData, size_type aLength = size_type(-1))
   {
     Append(static_cast<const char16_t*>(aData), aLength);
   }
 #endif
 
   void Append(const self_type& aStr)
   {
@@ -461,24 +458,24 @@ public:
   // array variable. Use Append or AppendASCII for those.
   template<int N>
   void AppendLiteral(const char_type (&aStr)[N])
   {
     ReplaceLiteral(base_string_type::mLength, 0, aStr, N - 1);
   }
 
   // Only enable for T = char16_t
-  template<int N, typename EnableIfChar16 = IsChar16>
+  template <int N, typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   void AppendLiteral(const incompatible_char_type (&aStr)[N])
   {
     AppendASCII(aStr, N - 1);
   }
 
   // Only enable for T = char16_t
-  template<int N, typename EnableIfChar16 = IsChar16>
+  template <int N, typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   MOZ_MUST_USE bool
   AppendLiteral(const incompatible_char_type (&aStr)[N], const fallible_t& aFallible)
   {
     return AppendASCII(aStr, N - 1, aFallible);
   }
 
   self_type& operator+=(char_type aChar)
   {
@@ -486,17 +483,17 @@ public:
     return *this;
   }
   self_type& operator+=(const char_type* aData)
   {
     Append(aData);
     return *this;
   }
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   self_type& operator+=(char16ptr_t aData)
   {
     Append(aData);
     return *this;
   }
 #endif
   self_type& operator+=(const self_type& aStr)
   {
@@ -514,17 +511,17 @@ public:
     Replace(aPos, 0, aChar);
   }
   void Insert(const char_type* aData, index_type aPos,
               size_type aLength = size_type(-1))
   {
     Replace(aPos, 0, aData, aLength);
   }
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   void Insert(char16ptr_t aData, index_type aPos,
               size_type aLength = size_type(-1))
   {
     Insert(static_cast<const char16_t*>(aData), aPos, aLength);
   }
 #endif
   void Insert(const self_type& aStr, index_type aPos)
   {
@@ -621,23 +618,23 @@ public:
       return 0;
     }
 
     *aData = base_string_type::mData;
     return base_string_type::mLength;
   }
 
 #if defined(MOZ_USE_CHAR16_WRAPPER)
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   size_type GetMutableData(wchar_t** aData, size_type aNewLen = size_type(-1))
   {
     return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen);
   }
 
-  template <typename EnableIfChar16 = IsChar16>
+  template <typename Q = T, typename EnableIfChar16 = mozilla::Char16OnlyT<Q>>
   size_type GetMutableData(wchar_t** aData, size_type aNewLen,
                            const fallible_t& aFallible)
   {
     return GetMutableData(reinterpret_cast<char16_t**>(aData), aNewLen,
                           aFallible);
   }
 #endif
 
@@ -667,39 +664,39 @@ public:
   {
     auto len = aSpan.Length();
     if (len > mozilla::MaxValue<size_type>::value) {
       return false;
     }
     return Append(aSpan.Elements(), len, aFallible);
   }
 
-  template <typename EnableIfChar = IsChar>
+  template <typename Q = T, typename EnableIfChar = mozilla::CharOnlyT<Q>>
   operator mozilla::Span<uint8_t>()
   {
     return mozilla::MakeSpan(reinterpret_cast<uint8_t*>(BeginWriting()),
                              base_string_type::Length());
   }
 
-  template <typename EnableIfChar = IsChar>
+  template <typename Q = T, typename EnableIfChar = mozilla::CharOnlyT<Q>>
   operator mozilla::Span<const uint8_t>() const
   {
     return mozilla::MakeSpan(reinterpret_cast<const uint8_t*>(base_string_type::BeginReading()),
                              base_string_type::Length());
   }
 
-  template <typename EnableIfChar = IsChar>
+  template <typename Q = T, typename EnableIfChar = mozilla::CharOnlyT<Q>>
   void Append(mozilla::Span<const uint8_t> aSpan)
   {
     auto len = aSpan.Length();
     MOZ_RELEASE_ASSERT(len <= mozilla::MaxValue<size_type>::value);
     Append(reinterpret_cast<const char*>(aSpan.Elements()), len);
   }
 
-  template <typename EnableIfChar = IsChar>
+  template <typename Q = T, typename EnableIfChar = mozilla::CharOnlyT<Q>>
   MOZ_MUST_USE bool Append(mozilla::Span<const uint8_t> aSpan,
                            const fallible_t& aFallible)
   {
     auto len = aSpan.Length();
     if (len > mozilla::MaxValue<size_type>::value) {
       return false;
     }
     return Append(