Introduce __fits_in_sso() to put the constexpr tests into a central place.
Details
- Reviewers
• Quuxplusone ldionne Mordante - Group Reviewers
Restricted Project - Commits
- rGfcfc0e7ad34c: [libc++] Introduce __fits_in_sso()
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
libcxx/include/string | ||
---|---|---|
1469 | IIUC, I think this should be named __is_less_than_sso_capacity(x), or at least __fits_in_sso(x). __use_sso reads like an imperative instruction, but what this is is a query. Also, the commit message (PR summary) should explain where we're going with this. Are we going to change the body of this function to return (!__libcpp_is_constant_evaluated()) && (__sz < __min_cap); ? If so, then you should preemptively put this function's body on its own line; otherwise you're just making the next diff more complicated. We want the next diff to be as simple as _LIBCPP_HIDE_FROM_ABI static bool __use_sso(size_type __sz) { - return __sz < __min_cap; + return (!__libcpp_is_constant_evaluated()) && (__sz < __min_cap); } | |
2358–2360 | Here and line 2606, I think your change is correct, but it feels really subtle and someone-not-me should look really closely and see if they agree this is right. if (!__libcpp_is_constant_evaluated() && __builtin_constant_p(__n) && __n < __min_cap) which is equivalent to what you wrote. But I'd like the next reviewer to also check and see if they agree. |
libcxx/include/string | ||
---|---|---|
1469 | How about directly making the function constexpr and implement the final version? That avoids writing a commit message and modifying the code later. |
Are there any tests that need to be modified? I would assume that tests that check for reallocation when a string is assigned-to (or similar stuff) will need to be conditionalized on whether the test is running inside a constexpr context too, right?
Finally, and most importantly -- is this implementation strategy really valid? I had thought about this when I originally wrote the constexpr string paper, and IIRC one of the issues was this:
constexpr std::string s = "short"; // Anything short enough to normally use the SSO. Since this is inside constexpr, though, SSO isn't used. void use(std::string const& str) { // ... } int main() { use(s); }
The problem here is that when s is constructed, it is a constexpr context so we don't use the SSO. When we actually *use* s, though, it's not a constexpr context so if we check s's size and ask whether it is using SSO, we'll believe that yes, we are using it (because it would normally fit in the small buffer).
Is this not an issue somehow? It's been a while since I've thought about this.
Edit: Well, in the current state of things, this can't be an issue because __fits_in_sso is only used from code paths that mutate the string, and those code paths can't be called at runtime on a constexpr-created std::string. So this at least does seem correct to me in its current state. However, if we ever queried __fits_in_sso for a read-only operation, it would have the possibility to lie if called on a std::string created with a different constexpr-ness than the context in which __fits_in_sso is called. I think this merits at least a comment.
libcxx/include/string | ||
---|---|---|
1470 | Can you add a comment saying something like "We never use the SSO in constexpr contexts because it requires some amount of reinterpret-casting, which is not constexpr-friendly"? Otherwise, one would rightly wonder why a function named __fits_in_sso would return something different based on whether it is called in a constexpr context or not. |
You have to deallocate anything that you allocated during constant evaluation. So using a constexpr string in a non-constexpr context is illegal IIUC. https://godbolt.org/z/KKv3YnP4a
Right, it looks like I got confused with promotion to static storage, which was pulled from the paper IIRC.
So I think this works, however if/when we allow something like constexpr std::string s = "hello"; at file scope (which entails that we'll do some sort of promotion of the constexpr-allocated memory to static storage), we'll have to tackle this problem cause the possibility of __fits_in_sso() lying is going to become real. For the time being, no problem though.
This is not a NFC -- the commit shouldn't say NFC.
libcxx/include/string | ||
---|---|---|
2358–2360 | That sounds right to me. |
IIUC, I think this should be named __is_less_than_sso_capacity(x), or at least __fits_in_sso(x). __use_sso reads like an imperative instruction, but what this is is a query.
Also, the commit message (PR summary) should explain where we're going with this. Are we going to change the body of this function to
? If so, then you should preemptively put this function's body on its own line; otherwise you're just making the next diff more complicated. We want the next diff to be as simple as