Bug 1221368 - Change MakeTuple to decay the types of its arguments (r=froydnj)
☠☠ backed out by f620224aae8f ☠ ☠
authorBill McCloskey <billm@mozilla.com>
Mon, 02 Nov 2015 16:50:06 -0800
changeset 271685 3a22461c8ce8ee33cd7b37328c774296acc7ee2c
parent 271684 0733342d8c56d4a4b1d5f87f8be0f535ef8e6d7a
child 271686 bd99e5060e1e3ecf627a7d5419dda8620c6b5fa6
push id29650
push usercbook@mozilla.com
push dateMon, 09 Nov 2015 13:56:12 +0000
treeherdermozilla-central@e1ef2be156de [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1221368
milestone45.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 1221368 - Change MakeTuple to decay the types of its arguments (r=froydnj)
mfbt/Assertions.h
mfbt/Tuple.h
mfbt/TypeTraits.h
mfbt/tests/TestTuple.cpp
mfbt/tests/TestTypeTraits.cpp
--- a/mfbt/Assertions.h
+++ b/mfbt/Assertions.h
@@ -331,28 +331,16 @@ MOZ_ReportCrash(const char* aStr, const 
  */
 
 #ifdef __cplusplus
 #  include "mozilla/TypeTraits.h"
 namespace mozilla {
 namespace detail {
 
 template<typename T>
-struct IsFunction
-{
-  static const bool value = false;
-};
-
-template<typename R, typename... A>
-struct IsFunction<R(A...)>
-{
-  static const bool value = true;
-};
-
-template<typename T>
 struct AssertionConditionType
 {
   typedef typename RemoveReference<T>::Type ValueT;
   static_assert(!IsArray<ValueT>::value,
                 "Expected boolean assertion condition, got an array or a "
                 "string!");
   static_assert(!IsFunction<ValueT>::value,
                 "Expected boolean assertion condition, got a function! Did "
--- a/mfbt/Tuple.h
+++ b/mfbt/Tuple.h
@@ -412,35 +412,37 @@ auto Get(Tuple<Elements...>&& aTuple)
  * values without specifying the type of the tuple.
  * The type of the tuple is deduced from the types of its elements.
  *
  * Example:
  *
  * auto tuple = MakeTuple(42, 0.5f, 'c');  // has type Tuple<int, float, char>
  */
 template<typename... Elements>
-Tuple<Elements...> MakeTuple(Elements&&... aElements)
+inline Tuple<typename Decay<Elements>::Type...>
+MakeTuple(Elements&&... aElements)
 {
-  return Tuple<Elements...>(Forward<Elements>(aElements)...);
+  return Tuple<typename Decay<Elements>::Type...>(Forward<Elements>(aElements)...);
 }
 
 /**
  * A convenience function for constructing a tuple of references to a
  * sequence of variables. Since assignments to the elements of the tuple
  * "go through" to the referenced variables, this can be used to "unpack"
  * a tuple into individual variables.
  *
  * Example:
  *
  * int i;
  * float f;
  * char c;
  * Tie(i, f, c) = FunctionThatReturnsATuple();
  */
 template<typename... Elements>
-Tuple<Elements&...> Tie(Elements&... aVariables)
+inline Tuple<Elements&...>
+Tie(Elements&... aVariables)
 {
   return Tuple<Elements&...>(aVariables...);
 }
 
 } // namespace mozilla
 
 #endif /* mozilla_Tuple_h */
--- a/mfbt/TypeTraits.h
+++ b/mfbt/TypeTraits.h
@@ -160,16 +160,50 @@ struct IsArrayHelper<T[]> : TrueType {};
  */
 template<typename T>
 struct IsArray : detail::IsArrayHelper<typename RemoveCV<T>::Type>
 {};
 
 namespace detail {
 
 template<typename T>
+struct IsFunPtr;
+
+template<typename>
+struct IsFunPtr
+  : public FalseType
+{};
+
+template<typename Result, typename... ArgTypes>
+struct IsFunPtr<Result(*)(ArgTypes...)>
+  : public TrueType
+{};
+
+}; // namespace detail
+
+/**
+ * IsFunction determines whether a type is a function type. Function pointers
+ * don't qualify here--only the type of an actual function symbol. We do not
+ * correctly handle varags function types because of a bug in MSVC.
+ *
+ * Given the function:
+ *   void f(int) {}
+ *
+ * mozilla::IsFunction<void(int)> is true;
+ * mozilla::IsFunction<void(*)(int)> is false;
+ * mozilla::IsFunction<decltype(f)> is true.
+ */
+template<typename T>
+struct IsFunction
+  : public detail::IsFunPtr<typename RemoveCV<T>::Type *>
+{};
+
+namespace detail {
+
+template<typename T>
 struct IsPointerHelper : FalseType {};
 
 template<typename T>
 struct IsPointerHelper<T*> : TrueType {};
 
 } // namespace detail
 
 /**
@@ -1058,16 +1092,32 @@ struct RemovePointerHelper<T, Pointee*>
  * mozilla::RemovePointer<void (*)()>::Type is void();
  * mozilla::RemovePointer<bool S::*>::Type is bool S::*.
  */
 template<typename T>
 struct RemovePointer
   : detail::RemovePointerHelper<T, typename RemoveCV<T>::Type>
 {};
 
+/**
+ * Converts T& to T*. Otherwise returns T* given T. Note that C++17 wants
+ * std::add_pointer to work differently for function types. We don't implement
+ * that behavior here.
+ *
+ * mozilla::AddPointer<int> is int*;
+ * mozilla::AddPointer<int*> is int**;
+ * mozilla::AddPointer<int&> is int*;
+ * mozilla::AddPointer<int* const> is int** const.
+ */
+template<typename T>
+struct AddPointer
+{
+  typedef typename RemoveReference<T>::Type* Type;
+};
+
 /* 20.9.7.6 Other transformations [meta.trans.other] */
 
 /**
  * EnableIf is a struct containing a typedef of T if and only if B is true.
  *
  * mozilla::EnableIf<true, int>::Type is int;
  * mozilla::EnableIf<false, int>::Type is a compile-time error.
  *
@@ -1106,11 +1156,56 @@ struct Conditional
 };
 
 template<class A, class B>
 struct Conditional<false, A, B>
 {
   typedef B Type;
 };
 
+namespace detail {
+
+template<typename U,
+         bool IsArray = IsArray<U>::value,
+         bool IsFunction = IsFunction<U>::value>
+struct DecaySelector;
+
+template<typename U>
+struct DecaySelector<U, false, false>
+{
+  typedef typename RemoveCV<U>::Type Type;
+};
+
+template<typename U>
+struct DecaySelector<U, true, false>
+{
+  typedef typename RemoveExtent<U>::Type* Type;
+};
+
+template<typename U>
+struct DecaySelector<U, false, true>
+{
+  typedef typename AddPointer<U>::Type Type;
+};
+
+}; // namespace detail
+
+/**
+ * Strips const/volatile off a type and decays it from an lvalue to an
+ * rvalue. So function types are converted to function pointers, arrays to
+ * pointers, and references are removed.
+ *
+ * mozilla::Decay<int>::Type is int
+ * mozilla::Decay<int&>::Type is int
+ * mozilla::Decay<int&&>::Type is int
+ * mozilla::Decay<const int&>::Type is int
+ * mozilla::Decay<int[2]>::Type is int*
+ * mozilla::Decay<int(int)>::Type is int(*)(int)
+ */
+template<typename T>
+class Decay
+  : public detail::DecaySelector<typename RemoveReference<T>::Type>
+{
+};
+
 } /* namespace mozilla */
 
 #endif /* mozilla_TypeTraits_h */
--- a/mfbt/tests/TestTuple.cpp
+++ b/mfbt/tests/TestTuple.cpp
@@ -231,25 +231,31 @@ TestGet()
   CHECK(Get<0>(tuple) == 41);
 
   // Writing through reference elements
   Get<1>(tuple) = 42;
   CHECK(Get<1>(tuple) == 42);
   CHECK(y == 42);
 }
 
-static bool
+static void
 TestMakeTuple()
 {
   auto tuple = MakeTuple(42, 0.5f, 'c');
   CHECK_TYPE(tuple, Tuple<int, float, char>);
   CHECK(Get<0>(tuple) == 42);
   CHECK(Get<1>(tuple) == 0.5f);
   CHECK(Get<2>(tuple) == 'c');
-  return true;
+
+  // Make sure we don't infer the type to be Tuple<int&>.
+  int x = 1;
+  auto tuple2 = MakeTuple(x);
+  CHECK_TYPE(tuple2, Tuple<int>);
+  x = 2;
+  CHECK(Get<0>(tuple2) == 1);
 }
 
 static bool
 TestTie()
 {
   int i;
   float f;
   char c;
--- a/mfbt/tests/TestTypeTraits.cpp
+++ b/mfbt/tests/TestTypeTraits.cpp
@@ -3,18 +3,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/TypeTraits.h"
 
 using mozilla::AddLvalueReference;
+using mozilla::AddPointer;
 using mozilla::AddRvalueReference;
+using mozilla::Decay;
 using mozilla::DeclVal;
+using mozilla::IsFunction;
 using mozilla::IsArray;
 using mozilla::IsBaseOf;
 using mozilla::IsClass;
 using mozilla::IsConvertible;
 using mozilla::IsEmpty;
 using mozilla::IsLvalueReference;
 using mozilla::IsPointer;
 using mozilla::IsReference;
@@ -22,16 +25,23 @@ using mozilla::IsRvalueReference;
 using mozilla::IsSame;
 using mozilla::IsSigned;
 using mozilla::IsUnsigned;
 using mozilla::MakeSigned;
 using mozilla::MakeUnsigned;
 using mozilla::RemoveExtent;
 using mozilla::RemovePointer;
 
+static_assert(!IsFunction<int>::value,
+              "int is not a function type");
+static_assert(IsFunction<void(int)>::value,
+              "void(int) is a function type");
+static_assert(!IsFunction<void(*)(int)>::value,
+              "void(*)(int) is not a function type");
+
 static_assert(!IsArray<bool>::value,
               "bool not an array");
 static_assert(IsArray<bool[]>::value,
               "bool[] is an array");
 static_assert(IsArray<bool[5]>::value,
               "bool[5] is an array");
 
 static_assert(!IsPointer<bool>::value,
@@ -484,16 +494,46 @@ static_assert(IsSame<RemovePointer<void 
                                    void (TestRemovePointer::*)()>::value,
               "removing pointer from void (S::*)() must return void (S::*)()");
 static_assert(IsSame<RemovePointer<void (*)()>::Type, void()>::value,
               "removing pointer from void (*)() must return void()");
 static_assert(IsSame<RemovePointer<bool TestRemovePointer::*>::Type,
                                    bool TestRemovePointer::*>::value,
               "removing pointer from bool S::* must return bool S::*");
 
+static_assert(IsSame<AddPointer<int>::Type, int*>::value,
+              "adding pointer to int must return int*");
+static_assert(IsSame<AddPointer<int*>::Type, int**>::value,
+              "adding pointer to int* must return int**");
+static_assert(IsSame<AddPointer<int&>::Type, int*>::value,
+              "adding pointer to int& must return int*");
+static_assert(IsSame<AddPointer<int* const>::Type, int* const*>::value,
+              "adding pointer to int* const must return int* const*");
+static_assert(IsSame<AddPointer<int* volatile>::Type, int* volatile*>::value,
+              "adding pointer to int* volatile must return int* volatile*");
+
+static_assert(IsSame<Decay<int>::Type, int>::value,
+              "decaying int must return int");
+static_assert(IsSame<Decay<int*>::Type, int*>::value,
+              "decaying int* must return int*");
+static_assert(IsSame<Decay<int* const>::Type, int*>::value,
+              "decaying int* const must return int*");
+static_assert(IsSame<Decay<int* volatile>::Type, int*>::value,
+              "decaying int* volatile must return int*");
+static_assert(IsSame<Decay<int&>::Type, int>::value,
+              "decaying int& must return int");
+static_assert(IsSame<Decay<const int&>::Type, int>::value,
+              "decaying const int& must return int");
+static_assert(IsSame<Decay<int&&>::Type, int>::value,
+              "decaying int&& must return int");
+static_assert(IsSame<Decay<int[1]>::Type, int*>::value,
+              "decaying int[1] must return int*");
+static_assert(IsSame<Decay<void(int)>::Type, void(*)(int)>::value,
+              "decaying void(int) must return void(*)(int)");
+
 /*
  * Android's broken [u]intptr_t inttype macros are broken because its PRI*PTR
  * macros are defined as "ld", but sizeof(long) is 8 and sizeof(intptr_t)
  * is 4 on 32-bit Android. We redefine Android's PRI*PTR macros in
  * IntegerPrintfMacros.h and assert here that our new definitions match the
  * actual type sizes seen at compile time.
  */
 #if defined(ANDROID) && !defined(__LP64__)