Add new style meta-programming primatives.

Using class templates instead of alias templates causes a lot of
instantiations. As part of the move away from C++03, we want to
improve the efficiency of our meta-programming.

This patch lays the groundwork by introducing new _If, _EnableIf,
_And, _Or, and _IsValidExpansion (detect member). Future patches
will replace the existing implementations after verifying there
compile time differences.

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@364114 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/type_traits b/include/type_traits
index 2149492..05405c9 100644
--- a/include/type_traits
+++ b/include/type_traits
@@ -427,6 +427,93 @@
 template <class _Tp> class _LIBCPP_TEMPLATE_VIS reference_wrapper;
 template <class _Tp> struct _LIBCPP_TEMPLATE_VIS hash;
 
+
+template <class _Tp, _Tp __v>
+struct _LIBCPP_TEMPLATE_VIS integral_constant
+{
+  static _LIBCPP_CONSTEXPR const _Tp      value = __v;
+  typedef _Tp               value_type;
+  typedef integral_constant type;
+  _LIBCPP_INLINE_VISIBILITY
+  _LIBCPP_CONSTEXPR operator value_type() const _NOEXCEPT {return value;}
+#if _LIBCPP_STD_VER > 11
+  _LIBCPP_INLINE_VISIBILITY
+  constexpr value_type operator ()() const _NOEXCEPT {return value;}
+#endif
+};
+
+template <class _Tp, _Tp __v>
+_LIBCPP_CONSTEXPR const _Tp integral_constant<_Tp, __v>::value;
+
+#if _LIBCPP_STD_VER > 14
+template <bool __b>
+using bool_constant = integral_constant<bool, __b>;
+#define _LIBCPP_BOOL_CONSTANT(__b) bool_constant<(__b)>
+#else
+#define _LIBCPP_BOOL_CONSTANT(__b) integral_constant<bool,(__b)>
+#endif
+
+typedef _LIBCPP_BOOL_CONSTANT(true)  true_type;
+typedef _LIBCPP_BOOL_CONSTANT(false) false_type;
+
+template <bool _Val>
+using _BoolConstant _LIBCPP_NODEBUG_TYPE = integral_constant<bool, _Val>;
+
+template <bool> struct _MetaBase;
+template <>
+struct _MetaBase<true> {
+  template <class _Tp, class _Up>
+  using _SelectImpl _LIBCPP_NODEBUG_TYPE = _Tp;
+  template <template <class...> class _FirstFn, template <class...> class, class ..._Args>
+  using _SelectApplyImpl _LIBCPP_NODEBUG_TYPE = _FirstFn<_Args...>;
+  template <class _First, class...>
+  using _FirstImpl _LIBCPP_NODEBUG_TYPE = _First;
+  template <class, class _Second, class...>
+  using _SecondImpl _LIBCPP_NODEBUG_TYPE = _Second;
+  template <class _Tp = void>
+  using _EnableIfImpl _LIBCPP_NODEBUG_TYPE = _Tp;
+  template <class _Result, class _First, class ..._Rest>
+  using _OrImpl _LIBCPP_NODEBUG_TYPE = typename _MetaBase<_First::type::value != true && sizeof...(_Rest) != 0>::template _OrImpl<_First, _Rest...>;
+  template <class _Result, class _First, class ..._Rest>
+  using _AndImpl _LIBCPP_NODEBUG_TYPE = typename _MetaBase<_First::type::value == true && sizeof...(_Rest) != 0>::template _AndImpl<_First, _Rest...>;
+};
+
+template <>
+struct _MetaBase<false> {
+  template <class _Tp, class _Up>
+  using _SelectImpl _LIBCPP_NODEBUG_TYPE = _Up;
+  template <template <class...> class, template <class...> class _SecondFn, class ..._Args>
+  using _SelectApplyImpl _LIBCPP_NODEBUG_TYPE = _SecondFn<_Args...>;
+  template <class _Result, class ...>
+  using _OrImpl _LIBCPP_NODEBUG_TYPE = _Result;
+  template <class _Result, class ...>
+  using _AndImpl _LIBCPP_NODEBUG_TYPE = _Result;
+};
+template <bool _Cond, class _Ret = void>
+using _EnableIf _LIBCPP_NODEBUG_TYPE = typename _MetaBase<_Cond>::template _EnableIfImpl<_Ret>;
+template <bool _Cond, class _IfRes, class _ElseRes>
+using _If _LIBCPP_NODEBUG_TYPE = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>;
+template <class ..._Rest>
+using _Or _LIBCPP_NODEBUG_TYPE = typename _MetaBase< sizeof...(_Rest) != 0 >::template _OrImpl<false_type, _Rest...>;
+template <class ..._Rest>
+using _And _LIBCPP_NODEBUG_TYPE = typename _MetaBase< sizeof...(_Rest) != 0 >::template _AndImpl<true_type, _Rest...>;
+template <class _Pred>
+struct _Not : _BoolConstant<!_Pred::type::value> {};
+template <class ..._Args>
+using _FirstType _LIBCPP_NODEBUG_TYPE = typename _MetaBase<(sizeof...(_Args) >= 1)>::template _FirstImpl<_Args...>;
+template <class ..._Args>
+using _SecondType _LIBCPP_NODEBUG_TYPE = typename _MetaBase<(sizeof...(_Args) >= 2)>::template _SecondImpl<_Args...>;
+
+// Member detector base
+
+template <template <class...> class _Templ, class ..._Args>
+true_type __sfinae_test_impl(_FirstType<int, _Templ<_Args...> >);
+template <template <class...> class, class ...>
+false_type __sfinae_test_impl(...);
+
+template <template <class ...> class _Templ, class ..._Args>
+using _IsValidExpansion _LIBCPP_NODEBUG_TYPE = decltype(std::__sfinae_test_impl<_Templ, _Args...>(0));
+
 template <class>
 struct __void_t { typedef void type; };
 
@@ -528,34 +615,6 @@
 
 // helper class:
 
-template <class _Tp, _Tp __v>
-struct _LIBCPP_TEMPLATE_VIS integral_constant
-{
-    static _LIBCPP_CONSTEXPR const _Tp      value = __v;
-    typedef _Tp               value_type;
-    typedef integral_constant type;
-    _LIBCPP_INLINE_VISIBILITY
-        _LIBCPP_CONSTEXPR operator value_type() const _NOEXCEPT {return value;}
-#if _LIBCPP_STD_VER > 11
-    _LIBCPP_INLINE_VISIBILITY
-         constexpr value_type operator ()() const _NOEXCEPT {return value;}
-#endif
-};
-
-template <class _Tp, _Tp __v>
-_LIBCPP_CONSTEXPR const _Tp integral_constant<_Tp, __v>::value;
-
-#if _LIBCPP_STD_VER > 14
-template <bool __b>
-using bool_constant = integral_constant<bool, __b>;
-#define _LIBCPP_BOOL_CONSTANT(__b) bool_constant<(__b)>
-#else
-#define _LIBCPP_BOOL_CONSTANT(__b) integral_constant<bool,(__b)>
-#endif
-
-typedef _LIBCPP_BOOL_CONSTANT(true)  true_type;
-typedef _LIBCPP_BOOL_CONSTANT(false) false_type;
-
 #if !defined(_LIBCPP_CXX03_LANG)
 
 // __lazy_and
diff --git a/test/libcxx/utilities/meta/meta_base.pass.cpp b/test/libcxx/utilities/meta/meta_base.pass.cpp
new file mode 100644
index 0000000..f04ae60
--- /dev/null
+++ b/test/libcxx/utilities/meta/meta_base.pass.cpp
@@ -0,0 +1,91 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+
+struct Bomb;
+template <int N, class T = Bomb >
+struct BOOM {
+  using Explode = typename T::BOOMBOOM;
+};
+
+using True = std::true_type;
+using False = std::false_type;
+
+void test_if() {
+  ASSERT_SAME_TYPE(std::_If<true, int, long>, int);
+  ASSERT_SAME_TYPE(std::_If<false, int, long>, long);
+}
+
+void test_and() {
+  static_assert(std::_And<True>::value, "");
+  static_assert(!std::_And<False>::value, "");
+  static_assert(std::_And<True, True>::value, "");
+  static_assert(!std::_And<False, BOOM<1> >::value, "");
+  static_assert(!std::_And<True, True, True, False, BOOM<2> >::value, "");
+}
+
+void test_or() {
+  static_assert(std::_Or<True>::value, "");
+  static_assert(!std::_Or<False>::value, "");
+  static_assert(std::_Or<False, True>::value, "");
+  static_assert(std::_Or<True, std::_Not<BOOM<3> > >::value, "");
+  static_assert(!std::_Or<False, False>::value, "");
+  static_assert(std::_Or<True, BOOM<1> >::value, "");
+  static_assert(std::_Or<False, False, False, False, True, BOOM<2> >::value, "");
+}
+
+void test_combined() {
+  static_assert(std::_And<True, std::_Or<False, True, BOOM<4> > >::value, "");
+  static_assert(std::_And<True, std::_Or<False, True, BOOM<4> > >::value, "");
+  static_assert(std::_Not<std::_And<True, False, BOOM<5> > >::value, "");
+}
+
+struct MemberTest {
+  static int foo;
+  using type = long;
+
+  void func(int);
+};
+struct Empty {};
+struct MemberTest2 {
+  using foo = int;
+};
+template <class T>
+using HasFooData = decltype(T::foo);
+template <class T>
+using HasFooType = typename T::foo;
+
+template <class T, class U>
+using FuncCallable = decltype(std::declval<T>().func(std::declval<U>()));
+template <class T>
+using BadCheck = typename T::DOES_NOT_EXIST;
+
+void test_is_valid_trait() {
+  static_assert(std::_IsValidExpansion<HasFooData, MemberTest>::value, "");
+  static_assert(!std::_IsValidExpansion<HasFooType, MemberTest>::value, "");
+  static_assert(!std::_IsValidExpansion<HasFooData, MemberTest2>::value, "");
+  static_assert(std::_IsValidExpansion<HasFooType, MemberTest2>::value, "");
+  static_assert(std::_IsValidExpansion<FuncCallable, MemberTest, int>::value, "");
+  static_assert(!std::_IsValidExpansion<FuncCallable, MemberTest, void*>::value, "");
+}
+
+void test_first_and_second_type() {
+  ASSERT_SAME_TYPE(std::_FirstType<int, long, void*>, int);
+  ASSERT_SAME_TYPE(std::_FirstType<char>, char);
+  ASSERT_SAME_TYPE(std::_SecondType<char, long>, long);
+  ASSERT_SAME_TYPE(std::_SecondType<long long, int, void*>, int);
+}
+
+int main(int, char**) {
+  return 0;
+}
diff --git a/test/libcxx/utilities/meta/stress_tests/stress_test_metafunctions.sh.cpp b/test/libcxx/utilities/meta/stress_tests/stress_test_metafunctions.sh.cpp
new file mode 100644
index 0000000..559759b
--- /dev/null
+++ b/test/libcxx/utilities/meta/stress_tests/stress_test_metafunctions.sh.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This is a dummy feature that prevents this test from running by default.
+// REQUIRES: template-const-testing
+
+// The table below compares the compile time and object size for each of the
+// variants listed in the RUN script.
+//
+//  Impl          Compile Time    Object Size
+// -------------------------------------------
+// _And:         3,498.639 ms     158 M
+// __lazy_and:  10,138.982 ms     334 M
+// __and_:      14,181.851 ms     648 M
+//
+
+// RUN: %cxx %flags %compile_flags -c %s -o %S/new.o -ggdb  -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace -std=c++17
+// RUN: %cxx %flags %compile_flags -c %s -o %S/lazy.o -ggdb  -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace  -std=c++17 -DTEST_LAZY_AND
+// RUN: %cxx %flags %compile_flags -c %s -o %S/std.o -ggdb  -ggnu-pubnames -ftemplate-depth=5000 -ftime-trace   -std=c++17 -DTEST_STD_AND
+
+#include <type_traits>
+#include <cassert>
+
+#include "test_macros.h"
+#include "template_cost_testing.h"
+using std::true_type;
+using std::false_type;
+
+#define FALSE_T() std::false_type,
+#define TRUE_T() std::true_type,
+
+#ifdef TEST_LAZY_AND
+#define TEST_AND std::__lazy_and
+#define TEST_OR std::__lazy_or
+#elif defined(TEST_STD_AND)
+#define TEST_AND std::__and_
+#define TEST_OR std::__or_
+#else
+#define TEST_AND std::_And
+#define TEST_OR std::_Or
+#endif
+
+void sink(...);
+
+void Foo1(TEST_AND < REPEAT_1000(TRUE_T) true_type > t1) { sink(&t1); }
+void Foo2(TEST_AND < REPEAT_1000(TRUE_T) REPEAT_1000(TRUE_T) true_type > t2) { sink(&t2); }
+void Foo3(TEST_AND < REPEAT_1000(TRUE_T) true_type, false_type > t3) { sink(&t3); }
+void Foo4(TEST_AND < REPEAT_1000(TRUE_T) REPEAT_1000(TRUE_T) true_type, false_type > t4) { sink(&t4); }
+void Foo5(TEST_AND < false_type, REPEAT_1000(TRUE_T) true_type > t5) { sink(&t5); }
+void Foo6(TEST_AND < false_type, REPEAT_1000(TRUE_T) REPEAT_1000(TRUE_T) true_type > t6) { sink(&t6); }
+
+void escape() {
+
+sink(&Foo1);
+sink(&Foo2);
+sink(&Foo3);
+sink(&Foo4);
+sink(&Foo5);
+sink(&Foo6);
+}
+
+