Skip to content

Commit

Permalink
[libcxx] Delay evaluation of __make_tuple_types to prevent blowing th…
Browse files Browse the repository at this point in the history
…e max template instantiation depth. Fixes Bug #18345

Summary:
http://llvm.org/bugs/show_bug.cgi?id=18345

Tuple's constructor and assignment operators for "tuple-like" types evaluates __make_tuple_types unnecessarily. In the case of a large array this can blow the template instantiation depth.

Ex:
```
#include <array>
#include <tuple>
#include <memory>
 
typedef std::array<int, 1256> array_t;
typedef std::tuple<array_t> tuple_t;

int main() {
  array_t a;
  tuple_t t(a); // broken
  t = a; // broken

  // make_shared uses tuple behind the scenes. This bug breaks this code.
  std::make_shared<array_t>(a);
}
```

To prevent this from happening we delay the instantiation of `__make_tuple_types` until after we perform the length check. Currently `__make_tuple_types` is instantiated at the same time that the length check .


Test Plan: Two tests have been added. One for the "tuple-like" constructors and another for the "tuple-like" assignment operator. 

Reviewers: mclow.lists, EricWF

Reviewed By: EricWF

Subscribers: K-ballo, cfe-commits

Differential Revision: http://reviews.llvm.org/D4467

llvm-svn: 220769
  • Loading branch information
EricWF committed Oct 28, 2014
1 parent 44067ee commit 295bce1
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 21 deletions.
72 changes: 51 additions & 21 deletions libcxx/include/__tuple
Expand Up @@ -245,83 +245,113 @@ struct __make_tuple_types

// __tuple_convertible

template <bool, class _Tp, class _Up>
template <class, class>
struct __tuple_convertible_imp : public false_type {};

template <class _Tp0, class ..._Tp, class _Up0, class ..._Up>
struct __tuple_convertible_imp<true, __tuple_types<_Tp0, _Tp...>, __tuple_types<_Up0, _Up...> >
struct __tuple_convertible_imp<__tuple_types<_Tp0, _Tp...>, __tuple_types<_Up0, _Up...> >
: public integral_constant<bool,
is_convertible<_Tp0, _Up0>::value &&
__tuple_convertible_imp<true, __tuple_types<_Tp...>, __tuple_types<_Up...> >::value> {};
__tuple_convertible_imp<__tuple_types<_Tp...>, __tuple_types<_Up...> >::value> {};

template <>
struct __tuple_convertible_imp<true, __tuple_types<>, __tuple_types<> >
struct __tuple_convertible_imp<__tuple_types<>, __tuple_types<> >
: public true_type {};

template <bool, class, class>
struct __tuple_convertible_apply : public false_type {};

template <class _Tp, class _Up>
struct __tuple_convertible_apply<true, _Tp, _Up>
: public __tuple_convertible_imp<
typename __make_tuple_types<_Tp>::type
, typename __make_tuple_types<_Up>::type
>
{};

template <class _Tp, class _Up, bool = __tuple_like<typename remove_reference<_Tp>::type>::value,
bool = __tuple_like<_Up>::value>
struct __tuple_convertible
: public false_type {};

template <class _Tp, class _Up>
struct __tuple_convertible<_Tp, _Up, true, true>
: public __tuple_convertible_imp<tuple_size<typename remove_reference<_Tp>::type>::value ==
tuple_size<_Up>::value,
typename __make_tuple_types<_Tp>::type, typename __make_tuple_types<_Up>::type>
: public __tuple_convertible_apply<tuple_size<typename remove_reference<_Tp>::type>::value ==
tuple_size<_Up>::value, _Tp, _Up>
{};

// __tuple_constructible

template <bool, class _Tp, class _Up>
template <class, class>
struct __tuple_constructible_imp : public false_type {};

template <class _Tp0, class ..._Tp, class _Up0, class ..._Up>
struct __tuple_constructible_imp<true, __tuple_types<_Tp0, _Tp...>, __tuple_types<_Up0, _Up...> >
struct __tuple_constructible_imp<__tuple_types<_Tp0, _Tp...>, __tuple_types<_Up0, _Up...> >
: public integral_constant<bool,
is_constructible<_Up0, _Tp0>::value &&
__tuple_constructible_imp<true, __tuple_types<_Tp...>, __tuple_types<_Up...> >::value> {};
__tuple_constructible_imp<__tuple_types<_Tp...>, __tuple_types<_Up...> >::value> {};

template <>
struct __tuple_constructible_imp<true, __tuple_types<>, __tuple_types<> >
struct __tuple_constructible_imp<__tuple_types<>, __tuple_types<> >
: public true_type {};

template <bool _SameSize, class, class>
struct __tuple_constructible_apply : public false_type {};

template <class _Tp, class _Up>
struct __tuple_constructible_apply<true, _Tp, _Up>
: public __tuple_constructible_imp<
typename __make_tuple_types<_Tp>::type
, typename __make_tuple_types<_Up>::type
>
{};

template <class _Tp, class _Up, bool = __tuple_like<typename remove_reference<_Tp>::type>::value,
bool = __tuple_like<_Up>::value>
struct __tuple_constructible
: public false_type {};

template <class _Tp, class _Up>
struct __tuple_constructible<_Tp, _Up, true, true>
: public __tuple_constructible_imp<tuple_size<typename remove_reference<_Tp>::type>::value ==
tuple_size<_Up>::value,
typename __make_tuple_types<_Tp>::type, typename __make_tuple_types<_Up>::type>
: public __tuple_constructible_apply<tuple_size<typename remove_reference<_Tp>::type>::value ==
tuple_size<_Up>::value, _Tp, _Up>
{};

// __tuple_assignable

template <bool, class _Tp, class _Up>
template <class, class>
struct __tuple_assignable_imp : public false_type {};

template <class _Tp0, class ..._Tp, class _Up0, class ..._Up>
struct __tuple_assignable_imp<true, __tuple_types<_Tp0, _Tp...>, __tuple_types<_Up0, _Up...> >
struct __tuple_assignable_imp<__tuple_types<_Tp0, _Tp...>, __tuple_types<_Up0, _Up...> >
: public integral_constant<bool,
is_assignable<_Up0&, _Tp0>::value &&
__tuple_assignable_imp<true, __tuple_types<_Tp...>, __tuple_types<_Up...> >::value> {};
__tuple_assignable_imp<__tuple_types<_Tp...>, __tuple_types<_Up...> >::value> {};

template <>
struct __tuple_assignable_imp<true, __tuple_types<>, __tuple_types<> >
struct __tuple_assignable_imp<__tuple_types<>, __tuple_types<> >
: public true_type {};

template <bool, class, class>
struct __tuple_assignable_apply : public false_type {};

template <class _Tp, class _Up>
struct __tuple_assignable_apply<true, _Tp, _Up>
: __tuple_assignable_imp<
typename __make_tuple_types<_Tp>::type
, typename __make_tuple_types<_Up>::type
>
{};

template <class _Tp, class _Up, bool = __tuple_like<typename remove_reference<_Tp>::type>::value,
bool = __tuple_like<_Up>::value>
struct __tuple_assignable
: public false_type {};

template <class _Tp, class _Up>
struct __tuple_assignable<_Tp, _Up, true, true>
: public __tuple_assignable_imp<tuple_size<typename remove_reference<_Tp>::type>::value ==
tuple_size<_Up>::value,
typename __make_tuple_types<_Tp>::type, typename __make_tuple_types<_Up>::type>
: public __tuple_assignable_apply<tuple_size<typename remove_reference<_Tp>::type>::value ==
tuple_size<_Up>::value, _Tp, _Up>
{};

_LIBCPP_END_NAMESPACE_STD
Expand Down
@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// <tuple>

// template <class... Types> class tuple;

// template <class Tuple, __tuple_assignable<Tuple, tuple> >
// tuple & operator=(Tuple &&);

// This test checks that we do not evaluate __make_tuple_types
// on the array when it doesn't match the size of the tuple.

#include <array>
#include <tuple>

// Use 1256 to try and blow the template instantiation depth for all compilers.
typedef std::array<char, 1256> array_t;
typedef std::tuple<array_t> tuple_t;

int main()
{
array_t arr;
tuple_t tup;
tup = arr;
}
@@ -0,0 +1,34 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// <tuple>

// template <class... Types> class tuple;

// template <class Tuple, __tuple_convertible<Tuple, tuple> >
// tuple(Tuple &&);
//
// template <class Tuple, __tuple_constructible<Tuple, tuple> >
// tuple(Tuple &&);

// This test checks that we do not evaluate __make_tuple_types
// on the array.

#include <array>
#include <tuple>

// Use 1256 to try and blow the template instantiation depth for all compilers.
typedef std::array<char, 1256> array_t;
typedef std::tuple<array_t> tuple_t;

int main()
{
array_t arr;
tuple_t tup(arr);
}

0 comments on commit 295bce1

Please sign in to comment.