This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Disallow volatile types in std::allocator
ClosedPublic

Authored by jloser on Sep 1 2021, 5:39 AM.

Details

Summary

LWG 2447 is marked as Complete, but there is no static_assert to
reject volatile types in std::allocator. See the discussion at
https://reviews.llvm.org/D108856.

Add static_assert in std::allocator to disallow volatile types. Since this
is an implementation choice, mark the binding test as libc++ only.

Remove tests that use containers backed by std::allocator that test
the container when used with a volatile type.

Diff Detail

Event Timeline

jloser requested review of this revision.Sep 1 2021, 5:39 AM
jloser created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptSep 1 2021, 5:39 AM
Herald added a reviewer: Restricted Project. · View Herald Transcript
ldionne requested changes to this revision.Sep 7 2021, 1:15 PM

Instead, would it be possible to add a static_assert in std::allocator that we're not passing a volatile type, and leave this as completed? Technically, I think this requires no change at all (and so we are correct in saying that we implement it) since the program is free to be ill-formed if one passes a volatile type to std::allocator (well, the resulting std::allocator<T> is not required to be a Cpp17Allocator). But I think it's good to explicitly error-out.

We could also add a libc++ specific test for that behavior.

This revision now requires changes to proceed.Sep 7 2021, 1:15 PM
jloser updated this revision to Diff 371595.Sep 9 2021, 7:46 AM
jloser retitled this revision from [libc++][docs] Mark LWG 2447 as incomplete to [libc++] Reject volatile types in std::allocator.
jloser edited the summary of this revision. (Show Details)

Add static_assert in std::allocator to reject cv volatile types.
Add libc++ specific test

jloser added a comment.Sep 9 2021, 7:48 AM

Instead, would it be possible to add a static_assert in std::allocator that we're not passing a volatile type, and leave this as completed? Technically, I think this requires no change at all (and so we are correct in saying that we implement it) since the program is free to be ill-formed if one passes a volatile type to std::allocator (well, the resulting std::allocator<T> is not required to be a Cpp17Allocator). But I think it's good to explicitly error-out.

We could also add a libc++ specific test for that behavior.

That works for me. I just added a static_assert in std::allocator along with a libc++ verify test. Let me know what you think.

We can gate the static_assert on C++17 and later if you prefer (since it was raised as a C++17 issue); right now I left it to apply to all standards.

We have some tests that show we can use std::make_shared<T> when T is volatile type (see make_shared.volatile.pass.cpp). If we want this to work still, we can't have the static_assert in std::allocator.

@ldionne do you still think we should reject volatile types in std::allocator?

jloser updated this revision to Diff 372030.Sep 10 2021, 4:11 PM
jloser retitled this revision from [libc++] Reject volatile types in std::allocator to [libc++] Disallow volatile types in std::allocator.
jloser edited the summary of this revision. (Show Details)

Update std::make_shared tests to not worry about testing support for volatile types.
Use __is_volatile so std::allocator rejects volatile types correctly on all standard modes (including C++03).

Quuxplusone requested changes to this revision.Sep 10 2021, 4:23 PM

libstdc++ actually broke and then deliberately restored support for make_shared<volatile T> in GCC 8, circa September 2018.
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87278
Therefore I think we need very strong justification for libc++ to stop supporting make_shared<volatile T>.

If the only problem is that make_shared<T> has an unnecessary dependency on allocator<T>, maybe we could do something like

--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -1109,7 +1109,7 @@ template<class _Tp, class ..._Args, class = __enable_if_t<!is_array<_Tp>::value>
 _LIBCPP_HIDE_FROM_ABI
 shared_ptr<_Tp> make_shared(_Args&& ...__args)
 {
-    return _VSTD::allocate_shared<_Tp>(allocator<_Tp>(), _VSTD::forward<_Args>(__args)...);
+    return _VSTD::allocate_shared<_Tp>(allocator<int>(), _VSTD::forward<_Args>(__args)...);
 }

to work around that. The allocator gets rebound anyway, so there's no real reason to bother instantiating allocator<_Tp> in particular... and all allocator<T>s happen to have the same layout, so it wouldn't even break ABI to do this IMHO.

FWIW I also think the status quo is fine: so allocator<volatile T> is IFNDR instead of a static_assert; does anyone really care?

This revision now requires changes to proceed.Sep 10 2021, 4:23 PM

libstdc++ actually broke and then deliberately restored support for make_shared<volatile T> in GCC 8, circa September 2018.
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87278
Therefore I think we need very strong justification for libc++ to stop supporting make_shared<volatile T>.

If the only problem is that make_shared<T> has an unnecessary dependency on allocator<T>, maybe we could do something like

--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -1109,7 +1109,7 @@ template<class _Tp, class ..._Args, class = __enable_if_t<!is_array<_Tp>::value>
 _LIBCPP_HIDE_FROM_ABI
 shared_ptr<_Tp> make_shared(_Args&& ...__args)
 {
-    return _VSTD::allocate_shared<_Tp>(allocator<_Tp>(), _VSTD::forward<_Args>(__args)...);
+    return _VSTD::allocate_shared<_Tp>(allocator<int>(), _VSTD::forward<_Args>(__args)...);
 }

to work around that. The allocator gets rebound anyway, so there's no real reason to bother instantiating allocator<_Tp> in particular... and all allocator<T>s happen to have the same layout, so it wouldn't even break ABI to do this IMHO.

FWIW I also think the status quo is fine: so allocator<volatile T> is IFNDR instead of a static_assert; does anyone really care?

Simply put, I disagree with Jonathan's call on https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87278. As he said there, the Committee is moving away from supporting volatile in the standard library, and I think that sort of benign breakage is acceptable. After all, the Standard says that allocator<volatile T> is IFNDR, so we shouldn't encourage people to use that type.

libcxx/include/__memory/allocator.h
83

You can use the is_volatile type trait, since we support that trait even in C++03 mode. Generally speaking, libc++ supports all C++11 type traits in C++03 language mode as well.

jloser updated this revision to Diff 373726.Sep 20 2021, 3:25 PM
jloser edited the summary of this revision. (Show Details)

Remove tests that use containers with a (const) volatile type since they would trigger the static_assert.
Use is_volatile since the type trait exists even in C++03 mode.

jloser marked an inline comment as done.Sep 20 2021, 3:26 PM
ldionne accepted this revision.Sep 20 2021, 4:59 PM

LGTM once CI passes.

libcxx/test/libcxx/memory/allocator_volatile.verify.cpp
16–17

So this works in C++03 mode.

jloser updated this revision to Diff 373901.Sep 21 2021, 6:44 AM

Fix test to not use auto so it works in C++03 mode.

jloser marked an inline comment as done.Sep 21 2021, 6:44 AM

LGTM once CI passes.

Do we want to land this if CI is passing shortly or do internal builds at Apple/Google first for this breakage?

jloser updated this revision to Diff 374067.Sep 21 2021, 4:32 PM

Rebase to fix CI failures fixed on main now

ldionne accepted this revision.Sep 22 2021, 7:05 AM

Let's land this, I don't foresee any issues, and we can always revert in case something bad happens. I'll run a build in the meantime.

Please apply my suggested change just for consistency.

libcxx/test/libcxx/memory/allocator_volatile.verify.cpp
12
This revision was not accepted when it landed; it landed in state Needs Review.Sep 22 2021, 8:47 AM
This revision was automatically updated to reflect the committed changes.
libcxx/test/std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.create/make_shared.pass.cpp