Details
- Reviewers
Mordante • Quuxplusone ldionne - Group Reviewers
Restricted Project - Commits
- rG312ad74aea48: [libc++] Implement P1951, default arguments for pair's forwarding constructor
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
LGTM, provided it passes the build. Please update the modification date in libcxx/docs/Status/Cxx2b.rst, which I usually also forget about ;-)
There was an issue with Buildkite, can you reupload the diff to start the build again?
libcxx/test/std/utilities/utility/pairs/pairs.pair/U_V.pass.cpp | ||
---|---|---|
124 | I'd like to see some tests for:
struct ExplicitBraceT { explicit ExplicitBraceT(); }; void f(std::pair<ExplicitBraceT, ExplicitBraceT>); // #1 void f(std::pair<BraceInit, BraceInit>); // #2 int main() { f({{}, {}}); } // unambiguously calls #2, right? Btw, there's precedent for this kind of "in standards greater than X, or _LIBCPP_VERSION" test, here: libcxx/test/std/utilities/utility/forward/{move,forward}.pass.cpp |
Please update the modification date in libcxx/docs/Status/Cxx2b.rst, which I usually also forget about ;-)
Is it really relevant to provide a modification date at all? I mean, it doesn't really add anything to the documentation IMO, and it's an obvious trap to forget to update it. IMO we should just remove those. Let's move this to D109087.
libcxx/test/std/utilities/utility/pairs/pairs.pair/U_V.pass.cpp | ||
---|---|---|
124 |
Done.
Done.
Added.
Done.
I much prefer the way I've done it here - unless I misunderstand something, in libcxx/test/std/utilities/utility/forward/forward.pass.cpp, they duplicate the testing code just to account for the special case C++ == 11 && libc++. |
Looks like some GCC/Clang implementation divergence in re explicit_vs_implicit_brace_init({{}, {}}). I don't know who's correct.
Anyway, LGTM.
(Mild surprise that we're backporting this still-only-tentatively-C++23 change all the way back to C++11, but I have no better suggestion.)
libcxx/test/std/utilities/utility/pairs/pairs.pair/U_V.pass.cpp | ||
---|---|---|
120 | (void)p; on its own line? |
Thanks. I'll look into the failure, I'll probably need to file a bug report and see who's right.
(Mild surprise that we're backporting this still-only-tentatively-C++23 change all the way back to C++11, but I have no better suggestion.)
Yeah.. I thought about doing it strictly like I always fight for, however in this case this seems to be closer to a DR than anything else. I mean the code previously would work just as well, but it would make a copy instead by using the pair(T1 const&, T2 const&) ctor. So IMO this is in essence closer to a LWG issue being fixed than an actual feature. If someone disagrees, LMK and I'll look into enabling this only in C++23, like we do for features/papers.
Actually I was surprised it wasn't a LWG issue. I didn't question the back-porting since I agree this is a good exception of our rule.
I mean the code previously would work just as well, but it would make a copy instead by using the pair(T1 const&, T2 const&) ctor.
Well, now I'm even less sure I understand the procedural issues here. Surely if std::pair<T,U>({}, {}) was supposed to compile in C++20, then we should have some tests to prove that it compiles in C++20. It seems like those tests are the ones you're adding in U_V.pass.cpp; maybe we had no such tests before? But we're not running those tests pre-C++23 (except on libc++), so we still have no tests that test the expected C++20-and-earlier behavior? But on libc++ we don't even expect the expected behavior, anyway? 😛
Hmm, indeed, your point of view makes sense. Since the brace-init bits should always have worked, I added tests for those in all standard modes (except C++03, but whatever). Then, I added tests for just https://wg21.link/P1951 in C++23 and above (and on libc++ in all std modes as an extension).
Regarding the GCC issue, I filed a bug here: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=102247. I'll mark the test as XFAIL on GCC for now, and I'll revisit the test once I have more information.
This is a breaking change. For example, this is valid code in C++20 and earlier but is now rejected by libc++:
void f(const std::pair<const std::string&, std::vector<int>> x) {} void g() { f({"foo", {1, 2}}); }
The reason is that we used to call the pair(const string&, const vector<int>&) constructor (the converting constructor is not usable because we can't deduce from an initializer list), which created the string temporary as part of the call to f. We now call the pair(U&&, V&&) constructor, which creates a string temporary inside the pair constructor, and that temporary doesn't live long enough any more.
For what it's worth, I don't think it's super important to support this code: it's relying on a very fragile selection of pair constructor, and I think it's working by chance rather than by design, but this is reduced from real, valid code whose build broke after this patch landed. For the sake of conformance, perhaps we should do one of these things:
- Get this change officially blessed as a DR, or
- Add a !__reference_binds_to_temporary SFINAE check to the converting constructor in at least C++20 and before, or
- Restrict this change to C++23 and later.
Another example of valid C++20-and-before code (reduced from real code) that libc++ now rejects after this change:
void f() { const int n = 5; [] { std::pair<int, long>({1}, n); }; }
Here, n doesn't need to be captured if we call the pair(const int&, const long&) constructor, because the lvalue-to-rvalue conversion happens in the lambda. But if we call the pair(U&&, V&&) constructor (deducing V = int), then n does need to be captured and the build breaks.
I'm addressing this issue in https://reviews.llvm.org/D110347, see that review for details.
(void)p; on its own line?