-
Notifications
You must be signed in to change notification settings - Fork 12k
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
[libc++] Initialization of std::array of size 0 in constexpr contexts #74375
Comments
The standard is unfortunately notoriously underspecified in these regards. I don't really see a neat way to do this without incurring potentially significant runtime overhead. The best thing would probably have been to never add any data in an empty array, but that opportunity is long gone. |
I think this requires an ABI flag to fix, but that is probably the correct solution? |
Well yeah, in the unstable ABI it's trivial to fix. I was thinking about the stable ABI, which probably 90%+ of people use. |
I think I will add this macro in the ABI version 2:
Any objections? |
I don't have an objection to doing this, but I do object to this being a fix for the bug. |
If we want to fix this in the stable ABI as well, does this require us to initialize the member in all cases? I'm not sure if this can be fixed without compiler support. I could add code to initialize it with = {} starting from C++14 onwards, but in C++11 the object would no longer be considered an aggregate... not sure how to fix that without adding some kind of compiler builtin for this special case? Also that solution adds runtime overhead, which I think is bad. |
These are all great questions which I don't have the answers to. Worst case we'd have to add some minor compiler magic, but IMO that's better than not fixing this for most people. Maybe the runtime overhead isn't really that bad. I mean, how often does it actually happen that you initialize a bunch of empty arrays? Probably almost never, but I don't know. |
This extra data field might have some additional consequences that removing it does also break the current API. Looks like expressions like:
Currently compile. I am fairly confident they should not compile if the array has a size of 0, but what is the policy around breaking API compatibility here? |
Looks like there is an open issue related to this: https://cplusplus.github.io/LWG/issue2157 I am not sure I like the idea of |
This is actually what is addressed in #34839 which is why I referenced it, although there seem to have been quite a few updates since then. Unfortunately it looks like the corresponding commit 59cdf90 which explicitly mentions LWG 2157 was not linked with that issue in the migration to GitHub? See also microsoft/STL#2296 for their discussion. |
it seems like adding a zero length array gets us semantics similar to glibc. The Edit: actually no, this is different in some corner cases. |
This patch is also relevant: 7265ff9
However, while this patch moved |
Actually, it looks like the following code works just fine in C++20 mode:
It only fails in C++11, C++14 and C++17. https://godbolt.org/z/3nq8MTax5 It also seems like libstdc++ and libc++ agree exactly on when it should work vs not work, and that what we implement is the resolution of LWG2157 (which wasn't officially voted into the standard, though). I'm tempted to say that this behaves correctly. The only thing we could potentially do is reduce the size of |
I am going to tentatively close this as NTBF to keep the issues list tidy and make sure this doesn't stay open forever, but if you think this should stay open, let's discuss (and reopen). |
I am curious why std::array<T, 0>::begin() needs to return nullptr, is it because we cannot reinterpret cast in a constexpr function? I have a fix for the original complaint. An array of _EmptyAggrgate can be used to maintain ABI compatibility while allowing default construction. |
Yes, exactly.
Can you expand on the fix you suggest? I'm not sure I see what you mean. Regardless I think it's worth keeping this open since I had misunderstood the original report. It looks like while
works, the original report was about |
I am not convinced this is a problem. |
Drafted a pull request, #74657 This doesn't have an iterator fix though, which I'd like to fix as well while I'm at it. |
I'm adding "_EmptyArrayIterator" which to the best of my knowledge, satisfies the C++ standard requirements. For some compatibility with code of the form:
I am making It is possible this could trigger an API break, since the return type of |
It looks like we raced on this issue. I also have a different PR -- I'll open it and we can take a look at both and decide what to do. I have to go now but I'll take a look in the morning. |
This patch fixes constexpr default initialization of empty arrays and improves the tests accordingly. Fixes llvm#74375
I have used a similar fix, but I also added an ABI change (to make the size 1 byte on the unstable abi). However, I'm not convinced that |
I sent some messages to std-proposals, but it doesn't look like the standard specifies whether or not they are allowed. I suggest we follow glibc and allow them though. |
It turns out this doesn't work either. I think I agree... the standard behavior is not possible to implement. Somewhere along the line (I think C++20? didn't check) they added a requirement that array<T, N>::iterator is a contiguous iterator, which obviously isn't possible using a custom iterator class. It could be done with a compiler builtin, but there's no way to do it with generic syntax. |
Going back to
The unique value mandates that two distinct array objects return different values for begin, even when empty. Why did we change that? I need to better understand that to consider what to best do here. |
Sorry, I should have read further. The context regarding However, what I don't get is this. The standard is VERY EXPLICIT about the It seems to me we would implement the behavior most clearly specified, and then ask the standard to figure its shit out. It seems like implementations here are papering over an unimplementable specification to the pain of all of our users. |
I think we need to add a compiler builtin to get that to work as written. However, I think it's better for someone to pursue this as a defect report in the standard. |
Correction, I think I might have found a solution to maintain standard conformance. It requires a bit of hackery since the C++11 and C++14+ solutions aren't compatible. (I realized, I can just reinterpret cast in C++11 since begin isn't required to be constexpr there) I'll update my patch with this experimental solution idea and see if I can get it to pass the existing tests maybe. |
Okay, my fix should theoretically work, but it doesn't work because it's blocked by this bug: #56814 |
This patch fixes constexpr default initialization of empty arrays and improves the tests accordingly. Fixes #74375
This fails to compile with libc++ (https://godbolt.org/z/drcGsW84x), since its zero-length
std::array
specialization has some dummy data (see #34839) that is left uninitialized.This is quite unfortunate for a case like
which will fail to compile only when used in a
constexpr
context withN = 0
(https://godbolt.org/z/jxMGx1941). Usingstd::array<int, N> a{};
fixes that, but it may also hide programming errors (i.e. missed elements) in the following actually intended initialization code forN > 0
that would otherwise be diagnosed.Microsoft's STL was in a very similar situation and fixed this just recently (see microsoft/STL#3863), so I was hoping that maybe libc++ might want to follow. Seems like for libstdc++ this was never a problem since their
std::array<T, 0>
is actually empty.The text was updated successfully, but these errors were encountered: