Commit fe807059 by Jonathan Wakely Committed by Jonathan Wakely

re PR libstdc++/51365 (cannot use final empty class in std::tuple)

	PR libstdc++/51365
	* include/bits/shared_ptr_base (_Sp_ebo_helper): Helper class to
	implement EBO safely.
	(_Sp_counted_base::_M_get_deleter): Add noexcept.
	(_Sp_counter_ptr): Use noexcept instead of comments.
	(_Sp_counted_deleter): Likewise. Use _Sp_ebo_helper.
	(_Sp_counted_ptr_inplace): Likewise.
	* testsuite/20_util/shared_ptr/cons/51365.cc: New.
	* testsuite/20_util/shared_ptr/cons/52924.cc: Add rebind member to
	custom allocator and test construction with custom allocator.
	* testsuite/20_util/shared_ptr/cons/43820_neg.cc: Adjust dg-error
	line number.

From-SVN: r198367
parent 35eb492b
2013-04-28 Jonathan Wakely <jwakely.gcc@gmail.com>
PR libstdc++/51365
* include/bits/shared_ptr_base (_Sp_ebo_helper): Helper class to
implement EBO safely.
(_Sp_counted_base::_M_get_deleter): Add noexcept.
(_Sp_counter_ptr): Use noexcept instead of comments.
(_Sp_counted_deleter): Likewise. Use _Sp_ebo_helper.
(_Sp_counted_ptr_inplace): Likewise.
* testsuite/20_util/shared_ptr/cons/51365.cc: New.
* testsuite/20_util/shared_ptr/cons/52924.cc: Add rebind member to
custom allocator and test construction with custom allocator.
* testsuite/20_util/shared_ptr/cons/43820_neg.cc: Adjust dg-error
line number.
2013-04-26 Paolo Carlini <paolo.carlini@oracle.com> 2013-04-26 Paolo Carlini <paolo.carlini@oracle.com>
* testsuite/23_containers/unordered_set/insert/move_range.cc: Adjust * testsuite/23_containers/unordered_set/insert/move_range.cc: Adjust
......
...@@ -126,7 +126,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -126,7 +126,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ delete this; } { delete this; }
virtual void* virtual void*
_M_get_deleter(const std::type_info&) = 0; _M_get_deleter(const std::type_info&) noexcept = 0;
void void
_M_add_ref_copy() _M_add_ref_copy()
...@@ -284,7 +284,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -284,7 +284,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ {
public: public:
explicit explicit
_Sp_counted_ptr(_Ptr __p) _Sp_counted_ptr(_Ptr __p) noexcept
: _M_ptr(__p) { } : _M_ptr(__p) { }
virtual void virtual void
...@@ -296,14 +296,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -296,14 +296,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ delete this; } { delete this; }
virtual void* virtual void*
_M_get_deleter(const std::type_info&) _M_get_deleter(const std::type_info&) noexcept
{ return 0; } { return nullptr; }
_Sp_counted_ptr(const _Sp_counted_ptr&) = delete; _Sp_counted_ptr(const _Sp_counted_ptr&) = delete;
_Sp_counted_ptr& operator=(const _Sp_counted_ptr&) = delete; _Sp_counted_ptr& operator=(const _Sp_counted_ptr&) = delete;
protected: private:
_Ptr _M_ptr; // copy constructor must not throw _Ptr _M_ptr;
}; };
template<> template<>
...@@ -318,59 +318,91 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -318,59 +318,91 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
inline void inline void
_Sp_counted_ptr<nullptr_t, _S_atomic>::_M_dispose() noexcept { } _Sp_counted_ptr<nullptr_t, _S_atomic>::_M_dispose() noexcept { }
template<int _Nm, typename _Tp,
bool __use_ebo = !__is_final(_Tp) && __is_empty(_Tp)>
struct _Sp_ebo_helper;
/// Specialization using EBO.
template<int _Nm, typename _Tp>
struct _Sp_ebo_helper<_Nm, _Tp, true> : private _Tp
{
explicit _Sp_ebo_helper(const _Tp& __tp) : _Tp(__tp) { }
static _Tp&
_S_get(_Sp_ebo_helper& __eboh) { return static_cast<_Tp&>(__eboh); }
};
/// Specialization not using EBO.
template<int _Nm, typename _Tp>
struct _Sp_ebo_helper<_Nm, _Tp, false>
{
explicit _Sp_ebo_helper(const _Tp& __tp) : _M_tp(__tp) { }
static _Tp&
_S_get(_Sp_ebo_helper& __eboh)
{ return __eboh._M_tp; }
private:
_Tp _M_tp;
};
// Support for custom deleter and/or allocator // Support for custom deleter and/or allocator
template<typename _Ptr, typename _Deleter, typename _Alloc, _Lock_policy _Lp> template<typename _Ptr, typename _Deleter, typename _Alloc, _Lock_policy _Lp>
class _Sp_counted_deleter final : public _Sp_counted_base<_Lp> class _Sp_counted_deleter final : public _Sp_counted_base<_Lp>
{ {
// Helper class that stores the Deleter and also acts as an allocator. class _Impl : _Sp_ebo_helper<0, _Deleter>, _Sp_ebo_helper<1, _Alloc>
// Used to dispose of the owned pointer and the internal refcount
// Requires that copies of _Alloc can free each other's memory.
struct _My_Deleter
: public _Alloc // copy constructor must not throw
{ {
_Deleter _M_del; // copy constructor must not throw typedef _Sp_ebo_helper<0, _Deleter> _Del_base;
_My_Deleter(_Deleter __d, const _Alloc& __a) typedef _Sp_ebo_helper<1, _Alloc> _Alloc_base;
: _Alloc(__a), _M_del(__d) { }
public:
_Impl(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept
: _M_ptr(__p), _Del_base(__d), _Alloc_base(__a)
{ }
_Deleter& _M_del() noexcept { return _Del_base::_S_get(*this); }
_Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); }
_Ptr _M_ptr;
}; };
public: public:
// __d(__p) must not throw. // __d(__p) must not throw.
_Sp_counted_deleter(_Ptr __p, _Deleter __d) _Sp_counted_deleter(_Ptr __p, _Deleter __d) noexcept
: _M_ptr(__p), _M_del(__d, _Alloc()) { } : _M_impl(__p, __d, _Alloc()) { }
// __d(__p) must not throw. // __d(__p) must not throw.
_Sp_counted_deleter(_Ptr __p, _Deleter __d, const _Alloc& __a) _Sp_counted_deleter(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept
: _M_ptr(__p), _M_del(__d, __a) { } : _M_impl(__p, __d, __a) { }
~_Sp_counted_deleter() noexcept { } ~_Sp_counted_deleter() noexcept { }
virtual void virtual void
_M_dispose() noexcept _M_dispose() noexcept
{ _M_del._M_del(_M_ptr); } { _M_impl._M_del()(_M_impl._M_ptr); }
virtual void virtual void
_M_destroy() noexcept _M_destroy() noexcept
{ {
typedef typename allocator_traits<_Alloc>::template typedef typename allocator_traits<_Alloc>::template
rebind_traits<_Sp_counted_deleter> _Alloc_traits; rebind_traits<_Sp_counted_deleter> _Alloc_traits;
typename _Alloc_traits::allocator_type __a(_M_del); typename _Alloc_traits::allocator_type __a(_M_impl._M_alloc());
_Alloc_traits::destroy(__a, this); _Alloc_traits::destroy(__a, this);
_Alloc_traits::deallocate(__a, this, 1); _Alloc_traits::deallocate(__a, this, 1);
} }
virtual void* virtual void*
_M_get_deleter(const std::type_info& __ti) _M_get_deleter(const std::type_info& __ti) noexcept
{ {
#ifdef __GXX_RTTI #ifdef __GXX_RTTI
return __ti == typeid(_Deleter) ? &_M_del._M_del : 0; return __ti == typeid(_Deleter) ? &_M_impl._M_del() : nullptr;
#else #else
return 0; return nullptr;
#endif #endif
} }
protected: private:
_Ptr _M_ptr; // copy constructor must not throw _Impl _M_impl;
_My_Deleter _M_del; // copy constructor must not throw
}; };
// helpers for make_shared / allocate_shared // helpers for make_shared / allocate_shared
...@@ -380,25 +412,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -380,25 +412,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp, typename _Alloc, _Lock_policy _Lp> template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp> class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp>
{ {
// Helper class that stores the pointer and also acts as an allocator. class _Impl : _Sp_ebo_helper<0, _Alloc>
// Used to dispose of the owned pointer and the internal refcount
// Requires that copies of _Alloc can free each other's memory.
struct _Impl
: public _Alloc // copy constructor must not throw
{ {
_Impl(_Alloc __a) : _Alloc(__a), _M_ptr() { } typedef _Sp_ebo_helper<0, _Alloc> _A_base;
_Tp* _M_ptr;
public:
explicit _Impl(_Alloc __a) noexcept : _A_base(__a) { }
_Alloc& _M_alloc() noexcept { return _A_base::_S_get(*this); }
__gnu_cxx::__aligned_buffer<_Tp> _M_storage;
}; };
public: public:
template<typename... _Args> template<typename... _Args>
_Sp_counted_ptr_inplace(_Alloc __a, _Args&&... __args) _Sp_counted_ptr_inplace(_Alloc __a, _Args&&... __args)
: _M_impl(__a), _M_storage() : _M_impl(__a)
{ {
_M_impl._M_ptr = _M_storage._M_ptr();
// _GLIBCXX_RESOLVE_LIB_DEFECTS // _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2070. allocate_shared should use allocator_traits<A>::construct // 2070. allocate_shared should use allocator_traits<A>::construct
allocator_traits<_Alloc>::construct(__a, _M_impl._M_ptr, allocator_traits<_Alloc>::construct(__a, _M_ptr(),
std::forward<_Args>(__args)...); // might throw std::forward<_Args>(__args)...); // might throw
} }
...@@ -406,7 +439,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -406,7 +439,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
virtual void virtual void
_M_dispose() noexcept _M_dispose() noexcept
{ allocator_traits<_Alloc>::destroy(_M_impl, _M_impl._M_ptr); } {
allocator_traits<_Alloc>::destroy(_M_impl._M_alloc(), _M_ptr());
}
// Override because the allocator needs to know the dynamic type // Override because the allocator needs to know the dynamic type
virtual void virtual void
...@@ -414,7 +449,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -414,7 +449,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ {
typedef typename allocator_traits<_Alloc>::template typedef typename allocator_traits<_Alloc>::template
rebind_traits<_Sp_counted_ptr_inplace> _Alloc_traits; rebind_traits<_Sp_counted_ptr_inplace> _Alloc_traits;
typename _Alloc_traits::allocator_type __a(_M_impl); typename _Alloc_traits::allocator_type __a(_M_impl._M_alloc());
_Alloc_traits::destroy(__a, this); _Alloc_traits::destroy(__a, this);
_Alloc_traits::deallocate(__a, this, 1); _Alloc_traits::deallocate(__a, this, 1);
} }
...@@ -424,17 +459,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -424,17 +459,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_M_get_deleter(const std::type_info& __ti) noexcept _M_get_deleter(const std::type_info& __ti) noexcept
{ {
#ifdef __GXX_RTTI #ifdef __GXX_RTTI
return __ti == typeid(_Sp_make_shared_tag) ? _M_storage._M_addr() : 0; return __ti == typeid(_Sp_make_shared_tag) ? _M_ptr() : nullptr;
#else #else
return 0; return nullptr;
#endif #endif
} }
private: private:
_Tp* _M_ptr() noexcept { return _M_impl._M_storage._M_ptr(); }
_Impl _M_impl; _Impl _M_impl;
__gnu_cxx::__aligned_buffer<_Tp> _M_storage;
}; };
template<_Lock_policy _Lp> template<_Lock_policy _Lp>
class __shared_count class __shared_count
{ {
...@@ -592,7 +629,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -592,7 +629,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
void* void*
_M_get_deleter(const std::type_info& __ti) const noexcept _M_get_deleter(const std::type_info& __ti) const noexcept
{ return _M_pi ? _M_pi->_M_get_deleter(__ti) : 0; } { return _M_pi ? _M_pi->_M_get_deleter(__ti) : nullptr; }
bool bool
_M_less(const __shared_count& __rhs) const noexcept _M_less(const __shared_count& __rhs) const noexcept
......
...@@ -32,7 +32,7 @@ void test01() ...@@ -32,7 +32,7 @@ void test01()
{ {
X* px = 0; X* px = 0;
std::shared_ptr<X> p1(px); // { dg-error "here" } std::shared_ptr<X> p1(px); // { dg-error "here" }
// { dg-error "incomplete" "" { target *-*-* } 770 } // { dg-error "incomplete" "" { target *-*-* } 807 }
std::shared_ptr<X> p9(ap()); // { dg-error "here" } std::shared_ptr<X> p9(ap()); // { dg-error "here" }
// { dg-error "incomplete" "" { target *-*-* } 307 } // { dg-error "incomplete" "" { target *-*-* } 307 }
......
// { dg-options "-std=gnu++0x" }
// { dg-do compile }
// Copyright (C) 2012-2013 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
#include <memory>
// libstdc++/51365
// Test with 'final' deleter and allocator.
struct A { };
struct D final
{
void operator()(A*) { }
};
template<typename T>
struct Alloc final : std::allocator<T>
{
Alloc() = default;
template<typename U> Alloc(const Alloc<U>&) { }
template<typename U>
struct rebind
{ typedef Alloc<U> other; };
};
A a;
D d;
Alloc<A> al;
auto sd = std::shared_ptr<A>(&a, d);
auto sa = std::shared_ptr<A>(&a, d, al);
auto as = std::allocate_shared<A>(al);
...@@ -22,14 +22,12 @@ ...@@ -22,14 +22,12 @@
// libstdc++/52924 // libstdc++/52924
struct A { } a; struct A { };
struct D { struct D {
~D() noexcept(false) { } ~D() noexcept(false) { }
void operator()(A*) { } void operator()(A*) { }
} d; };
auto sp = std::shared_ptr<A>(&a, d);
template<typename T> template<typename T>
struct Alloc : std::allocator<T> struct Alloc : std::allocator<T>
...@@ -37,8 +35,16 @@ struct Alloc : std::allocator<T> ...@@ -37,8 +35,16 @@ struct Alloc : std::allocator<T>
Alloc() = default; Alloc() = default;
~Alloc() noexcept(false) { } ~Alloc() noexcept(false) { }
template<typename U> Alloc(const Alloc<U>&) { } template<typename U> Alloc(const Alloc<U>&) { }
template<typename U>
struct rebind
{ typedef Alloc<U> other; };
}; };
A a;
D d;
Alloc<A> al; Alloc<A> al;
auto sd = std::shared_ptr<A>(&a, d);
auto sa = std::shared_ptr<A>(&a, d, al);
auto as = std::allocate_shared<A>(al); auto as = std::allocate_shared<A>(al);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment