Commit 85f24114 by Jonathan Wakely Committed by Jonathan Wakely

PR c++/91369 Implement P0784R7 changes to allocation and construction

This patch is the first part of library support for constexpr
std::vector and std::string. This only includes the changes to
std::allocator, std::allocator_traits, std::construct_at,
std::destroy_at, std::destroy and std::destroy_n.

std::allocator::allocate and std::allocator::deallocate need to be
added so that they can be intercepted by the compiler during constant
evaluation. Outside of constant evaluation those new member functions
just forward to the existing implementation in the base class.

	PR c++/91369 Implement P0784R7 changes to allocation and construction
	* include/bits/alloc_traits.h: Include <bits/stl_construct.h>.
	(allocator_traits::_S_allocate, allocator_traits::_S_construct)
	(allocator_traits::_S_destroy, allocator_traits::_S_max_size)
	(allocator_traits::_S_select, allocator_traits::allocate)
	(allocator_traits::deallocate, allocator_traits::construct)
	(allocator_traits::destroy, allocator_traits::max_size)
	(allocator_traits::select_on_container_copy_construction)
	(allocator_traits<allocator<T>>): Add constexpr specifier for C++20.
	(allocator_traits<allocator<T>>::construct): Use construct_at.
	(allocator_traits<allocator<T>>::destroy): Use destroy_at.
	(__alloc_on_copy, __alloc_on_move, __alloc_on_swap): Add constexpr
	specifier.
	(_Destroy(ForwardIterator, ForwardIterator, Alloc&))
	(_Destroy(ForwardIterator, ForwardIterator, allocator<T>&)): Move here
	from <bits/stl_construct.h>.
	* include/bits/allocator.h (allocator::~allocator): Remove for C++20.
	(allocator::allocate, allocate::deallocate): Define for C++20 and up.
	(operator==, operator!=): Add constexpr specifier for C++20.
	* include/bits/stl_construct.h: Don't include <ext/alloc_traits.h>.
	(destroy_at): For C++20 add constexpr specifier and support for
	destroying arrays.
	(construct_at): Define new function for C++20.
	(_Construct): Return result of placement new-expression. For C++11 and
	up add constexpr. For C++20 dispatch to std::construct_at during
	constant evaluation.
	(_Destroy(pointer)): Add constexpr specifier. For C++20 dispatch to
	std::destroy_at during constant evaluation.
	(_Destroy_aux::__destroy, _Destroy_n_aux::__destroy_n): Add constexpr
	specifier for C++20.
	(_Destroy(ForwardIterator, ForwardIterator))
	(_Destroy(ForwardIterator, Size)): Likewise. Do not elide trivial
	destructors during constant evaluation.
	(destroy, destroy_n): Add constexpr specifier for C++20.
	(_Destroy(ForwardIterator, ForwardIterator, Alloc&))
	(_Destroy(ForwardIterator, ForwardIterator, allocator<T>&)): Move to
	<bits/alloc_traits.h>, to remove dependency on allocators.
	* include/bits/stl_uninitialized.h: Include <ext/alloc_traits.h>.
	Include <bits/stl_pair.h> instead of <utility>.
	* include/ext/alloc_traits.h: Always include <bits/alloc_traits.h>.
	(__alloc_traits::construct, __alloc_traits::destroy)
	(__alloc_traits::_S_select_on_copy, __alloc_traits::_S_on_swap): Add
	constexpr specifier.
	* include/ext/malloc_allocator.h  (operator==, operator!=): Add
	constexpr specifier for C++20.
	* include/ext/new_allocator.h (operator==, operator!=): Likewise.
	* testsuite/20_util/headers/memory/synopsis.cc: Add constexpr.
	* testsuite/20_util/scoped_allocator/69293_neg.cc: Ignore additional
	errors due to constexpr function called after failed static_assert.
	* testsuite/20_util/specialized_algorithms/construct_at/1.cc: New test.
	* testsuite/23_containers/vector/cons/destructible_debug_neg.cc:
	Ignore additional errors due to constexpr function called after failed
	static_assert.
	* testsuite/23_containers/vector/cons/destructible_neg.cc: Likewise.

From-SVN: r277342
parent 0744333e
2019-10-23 Jonathan Wakely <jwakely@redhat.com>
PR c++/91369 Implement P0784R7 changes to allocation and construction
* include/bits/alloc_traits.h: Include <bits/stl_construct.h>.
(allocator_traits::_S_allocate, allocator_traits::_S_construct)
(allocator_traits::_S_destroy, allocator_traits::_S_max_size)
(allocator_traits::_S_select, allocator_traits::allocate)
(allocator_traits::deallocate, allocator_traits::construct)
(allocator_traits::destroy, allocator_traits::max_size)
(allocator_traits::select_on_container_copy_construction)
(allocator_traits<allocator<T>>): Add constexpr specifier for C++20.
(allocator_traits<allocator<T>>::construct): Use construct_at.
(allocator_traits<allocator<T>>::destroy): Use destroy_at.
(__alloc_on_copy, __alloc_on_move, __alloc_on_swap): Add constexpr
specifier.
(_Destroy(ForwardIterator, ForwardIterator, Alloc&))
(_Destroy(ForwardIterator, ForwardIterator, allocator<T>&)): Move here
from <bits/stl_construct.h>.
* include/bits/allocator.h (allocator::~allocator): Remove for C++20.
(allocator::allocate, allocate::deallocate): Define for C++20 and up.
(operator==, operator!=): Add constexpr specifier for C++20.
* include/bits/stl_construct.h: Don't include <ext/alloc_traits.h>.
(destroy_at): For C++20 add constexpr specifier and support for
destroying arrays.
(construct_at): Define new function for C++20.
(_Construct): Return result of placement new-expression. For C++11 and
up add constexpr. For C++20 dispatch to std::construct_at during
constant evaluation.
(_Destroy(pointer)): Add constexpr specifier. For C++20 dispatch to
std::destroy_at during constant evaluation.
(_Destroy_aux::__destroy, _Destroy_n_aux::__destroy_n): Add constexpr
specifier for C++20.
(_Destroy(ForwardIterator, ForwardIterator))
(_Destroy(ForwardIterator, Size)): Likewise. Do not elide trivial
destructors during constant evaluation.
(destroy, destroy_n): Add constexpr specifier for C++20.
(_Destroy(ForwardIterator, ForwardIterator, Alloc&))
(_Destroy(ForwardIterator, ForwardIterator, allocator<T>&)): Move to
<bits/alloc_traits.h>, to remove dependency on allocators.
* include/bits/stl_uninitialized.h: Include <ext/alloc_traits.h>.
Include <bits/stl_pair.h> instead of <utility>.
* include/ext/alloc_traits.h: Always include <bits/alloc_traits.h>.
(__alloc_traits::construct, __alloc_traits::destroy)
(__alloc_traits::_S_select_on_copy, __alloc_traits::_S_on_swap): Add
constexpr specifier.
* include/ext/malloc_allocator.h (operator==, operator!=): Add
constexpr specifier for C++20.
* include/ext/new_allocator.h (operator==, operator!=): Likewise.
* testsuite/20_util/headers/memory/synopsis.cc: Add constexpr.
* testsuite/20_util/scoped_allocator/69293_neg.cc: Ignore additional
errors due to constexpr function called after failed static_assert.
* testsuite/20_util/specialized_algorithms/construct_at/1.cc: New test.
* testsuite/23_containers/vector/cons/destructible_debug_neg.cc:
Ignore additional errors due to constexpr function called after failed
static_assert.
* testsuite/23_containers/vector/cons/destructible_neg.cc: Likewise.
* testsuite/20_util/bind/91371.cc: Fix test to compile as C++11.
* include/debug/helper_functions.h (__valid_range): Change
......
......@@ -154,13 +154,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX20_CONSTEXPR
allocator(const allocator<_Tp1>&) _GLIBCXX_NOTHROW { }
#if __cplusplus <= 201703L
~allocator() _GLIBCXX_NOTHROW { }
#endif
#if __cplusplus > 201703L
[[nodiscard,__gnu__::__always_inline__]]
constexpr _Tp*
allocate(size_t __n)
{
#ifdef __cpp_lib_is_constant_evaluated
if (std::is_constant_evaluated())
return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
#endif
return __allocator_base<_Tp>::allocate(__n, 0);
}
[[__gnu__::__always_inline__]]
constexpr void
deallocate(_Tp* __p, size_t __n)
{
#ifdef __cpp_lib_is_constant_evaluated
if (std::is_constant_evaluated())
{
::operator delete(__p);
return;
}
#endif
__allocator_base<_Tp>::deallocate(__p, __n);
}
#endif // C++20
friend bool
friend _GLIBCXX20_CONSTEXPR bool
operator==(const allocator&, const allocator&) _GLIBCXX_NOTHROW
{ return true; }
friend bool
friend _GLIBCXX20_CONSTEXPR bool
operator!=(const allocator&, const allocator&) _GLIBCXX_NOTHROW
{ return false; }
......@@ -168,13 +197,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
};
template<typename _T1, typename _T2>
inline bool
inline _GLIBCXX20_CONSTEXPR bool
operator==(const allocator<_T1>&, const allocator<_T2>&)
_GLIBCXX_NOTHROW
{ return true; }
template<typename _T1, typename _T2>
inline bool
inline _GLIBCXX20_CONSTEXPR bool
operator!=(const allocator<_T1>&, const allocator<_T2>&)
_GLIBCXX_NOTHROW
{ return false; }
......
......@@ -58,29 +58,69 @@
#include <new>
#include <bits/move.h>
#include <ext/alloc_traits.h>
#include <bits/stl_iterator_base_types.h> // for iterator_traits
#include <bits/stl_iterator_base_funcs.h> // for advance
/* This file provides the C++17 functions std::destroy_at, std::destroy, and
* std::destroy_n, and the C++20 function std::construct_at.
* It also provides std::_Construct, std::_Destroy,and std::_Destroy_n functions
* which are defined in all standard modes and so can be used in C++98-14 code.
* The _Construct and _Destroy functions will dispatch to construct_at and
* destroy_at during constant evaluation, because calls to those functions are
* intercepted by the compiler to allow use in constant expressions.
*/
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cplusplus >= 201703L
template <typename _Tp>
_GLIBCXX20_CONSTEXPR inline void
destroy_at(_Tp* __location)
{
if constexpr (__cplusplus > 201703L && is_array_v<_Tp>)
{
for (auto& __x : *__location)
std::destroy_at(std::__addressof(__x));
}
else
__location->~_Tp();
}
#if __cplusplus > 201703L
template<typename _Tp, typename... _Args>
constexpr auto
construct_at(_Tp* __location, _Args&&... __args)
noexcept(noexcept(::new((void*)0) _Tp(std::declval<_Args>()...)))
-> decltype(::new((void*)0) _Tp(std::declval<_Args>()...))
{ return ::new((void*)__location) _Tp(std::forward<_Args>(__args)...); }
#endif // C++20
#endif// C++17
/**
* Constructs an object in existing memory by invoking an allocated
* object's constructor with an initializer.
*/
#if __cplusplus >= 201103L
template<typename _T1, typename... _Args>
inline void
_Construct(_T1* __p, _Args&&... __args)
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
template<typename _Tp, typename... _Args>
constexpr _Tp*
_Construct(_Tp* __p, _Args&&... __args)
{
#if __cplusplus > 201703L
return std::construct_at(__p, std::forward<_Args>(__args)...);
#else
return ::new(static_cast<void*>(__p)) _Tp(std::forward<_Args>(__args)...);
#endif
}
#else
template<typename _T1, typename _T2>
inline void
inline _T1*
_Construct(_T1* __p, const _T2& __value)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_]allocator::construct
::new(static_cast<void*>(__p)) _T1(__value);
return ::new(static_cast<void*>(__p)) _T1(__value);
}
#endif
......@@ -89,20 +129,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Construct_novalue(_T1* __p)
{ ::new(static_cast<void*>(__p)) _T1; }
template<typename _ForwardIterator>
_GLIBCXX20_CONSTEXPR void
_Destroy(_ForwardIterator __first, _ForwardIterator __last);
/**
* Destroy the object pointed to by a pointer type.
*/
template<typename _Tp>
inline void
_GLIBCXX_CONSTEXPR inline void
_Destroy(_Tp* __pointer)
{ __pointer->~_Tp(); }
{
#if __cplusplus > 201703L
std::destroy_at(__pointer);
#else
__pointer->~_Tp();
#endif
}
template<bool>
struct _Destroy_aux
{
template<typename _ForwardIterator>
static void
__destroy(_ForwardIterator __first, _ForwardIterator __last)
static _GLIBCXX20_CONSTEXPR void
__destroy(_ForwardIterator __first, _ForwardIterator __last)
{
for (; __first != __last; ++__first)
std::_Destroy(std::__addressof(*__first));
......@@ -123,7 +173,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* away, otherwise the objects' destructors must be invoked.
*/
template<typename _ForwardIterator>
inline void
_GLIBCXX20_CONSTEXPR inline void
_Destroy(_ForwardIterator __first, _ForwardIterator __last)
{
typedef typename iterator_traits<_ForwardIterator>::value_type
......@@ -133,6 +183,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static_assert(is_destructible<_Value_type>::value,
"value type is destructible");
#endif
#if __cplusplus > 201703L && defined __cpp_lib_is_constant_evaluated
if (std::is_constant_evaluated())
return _Destroy_aux<false>::__destroy(__first, __last);
#endif
std::_Destroy_aux<__has_trivial_destructor(_Value_type)>::
__destroy(__first, __last);
}
......@@ -141,8 +195,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct _Destroy_n_aux
{
template<typename _ForwardIterator, typename _Size>
static _ForwardIterator
__destroy_n(_ForwardIterator __first, _Size __count)
static _GLIBCXX20_CONSTEXPR _ForwardIterator
__destroy_n(_ForwardIterator __first, _Size __count)
{
for (; __count > 0; (void)++__first, --__count)
std::_Destroy(std::__addressof(*__first));
......@@ -168,7 +222,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* away, otherwise the objects' destructors must be invoked.
*/
template<typename _ForwardIterator, typename _Size>
inline _ForwardIterator
_GLIBCXX20_CONSTEXPR inline _ForwardIterator
_Destroy_n(_ForwardIterator __first, _Size __count)
{
typedef typename iterator_traits<_ForwardIterator>::value_type
......@@ -178,59 +232,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static_assert(is_destructible<_Value_type>::value,
"value type is destructible");
#endif
#if __cplusplus > 201703L && defined __cpp_lib_is_constant_evaluated
if (std::is_constant_evaluated())
return _Destroy_n_aux<false>::__destroy_n(__first, __count);
#endif
return std::_Destroy_n_aux<__has_trivial_destructor(_Value_type)>::
__destroy_n(__first, __count);
}
/**
* Destroy a range of objects using the supplied allocator. For
* nondefault allocators we do not optimize away invocation of
* destroy() even if _Tp has a trivial destructor.
*/
template<typename _ForwardIterator, typename _Allocator>
void
_Destroy(_ForwardIterator __first, _ForwardIterator __last,
_Allocator& __alloc)
{
typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
for (; __first != __last; ++__first)
__traits::destroy(__alloc, std::__addressof(*__first));
}
template<typename _ForwardIterator, typename _Tp>
inline void
_Destroy(_ForwardIterator __first, _ForwardIterator __last,
allocator<_Tp>&)
{
_Destroy(__first, __last);
}
#if __cplusplus > 201402L
template <typename _Tp>
inline void
destroy_at(_Tp* __location)
{
std::_Destroy(__location);
}
#if __cplusplus >= 201703L
template <typename _ForwardIterator>
inline void
_GLIBCXX20_CONSTEXPR inline void
destroy(_ForwardIterator __first, _ForwardIterator __last)
{
std::_Destroy(__first, __last);
}
template <typename _ForwardIterator, typename _Size>
inline _ForwardIterator
_GLIBCXX20_CONSTEXPR inline _ForwardIterator
destroy_n(_ForwardIterator __first, _Size __count)
{
return std::_Destroy_n(__first, __count);
}
#endif
#endif // C++17
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif /* _STL_CONSTRUCT_H */
......@@ -57,13 +57,15 @@
#define _STL_UNINITIALIZED_H 1
#if __cplusplus > 201402L
#include <utility>
#include <bits/stl_pair.h>
#endif
#if __cplusplus >= 201103L
#include <type_traits>
#endif
#include <ext/alloc_traits.h>
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
......
......@@ -31,10 +31,8 @@
#pragma GCC system_header
#if __cplusplus >= 201103L
# include <bits/move.h>
# include <bits/alloc_traits.h>
#else
#if __cplusplus < 201103L
# include <bits/allocator.h> // for __alloc_swap
#endif
......@@ -78,7 +76,7 @@ template<typename _Alloc, typename = typename _Alloc::value_type>
public:
// overload construct for non-standard pointer types
template<typename _Ptr, typename... _Args>
static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type
static constexpr std::__enable_if_t<__is_custom_pointer<_Ptr>::value>
construct(_Alloc& __a, _Ptr __p, _Args&&... __args)
noexcept(noexcept(_Base_type::construct(__a, std::__to_address(__p),
std::forward<_Args>(__args)...)))
......@@ -89,15 +87,15 @@ template<typename _Alloc, typename = typename _Alloc::value_type>
// overload destroy for non-standard pointer types
template<typename _Ptr>
static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type
static constexpr std::__enable_if_t<__is_custom_pointer<_Ptr>::value>
destroy(_Alloc& __a, _Ptr __p)
noexcept(noexcept(_Base_type::destroy(__a, std::__to_address(__p))))
{ _Base_type::destroy(__a, std::__to_address(__p)); }
static _Alloc _S_select_on_copy(const _Alloc& __a)
static constexpr _Alloc _S_select_on_copy(const _Alloc& __a)
{ return _Base_type::select_on_container_copy_construction(__a); }
static void _S_on_swap(_Alloc& __a, _Alloc& __b)
static constexpr void _S_on_swap(_Alloc& __a, _Alloc& __b)
{ std::__alloc_on_swap(__a, __b); }
static constexpr bool _S_propagate_on_copy_assign()
......@@ -118,7 +116,7 @@ template<typename _Alloc, typename = typename _Alloc::value_type>
template<typename _Tp>
struct rebind
{ typedef typename _Base_type::template rebind_alloc<_Tp> other; };
#else
#else // ! C++11
typedef typename _Alloc::pointer pointer;
typedef typename _Alloc::const_pointer const_pointer;
......@@ -162,7 +160,7 @@ template<typename _Alloc, typename = typename _Alloc::value_type>
template<typename _Tp>
struct rebind
{ typedef typename _Alloc::template rebind<_Tp>::other other; };
#endif
#endif // C++11
};
_GLIBCXX_END_NAMESPACE_VERSION
......
......@@ -169,13 +169,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif // ! C++20
template<typename _Up>
friend bool
friend _GLIBCXX20_CONSTEXPR bool
operator==(const malloc_allocator&, const malloc_allocator<_Up>&)
_GLIBCXX_NOTHROW
{ return true; }
template<typename _Up>
friend bool
friend _GLIBCXX20_CONSTEXPR bool
operator!=(const malloc_allocator&, const malloc_allocator<_Up>&)
_GLIBCXX_NOTHROW
{ return false; }
......
......@@ -168,13 +168,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif // ! C++20
template<typename _Up>
friend bool
friend _GLIBCXX20_CONSTEXPR bool
operator==(const new_allocator&, const new_allocator<_Up>&)
_GLIBCXX_NOTHROW
{ return true; }
template<typename _Up>
friend bool
friend _GLIBCXX20_CONSTEXPR bool
operator!=(const new_allocator&, const new_allocator<_Up>&)
_GLIBCXX_NOTHROW
{ return false; }
......
......@@ -25,8 +25,14 @@ namespace std {
template <class T> class allocator;
template <> class allocator<void>;
template <class T, class U>
#if __cplusplus > 201703L
constexpr
#endif
bool operator==(const allocator<T>&, const allocator<U>&) throw();
template <class T, class U>
#if __cplusplus > 201703L
constexpr
#endif
bool operator!=(const allocator<T>&, const allocator<U>&) throw();
// lib.storage.iterator, raw storage iterator:
......
......@@ -48,3 +48,6 @@ test01()
sa.construct(p); // this is required to be ill-formed
// { dg-error "failed: .* uses_allocator is true" "" { target *-*-* } 0 }
}
// Needed because of PR c++/92193
// { dg-prune-output "no matching function for call to" }
// Copyright (C) 2019 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/>.
// { dg-options "-std=gnu++2a" }
// { dg-do run { target c++2a } }
#include <memory>
#include <testsuite_hooks.h>
template<typename T, typename... Args>
concept can_construct_at = requires(T* p, Args&&... args)
{
p = std::construct_at(p, std::forward<Args>(args)...);
};
static_assert( can_construct_at<int> );
static_assert( can_construct_at<int, int> );
static_assert( !can_construct_at<int, int, int> );
// Not required by C++20:
static_assert( noexcept(std::construct_at(std::declval<int*>(), 1)) );
void
test01()
{
int i = -1;
auto p = std::construct_at(&i);
VERIFY( p == &i );
VERIFY( i == 0 );
p = std::construct_at(&i, 42);
VERIFY( p == &i );
VERIFY( i == 42 );
}
struct X {
X(char&, void*) { }
};
static_assert( can_construct_at<X, char&, void*> );
static_assert( !can_construct_at<X> );
static_assert( !can_construct_at<X, char> );
static_assert( !can_construct_at<X, char&, const void*> );
static_assert( !noexcept(std::construct_at(std::declval<X*>(), std::declval<char&>(), std::declval<void*>())) );
int
main()
{
test01();
}
......@@ -47,3 +47,7 @@ test02()
// In Debug Mode the "required from here" errors come from <debug/vector>
// { dg-error "required from here" "" { target *-*-* } 163 }
// Needed because of PR c++/92193
// { dg-prune-output "deleted function" }
// { dg-prune-output "private within this context" }
......@@ -43,3 +43,7 @@ test02()
}
// { dg-error "value type is destructible" "" { target *-*-* } 0 }
// Needed because of PR c++/92193
// { dg-prune-output "deleted function" }
// { dg-prune-output "private within this context" }
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