Author: Louis Dionne Date: 2021-06-22T12:04:29-04:00 New Revision: 275ffa580880f6e18bf9742cad8e5dcab67b1f1d
URL: https://github.com/llvm/llvm-project/commit/275ffa580880f6e18bf9742cad8e5dcab67b1f1d DIFF: https://github.com/llvm/llvm-project/commit/275ffa580880f6e18bf9742cad8e5dcab67b1f1d.diff LOG: [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 71e4d434dc83b02a853712a5cb026ee2fa9ba67f) Differential Revision: https://reviews.llvm.org/D104486 Added: libcxx/test/libcxx/memory/allocator_void.trivial.compile.pass.cpp Modified: libcxx/include/memory Removed: ################################################################################ diff --git a/libcxx/include/memory b/libcxx/include/memory index efb10c8fd25bf..e02846b4035cf 100644 --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -810,10 +810,35 @@ public: }; #endif +// This class provides a non-trivial default constructor to the class that derives from it +// if the condition is satisfied. +// +// The second template parameter exists to allow giving a unique type to __non_trivial_if, +// which makes it possible to avoid breaking the ABI when making this a base class of an +// existing class. Without that, imagine we have classes D1 and D2, both of which used to +// have no base classes, but which now derive from __non_trivial_if. The layout of a class +// that inherits from both D1 and D2 will change because the two __non_trivial_if base +// classes are not allowed to share the same address. +// +// By making those __non_trivial_if base classes unique, we work around this problem and +// it is safe to start deriving from __non_trivial_if in existing classes. +template <bool _Cond, class _Unique> +struct __non_trivial_if { }; + +template <class _Unique> +struct __non_trivial_if<true, _Unique> { + _LIBCPP_INLINE_VISIBILITY + _LIBCPP_CONSTEXPR __non_trivial_if() _NOEXCEPT { } +}; + // allocator +// +// Note: For ABI compatibility between C++20 and previous standards, we make +// allocator<void> trivial in C++20. template <class _Tp> class _LIBCPP_TEMPLATE_VIS allocator + : private __non_trivial_if<!is_void<_Tp>::value, allocator<_Tp> > { public: typedef size_t size_type; @@ -823,7 +848,7 @@ public: typedef true_type is_always_equal; _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 - allocator() _NOEXCEPT { } + allocator() _NOEXCEPT _LIBCPP_DEFAULT template <class _Up> _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 @@ -895,6 +920,7 @@ public: template <class _Tp> class _LIBCPP_TEMPLATE_VIS allocator<const _Tp> + : private __non_trivial_if<!is_void<_Tp>::value, allocator<const _Tp> > { public: typedef size_t size_type; @@ -904,7 +930,7 @@ public: typedef true_type is_always_equal; _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 - allocator() _NOEXCEPT { } + allocator() _NOEXCEPT _LIBCPP_DEFAULT template <class _Up> _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 diff --git a/libcxx/test/libcxx/memory/allocator_void.trivial.compile.pass.cpp b/libcxx/test/libcxx/memory/allocator_void.trivial.compile.pass.cpp new file mode 100644 index 0000000000000..f9d67c065de85 --- /dev/null +++ b/libcxx/test/libcxx/memory/allocator_void.trivial.compile.pass.cpp @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Make sure that std::allocator<void> is trivial. This was the case before C++20 +// with the std::allocator<void> explicit specialization, and this test makes sure +// that we maintain that property across all standards. +// +// This is important since triviality has implications on how the type is passed +// as a function argument in the ABI. + +#include <memory> +#include <type_traits> + +typedef std::allocator<void> A1; +typedef std::allocator<void const> A2; +struct A3 : std::allocator<void> { }; +struct A4 : std::allocator<void const> { }; + +static_assert(std::is_trivially_default_constructible<A1>::value, ""); +static_assert(std::is_trivial<A1>::value, ""); + +static_assert(std::is_trivially_default_constructible<A2>::value, ""); +static_assert(std::is_trivial<A2>::value, ""); + +static_assert(std::is_trivially_default_constructible<A3>::value, ""); +static_assert(std::is_trivial<A3>::value, ""); + +static_assert(std::is_trivially_default_constructible<A4>::value, ""); +static_assert(std::is_trivial<A4>::value, ""); _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits