Skip to content

Commit 275ffa5

Browse files
committed
[libc++] Make sure std::allocator<void> is always trivial
When we removed the allocator<void> specialization, the triviality of std::allocator<void> changed because the primary template had a non-trivial default constructor and the specialization didn't (so std::allocator<void> went from trivial to non-trivial). This commit fixes that oversight by giving a trivial constructor to the primary template when instantiated on cv-void. This was reported in https://llvm.org/PR50299. (cherry picked from commit 71e4d43) Differential Revision: https://reviews.llvm.org/D104486
1 parent b690ec5 commit 275ffa5

File tree

2 files changed

+62
-2
lines changed

2 files changed

+62
-2
lines changed

libcxx/include/memory

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,10 +810,35 @@ public:
810810
};
811811
#endif
812812

813+
// This class provides a non-trivial default constructor to the class that derives from it
814+
// if the condition is satisfied.
815+
//
816+
// The second template parameter exists to allow giving a unique type to __non_trivial_if,
817+
// which makes it possible to avoid breaking the ABI when making this a base class of an
818+
// existing class. Without that, imagine we have classes D1 and D2, both of which used to
819+
// have no base classes, but which now derive from __non_trivial_if. The layout of a class
820+
// that inherits from both D1 and D2 will change because the two __non_trivial_if base
821+
// classes are not allowed to share the same address.
822+
//
823+
// By making those __non_trivial_if base classes unique, we work around this problem and
824+
// it is safe to start deriving from __non_trivial_if in existing classes.
825+
template <bool _Cond, class _Unique>
826+
struct __non_trivial_if { };
827+
828+
template <class _Unique>
829+
struct __non_trivial_if<true, _Unique> {
830+
_LIBCPP_INLINE_VISIBILITY
831+
_LIBCPP_CONSTEXPR __non_trivial_if() _NOEXCEPT { }
832+
};
833+
813834
// allocator
835+
//
836+
// Note: For ABI compatibility between C++20 and previous standards, we make
837+
// allocator<void> trivial in C++20.
814838

815839
template <class _Tp>
816840
class _LIBCPP_TEMPLATE_VIS allocator
841+
: private __non_trivial_if<!is_void<_Tp>::value, allocator<_Tp> >
817842
{
818843
public:
819844
typedef size_t size_type;
@@ -823,7 +848,7 @@ public:
823848
typedef true_type is_always_equal;
824849

825850
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
826-
allocator() _NOEXCEPT { }
851+
allocator() _NOEXCEPT _LIBCPP_DEFAULT
827852

828853
template <class _Up>
829854
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
@@ -895,6 +920,7 @@ public:
895920

896921
template <class _Tp>
897922
class _LIBCPP_TEMPLATE_VIS allocator<const _Tp>
923+
: private __non_trivial_if<!is_void<_Tp>::value, allocator<const _Tp> >
898924
{
899925
public:
900926
typedef size_t size_type;
@@ -904,7 +930,7 @@ public:
904930
typedef true_type is_always_equal;
905931

906932
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
907-
allocator() _NOEXCEPT { }
933+
allocator() _NOEXCEPT _LIBCPP_DEFAULT
908934

909935
template <class _Up>
910936
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// Make sure that std::allocator<void> is trivial. This was the case before C++20
10+
// with the std::allocator<void> explicit specialization, and this test makes sure
11+
// that we maintain that property across all standards.
12+
//
13+
// This is important since triviality has implications on how the type is passed
14+
// as a function argument in the ABI.
15+
16+
#include <memory>
17+
#include <type_traits>
18+
19+
typedef std::allocator<void> A1;
20+
typedef std::allocator<void const> A2;
21+
struct A3 : std::allocator<void> { };
22+
struct A4 : std::allocator<void const> { };
23+
24+
static_assert(std::is_trivially_default_constructible<A1>::value, "");
25+
static_assert(std::is_trivial<A1>::value, "");
26+
27+
static_assert(std::is_trivially_default_constructible<A2>::value, "");
28+
static_assert(std::is_trivial<A2>::value, "");
29+
30+
static_assert(std::is_trivially_default_constructible<A3>::value, "");
31+
static_assert(std::is_trivial<A3>::value, "");
32+
33+
static_assert(std::is_trivially_default_constructible<A4>::value, "");
34+
static_assert(std::is_trivial<A4>::value, "");

0 commit comments

Comments
 (0)