Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion libcxx/include/__config
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,21 @@ typedef __char32_t char32_t;
# define _LIBCPP_EXCEPTIONS_SIG e
# endif

# if !_LIBCPP_HAS_EXCEPTIONS
# define _LIBCPP_EXCEPTIONS_SIG n
# else
# define _LIBCPP_EXCEPTIONS_SIG e
# endif

# if defined(__POINTER_FIELD_PROTECTION__)
# define _LIBCPP_PFP_SIG p
# else
# define _LIBCPP_PFP_SIG
# endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that pfp changes the layout of certain types? Why should there be an ABI tag for it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the in-memory pointer format changes so it's effectively a layout (ABI) change. Therefore we need an ABI tag change to detect/prevent linking against mismatching ABIs. This was requested by @mordante in #133538.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's only for ceses where we want different TUs to link together. Since PFP changes the layout of types that's not something we want users to do, which in turn means that we don't want to make it visible in the ABI tag. We should instead change the whole inline namespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I removed this logic and added CMake logic to modify the namespace name if PFP is enabled.


# define _LIBCPP_ODR_SIGNATURE \
_LIBCPP_CONCAT(_LIBCPP_CONCAT(_LIBCPP_HARDENING_SIG, _LIBCPP_EXCEPTIONS_SIG), _LIBCPP_VERSION)
_LIBCPP_CONCAT(_LIBCPP_CONCAT(_LIBCPP_CONCAT(_LIBCPP_HARDENING_SIG, _LIBCPP_EXCEPTIONS_SIG), _LIBCPP_PFP_SIG), \
_LIBCPP_VERSION)

// This macro marks a symbol as being hidden from libc++'s ABI. This is achieved
// on two levels:
Expand Down Expand Up @@ -1262,6 +1275,14 @@ typedef __char32_t char32_t;
# define _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER 0
# endif

# if defined(__POINTER_FIELD_PROTECTION__)
# define _LIBCPP_PFP [[clang::pointer_field_protection]]
# define _LIBCPP_NO_PFP [[clang::no_field_protection]]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be _Uglified. Do these attributes do anything with pfp disabled? If no, why not simply check for their availability like with other attributes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[[clang::pointer_field_protection]] will enable PFP on a type even if the command line flag is not passed, so we should avoid it when PFP is not enabled on the command line to avoid ABI breaks.

Checking attribute availability for [[clang::no_field_protection]] sounds fine to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

# else
# define _LIBCPP_PFP
# define _LIBCPP_NO_PFP
# endif

#endif // __cplusplus

#endif // _LIBCPP___CONFIG
3 changes: 3 additions & 0 deletions libcxx/include/__type_traits/is_trivially_relocatable.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ template <class _Tp, class = void>
struct __libcpp_is_trivially_relocatable : is_trivially_copyable<_Tp> {};
#endif

// __trivially_relocatable on libc++'s builtin types does not currently return the right answer with PFP.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// __trivially_relocatable on libc++'s builtin types does not currently return the right answer with PFP.
// __trivially_relocatable on libc++'s builtin types does not currently return the right answer with PFP.

What do you mean by "libc++ builtin types"? Do you mean libc++ types like std::string & friends? We are currently not using Clang's notion of trivially-relocatable for anything, so I'm not certain we need to disable this when PFP is enabled. IMO we need to take a look at each individual type who advertises __trivially_relocatable and check whether that's fine with PFP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at all the types and I think they would all be non-trivially-relocatable with PFP because they can all contain pointer fields.

We could surround each using __trivially_relocatable with an #ifndef __POINTER_FIELD_PROTECTION__, but that could make it easier to accidentally introduce a PFP-incompatible type, which would hopefully be detectable via testing, but it's possible that the tests will not trigger the bug. Disabling it like this seemed like the most robust approach.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like what really makes the most sense is to disable it on a per type basis. But perhaps we can extract the fundamental property that makes these types non-TR with PFP into something general in a way that doesn't require an explicit #if __POINTER_FIELD_PROTECTION__ at each place where we determine TR.

So I guess my question is: what is the fundamental property that makes these types non-TR when PFP is enabled?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The property is that the type must satisfy both of the following:

  • contains a field of pointer type, either in the type itself or in one of its fields or bases
  • the type containing the pointer field is not trivially copyable

Given that many of the affected types are containers, the trivial relocatability property will in some cases depend on a template argument. In other cases, the type is unconditionally non-trivially relocatable due to a pointer field.

In principle, we would be able to robustly detect this property via a type trait, and in an earlier version of the Clang patch I introduced such a trait: pcc@ad5ee79

But in #133538 @ojhunt reasonably asked for this to be removed for the initial version of the patch. We can revisit this as an optimization once trivial relocatability is added to the standard. Given that trivial relocatability will not be making it into C++26, I think this should not be a priority for now.

#if !defined(__POINTER_FIELD_PROTECTION__)
template <class _Tp>
struct __libcpp_is_trivially_relocatable<_Tp,
__enable_if_t<is_same<_Tp, typename _Tp::__trivially_relocatable>::value> >
: true_type {};
#endif

_LIBCPP_END_NAMESPACE_STD

Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/typeinfo
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ class _LIBCPP_EXPORTED_FROM_ABI _LIBCPP_TYPE_INFO_VTABLE_POINTER_AUTH type_info
protected:
typedef __type_info_implementations::__impl __impl;

__impl::__type_name_t __type_name;
_LIBCPP_NO_PFP __impl::__type_name_t __type_name;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To reiterate my comment on the RFC, I would much rather we teach Clang how to generate the right thing here. I understand this might reduce the amount of work needed in Clang, but at the end of the day the PFP implementation in Clang will be more complete and more robust if it can handle this out of the box -- even if the actual security benefits are marginal.

The same comment holds for the changes in private_typeinfo.h -- that would actually have the nice effect of not requiring any changes to libc++abi.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would have the following downsides:

  1. More complexity in the compiler for no real benefit.
  2. The names of the fields would become part of the ABI, i.e. all ABI implementations would need to use the same names in order to avoid mismatches with clang-generated code. Fortunately it looks like gcc is using the same field names here, so maybe that doesn't matter.
  3. Would no longer be able to link a PFP libc++ and a non-PFP libc++ into the same program, because the libc++abi implementations would expect different ABIs (the PFP side would expect these fields to be signed).

That being said, I don't feel strongly about it, so if it doesn't take too much time I guess I don't mind doing it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. More complexity in the compiler for no real benefit.

Whether there is a "real benefit" is subjective, though. Not having to think about PFP in libc++abi is a very real (and valuable) benefit from my perspective (but perhaps not from someone else's perspective).

  1. The names of the fields would become part of the ABI, i.e. all ABI implementations would need to use the same names in order to avoid mismatches with clang-generated code. Fortunately it looks like gcc is using the same field names here, so maybe that doesn't matter.

Can you explain why that's the case? Where/why is the name of the field relevant when PFP is enabled? That's probably obvious when you know PFP but I'm not seeing it.

  1. Would no longer be able to link a PFP libc++ and a non-PFP libc++ into the same program, because the libc++abi implementations would expect different ABIs (the PFP side would expect these fields to be signed).

IIUC, you are implying that right now, we could link both a PFP libc++ and a non-PFP libc++ into the same program without issues because they use a different inline namespace, and libc++abi.dylib doesn't have an inline namespace. So IIUC what you're saying is that you could use pfp-libc++.dylib, nonpfp-libc++.dylib and libc++abi.dylib (which would be PFP-agnostic) into the same program. Is that the idea? My first reaction to this is that it seems like a very fragile setup.

That being said, I don't feel strongly about it, so if it doesn't take too much time I guess I don't mind doing it.

Let's finish this discussion first!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whether there is a "real benefit" is subjective, though. Not having to think about PFP in libc++abi is a very real (and valuable) benefit from my perspective (but perhaps not from someone else's perspective).

Sure, but it seems like a relatively small benefit. It lets us save changing 4 lines of code in libc++ and libc++abi which seem to need very little maintenance (because they reflect ABI). According to git blame these lines were last changed in libc++abi 10 years ago, and in libc++ 6 years ago ignoring a formatting change. Whereas on the Clang side I think we would need somewhere around 100 lines of code to generate initializers for these fields, which will need to be maintained as Clang's API develops (and will need to be reimplemented in CIR, etc). Generating these initializers will need a separate code path because these RTTI types are not defined in the Clang AST like other types are.

Can you explain why that's the case? Where/why is the name of the field relevant when PFP is enabled? That's probably obvious when you know PFP but I'm not seeing it.

That's because the field names are encoded into names of deactivation symbols (https://discourse.llvm.org/t/rfc-deactivation-symbols/85556) as well as being hashed into the discriminator used to sign pointers. In principle, we could encode field offsets instead of names, but I've frequently found the explicit names useful for debugging.

IIUC, you are implying that right now, we could link both a PFP libc++ and a non-PFP libc++ into the same program without issues because they use a different inline namespace, and libc++abi.dylib doesn't have an inline namespace. So IIUC what you're saying is that you could use pfp-libc++.dylib, nonpfp-libc++.dylib and libc++abi.dylib (which would be PFP-agnostic) into the same program. Is that the idea? My first reaction to this is that it seems like a very fragile setup.

Yes, that's pretty much it (except that PFP doesn't support dynamic linking, so it would be .a instead of .dylib). As I mentioned earlier, we have no need for this. But maybe if we can avoid obvious breakage to this setup, that may be worthwhile in case someone needs it in the future (at which point it may be too late to fix it because the ABI will be set in stone).


_LIBCPP_HIDE_FROM_ABI explicit type_info(const char* __n) : __type_name(__impl::__string_to_type_name(__n)) {}

Expand Down
15 changes: 15 additions & 0 deletions libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,12 @@ void unique_ptr_test() {
ComparePrettyPrintToRegex(std::move(forty_two),
R"(std::unique_ptr<int> containing = {__ptr_ = 0x[a-f0-9]+})");

#if !defined(__POINTER_FIELD_PROTECTION__)
// GDB doesn't know how to read PFP fields correctly yet.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does GCC have pfp in general? If not, IMO we should just disable the pretty printer test with pfp enabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The support for this feature in GCC is independent of support in GDB. We could imagine debug info extensions being developed in the future to make it possible for this to pass in GDB even without GCC support.

That being said, disabling the test is also fine with me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

std::unique_ptr<int> this_is_null;
ComparePrettyPrintToChars(std::move(this_is_null),
R"(std::unique_ptr is nullptr)");
#endif
}

void bitset_test() {
Expand Down Expand Up @@ -354,6 +357,8 @@ void map_test() {
}

void multimap_test() {
#if !defined(__POINTER_FIELD_PROTECTION__)
// GDB doesn't know how to read PFP fields correctly yet.
std::multimap<int, int> i_am_empty{};
ComparePrettyPrintToChars(i_am_empty, "std::multimap is empty");

Expand All @@ -369,6 +374,7 @@ void multimap_test() {
"std::multimap with 6 elements = "
R"({[1] = "one", [1] = "ein", [1] = "bir", )"
R"([2] = "two", [2] = "zwei", [3] = "three"})");
#endif
}

void queue_test() {
Expand Down Expand Up @@ -441,13 +447,16 @@ void stack_test() {
}

void multiset_test() {
#if !defined(__POINTER_FIELD_PROTECTION__)
// GDB doesn't know how to read PFP fields correctly yet.
std::multiset<int> i_am_empty;
ComparePrettyPrintToChars(i_am_empty, "std::multiset is empty");

std::multiset<std::string> one_two_three {"1:one", "2:two", "3:three", "1:one"};
ComparePrettyPrintToChars(one_two_three,
"std::multiset with 4 elements = {"
R"("1:one", "1:one", "2:two", "3:three"})");
#endif
}

void vector_test() {
Expand Down Expand Up @@ -481,10 +490,13 @@ void vector_test() {
"std::vector of length "
"3, capacity 3 = {5, 6, 7}");

#if !defined(__POINTER_FIELD_PROTECTION__)
// GDB doesn't know how to read PFP fields correctly yet.
std::vector<int, UncompressibleAllocator<int>> test3({7, 8});
ComparePrettyPrintToChars(std::move(test3),
"std::vector of length "
"2, capacity 2 = {7, 8}");
#endif
}

void set_iterator_test() {
Expand Down Expand Up @@ -655,8 +667,11 @@ void shared_ptr_test() {
test0,
R"(std::shared_ptr<int> count [3\?], weak [0\?]( \(libc\+\+ missing debug info\))? containing = {__ptr_ = 0x[a-f0-9]+})");

#if !defined(__POINTER_FIELD_PROTECTION__)
// GDB doesn't know how to read PFP fields correctly yet.
std::shared_ptr<const int> test3;
ComparePrettyPrintToChars(test3, "std::shared_ptr is nullptr");
#endif
}

void streampos_test() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
# include <locale>
#endif

#if defined(__POINTER_FIELD_PROTECTION__)
constexpr bool pfp_disabled = false;
#else
constexpr bool pfp_disabled = true;
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, can we just disable the test instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine with me I suppose. The correct result for __libcpp_is_trivially_relocatable is implicitly tested by the other tests (which would crash if it was wrong).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


static_assert(std::__libcpp_is_trivially_relocatable<char>::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<int>::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<double>::value, "");
Expand Down Expand Up @@ -68,8 +74,8 @@ static_assert(!std::__libcpp_is_trivially_relocatable<NonTrivialDestructor>::val
// ----------------------

// __split_buffer
static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<int> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<NotTriviallyCopyable> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<int> >::value == pfp_disabled, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<NotTriviallyCopyable> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::__split_buffer<int, test_allocator<int> > >::value, "");

// standard library types
Expand All @@ -82,7 +88,7 @@ static_assert(std::__libcpp_is_trivially_relocatable<std::array<std::unique_ptr<

static_assert(std::__libcpp_is_trivially_relocatable<std::array<int, 1> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::array<NotTriviallyCopyable, 1> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::array<std::unique_ptr<int>, 1> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::array<std::unique_ptr<int>, 1> >::value == pfp_disabled, "");

// basic_string
#if !__has_feature(address_sanitizer) || !_LIBCPP_INSTRUMENTED_WITH_ASAN
Expand All @@ -97,17 +103,17 @@ struct NotTriviallyRelocatableCharTraits : constexpr_char_traits<T> {
};

static_assert(std::__libcpp_is_trivially_relocatable<
std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::value,
std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::value == pfp_disabled,
"");
static_assert(std::__libcpp_is_trivially_relocatable<
std::basic_string<char, NotTriviallyRelocatableCharTraits<char>, std::allocator<char> > >::value,
std::basic_string<char, NotTriviallyRelocatableCharTraits<char>, std::allocator<char> > >::value == pfp_disabled,
"");
static_assert(std::__libcpp_is_trivially_relocatable<
std::basic_string<MyChar, constexpr_char_traits<MyChar>, std::allocator<MyChar> > >::value,
std::basic_string<MyChar, constexpr_char_traits<MyChar>, std::allocator<MyChar> > >::value == pfp_disabled,
"");
static_assert(
std::__libcpp_is_trivially_relocatable<
std::basic_string<MyChar, NotTriviallyRelocatableCharTraits<MyChar>, std::allocator<MyChar> > >::value,
std::basic_string<MyChar, NotTriviallyRelocatableCharTraits<MyChar>, std::allocator<MyChar> > >::value == pfp_disabled,
"");
static_assert(!std::__libcpp_is_trivially_relocatable<
std::basic_string<char, std::char_traits<char>, test_allocator<char> > >::value,
Expand All @@ -119,21 +125,21 @@ static_assert(
#endif

// deque
static_assert(std::__libcpp_is_trivially_relocatable<std::deque<int> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::deque<NotTriviallyCopyable> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::deque<int> >::value == pfp_disabled, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::deque<NotTriviallyCopyable> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::deque<int, test_allocator<int> > >::value, "");

// exception_ptr
#ifndef _LIBCPP_ABI_MICROSOFT // FIXME: Is this also the case on windows?
static_assert(std::__libcpp_is_trivially_relocatable<std::exception_ptr>::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::exception_ptr>::value == pfp_disabled, "");
#endif

// expected
#if TEST_STD_VER >= 23
static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, int> >::value);
static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, int>>::value);
static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, std::unique_ptr<int>>>::value);
static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, std::unique_ptr<int>>>::value);
static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, int> >::value == pfp_disabled);
static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, int>>::value == pfp_disabled);
static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, std::unique_ptr<int>>>::value == pfp_disabled);
static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, std::unique_ptr<int>>>::value == pfp_disabled);

static_assert(!std::__libcpp_is_trivially_relocatable<std::expected<int, NotTriviallyCopyable>>::value);
static_assert(!std::__libcpp_is_trivially_relocatable<std::expected<NotTriviallyCopyable, int>>::value);
Expand All @@ -143,42 +149,42 @@ static_assert(

// locale
#ifndef TEST_HAS_NO_LOCALIZATION
static_assert(std::__libcpp_is_trivially_relocatable<std::locale>::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::locale>::value == pfp_disabled, "");
#endif

// optional
#if TEST_STD_VER >= 17
static_assert(std::__libcpp_is_trivially_relocatable<std::optional<int>>::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::optional<NotTriviallyCopyable>>::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::optional<std::unique_ptr<int>>>::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::optional<std::unique_ptr<int>>>::value == pfp_disabled, "");
#endif // TEST_STD_VER >= 17

// pair
static_assert(std::__libcpp_is_trivially_relocatable<std::pair<int, int> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::pair<int, int> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::pair<NotTriviallyCopyable, int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::pair<int, NotTriviallyCopyable> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::pair<NotTriviallyCopyable, NotTriviallyCopyable> >::value,
"");
static_assert(std::__libcpp_is_trivially_relocatable<std::pair<std::unique_ptr<int>, std::unique_ptr<int> > >::value,
static_assert(std::__libcpp_is_trivially_relocatable<std::pair<std::unique_ptr<int>, std::unique_ptr<int> > >::value == pfp_disabled,
"");

// shared_ptr
static_assert(std::__libcpp_is_trivially_relocatable<std::shared_ptr<NotTriviallyCopyable> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::shared_ptr<NotTriviallyCopyable> >::value == pfp_disabled, "");

// tuple
#if TEST_STD_VER >= 11
static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<> >::value, "");

static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<NotTriviallyCopyable> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int> > >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int> > >::value == pfp_disabled, "");

static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int, int> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int, int> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<NotTriviallyCopyable, int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<int, NotTriviallyCopyable> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<NotTriviallyCopyable, NotTriviallyCopyable> >::value,
"");
static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int>, std::unique_ptr<int> > >::value,
static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int>, std::unique_ptr<int> > >::value == pfp_disabled,
"");
#endif // TEST_STD_VER >= 11

Expand All @@ -203,9 +209,9 @@ struct NotTriviallyRelocatablePointer {
void operator()(T*);
};

static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<int> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<NotTriviallyCopyable> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<int[]> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<int> >::value == pfp_disabled, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<NotTriviallyCopyable> >::value == pfp_disabled, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::unique_ptr<int[]> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::unique_ptr<int, NotTriviallyRelocatableDeleter> >::value,
"");
static_assert(!std::__libcpp_is_trivially_relocatable<std::unique_ptr<int[], NotTriviallyRelocatableDeleter> >::value,
Expand All @@ -219,23 +225,23 @@ static_assert(!std::__libcpp_is_trivially_relocatable<std::unique_ptr<int[], Not
#if TEST_STD_VER >= 17
static_assert(std::__libcpp_is_trivially_relocatable<std::variant<int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<NotTriviallyCopyable> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int> > >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int> > >::value == pfp_disabled, "");

static_assert(std::__libcpp_is_trivially_relocatable<std::variant<int, int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<NotTriviallyCopyable, int> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<int, NotTriviallyCopyable> >::value, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<NotTriviallyCopyable, NotTriviallyCopyable> >::value,
"");
static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int>, std::unique_ptr<int> > >::value,
static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int>, std::unique_ptr<int> > >::value == pfp_disabled,
"");
#endif // TEST_STD_VER >= 17

// vector
static_assert(std::__libcpp_is_trivially_relocatable<std::vector<int> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::vector<NotTriviallyCopyable> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::vector<int> >::value == pfp_disabled, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::vector<NotTriviallyCopyable> >::value == pfp_disabled, "");
static_assert(!std::__libcpp_is_trivially_relocatable<std::vector<int, test_allocator<int> > >::value, "");

// weak_ptr
static_assert(std::__libcpp_is_trivially_relocatable<std::weak_ptr<NotTriviallyCopyable> >::value, "");
static_assert(std::__libcpp_is_trivially_relocatable<std::weak_ptr<NotTriviallyCopyable> >::value == pfp_disabled, "");

// TODO: Mark all the trivially relocatable STL types as such
10 changes: 10 additions & 0 deletions libcxxabi/include/__cxxabi_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,14 @@
# define _LIBCXXABI_NOEXCEPT noexcept
#endif

#if defined(_LIBCXXABI_COMPILER_CLANG)
# if defined(__POINTER_FIELD_PROTECTION__)
# define _LIBCXXABI_NO_PFP [[clang::no_field_protection]]
# else
# define _LIBCXXABI_NO_PFP
# endif
#else
# define _LIBCXXABI_NO_PFP
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this different from the libc++ version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense to change it to be the same. I'll do that. This was a leftover from when __POINTER_FIELD_PROTECTION__ was under __has_extension.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


#endif // ____CXXABI_CONFIG_H
6 changes: 3 additions & 3 deletions libcxxabi/src/private_typeinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class _LIBCXXABI_TYPE_VIS __class_type_info : public __shim_type_info {
// Has one non-virtual public base class at offset zero
class _LIBCXXABI_TYPE_VIS __si_class_type_info : public __class_type_info {
public:
const __class_type_info *__base_type;
_LIBCXXABI_NO_PFP const __class_type_info *__base_type;

_LIBCXXABI_HIDDEN virtual ~__si_class_type_info();

Expand Down Expand Up @@ -204,7 +204,7 @@ class _LIBCXXABI_TYPE_VIS __vmi_class_type_info : public __class_type_info {
class _LIBCXXABI_TYPE_VIS __pbase_type_info : public __shim_type_info {
public:
unsigned int __flags;
const __shim_type_info *__pointee;
_LIBCXXABI_NO_PFP const __shim_type_info *__pointee;

enum __masks {
__const_mask = 0x1,
Expand Down Expand Up @@ -245,7 +245,7 @@ class _LIBCXXABI_TYPE_VIS __pointer_type_info : public __pbase_type_info {
class _LIBCXXABI_TYPE_VIS __pointer_to_member_type_info
: public __pbase_type_info {
public:
const __class_type_info *__context;
_LIBCXXABI_NO_PFP const __class_type_info *__context;

_LIBCXXABI_HIDDEN virtual ~__pointer_to_member_type_info();
_LIBCXXABI_HIDDEN virtual bool can_catch(const __shim_type_info *,
Expand Down
Loading