-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Specially handle array<T, 0> for non-default-constructible T
#2296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Previously, these types were non-instantiable; we can't break ABI for something that doesn't compile. Fixes microsoft#942
stl/inc/array
Outdated
| } | ||
|
|
||
| _Ty _Elems[1]; | ||
| conditional_t<is_default_constructible_v<_Ty>, _Ty, char> _Elems[1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could just as well be a char as char[1] - the only requirement is that {} is a valid initializer. (If we were breaking ABI, I'd have array<T, 0> inherit from an empty base type so it could be empty and still initializable with {{}}. Such a beautiful hack!)
Anyone see a reason to prefer either char or char[1]?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer something with [[msvc:no_unique_address]], if clang had that.
And I prefer char[1], as it is, for no particular reason, just because it is an array
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer something with
[[msvc:no_unique_address]], if clang had that.
Good point! An empty aggregate member would work with [[no_unique_address]] in ABI-broken world.
And I prefer
char[1], as it is, for no particular reason, just because it is an array
Yeah, I have vague concerns that code "somewhere" outside this header expects array::_Elems to exist and have array type.
miscco
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hate array<T, 0> even more than I fear input_iterator
Because you're an STL maintainer, Casey. |
I don't think "non-instantiable" is correct. #include <array>
struct A { const int i; };
static_assert(not std::is_default_constructible_v<A>);
// std::array<A, 0> a; // Error, but...
std::array<A, 0> a = {}; // OKAlso, it'd be nice if copying a default-initialized array of zero doesn't give me core language undefined behavior from copying an uninitialized |
Thank you for the reminder that default-constructible doesn't mean the same thing as default-initializable! I've adjusted the new behavior so it's enabled only when the
Fair enough. |
stl/inc/array
Outdated
| } | ||
|
|
||
| _Ty _Elems[1]; | ||
| conditional_t<_Is_implicitly_default_constructible<_Ty>::value, _Ty, _Empty_array_element> _Elems[1]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this needs to check either? If is_default_constructible is true then array<T, 0> could be default-initialized (or value-initialized), while if _Is_implicitly_default_constructible is true then array<T, 0> could be copy-list-initialized from {}. Either way you have ABI issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a hard time believing that default-constructible types that are not {}-constructible are so common that someone out there has an array of them compiled into an object file, but since you went to the trouble to make the comment I'll make the change.
I guess it makes sense to follow what other implementations do, but wouldn't it be much more consistent if it data would point to the start of the array object itself, just like for any other std::array type? Asked differently: What is the motivation to return a nullptr there (assuming you know)? |
Returning something other than |
Right, I forgot about constexpr and that
Shouldn't those trigger a compilation error? |
WG21 tries to avoid API discontinuity for particular template specializations to make it easier to write generic code. For example, you might write |
|
I'm mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed, or if more work is required. |
|
Thanks for fixing this previously-thought-to-be-impossible bug! 🚀 🪐 😸 |
Previously, these types were non-instantiable; we can't break ABI for something that doesn't compile. (Why do I always end up coding the PRs that fire salvos at the ODR?)
Fixes #942
Fixes VSO-207715 / AB#207715.
Note that the proposed resolution of LWG-2157 clarifies how
array<T, 0>should behave. It hasn't been merged yet due to many of us despising the bit about "Whenaandbare distinct objects of the same zero-sized array type,a.begin()andb.begin()are not iterators over the same underlying sequence. [Note: Thereforebegin()does not return a value-initialized iterator — end note]". Ifbeginandendcannot return a value-initialized iterator (either anullptror a wrapper around anullptr) it's not clear how they are to be implemented so as to be usable inconstexprcontext.After applying this change, we will (at least in some cases) agree with the behavior of libc++ and libstdc++ which both return
iterator{data()}withdata()beingnullptr. I will use this as evidence that the implementations don't agree with the cited bit of LWG-2157 and hopefully convince LWG to yoink that and merge the rest.