Skip to content

Commit

Permalink
Assert when we try to use an incomplete type on a trait which require…
Browse files Browse the repository at this point in the history
…s complete types as arguments.

This prevents some traits from doing the wrong thing (ie compiling but giving the wrong answer when handed an incomplete type.
See https://svn.boost.org/trac10/ticket/12285.
A by-product of this is we add is_complete as a new trait.
  • Loading branch information
jzmaddock committed Feb 4, 2018
1 parent 85a03d3 commit ac35139
Show file tree
Hide file tree
Showing 35 changed files with 572 additions and 17 deletions.
7 changes: 7 additions & 0 deletions include/boost/type_traits/common_type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
#include <boost/type_traits/decay.hpp>
#include <boost/type_traits/declval.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/type_traits/is_void.hpp>
#include <boost/type_traits/is_array.hpp>
#include <boost/static_assert.hpp>

#if defined(BOOST_NO_CXX11_DECLTYPE)
#include <boost/type_traits/detail/common_type_impl.hpp>
Expand Down Expand Up @@ -75,6 +79,7 @@ struct common_type: common_type<typename common_type<T1, T2>::type, T3, T4, T5,

template<class T> struct common_type<T>: boost::decay<T>
{
BOOST_STATIC_ASSERT_MSG(::boost::is_complete<T>::value || ::boost::is_void<T>::value || ::boost::is_array<T>::value, "Arguments to common_type must both be complete types");
};

// two arguments
Expand Down Expand Up @@ -138,6 +143,8 @@ template<class T1, class T2> struct common_type_decay_helper<T1, T2, T1, T2>: co

template<class T1, class T2> struct common_type<T1, T2>: type_traits_detail::common_type_decay_helper<T1, T2>
{
BOOST_STATIC_ASSERT_MSG(::boost::is_complete<T1>::value || ::boost::is_void<T1>::value || ::boost::is_array<T1>::value, "Arguments to common_type must both be complete types");
BOOST_STATIC_ASSERT_MSG(::boost::is_complete<T2>::value || ::boost::is_void<T2>::value || ::boost::is_array<T2>::value, "Arguments to common_type must both be complete types");
};

} // namespace boost
Expand Down
13 changes: 11 additions & 2 deletions include/boost/type_traits/has_nothrow_destructor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

#include <boost/type_traits/declval.hpp>
#include <boost/type_traits/is_destructible.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/static_assert.hpp>

namespace boost{

Expand All @@ -27,12 +29,19 @@ namespace boost{

}

template <class T> struct has_nothrow_destructor : public detail::has_nothrow_destructor_imp<T, boost::is_destructible<T>::value>{};
template <class T, std::size_t N> struct has_nothrow_destructor<T[N]> : public has_nothrow_destructor<T>{};
template <class T> struct has_nothrow_destructor : public detail::has_nothrow_destructor_imp<T, boost::is_destructible<T>::value>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to has_nothrow_destructor must be complete types");
};
template <class T, std::size_t N> struct has_nothrow_destructor<T[N]> : public has_nothrow_destructor<T>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to has_nothrow_destructor must be complete types");
};
template <class T> struct has_nothrow_destructor<T&> : public integral_constant<bool, false>{};
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <class T> struct has_nothrow_destructor<T&&> : public integral_constant<bool, false>{};
#endif
template <> struct has_nothrow_destructor<void> : public false_type {};
}
#else

Expand Down
12 changes: 10 additions & 2 deletions include/boost/type_traits/is_assignable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <cstddef> // size_t
#include <boost/type_traits/integral_constant.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/static_assert.hpp>

namespace boost{

Expand Down Expand Up @@ -39,7 +41,10 @@ namespace boost{

}

template <class T, class U> struct is_assignable : public integral_constant<bool, sizeof(detail::is_assignable_imp::test<T, U>(0)) == sizeof(boost::type_traits::yes_type)>{};
template <class T, class U> struct is_assignable : public integral_constant<bool, sizeof(detail::is_assignable_imp::test<T, U>(0)) == sizeof(boost::type_traits::yes_type)>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_assignable must be complete types");
};
template <class T, std::size_t N, class U> struct is_assignable<T[N], U> : public is_assignable<T, U>{};
template <class T, std::size_t N, class U> struct is_assignable<T(&)[N], U> : public is_assignable<T&, U>{};
template <class T, class U> struct is_assignable<T[], U> : public is_assignable<T, U>{};
Expand All @@ -57,7 +62,10 @@ namespace boost{
namespace boost{

// We don't know how to implement this:
template <class T, class U> struct is_assignable : public integral_constant<bool, false>{};
template <class T, class U> struct is_assignable : public integral_constant<bool, false>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_assignable must be complete types");
};
template <class T, class U> struct is_assignable<T&, U> : public integral_constant<bool, is_pod<T>::value && is_pod<typename remove_reference<U>::type>::value>{};
template <class T, class U> struct is_assignable<const T&, U> : public integral_constant<bool, false>{};
template <class U> struct is_assignable<void, U> : public integral_constant<bool, false>{};
Expand Down
81 changes: 81 additions & 0 deletions include/boost/type_traits/is_complete.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@

// (C) Copyright John Maddock 2017.
// Use, modification and distribution are subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt).
//
// See http://www.boost.org/libs/type_traits for most recent version including documentation.

#ifndef BOOST_TT_IS_COMPLETE_HPP_INCLUDED
#define BOOST_TT_IS_COMPLETE_HPP_INCLUDED

#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/remove_reference.hpp>
#include <boost/type_traits/is_function.hpp>

/*
* CAUTION:
* ~~~~~~~~
*
* THIS TRAIT EXISTS SOLELY TO GENERATE HARD ERRORS WHEN A ANOTHER TRAIT
* WHICH REQUIRES COMPLETE TYPES AS ARGUMENTS IS PASSED AN INCOMPLETE TYPE
*
* DO NOT MAKE GENERAL USE OF THIS TRAIT, AS THE COMPLETENESS OF A TYPE
* VARIES ACROSS TRANSLATION UNITS AS WELL AS WITHIN A SINGLE UNIT.
*
*/

namespace boost {

#ifndef BOOST_NO_SFINAE_EXPR

namespace detail{

template <unsigned N>
struct ok_tag { double d; char c[N]; };

template <class T>
ok_tag<sizeof(T)> check_is_complete(int);
template <class T>
char check_is_complete(...);
}

template <class T> struct is_complete
: public integral_constant<bool, ::boost::is_function<typename boost::remove_reference<T>::type>::value || (sizeof(detail::check_is_complete<T>(0)) != sizeof(char))> {};

#elif !defined(BOOST_NO_SFINAE)

namespace detail
{

template <class T>
struct is_complete_imp
{
template <class U, class = decltype(sizeof(std::declval< U >())) >
static type_traits::yes_type check(U*);

template <class U>
static type_traits::no_type check(...);

static const bool value = sizeof(check<T>(0)) == sizeof(type_traits::yes_type);
};

} // namespace detail


template <class T>
struct is_complete : std::integral_constant<bool, ::boost::is_function<typename boost::remove_reference<T>::type>::value || detail::is_complete_imp<T>::value>
{};
template <class T>
struct is_complete<T&> : is_complete<T> {};

#else

template <class T> struct is_complete
: public integral_constant<bool, true> {};

#endif

} // namespace boost

#endif // BOOST_TT_IS_COMPLETE_HPP_INCLUDED
12 changes: 10 additions & 2 deletions include/boost/type_traits/is_constructible.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <boost/type_traits/is_default_constructible.hpp>
#include <boost/type_traits/detail/yes_no_type.hpp>
#include <boost/type_traits/declval.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/static_assert.hpp>

#define BOOST_TT_IS_CONSTRUCTIBLE_CONFORMING 1

Expand Down Expand Up @@ -45,8 +47,14 @@ namespace boost{

}

template <class T, class ...Args> struct is_constructible : public integral_constant<bool, sizeof(detail::is_constructible_imp::test<T, Args...>(0)) == sizeof(boost::type_traits::yes_type)>{};
template <class T, class Arg> struct is_constructible<T, Arg> : public integral_constant<bool, is_destructible<T>::value && sizeof(detail::is_constructible_imp::test1<T, Arg>(0)) == sizeof(boost::type_traits::yes_type)>{};
template <class T, class ...Args> struct is_constructible : public integral_constant<bool, sizeof(detail::is_constructible_imp::test<T, Args...>(0)) == sizeof(boost::type_traits::yes_type)>
{
BOOST_STATIC_ASSERT_MSG(::boost::is_complete<T>::value, "The target type must be complete in order to test for constructibility");
};
template <class T, class Arg> struct is_constructible<T, Arg> : public integral_constant<bool, is_destructible<T>::value && sizeof(detail::is_constructible_imp::test1<T, Arg>(0)) == sizeof(boost::type_traits::yes_type)>
{
BOOST_STATIC_ASSERT_MSG(::boost::is_complete<T>::value, "The target type must be complete in order to test for constructibility");
};
template <class Ref, class Arg> struct is_constructible<Ref&, Arg> : public integral_constant<bool, sizeof(detail::is_constructible_imp::ref_test<Ref&>(boost::declval<Arg>())) == sizeof(boost::type_traits::yes_type)>{};
template <class Ref, class Arg> struct is_constructible<Ref&&, Arg> : public integral_constant<bool, sizeof(detail::is_constructible_imp::ref_test<Ref&&>(boost::declval<Arg>())) == sizeof(boost::type_traits::yes_type)>{};

Expand Down
10 changes: 9 additions & 1 deletion include/boost/type_traits/is_convertible.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
#include <boost/type_traits/intrinsics.hpp>
#include <boost/type_traits/integral_constant.hpp>
#ifndef BOOST_IS_CONVERTIBLE
#include <boost/type_traits/is_complete.hpp>
#include <boost/type_traits/is_void.hpp>
#include <boost/type_traits/is_array.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/detail/yes_no_type.hpp>
#include <boost/type_traits/detail/config.hpp>
#include <boost/type_traits/is_array.hpp>
Expand Down Expand Up @@ -474,7 +478,11 @@ template <class From> struct is_convertible_impl_dispatch<From, void volatile> :
} // namespace detail

template <class From, class To>
struct is_convertible : public integral_constant<bool, ::boost::detail::is_convertible_impl_dispatch<From, To>::value> {};
struct is_convertible : public integral_constant<bool, ::boost::detail::is_convertible_impl_dispatch<From, To>::value>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<To>::value || boost::is_void<To>::value || boost::is_array<To>::value, "Destination argument type to is_convertible must be a complete type");
BOOST_STATIC_ASSERT_MSG(boost::is_complete<From>::value || boost::is_void<From>::value || boost::is_array<From>::value, "From argument type to is_convertible must be a complete type");
};

#else

Expand Down
12 changes: 10 additions & 2 deletions include/boost/type_traits/is_default_constructible.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <cstddef> // size_t
#include <boost/type_traits/integral_constant.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/static_assert.hpp>

#if BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40700)
#include <boost/type_traits/is_abstract.hpp>
Expand Down Expand Up @@ -51,9 +53,15 @@ namespace boost{
}

#if BOOST_WORKAROUND(BOOST_GCC_VERSION, < 40700)
template <class T> struct is_default_constructible : public integral_constant<bool, detail::is_default_constructible_abstract_filter<T, boost::is_abstract<T>::value>::value>{};
template <class T> struct is_default_constructible : public integral_constant<bool, detail::is_default_constructible_abstract_filter<T, boost::is_abstract<T>::value>::value>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_default_constructible must be complete types");
};
#else
template <class T> struct is_default_constructible : public integral_constant<bool, sizeof(detail::is_default_constructible_imp::test<T>(0)) == sizeof(boost::type_traits::yes_type)>{};
template <class T> struct is_default_constructible : public integral_constant<bool, sizeof(detail::is_default_constructible_imp::test<T>(0)) == sizeof(boost::type_traits::yes_type)>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_default_constructible must be complete types");
};
#endif
template <class T, std::size_t N> struct is_default_constructible<T[N]> : public is_default_constructible<T>{};
template <class T> struct is_default_constructible<T[]> : public is_default_constructible<T>{};
Expand Down
12 changes: 10 additions & 2 deletions include/boost/type_traits/is_destructible.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <cstddef> // size_t
#include <boost/type_traits/integral_constant.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/static_assert.hpp>

#if !defined(BOOST_NO_CXX11_DECLTYPE) && !BOOST_WORKAROUND(BOOST_MSVC, < 1800)

Expand All @@ -32,7 +34,10 @@ namespace boost{

}

template <class T> struct is_destructible : public integral_constant<bool, sizeof(detail::is_destructible_imp::test<T>(0)) == sizeof(boost::type_traits::yes_type)>{};
template <class T> struct is_destructible : public integral_constant<bool, sizeof(detail::is_destructible_imp::test<T>(0)) == sizeof(boost::type_traits::yes_type)>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_destructible must be complete types");
};

#else

Expand All @@ -42,7 +47,10 @@ namespace boost{
namespace boost{

// We don't know how to implement this:
template <class T> struct is_destructible : public integral_constant<bool, is_pod<T>::value || is_class<T>::value>{};
template <class T> struct is_destructible : public integral_constant<bool, is_pod<T>::value || is_class<T>::value>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_destructible must be complete types");
};
#endif

template <> struct is_destructible<void> : public false_type{};
Expand Down
4 changes: 4 additions & 0 deletions include/boost/type_traits/is_list_constructible.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <boost/config.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/type_traits/declval.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/static_assert.hpp>

namespace boost
{
Expand All @@ -18,6 +20,7 @@ namespace boost

template<class T, class = void, class = void, class = void, class = void, class = void, class = void> struct is_list_constructible: false_type
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_list_constructible must be complete types");
};

#else
Expand All @@ -32,6 +35,7 @@ template<class T, class... A> false_type is_list_constructible_impl( ... );

template<class T, class... A> struct is_list_constructible: decltype( type_traits_detail::is_list_constructible_impl<T, A...>(0) )
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_list_constructible must be complete types");
};

#endif
Expand Down
17 changes: 14 additions & 3 deletions include/boost/type_traits/is_nothrow_move_assignable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@
#include <boost/type_traits/is_reference.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/declval.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/static_assert.hpp>

namespace boost {

#ifdef BOOST_IS_NOTHROW_MOVE_ASSIGN

template <class T>
struct is_nothrow_move_assignable : public integral_constant<bool, BOOST_IS_NOTHROW_MOVE_ASSIGN(T)>{};
struct is_nothrow_move_assignable : public integral_constant<bool, BOOST_IS_NOTHROW_MOVE_ASSIGN(T)>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_nothrow_move_assignable must be complete types");
};
template <class T> struct is_nothrow_move_assignable<T const> : public false_type{};
template <class T> struct is_nothrow_move_assignable<T volatile> : public false_type{};
template <class T> struct is_nothrow_move_assignable<T const volatile> : public false_type{};
Expand All @@ -50,7 +55,10 @@ struct false_or_cpp11_noexcept_move_assignable <
}

template <class T>
struct is_nothrow_move_assignable : public integral_constant<bool, ::boost::detail::false_or_cpp11_noexcept_move_assignable<T>::value>{};
struct is_nothrow_move_assignable : public integral_constant<bool, ::boost::detail::false_or_cpp11_noexcept_move_assignable<T>::value>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_nothrow_move_assignable must be complete types");
};

template <class T> struct is_nothrow_move_assignable<T const> : public ::boost::false_type {};
template <class T> struct is_nothrow_move_assignable<T const volatile> : public ::boost::false_type{};
Expand All @@ -64,7 +72,10 @@ template <class T> struct is_nothrow_move_assignable<T&&> : public ::boost::fals

template <class T>
struct is_nothrow_move_assignable : public integral_constant<bool,
(::boost::has_trivial_move_assign<T>::value || ::boost::has_nothrow_assign<T>::value) && ! ::boost::is_array<T>::value>{};
(::boost::has_trivial_move_assign<T>::value || ::boost::has_nothrow_assign<T>::value) && ! ::boost::is_array<T>::value>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_nothrow_move_assignable must be complete types");
};

#endif

Expand Down
16 changes: 13 additions & 3 deletions include/boost/type_traits/is_nothrow_move_constructible.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@
#include <boost/type_traits/intrinsics.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/type_traits/is_complete.hpp>
#include <boost/static_assert.hpp>

#ifdef BOOST_IS_NOTHROW_MOVE_CONSTRUCT

namespace boost {

template <class T>
struct is_nothrow_move_constructible : public integral_constant<bool, BOOST_IS_NOTHROW_MOVE_CONSTRUCT(T)>{};
struct is_nothrow_move_constructible : public integral_constant<bool, BOOST_IS_NOTHROW_MOVE_CONSTRUCT(T)>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_nothrow_move_constructible must be complete types");
};

template <class T> struct is_nothrow_move_constructible<volatile T> : public ::boost::false_type {};
template <class T> struct is_nothrow_move_constructible<const volatile T> : public ::boost::false_type{};
Expand All @@ -47,7 +52,10 @@ struct false_or_cpp11_noexcept_move_constructible <
}

template <class T> struct is_nothrow_move_constructible
: public integral_constant<bool, ::boost::detail::false_or_cpp11_noexcept_move_constructible<T>::value>{};
: public integral_constant<bool, ::boost::detail::false_or_cpp11_noexcept_move_constructible<T>::value>
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_nothrow_move_constructible must be complete types");
};

template <class T> struct is_nothrow_move_constructible<volatile T> : public ::boost::false_type {};
template <class T> struct is_nothrow_move_constructible<const volatile T> : public ::boost::false_type{};
Expand All @@ -66,7 +74,9 @@ template <class T>
struct is_nothrow_move_constructible
: public integral_constant<bool,
(::boost::has_trivial_move_constructor<T>::value || ::boost::has_nothrow_copy<T>::value) && !::boost::is_array<T>::value>
{};
{
BOOST_STATIC_ASSERT_MSG(boost::is_complete<T>::value, "Arguments to is_nothrow_move_constructible must be complete types");
};

#endif

Expand Down
Loading

0 comments on commit ac35139

Please sign in to comment.